You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2006/07/12 15:33:27 UTC
svn commit: r421270 [15/23] - in /jackrabbit/trunk/contrib/spi: ./ commons/
commons/src/ commons/src/main/ commons/src/main/java/
commons/src/main/java/org/ commons/src/main/java/org/apache/
commons/src/main/java/org/apache/jackrabbit/ commons/src/main...
Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,1394 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.jcr2spi.state;
+
+import org.apache.commons.collections.MapIterator;
+import org.apache.commons.collections.OrderedMapIterator;
+import org.apache.commons.collections.map.LinkedMap;
+import org.apache.jackrabbit.util.WeakIdentityCollection;
+import org.apache.jackrabbit.spi.IdFactory;
+import org.apache.jackrabbit.spi.QNodeDefinition;
+import org.apache.jackrabbit.name.Path;
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.PropertyId;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Set;
+
+/**
+ * <code>NodeState</code> represents the state of a <code>Node</code>.
+ */
+public class NodeState extends ItemState {
+
+ private static Logger log = LoggerFactory.getLogger(NodeState.class);
+
+ /**
+ * the name of this node's primary type
+ */
+ private QName nodeTypeName;
+
+ /**
+ * the names of this node's mixin types
+ */
+ private QName[] mixinTypeNames = new QName[0];
+
+ /**
+ * The id of this node state.
+ */
+ private NodeId id;
+
+ /**
+ * The id of the parent <code>NodeState</code> or <code>null</code> if this
+ * instance represents the root node.
+ */
+ private NodeId parentId;
+
+ /**
+ * this node's definition
+ */
+ private QNodeDefinition def;
+
+ /**
+ * insertion-ordered collection of ChildNodeEntry objects
+ */
+ private ChildNodeEntries childNodeEntries = new ChildNodeEntries();
+
+ /**
+ * Set to <code>true</code> if {@link #childNodeEntries} are shared between
+ * different <code>NodeState</code> instance.
+ */
+ private boolean sharedChildNodeEntries = false;
+
+ /**
+ * set of property names (QName objects)
+ */
+ private HashSet propertyNames = new HashSet();
+
+ /**
+ * Set to <code>true</code> if {@link #propertyNames} is shared between
+ * different <code>NodeState</code> instances.
+ */
+ private boolean sharedPropertyNames = false;
+
+ /**
+ * Listeners (weak references)
+ */
+ private final transient Collection listeners = new WeakIdentityCollection(3);
+
+ // DIFF JR: limit creation of property-ids to the nodeState
+ // TODO: check again....
+ private final IdFactory idFactory;
+
+ /**
+ * Constructs a new <code>NodeState</code> that is initially connected to
+ * an overlayed state.
+ *
+ * @param overlayedState the backing node state being overlayed
+ * @param initialStatus the initial status of the node state object
+ * @param isTransient flag indicating whether this state is transient or not
+ */
+ public NodeState(NodeState overlayedState, int initialStatus,
+ boolean isTransient) {
+ super(overlayedState, initialStatus, isTransient);
+ pull();
+ idFactory = overlayedState.idFactory;
+ }
+
+ /**
+ * Constructs a new node state that is not connected.
+ *
+ * @param id id of this node
+ * @param nodeTypeName node type of this node
+ * @param parentId id of the parent node
+ * @param initialStatus the initial status of the node state object
+ * @param isTransient flag indicating whether this state is transient or not
+ */
+ public NodeState(NodeId id, QName nodeTypeName, NodeId parentId,
+ int initialStatus, boolean isTransient, IdFactory idFactory) {
+ super(initialStatus, isTransient);
+ this.id = id;
+ this.parentId = parentId;
+ this.nodeTypeName = nodeTypeName;
+ this.idFactory = idFactory;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected synchronized void copy(ItemState state) {
+ synchronized (state) {
+ NodeState nodeState = (NodeState) state;
+ id = nodeState.id;
+ parentId = nodeState.parentId;
+ nodeTypeName = nodeState.nodeTypeName;
+ mixinTypeNames = nodeState.mixinTypeNames;
+ def = nodeState.getDefinition();
+ propertyNames = nodeState.propertyNames;
+ sharedPropertyNames = true;
+ nodeState.sharedPropertyNames = true;
+ childNodeEntries = nodeState.childNodeEntries;
+ sharedChildNodeEntries = true;
+ nodeState.sharedChildNodeEntries = true;
+ }
+ }
+
+ //-----------------------------------------------------< public methods >---
+ /**
+ * Determines if this item state represents a node.
+ *
+ * @return always true
+ * @see ItemState#isNode
+ */
+ public final boolean isNode() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public NodeId getParentId() {
+ return parentId;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ItemId getId() {
+ return id;
+ }
+
+ /**
+ * Returns the id of this node state.
+ * @return the id of this node state.
+ */
+ public NodeId getNodeId() {
+ return id;
+ }
+
+ /**
+ * Sets the Id of the parent <code>NodeState</code>.
+ *
+ * @param parentId the parent <code>NodeState</code>'s Id or <code>null</code>
+ * if either this node state should represent the root node or this node
+ * state should be 'free floating', i.e. detached from the repository's
+ * hierarchy.
+ */
+ public void setParentId(NodeId parentId) {
+ this.parentId = parentId;
+ }
+
+ /**
+ * Returns the name of this node's node type.
+ *
+ * @return the name of this node's node type.
+ */
+ public QName getNodeTypeName() {
+ return nodeTypeName;
+ }
+
+ /**
+ * Returns the names of this node's mixin types.
+ *
+ * @return a set of the names of this node's mixin types.
+ */
+ public synchronized QName[] getMixinTypeNames() {
+ return mixinTypeNames;
+ }
+
+ /**
+ * Sets the names of this node's mixin types.
+ *
+ * @param mixinTypeNames set of names of mixin types
+ */
+ public synchronized void setMixinTypeNames(QName[] mixinTypeNames) {
+ if (mixinTypeNames != null) {
+ this.mixinTypeNames = mixinTypeNames;
+ } else {
+ this.mixinTypeNames = new QName[0];
+ }
+ }
+
+ /**
+ * Return all nodetype names that apply to this <code>NodeState</code>
+ * including the primary nodetype and the mixins.
+ *
+ * @return
+ */
+ public synchronized QName[] getNodeTypeNames() {
+ // mixin types
+ QName[] types = new QName[mixinTypeNames.length + 1];
+ System.arraycopy(mixinTypeNames, 0, types, 0, mixinTypeNames.length);
+ // primary type
+ types[types.length - 1] = getNodeTypeName();
+ return types;
+ }
+
+ /**
+ * Returns the id of the definition applicable to this node state.
+ *
+ * @return the id of the definition
+ */
+ public QNodeDefinition getDefinition() {
+ return def;
+ }
+
+ /**
+ * Sets the id of the definition applicable to this node state.
+ *
+ * @param def the definition
+ */
+ public void setDefinition(QNodeDefinition def) {
+ this.def = def;
+ }
+
+ /**
+ * Determines if there are any child node entries.
+ *
+ * @return <code>true</code> if there are child node entries,
+ * <code>false</code> otherwise.
+ */
+ public boolean hasChildNodeEntries() {
+ return !childNodeEntries.isEmpty();
+ }
+
+ /**
+ * Determines if there is a <code>ChildNodeEntry</code> with the
+ * specified <code>name</code>.
+ *
+ * @param name <code>QName</code> object specifying a node name
+ * @return <code>true</code> if there is a <code>ChildNodeEntry</code> with
+ * the specified <code>name</code>.
+ */
+ public synchronized boolean hasChildNodeEntry(QName name) {
+ return !childNodeEntries.get(name).isEmpty();
+ }
+
+ /**
+ * Determines if there is a <code>ChildNodeEntry</code> with the
+ * specified <code>NodeId</code>.
+ *
+ * @param id the id of the child node
+ * @return <code>true</code> if there is a <code>ChildNodeEntry</code> with
+ * the specified <code>name</code>.
+ */
+ public synchronized boolean hasChildNodeEntry(NodeId id) {
+ return childNodeEntries.get(id) != null;
+ }
+
+ /**
+ * Determines if there is a <code>ChildNodeEntry</code> with the
+ * specified <code>name</code> and <code>index</code>.
+ *
+ * @param name <code>QName</code> object specifying a node name
+ * @param index 1-based index if there are same-name child node entries
+ * @return <code>true</code> if there is a <code>ChildNodeEntry</code> with
+ * the specified <code>name</code> and <code>index</code>.
+ */
+ public synchronized boolean hasChildNodeEntry(QName name, int index) {
+ return childNodeEntries.get(name, index) != null;
+ }
+
+ /**
+ * Determines if there is a property entry with the specified
+ * <code>QName</code>.
+ *
+ * @param propName <code>QName</code> object specifying a property name
+ * @return <code>true</code> if there is a property entry with the specified
+ * <code>QName</code>.
+ */
+ public synchronized boolean hasPropertyName(QName propName) {
+ return propertyNames.contains(propName);
+ }
+
+ /**
+ * Return the id of the Property with the specified name or <code>null</code>
+ * if no property exists with the given name.
+ *
+ * @param propName
+ * @return
+ */
+ public synchronized PropertyId getPropertyId(QName propName) {
+ if (hasPropertyName(propName)) {
+ return idFactory.createPropertyId(id, propName);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the <code>ChildNodeEntry</code> with the specified name and index
+ * or <code>null</code> if there's no matching entry.
+ *
+ * @param nodeName <code>QName</code> object specifying a node name
+ * @param index 1-based index if there are same-name child node entries
+ * @return the <code>ChildNodeEntry</code> with the specified name and index
+ * or <code>null</code> if there's no matching entry.
+ */
+ public synchronized ChildNodeEntry getChildNodeEntry(QName nodeName, int index) {
+ return childNodeEntries.get(nodeName, index);
+ }
+
+ /**
+ * Returns the <code>ChildNodeEntry</code> with the specified <code>NodeId</code> or
+ * <code>null</code> if there's no matching entry.
+ *
+ * @param id the id of the child node
+ * @return the <code>ChildNodeEntry</code> with the specified <code>NodeId</code> or
+ * <code>null</code> if there's no matching entry.
+ * @see #addChildNodeEntry
+ * @see #removeChildNodeEntry
+ */
+ public synchronized ChildNodeEntry getChildNodeEntry(NodeId id) {
+ return childNodeEntries.get(id);
+ }
+
+ /**
+ * Returns a list of <code>ChildNodeEntry</code> objects denoting the
+ * child nodes of this node.
+ *
+ * @return list of <code>ChildNodeEntry</code> objects
+ * @see #addChildNodeEntry
+ * @see #removeChildNodeEntry
+ */
+ public synchronized List getChildNodeEntries() {
+ return childNodeEntries;
+ }
+
+ /**
+ * Returns a list of <code>ChildNodeEntry</code>s with the specified name.
+ *
+ * @param nodeName name of the child node entries that should be returned
+ * @return list of <code>ChildNodeEntry</code> objects
+ * @see #addChildNodeEntry
+ * @see #removeChildNodeEntry
+ */
+ public synchronized List getChildNodeEntries(QName nodeName) {
+ return childNodeEntries.get(nodeName);
+ }
+
+ /**
+ * Adds a new <code>ChildNodeEntry</code>.
+ *
+ * @param nodeName <code>QName</code> object specifying the name of the new entry.
+ * @param id the id the new entry is refering to.
+ * @return the newly added <code>ChildNodeEntry</code>
+ */
+ public synchronized ChildNodeEntry addChildNodeEntry(QName nodeName,
+ NodeId id) {
+ if (sharedChildNodeEntries) {
+ childNodeEntries = (ChildNodeEntries) childNodeEntries.clone();
+ sharedChildNodeEntries = false;
+ }
+ ChildNodeEntry entry = childNodeEntries.add(nodeName, id);
+ notifyNodeAdded(entry);
+ return entry;
+ }
+
+ /**
+ * Renames a new <code>ChildNodeEntry</code>.
+ *
+ * @param oldName <code>QName</code> object specifying the entry's old name
+ * @param index 1-based index if there are same-name child node entries
+ * @param newName <code>QName</code> object specifying the entry's new name
+ * @return <code>true</code> if the entry was sucessfully renamed;
+ * otherwise <code>false</code>
+ */
+ public synchronized boolean renameChildNodeEntry(QName oldName, int index,
+ QName newName) {
+ if (sharedChildNodeEntries) {
+ childNodeEntries = (ChildNodeEntries) childNodeEntries.clone();
+ sharedChildNodeEntries = false;
+ }
+ ChildNodeEntry oldEntry = childNodeEntries.remove(oldName, index);
+ if (oldEntry != null) {
+ ChildNodeEntry newEntry =
+ childNodeEntries.add(newName, oldEntry.getId());
+ notifyNodeAdded(newEntry);
+ notifyNodeRemoved(oldEntry);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Removes a <code>ChildNodeEntry</code>.
+ *
+ * @param nodeName <code>ChildNodeEntry</code> object specifying a node name
+ * @param index 1-based index if there are same-name child node entries
+ * @return <code>true</code> if the specified child node entry was found
+ * in the list of child node entries and could be removed.
+ */
+ public synchronized boolean removeChildNodeEntry(QName nodeName, int index) {
+ if (sharedChildNodeEntries) {
+ childNodeEntries = (ChildNodeEntries) childNodeEntries.clone();
+ sharedChildNodeEntries = false;
+ }
+ ChildNodeEntry entry = childNodeEntries.remove(nodeName, index);
+ if (entry != null) {
+ notifyNodeRemoved(entry);
+ }
+ return entry != null;
+ }
+
+ /**
+ * Removes a <code>ChildNodeEntry</code>.
+ *
+ * @param id the id of the entry to be removed
+ * @return <code>true</code> if the specified child node entry was found
+ * in the list of child node entries and could be removed.
+ */
+ public synchronized boolean removeChildNodeEntry(NodeId id) {
+ if (sharedChildNodeEntries) {
+ childNodeEntries = (ChildNodeEntries) childNodeEntries.clone();
+ sharedChildNodeEntries = false;
+ }
+ ChildNodeEntry entry = childNodeEntries.remove(id);
+ if (entry != null) {
+ notifyNodeRemoved(entry);
+ }
+ return entry != null;
+ }
+
+ /**
+ * Removes all <code>ChildNodeEntry</code>s.
+ */
+ public synchronized void removeAllChildNodeEntries() {
+ if (sharedChildNodeEntries) {
+ childNodeEntries = (ChildNodeEntries) childNodeEntries.clone();
+ sharedChildNodeEntries = false;
+ }
+ childNodeEntries.removeAll();
+ }
+
+ /**
+ * Sets the list of <code>ChildNodeEntry</code> objects denoting the
+ * child nodes of this node.
+ */
+ public synchronized void setChildNodeEntries(List nodeEntries) {
+ if (nodeEntries instanceof ChildNodeEntries) {
+ // optimization
+ ChildNodeEntries entries = (ChildNodeEntries) nodeEntries;
+ childNodeEntries = (ChildNodeEntries) entries.clone();
+ sharedChildNodeEntries = false;
+ } else {
+ if (sharedChildNodeEntries) {
+ childNodeEntries = new ChildNodeEntries();
+ sharedChildNodeEntries = false;
+ } else {
+ childNodeEntries.removeAll();
+ }
+ childNodeEntries.addAll(nodeEntries);
+
+ }
+ notifyNodesReplaced();
+ }
+
+ /**
+ * Returns the names of this node's properties as a set of
+ * <code>QNames</code> objects.
+ *
+ * @return set of <code>QNames</code> objects
+ * @see #addPropertyName
+ * @see #removePropertyName
+ */
+ public synchronized Set getPropertyNames() {
+ return Collections.unmodifiableSet(propertyNames);
+ }
+
+ /**
+ * Adds a property name entry.
+ *
+ * @param propName <code>QName</code> object specifying the property name
+ */
+ public synchronized void addPropertyName(QName propName) {
+ if (sharedPropertyNames) {
+ propertyNames = (HashSet) propertyNames.clone();
+ sharedPropertyNames = false;
+ }
+ propertyNames.add(propName);
+ }
+
+ /**
+ * Removes a property name entry.
+ *
+ * @param propName <code>QName</code> object specifying the property name
+ * @return <code>true</code> if the specified property name was found
+ * in the list of property name entries and could be removed.
+ */
+ public synchronized boolean removePropertyName(QName propName) {
+ if (sharedPropertyNames) {
+ propertyNames = (HashSet) propertyNames.clone();
+ sharedPropertyNames = false;
+ }
+ return propertyNames.remove(propName);
+ }
+
+ /**
+ * Removes all property name entries.
+ */
+ public synchronized void removeAllPropertyNames() {
+ if (sharedPropertyNames) {
+ propertyNames = new HashSet();
+ sharedPropertyNames = false;
+ } else {
+ propertyNames.clear();
+ }
+ }
+
+ /**
+ * Sets the set of <code>QName</code> objects denoting the
+ * properties of this node.
+ */
+ public synchronized void setPropertyNames(Set propNames) {
+ if (propNames instanceof HashSet) {
+ HashSet names = (HashSet) propNames;
+ propertyNames = (HashSet) names.clone();
+ sharedPropertyNames = false;
+ } else {
+ if (sharedPropertyNames) {
+ propertyNames = new HashSet();
+ sharedPropertyNames = false;
+ } else {
+ propertyNames.clear();
+ }
+ propertyNames.addAll(propNames);
+ }
+ }
+
+ /**
+ * Set the node type name. Needed for deserialization and should therefore
+ * not change the internal status.
+ *
+ * @param nodeTypeName node type name
+ */
+ public synchronized void setNodeTypeName(QName nodeTypeName) {
+ this.nodeTypeName = nodeTypeName;
+ }
+
+ //---------------------------------------------------------< diff methods >
+ /**
+ * Returns a set of <code>QName</code>s denoting those properties that
+ * do not exist in the overlayed node state but have been added to
+ * <i>this</i> node state.
+ *
+ * @return set of <code>QName</code>s denoting the properties that have
+ * been added.
+ */
+ public synchronized Set getAddedPropertyNames() {
+ if (!hasOverlayedState()) {
+ return Collections.unmodifiableSet(propertyNames);
+ }
+
+ NodeState other = (NodeState) getOverlayedState();
+ HashSet set = new HashSet(propertyNames);
+ set.removeAll(other.propertyNames);
+ return set;
+ }
+
+ /**
+ * Returns a list of child node entries that do not exist in the overlayed
+ * node state but have been added to <i>this</i> node state.
+ *
+ * @return list of added child node entries
+ */
+ public synchronized List getAddedChildNodeEntries() {
+ if (!hasOverlayedState()) {
+ return childNodeEntries;
+ }
+
+ NodeState other = (NodeState) getOverlayedState();
+ return childNodeEntries.removeAll(other.childNodeEntries);
+ }
+
+ /**
+ * Returns a set of <code>QName</code>s denoting those properties that
+ * exist in the overlayed node state but have been removed from
+ * <i>this</i> node state.
+ *
+ * @return set of <code>QName</code>s denoting the properties that have
+ * been removed.
+ */
+ public synchronized Set getRemovedPropertyNames() {
+ if (!hasOverlayedState()) {
+ return Collections.EMPTY_SET;
+ }
+
+ NodeState other = (NodeState) getOverlayedState();
+ HashSet set = new HashSet(other.propertyNames);
+ set.removeAll(propertyNames);
+ return set;
+ }
+
+ /**
+ * Returns a list of child node entries, that exist in the overlayed node state
+ * but have been removed from <i>this</i> node state.
+ *
+ * @return list of removed child node entries
+ */
+ public synchronized List getRemovedChildNodeEntries() {
+ if (!hasOverlayedState()) {
+ return Collections.EMPTY_LIST;
+ }
+
+ NodeState other = (NodeState) getOverlayedState();
+ return other.childNodeEntries.removeAll(childNodeEntries);
+ }
+
+ /**
+ * Returns a list of child node entries that exist both in <i>this</i> node
+ * state and in the overlayed node state but have been reordered.
+ * <p/>
+ * The list may include only the minimal set of nodes that have been
+ * reordered. That is, even though a certain number of nodes have changed
+ * their absolute position the list may include less that this number of
+ * nodes.
+ * <p/>
+ * Example:<br/>
+ * Initial state:
+ * <pre>
+ * + node1
+ * + node2
+ * + node3
+ * </pre>
+ * After reorder:
+ * <pre>
+ * + node2
+ * + node3
+ * + node1
+ * </pre>
+ * All nodes have changed their absolute position. The returned list however
+ * may only return that <code>node1</code> has been reordered (from the
+ * first position to the end).
+ *
+ * @return list of reordered child node enties.
+ */
+ public synchronized List getReorderedChildNodeEntries() {
+ if (!hasOverlayedState()) {
+ return Collections.EMPTY_LIST;
+ }
+
+ ChildNodeEntries otherChildNodeEntries =
+ ((NodeState) overlayedState).childNodeEntries;
+
+ if (childNodeEntries.isEmpty()
+ || otherChildNodeEntries.isEmpty()) {
+ return Collections.EMPTY_LIST;
+ }
+
+ // build intersections of both collections,
+ // each preserving their relative order
+ List ours = childNodeEntries.retainAll(otherChildNodeEntries);
+ List others = otherChildNodeEntries.retainAll(childNodeEntries);
+
+ // do a lazy init
+ List reordered = null;
+ // both entry lists now contain the set of nodes that have not
+ // been removed or added, but they may have changed their position.
+ for (int i = 0; i < ours.size();) {
+ ChildNodeEntry entry = (ChildNodeEntry) ours.get(i);
+ ChildNodeEntry other = (ChildNodeEntry) others.get(i);
+ if (entry == other || entry.getId().equals(other.getId())) {
+ // no reorder, move to next child entry
+ i++;
+ } else {
+ // reordered entry detected
+ if (reordered == null) {
+ reordered = new ArrayList();
+ }
+ // Note that this check will not necessarily find the
+ // minimal reorder operations required to convert the overlayed
+ // child node entries into the current.
+
+ // is there a next entry?
+ if (i + 1 < ours.size()) {
+ // if entry is the next in the other list then probably
+ // the other entry at position <code>i</code> was reordered
+ if (entry.getId().equals(((ChildNodeEntry) others.get(i + 1)).getId())) {
+ // scan for the uuid of the other entry in our list
+ for (int j = i; j < ours.size(); j++) {
+ if (((ChildNodeEntry) ours.get(j)).getId().equals(other.getId())) {
+ // found it
+ entry = (ChildNodeEntry) ours.get(j);
+ break;
+ }
+ }
+ }
+ }
+
+ reordered.add(entry);
+ // remove the entry from both lists
+ // entries > i are already cleaned
+ for (int j = i; j < ours.size(); j++) {
+ if (((ChildNodeEntry) ours.get(j)).getId().equals(entry.getId())) {
+ ours.remove(j);
+ }
+ }
+ for (int j = i; j < ours.size(); j++) {
+ if (((ChildNodeEntry) others.get(j)).getId().equals(entry.getId())) {
+ others.remove(j);
+ }
+ }
+ // if a reorder has been detected index <code>i</code> is not
+ // incremented because entries will be shifted when the
+ // reordered entry is removed.
+ }
+ }
+ if (reordered == null) {
+ return Collections.EMPTY_LIST;
+ } else {
+ return reordered;
+ }
+ }
+
+ //--------------------------------------------------< ItemState overrides >
+ /**
+ * {@inheritDoc}
+ * <p/>
+ * If the listener passed is at the same time a <code>NodeStateListener</code>
+ * we add it to our list of specialized listeners.
+ */
+ public void addListener(ItemStateListener listener) {
+ if (listener instanceof NodeStateListener) {
+ synchronized (listeners) {
+ if (listeners.contains(listener)) {
+ log.debug("listener already registered: " + listener);
+ // no need to add to call ItemState.addListener()
+ return;
+ } else {
+ listeners.add(listener);
+ }
+ }
+ }
+ super.addListener(listener);
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p/>
+ * If the listener passed is at the same time a <code>NodeStateListener</code>
+ * we remove it from our list of specialized listeners.
+ */
+ public void removeListener(ItemStateListener listener) {
+ if (listener instanceof NodeStateListener) {
+ synchronized (listeners) {
+ listeners.remove(listener);
+ }
+ }
+ super.removeListener(listener);
+ }
+
+ //-------------------------------------------------< misc. helper methods >
+ /**
+ * Notify the listeners that a child node entry has been added
+ */
+ protected void notifyNodeAdded(ChildNodeEntry added) {
+ synchronized (listeners) {
+ Iterator iter = listeners.iterator();
+ while (iter.hasNext()) {
+ NodeStateListener l = (NodeStateListener) iter.next();
+ if (l != null) {
+ l.nodeAdded(this, added.getName(), added.getIndex(), added.getId());
+ }
+ }
+ }
+ }
+
+ /**
+ * Notify the listeners that the child node entries have been replaced
+ */
+ protected void notifyNodesReplaced() {
+ synchronized (listeners) {
+ Iterator iter = listeners.iterator();
+ while (iter.hasNext()) {
+ NodeStateListener l = (NodeStateListener) iter.next();
+ if (l != null) {
+ l.nodesReplaced(this);
+ }
+ }
+ }
+ }
+
+ /**
+ * Notify the listeners that a child node entry has been removed
+ */
+ protected void notifyNodeRemoved(ChildNodeEntry removed) {
+ synchronized (listeners) {
+ Iterator iter = listeners.iterator();
+ while (iter.hasNext()) {
+ NodeStateListener l = (NodeStateListener) iter.next();
+ if (l != null) {
+ l.nodeRemoved(this, removed.getName(),
+ removed.getIndex(), removed.getId());
+ }
+ }
+ }
+ }
+
+ //--------------------------------------------------------< inner classes >
+ /**
+ * <code>ChildNodeEntries</code> represents an insertion-ordered
+ * collection of <code>ChildNodeEntry</code>s that also maintains
+ * the index values of same-name siblings on insertion and removal.
+ * <p/>
+ * <code>ChildNodeEntries</code> also provides an unmodifiable
+ * <code>List</code> view.
+ */
+ private static class ChildNodeEntries implements List, Cloneable {
+
+ // insertion-ordered map of entries (key=NodeId, value=entry)
+ private LinkedMap entries;
+ // map used for lookup by name
+ // (key=name, value=either a single entry or a list of sns entries)
+ private HashMap nameMap;
+
+ ChildNodeEntries() {
+ entries = new LinkedMap();
+ nameMap = new HashMap();
+ }
+
+ ChildNodeEntry get(NodeId id) {
+ return (ChildNodeEntry) entries.get(id);
+ }
+
+ List get(QName nodeName) {
+ Object obj = nameMap.get(nodeName);
+ if (obj == null) {
+ return Collections.EMPTY_LIST;
+ }
+ if (obj instanceof ArrayList) {
+ // map entry is a list of siblings
+ return Collections.unmodifiableList((ArrayList) obj);
+ } else {
+ // map entry is a single child node entry
+ return Collections.singletonList(obj);
+ }
+ }
+
+ ChildNodeEntry get(QName nodeName, int index) {
+ if (index < Path.INDEX_DEFAULT) {
+ throw new IllegalArgumentException("index is 1-based");
+ }
+
+ Object obj = nameMap.get(nodeName);
+ if (obj == null) {
+ return null;
+ }
+ if (obj instanceof ArrayList) {
+ // map entry is a list of siblings
+ ArrayList siblings = (ArrayList) obj;
+ if (index <= siblings.size()) {
+ return (ChildNodeEntry) siblings.get(index - 1);
+ }
+ } else {
+ // map entry is a single child node entry
+ if (index == Path.INDEX_DEFAULT) {
+ return (ChildNodeEntry) obj;
+ }
+ }
+ return null;
+ }
+
+ ChildNodeEntry add(QName nodeName, NodeId id) {
+ List siblings = null;
+ int index = Path.INDEX_UNDEFINED;
+ Object obj = nameMap.get(nodeName);
+ if (obj != null) {
+ if (obj instanceof ArrayList) {
+ // map entry is a list of siblings
+ siblings = (ArrayList) obj;
+ } else {
+ // map entry is a single child node entry,
+ // convert to siblings list
+ siblings = new ArrayList();
+ siblings.add(obj);
+ nameMap.put(nodeName, siblings);
+ }
+ index = siblings.size();
+ }
+ index++;
+
+ ChildNodeEntry entry = new ChildNodeEntry(nodeName, id, index);
+ if (siblings != null) {
+ siblings.add(entry);
+ } else {
+ nameMap.put(nodeName, entry);
+ }
+ entries.put(id, entry);
+
+ return entry;
+ }
+
+ void addAll(List entriesList) {
+ Iterator iter = entriesList.iterator();
+ while (iter.hasNext()) {
+ ChildNodeEntry entry = (ChildNodeEntry) iter.next();
+ // delegate to add(QName, String) to maintain consistency
+ add(entry.getName(), entry.getId());
+ }
+ }
+
+ public ChildNodeEntry remove(QName nodeName, int index) {
+ if (index < Path.INDEX_DEFAULT) {
+ throw new IllegalArgumentException("index is 1-based");
+ }
+
+ Object obj = nameMap.get(nodeName);
+ if (obj == null) {
+ return null;
+ }
+
+ if (obj instanceof ChildNodeEntry) {
+ // map entry is a single child node entry
+ if (index != Path.INDEX_DEFAULT) {
+ return null;
+ }
+ ChildNodeEntry removedEntry = (ChildNodeEntry) obj;
+ nameMap.remove(nodeName);
+ entries.remove(removedEntry.getId());
+ return removedEntry;
+ }
+
+ // map entry is a list of siblings
+ List siblings = (ArrayList) obj;
+ if (index > siblings.size()) {
+ return null;
+ }
+
+ // remove from siblings list
+ ChildNodeEntry removedEntry = (ChildNodeEntry) siblings.remove(index - 1);
+ // remove from ordered entries map
+ entries.remove(removedEntry.getId());
+
+ // update indices of subsequent same-name siblings
+ for (int i = index - 1; i < siblings.size(); i++) {
+ ChildNodeEntry oldEntry = (ChildNodeEntry) siblings.get(i);
+ ChildNodeEntry newEntry = new ChildNodeEntry(nodeName, oldEntry.getId(), oldEntry.getIndex() - 1);
+ // overwrite old entry with updated entry in siblings list
+ siblings.set(i, newEntry);
+ // overwrite old entry with updated entry in ordered entries map
+ entries.put(newEntry.getId(), newEntry);
+ }
+
+ // clean up name lookup map if necessary
+ if (siblings.size() == 0) {
+ // no more entries with that name left:
+ // remove from name lookup map as well
+ nameMap.remove(nodeName);
+ } else if (siblings.size() == 1) {
+ // just one entry with that name left:
+ // discard siblings list and update name lookup map accordingly
+ nameMap.put(nodeName, siblings.get(0));
+ }
+
+ // we're done
+ return removedEntry;
+ }
+
+ /**
+ * Removes the child node entry refering to the node with the given id.
+ *
+ * @param id id of node whose entry is to be removed.
+ * @return the removed entry or <code>null</code> if there is no such entry.
+ */
+ ChildNodeEntry remove(NodeId id) {
+ ChildNodeEntry entry = (ChildNodeEntry) entries.get(id);
+ if (entry != null) {
+ return remove(entry.getName(), entry.getIndex());
+ }
+ return entry;
+ }
+
+ /**
+ * Removes the given child node entry.
+ *
+ * @param entry entry to be removed.
+ * @return the removed entry or <code>null</code> if there is no such entry.
+ */
+ public ChildNodeEntry remove(ChildNodeEntry entry) {
+ return remove(entry.getName(), entry.getIndex());
+ }
+
+ /**
+ * Removes all child node entries
+ */
+ public void removeAll() {
+ nameMap.clear();
+ entries.clear();
+ }
+
+ /**
+ * Returns a list of <code>ChildNodeEntry</code>s who do only exist in
+ * <code>this</code> but not in <code>other</code>.
+ * <p/>
+ * Note that two entries are considered identical in this context if
+ * they have the same name and uuid, i.e. the index is disregarded
+ * whereas <code>ChildNodeEntry.equals(Object)</code> also compares
+ * the index.
+ *
+ * @param other entries to be removed
+ * @return a new list of those entries that do only exist in
+ * <code>this</code> but not in <code>other</code>
+ */
+ List removeAll(ChildNodeEntries other) {
+ if (entries.isEmpty()) {
+ return Collections.EMPTY_LIST;
+ }
+ if (other.isEmpty()) {
+ return this;
+ }
+
+ List result = new ArrayList();
+ Iterator iter = iterator();
+ while (iter.hasNext()) {
+ ChildNodeEntry entry = (ChildNodeEntry) iter.next();
+ ChildNodeEntry otherEntry = other.get(entry.getId());
+ if (entry == otherEntry) {
+ continue;
+ }
+ if (otherEntry == null || !entry.getName().equals(otherEntry.getName())) {
+ result.add(entry);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a list of <code>ChildNodeEntry</code>s who do exist in
+ * <code>this</code> <i>and</i> in <code>other</code>.
+ * <p/>
+ * Note that two entries are considered identical in this context if
+ * they have the same name and uuid, i.e. the index is disregarded
+ * whereas <code>ChildNodeEntry.equals(Object)</code> also compares
+ * the index.
+ *
+ * @param other entries to be retained
+ * @return a new list of those entries that do exist in
+ * <code>this</code> <i>and</i> in <code>other</code>
+ */
+ List retainAll(ChildNodeEntries other) {
+ if (entries.isEmpty()
+ || other.isEmpty()) {
+ return Collections.EMPTY_LIST;
+ }
+
+ List result = new ArrayList();
+ Iterator iter = iterator();
+ while (iter.hasNext()) {
+ ChildNodeEntry entry = (ChildNodeEntry) iter.next();
+ ChildNodeEntry otherEntry = other.get(entry.getId());
+ if (entry == otherEntry) {
+ result.add(entry);
+ } else if (otherEntry != null
+ && entry.getName().equals(otherEntry.getName())) {
+ result.add(entry);
+ }
+ }
+
+ return result;
+ }
+
+ //-------------------------------------------< unmodifiable List view >
+ public boolean contains(Object o) {
+ if (o instanceof ChildNodeEntry) {
+ return entries.containsKey(((ChildNodeEntry) o).getId());
+ } else {
+ return false;
+ }
+ }
+
+ public boolean containsAll(Collection c) {
+ Iterator iter = c.iterator();
+ while (iter.hasNext()) {
+ if (!contains(iter.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public Object get(int index) {
+ return entries.getValue(index);
+ }
+
+ public int indexOf(Object o) {
+ if (o instanceof ChildNodeEntry) {
+ return entries.indexOf(((ChildNodeEntry) o).getId());
+ } else {
+ return -1;
+ }
+ }
+
+ public boolean isEmpty() {
+ return entries.isEmpty();
+ }
+
+ public int lastIndexOf(Object o) {
+ // entries are unique
+ return indexOf(o);
+ }
+
+ public Iterator iterator() {
+ return new EntriesIterator();
+ }
+
+ public ListIterator listIterator() {
+ return new EntriesIterator();
+ }
+
+ public ListIterator listIterator(int index) {
+ if (index < 0 || index >= entries.size()) {
+ throw new IndexOutOfBoundsException();
+ }
+ ListIterator iter = new EntriesIterator();
+ while (index-- > 0) {
+ iter.next();
+ }
+ return iter;
+ }
+
+ public int size() {
+ return entries.size();
+ }
+
+ public List subList(int fromIndex, int toIndex) {
+ // @todo FIXME does not fulfil the contract of List.subList(int,int)
+ return Collections.unmodifiableList(new ArrayList(this).subList(fromIndex, toIndex));
+ }
+
+ public Object[] toArray() {
+ ChildNodeEntry[] array = new ChildNodeEntry[size()];
+ return toArray(array);
+ }
+
+ public Object[] toArray(Object[] a) {
+ if (!a.getClass().getComponentType().isAssignableFrom(ChildNodeEntry.class)) {
+ throw new ArrayStoreException();
+ }
+ if (a.length < size()) {
+ a = new ChildNodeEntry[size()];
+ }
+ MapIterator iter = entries.mapIterator();
+ int i = 0;
+ while (iter.hasNext()) {
+ iter.next();
+ a[i] = entries.getValue(i);
+ i++;
+ }
+ while (i < a.length) {
+ a[i++] = null;
+ }
+ return a;
+ }
+
+ public void add(int index, Object element) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean add(Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean addAll(Collection c) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean addAll(int index, Collection c) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object remove(int index) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean remove(Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean removeAll(Collection c) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean retainAll(Collection c) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Object set(int index, Object element) {
+ throw new UnsupportedOperationException();
+ }
+
+ //------------------------------------------------< Cloneable support >
+ /**
+ * Returns a shallow copy of this <code>ChildNodeEntries</code> instance;
+ * the entries themselves are not cloned.
+ *
+ * @return a shallow copy of this instance.
+ */
+ protected Object clone() {
+ ChildNodeEntries clone = new ChildNodeEntries();
+ clone.entries = (LinkedMap) entries.clone();
+ clone.nameMap = new HashMap(nameMap.size());
+ for (Iterator it = nameMap.keySet().iterator(); it.hasNext();) {
+ Object key = it.next();
+ Object obj = nameMap.get(key);
+ if (obj instanceof ArrayList) {
+ // clone List
+ obj = ((ArrayList) obj).clone();
+ }
+ clone.nameMap.put(key, obj);
+ }
+ return clone;
+ }
+
+ //----------------------------------------------------< inner classes >
+ class EntriesIterator implements ListIterator {
+
+ private final OrderedMapIterator mapIter;
+
+ EntriesIterator() {
+ mapIter = entries.orderedMapIterator();
+ }
+
+ public boolean hasNext() {
+ return mapIter.hasNext();
+ }
+
+ public Object next() {
+ mapIter.next();
+ return mapIter.getValue();
+ }
+
+ public boolean hasPrevious() {
+ return mapIter.hasPrevious();
+ }
+
+ public int nextIndex() {
+ return entries.indexOf(mapIter.getKey()) + 1;
+ }
+
+ public Object previous() {
+ mapIter.previous();
+ return mapIter.getValue();
+ }
+
+ public int previousIndex() {
+ return entries.indexOf(mapIter.getKey()) - 1;
+ }
+
+ public void add(Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void set(Object o) {
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+
+ /**
+ * <code>ChildNodeEntry</code> specifies the name, index (in the case of
+ * same-name siblings) and the UUID of a child node entry.
+ * <p/>
+ * <code>ChildNodeEntry</code> instances are immutable.
+ */
+ public static final class ChildNodeEntry {
+
+ private int hash = 0;
+
+ private final QName name;
+ private final int index; // 1-based index for same-name siblings
+ private final NodeId id;
+
+ private ChildNodeEntry(QName name, NodeId id, int index) {
+ if (name == null) {
+ throw new IllegalArgumentException("name can not be null");
+ }
+ this.name = name;
+
+ if (id == null) {
+ throw new IllegalArgumentException("id can not be null");
+ }
+ this.id = id;
+
+ if (index < Path.INDEX_DEFAULT) {
+ throw new IllegalArgumentException("index is 1-based");
+ }
+ this.index = index;
+ }
+
+ public NodeId getId() {
+ return id;
+ }
+
+ public QName getName() {
+ return name;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+
+ //---------------------------------------< java.lang.Object overrides >
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof ChildNodeEntry) {
+ ChildNodeEntry other = (ChildNodeEntry) obj;
+ return (name.equals(other.name) && id.equals(other.id)
+ && index == other.index);
+ }
+ return false;
+ }
+
+ public String toString() {
+ return name.toString() + "[" + index + "] -> " + id;
+ }
+
+ public int hashCode() {
+ // ChildNodeEntry is immutable, we can store the computed hash code value
+ int h = hash;
+ if (h == 0) {
+ h = 17;
+ h = 37 * h + name.hashCode();
+ h = 37 * h + id.hashCode();
+ h = 37 * h + index;
+ hash = h;
+ }
+ return h;
+ }
+ }
+}
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeStateListener.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeStateListener.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeStateListener.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeStateListener.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.jcr2spi.state;
+
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.spi.NodeId;
+
+/**
+ * Extends the <code>ItemStateListener</code> allowing a client to be
+ * additionally informed about changes on a <code>NodeState</code>.
+ *
+ * @see NodeState#addListener
+ */
+public interface NodeStateListener extends TransientItemStateListener {
+
+ /**
+ * Called when a child node has been added
+ *
+ * @param state node state that changed
+ * @param name name of node that was added
+ * @param index index of new node
+ * @param id id of new node
+ */
+ void nodeAdded(NodeState state, QName name, int index, NodeId id);
+
+ /**
+ * Called when the children nodes were replaced by other nodes, typically
+ * as result of a reorder operation.
+ *
+ * @param state node state that changed
+ */
+ void nodesReplaced(NodeState state);
+
+ /**
+ * Called when a child node has been removed
+ *
+ * @param state node state that changed
+ * @param name name of node that was removed
+ * @param index index of removed node
+ * @param id id of removed node
+ */
+ public void nodeRemoved(NodeState state, QName name, int index, NodeId id);
+}
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeStateListener.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeStateListener.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.jcr2spi.state;
+
+import javax.jcr.PropertyType;
+import javax.jcr.ValueFormatException;
+import org.apache.jackrabbit.spi.QPropertyDefinition;
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.spi.PropertyId;
+import org.apache.jackrabbit.spi.ItemId;
+import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.value.QValue;
+
+/**
+ * <code>PropertyState</code> represents the state of a <code>Property</code>.
+ */
+public class PropertyState extends ItemState {
+
+ /**
+ * the id of this property state
+ */
+ private PropertyId id;
+
+ /**
+ * the internal values
+ */
+ private QValue[] values;
+
+ /**
+ * the type of this property state
+ */
+ private int type;
+
+ /**
+ * flag indicating if this is a multivalue property
+ */
+ private boolean multiValued;
+
+ private QPropertyDefinition def;
+
+ /**
+ * Constructs a new property state that is initially connected to an
+ * overlayed state.
+ *
+ * @param overlayedState the backing property state being overlayed
+ * @param initialStatus the initial status of the property state object
+ * @param isTransient flag indicating whether this state is transient or not
+ */
+ public PropertyState(PropertyState overlayedState, int initialStatus,
+ boolean isTransient) {
+ super(overlayedState, initialStatus, isTransient);
+ pull();
+ }
+
+ /**
+ * Create a new <code>PropertyState</code>
+ *
+ * @param id id of the property
+ * @param initialStatus the initial status of the property state object
+ * @param isTransient flag indicating whether this state is transient or not
+ */
+ public PropertyState(PropertyId id, int initialStatus, boolean isTransient) {
+ super(initialStatus, isTransient);
+ this.id = id;
+ type = PropertyType.UNDEFINED;
+ values = QValue.EMPTY_ARRAY;
+ multiValued = false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected synchronized void copy(ItemState state) {
+ synchronized (state) {
+ PropertyState propState = (PropertyState) state;
+ id = propState.id;
+ type = propState.type;
+ def = propState.getDefinition();
+ values = propState.values;
+ multiValued = propState.multiValued;
+ }
+ }
+
+ //-------------------------------------------------------< public methods >
+ /**
+ * Determines if this item state represents a node.
+ *
+ * @return always false
+ * @see ItemState#isNode
+ */
+ public boolean isNode() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ItemId getId() {
+ return id;
+ }
+
+ /**
+ * Returns the identifier of this property.
+ *
+ * @return the id of this property.
+ */
+ public PropertyId getPropertyId() {
+ return id;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public NodeId getParentId() {
+ return id.getParentId();
+ }
+
+ /**
+ * Returns the name of this property.
+ *
+ * @return the name of this property.
+ */
+ public QName getName() {
+ return id.getQName();
+ }
+
+ /**
+ * Sets the type of the property value(s)
+ *
+ * @param type the type to be set
+ * @see PropertyType
+ */
+ public void setType(int type) {
+ this.type = type;
+ }
+
+ /**
+ * Sets the flag indicating whether this property is multi-valued.
+ *
+ * @param multiValued flag indicating whether this property is multi-valued
+ */
+ public void setMultiValued(boolean multiValued) {
+ this.multiValued = multiValued;
+ }
+
+ /**
+ * Returns the type of the property value(s).
+ *
+ * @return the type of the property value(s).
+ * @see PropertyType
+ * @see QPropertyDefinition#getRequiredType() for the type required by the
+ * property definition. The effective type may differ from the required
+ * type if the latter is {@link PropertyType#UNDEFINED}.
+ */
+ public int getType() {
+ return type;
+ }
+
+ /**
+ * Returns true if this property is multi-valued, otherwise false.
+ *
+ * @return true if this property is multi-valued, otherwise false.
+ */
+ public boolean isMultiValued() {
+ return multiValued;
+ }
+
+ /**
+ * Returns the id of the definition applicable to this property state.
+ *
+ * @return the id of the definition
+ */
+ public QPropertyDefinition getDefinition() {
+ return def;
+ }
+
+ /**
+ * Sets the id of the definition applicable to this property state.
+ *
+ * @param def the id of the definition
+ */
+ public void setDefinition(QPropertyDefinition def) {
+ this.def = def;
+ }
+
+ /**
+ * Sets the value(s) of this property.
+ *
+ * @param values the new values
+ */
+ public void setValues(QValue[] values) {
+ this.values = values;
+ }
+
+ /**
+ * Returns the value(s) of this property.
+ *
+ * @return the value(s) of this property.
+ */
+ public QValue[] getValues() {
+ return values;
+ }
+
+ /**
+ * Convenience method for single valued property states.
+ *
+ * @return
+ * @throws ValueFormatException if {@link #isMultiValued()} returns true.
+ */
+ public QValue getValue() throws ValueFormatException {
+ if (isMultiValued()) {
+ throw new ValueFormatException("'getValue' may not be called on a multi-valued state.");
+ }
+ if (values == null || values.length == 0) {
+ return null;
+ } else {
+ return values[0];
+ }
+ }
+}
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/PropertyState.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url