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/11/01 10:04:57 UTC
svn commit: r469828 - in
/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi:
NodeImpl.java WorkspaceImpl.java state/ChildNodeEntries.java
state/ItemState.java state/NodeState.java
Author: angela
Date: Wed Nov 1 01:04:56 2006
New Revision: 469828
URL: http://svn.apache.org/viewvc?view=rev&rev=469828
Log:
work in progress
- move ChildNodeEntries out of NodeState
- minor improvement, javadoc
- Node is referenceable if uuid is null or if mix:referenceable is not part of ent.
Added:
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildNodeEntries.java (with props)
Modified:
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java
jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java?view=diff&rev=469828&r1=469827&r2=469828
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/NodeImpl.java Wed Nov 1 01:04:56 2006
@@ -545,7 +545,7 @@
public String getUUID() throws UnsupportedRepositoryOperationException, RepositoryException {
checkStatus();
String uuid = getNodeState().getUUID();
- if (!isNodeType(QName.MIX_REFERENCEABLE) && uuid != null) {
+ if (uuid == null || !isNodeType(QName.MIX_REFERENCEABLE)) {
throw new UnsupportedRepositoryOperationException();
}
// Node is referenceable -> NodeId must contain a UUID part
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java?view=diff&rev=469828&r1=469827&r2=469828
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/WorkspaceImpl.java Wed Nov 1 01:04:56 2006
@@ -81,7 +81,7 @@
*/
private final String name;
/**
- * The Session that created this <code>Workspace</code> object
+ * The Session that created this <code>Workspace</code> object.
*/
private final SessionImpl session;
@@ -94,7 +94,7 @@
/**
* The hierarchy manager that reflects workspace state only
* (i.e. that is isolated from transient changes made through
- * the session)
+ * the session).
*/
private HierarchyManager hierManager;
private LockManager lockManager;
Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildNodeEntries.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildNodeEntries.java?view=auto&rev=469828
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildNodeEntries.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildNodeEntries.java Wed Nov 1 01:04:56 2006
@@ -0,0 +1,786 @@
+/*
+ * 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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.jackrabbit.jcr2spi.state.entry.ChildNodeEntry;
+import org.apache.jackrabbit.jcr2spi.state.entry.ChildNodeReference;
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.name.Path;
+import org.apache.commons.collections.list.AbstractLinkedList;
+import org.apache.commons.collections.iterators.UnmodifiableIterator;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Iterator;
+import java.util.Collections;
+import java.util.AbstractList;
+import java.util.ArrayList;
+import java.util.ConcurrentModificationException;
+import java.util.NoSuchElementException;
+
+/**
+ * <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>Collection</code> view.
+ */
+final class ChildNodeEntries implements Collection {
+
+ private static Logger log = LoggerFactory.getLogger(ChildNodeEntries.class);
+
+ private final NodeState nodeState;
+
+ /**
+ * Linked list of {@link ChildNodeEntry} instances.
+ */
+ private final LinkedEntries entries = new LinkedEntries();
+
+ /**
+ * map used for lookup by name
+ * (key=name, value=either a single {@link AbstractLinkedList.Node} or a
+ * list of {@link AbstractLinkedList.Node}s which are sns entries)
+ */
+ private final Map nameMap = new HashMap();
+
+ /**
+ * Create <code>ChildNodeEntries</code> for the given node state.
+ *
+ * @param nodeState
+ */
+ ChildNodeEntries(NodeState nodeState) {
+ this.nodeState = nodeState;
+ }
+
+ /**
+ *
+ * @param name
+ * @param index
+ * @param uuid
+ * @return
+ */
+ boolean contains(QName name, int index, String uuid) {
+ if (!nameMap.containsKey(name)) {
+ // no matching child node entry
+ return false;
+ }
+ Object o = nameMap.get(name);
+ if (o instanceof List) {
+ // SNS
+ for (Iterator it = ((List) o).iterator(); it.hasNext(); ) {
+ LinkedEntries.LinkNode n = (LinkedEntries.LinkNode) it.next();
+ ChildNodeEntry cne = n.getChildNodeEntry();
+ if (uuid == null) {
+ if (cne.getIndex() == index) {
+ return true;
+ }
+ } else if (uuid.equals(cne.getUUID())) {
+ return true;
+ }
+ }
+ // no matching entry found
+ return false;
+ } else {
+ // single child node with this name
+ ChildNodeEntry cne = ((LinkedEntries.LinkNode) o).getChildNodeEntry();
+ if (uuid == null) {
+ return cne.getUUID() == null;
+ } else {
+ return uuid.equals(cne.getUUID());
+ }
+ }
+ }
+
+ /**
+ * Returns the <code>ChildNodeEntry</code> for the given
+ * <code>nodeState</code>.
+ *
+ * @param nodeState the node state.
+ * @return the <code>ChildNodeEntry</code> or <code>null</code> if there
+ * is no <code>ChildNodeEntry</code> for <code>nodeState</code>.
+ */
+ ChildNodeEntry get(NodeState nodeState) {
+ Object o = nameMap.get(nodeState.getQName());
+ if (o == null) {
+ // no matching child node entry
+ return null;
+ }
+ if (o instanceof List) {
+ // has same name sibling
+ for (Iterator it = ((List) o).iterator(); it.hasNext(); ) {
+ LinkedEntries.LinkNode n = (LinkedEntries.LinkNode) it.next();
+ ChildNodeEntry cne = n.getChildNodeEntry();
+ // only check available child node entries
+ try {
+ if (cne.isAvailable() && cne.getNodeState() == nodeState) {
+ return cne;
+ }
+ } catch (ItemStateException e) {
+ log.warn("error retrieving a child node state", e);
+ }
+ }
+ } else {
+ // single child node with this name
+ ChildNodeEntry cne = ((LinkedEntries.LinkNode) o).getChildNodeEntry();
+ try {
+ if (cne.isAvailable() && cne.getNodeState() == nodeState) {
+ return cne;
+ }
+ } catch (ItemStateException e) {
+ log.warn("error retrieving a child node state", e);
+ }
+ }
+ // not found
+ return null;
+ }
+
+ /**
+ * Returns a <code>List</code> of <code>ChildNodeEntry</code>s for the
+ * given <code>nodeName</code>. This method does <b>not</b> filter out
+ * removed <code>ChildNodeEntry</code>s!
+ *
+ * @param nodeName the child node name.
+ * @return same name sibling nodes with the given <code>nodeName</code>.
+ */
+ List get(QName nodeName) {
+ Object obj = nameMap.get(nodeName);
+ if (obj == null) {
+ return Collections.EMPTY_LIST;
+ }
+ if (obj instanceof List) {
+ final List sns = (List) obj;
+ // map entry is a list of siblings
+ return Collections.unmodifiableList(new AbstractList() {
+
+ public Object get(int index) {
+ return ((LinkedEntries.LinkNode) sns.get(index)).getChildNodeEntry();
+ }
+
+ public int size() {
+ return sns.size();
+ }
+
+ public Iterator iterator() {
+ return new Iterator() {
+
+ private Iterator iter = sns.iterator();
+
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ public Object next() {
+ return ((LinkedEntries.LinkNode) iter.next()).getChildNodeEntry();
+ }
+ };
+ }
+ });
+ } else {
+ // map entry is a single child node entry
+ return Collections.singletonList(((LinkedEntries.LinkNode) obj).getChildNodeEntry());
+ }
+ }
+
+ /**
+ * Returns the <code>ChildNodeEntry</code> with the given
+ * <code>nodeName</code> and <code>index</code>. This method ignores
+ * <code>ChildNodeEntry</code>s which are marked removed!
+ *
+ * @param nodeName name of the child node entry.
+ * @param index the index of the child node entry.
+ * @return the <code>ChildNodeEntry</code> or <code>null</code> if there
+ * is no such <code>ChildNodeEntry</code>.
+ */
+ 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 List) {
+ // map entry is a list of siblings
+ List siblings = (List) obj;
+ // filter out removed states
+ for (Iterator it = siblings.iterator(); it.hasNext(); ) {
+ ChildNodeEntry cne = ((LinkedEntries.LinkNode) it.next()).getChildNodeEntry();
+ if (cne.isAvailable()) {
+ try {
+ if (cne.getNodeState().isValid()) {
+ index--;
+ } else {
+ // child node removed
+ }
+ } catch (ItemStateException e) {
+ // should never happen, cne.isAvailable() returned true
+ }
+ } else {
+ // then this child node entry has never been accessed
+ // before and is assumed valid
+ index--;
+ }
+ if (index == 0) {
+ return cne;
+ }
+ }
+ } else {
+ // map entry is a single child node entry
+ if (index == Path.INDEX_DEFAULT) {
+ return ((LinkedEntries.LinkNode) obj).getChildNodeEntry();
+ }
+ }
+ return null;
+ }
+
+ /**
+ *
+ * @param nodeName
+ * @param uuid
+ * @return
+ * @throws IllegalArgumentException if the given uuid is null.
+ */
+ ChildNodeEntry get(QName nodeName, String uuid) {
+ if (uuid == null) {
+ throw new IllegalArgumentException();
+ }
+ List l = get(nodeName);
+ for (Iterator it = l.iterator(); it.hasNext();) {
+ ChildNodeEntry cne = (ChildNodeEntry) it.next();
+ if (uuid.equals(cne.getUUID())) {
+ return cne;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Insert a new childnode entry at the position indicated by index.
+ *
+ * @param nodeName
+ * @param uuid
+ * @param index
+ * @return
+ */
+ ChildNodeEntry add(QName nodeName, String uuid, int index) {
+ ChildNodeEntry cne = add(nodeName, uuid);
+ // TODO: in case of SNS, move new cne to the right position.
+ return cne;
+ }
+
+ /**
+ * Adds a <code>childNode</code> to the end of the list.
+ *
+ * @param childState the <code>NodeState</code> to add.
+ * @return the <code>ChildNodeEntry</code> which was created for
+ * <code>childNode</code>.
+ */
+ ChildNodeEntry add(NodeState childState) {
+ ChildNodeEntry cne = ChildNodeReference.create(childState, nodeState.isf, nodeState.idFactory);
+ add(cne);
+ return cne;
+ }
+
+ /**
+ * Adds a <code>ChildNodeEntry</code> for a child node with the given
+ * name and an optional <code>uuid</code>.
+ *
+ * @param nodeName the name of the child node.
+ * @param uuid the UUID of the child node if it can be identified
+ * with a UUID; otherwise <code>null</code>.
+ * @return the created ChildNodeEntry.
+ */
+ private ChildNodeEntry add(QName nodeName, String uuid) {
+ ChildNodeEntry cne = ChildNodeReference.create(nodeState, nodeName, uuid, nodeState.isf, nodeState.idFactory);
+ add(cne);
+ return cne;
+ }
+
+ /**
+ * Adds a <code>ChildNodeEntry</code> to the end of the list.
+ *
+ * @param cne the <code>ChildNodeEntry</code> to add.
+ */
+ private void add(ChildNodeEntry cne) {
+ QName nodeName = cne.getName();
+ List siblings = null;
+ Object obj = nameMap.get(nodeName);
+ if (obj != null) {
+ if (obj instanceof List) {
+ // 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);
+ }
+ }
+
+ LinkedEntries.LinkNode ln = entries.add(cne);
+
+ if (siblings != null) {
+ siblings.add(ln);
+ } else {
+ nameMap.put(nodeName, ln);
+ }
+ }
+
+ /**
+ * Removes the child node entry with the given <code>nodeName</code> and
+ * <code>index</code>.
+ *
+ * @param nodeName the name of the child node entry to remove.
+ * @param index the index of the child node entry to remove.
+ * @return the removed <code>ChildNodeEntry</code> or <code>null</code>
+ * if there is no matching <code>ChildNodeEntry</code>.
+ */
+ 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 LinkedEntries.LinkNode) {
+ // map entry is a single child node entry
+ if (index != Path.INDEX_DEFAULT) {
+ return null;
+ }
+ LinkedEntries.LinkNode ln = (LinkedEntries.LinkNode) obj;
+ nameMap.remove(nodeName);
+ // remove LinkNode from entries
+ ln.remove();
+ return ln.getChildNodeEntry();
+ }
+
+ // map entry is a list of siblings
+ List siblings = (List) obj;
+ if (index > siblings.size()) {
+ return null;
+ }
+
+ // remove from siblings list
+ LinkedEntries.LinkNode ln = (LinkedEntries.LinkNode) siblings.remove(index - 1);
+ ChildNodeEntry removedEntry = ln.getChildNodeEntry();
+ // remove from ordered entries
+ ln.remove();
+
+ // 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 state.
+ *
+ * @param nodeState the node state whose entry is to be removed.
+ * @return the removed entry or <code>null</code> if there is no such entry.
+ */
+ ChildNodeEntry remove(NodeState nodeState) {
+ ChildNodeEntry entry = null;
+ for (Iterator it = get(nodeState.getQName()).iterator(); it.hasNext(); ) {
+ ChildNodeEntry tmp = (ChildNodeEntry) it.next();
+ try {
+ if (tmp.isAvailable() && tmp.getNodeState() == nodeState) {
+ entry = tmp;
+ break;
+ }
+ } catch (ItemStateException e) {
+ log.warn("error accessing child node state: " + e.getMessage());
+ }
+ }
+ if (entry != null) {
+ return remove(entry.getName(), entry.getIndex());
+ }
+ return entry;
+ }
+
+ /**
+ * Removes all child node entries
+ */
+ void removeAll() {
+ nameMap.clear();
+ entries.clear();
+ }
+
+ /**
+ * Reorders an existing <code>NodeState</code> before another
+ * <code>NodeState</code>. If <code>beforeNode</code> is
+ * <code>null</code> <code>insertNode</code> is moved to the end of the
+ * child node entries.
+ *
+ * @param insertNode the node state to move.
+ * @param beforeNode the node state where <code>insertNode</code> is
+ * reordered to.
+ * @throws NoSuchItemStateException if <code>insertNode</code> or
+ * <code>beforeNode</code> does not have a <code>ChildNodeEntry</code>
+ * in this <code>ChildNodeEntries</code>.
+ */
+ void reorder(NodeState insertNode, NodeState beforeNode)
+ throws NoSuchItemStateException {
+ Object insertObj = nameMap.get(insertNode.getQName());
+ // the link node to move
+ LinkedEntries.LinkNode insertLN = getLinkNode(insertNode);
+ // the link node where insertLN is ordered before
+ LinkedEntries.LinkNode beforeLN = (beforeNode != null) ? getLinkNode(beforeNode) : null;
+
+ if (insertObj instanceof List) {
+ // adapt name lookup lists
+ List insertList = (List) insertObj;
+ if (beforeNode == null) {
+ // simply move to end of list
+ insertList.remove(insertLN);
+ insertList.add(insertLN);
+ } else {
+ // move based on position of beforeLN
+
+ // count our same name siblings until we reach beforeLN
+ int snsCount = 0;
+ QName insertName = insertNode.getQName();
+ for (Iterator it = entries.linkNodeIterator(); it.hasNext(); ) {
+ LinkedEntries.LinkNode ln = (LinkedEntries.LinkNode) it.next();
+ if (ln == beforeLN) {
+ insertList.remove(insertLN);
+ insertList.add(snsCount, insertLN);
+ break;
+ } else if (ln == insertLN) {
+ // do not increment snsCount for node to reorder
+ } else if (ln.getChildNodeEntry().getName().equals(insertName)) {
+ snsCount++;
+ }
+ }
+ }
+ } else {
+ // no same name siblings -> nothing to do.
+ }
+
+ // reorder in linked list
+ entries.reorderNode(insertLN, beforeLN);
+ }
+
+ /**
+ * If the given child state got a (new) uuid assigned or its removed,
+ * its childEntry must be adjusted.
+ *
+ * @param childState
+ */
+ void replaceEntry(NodeState childState) {
+ // NOTE: test if child-state needs to get a new entry not checked here.
+ try {
+ LinkedEntries.LinkNode ln = getLinkNode(childState);
+ ChildNodeEntry newCne = ChildNodeReference.create(childState, nodeState.isf, nodeState.idFactory);
+ entries.replaceNode(ln, newCne);
+ } catch (NoSuchItemStateException e) {
+ log.error("Internal error.", e);
+ }
+ }
+
+ /**
+ * Returns the matching <code>LinkNode</code> from a list or a single
+ * <code>LinkNode</code>.
+ *
+ * @param nodeState the <code>NodeState</code> which is the value
+ * of on of the <code>LinkNode</code>s.
+ * @return the matching <code>LinkNode</code>.
+ * @throws NoSuchItemStateException if none of the <code>LinkNode</code>s
+ * matches.
+ */
+ private LinkedEntries.LinkNode getLinkNode(NodeState nodeState)
+ throws NoSuchItemStateException {
+ Object listOrLinkNode = nameMap.get(nodeState.getQName());
+ if (listOrLinkNode == null) {
+ // no matching child node entry
+ throw new NoSuchItemStateException(nodeState.getQName().toString());
+ }
+
+ if (listOrLinkNode instanceof List) {
+ // has same name sibling
+ for (Iterator it = ((List) listOrLinkNode).iterator(); it.hasNext();) {
+ LinkedEntries.LinkNode n = (LinkedEntries.LinkNode) it.next();
+ ChildNodeEntry cne = n.getChildNodeEntry();
+ // only check available child node entries
+ try {
+ if (cne.isAvailable() && cne.getNodeState() == nodeState) {
+ return n;
+ }
+ } catch (ItemStateException e) {
+ log.warn("error retrieving a child node state", e);
+ }
+ }
+ } else {
+ // single child node with this name
+ ChildNodeEntry cne = ((LinkedEntries.LinkNode) listOrLinkNode).getChildNodeEntry();
+ try {
+ if (cne.isAvailable() && cne.getNodeState() == nodeState) {
+ return (LinkedEntries.LinkNode) listOrLinkNode;
+ }
+ } catch (ItemStateException e) {
+ log.warn("error retrieving a child node state", e);
+ }
+ }
+ throw new NoSuchItemStateException(nodeState.getQName().toString());
+ }
+
+ //---------------------------------------< unmodifiable Collection view >---
+
+ public boolean contains(Object o) {
+ if (o instanceof ChildNodeEntry) {
+ // narrow down to same name sibling nodes and check list
+ return get(((ChildNodeEntry) o).getName()).contains(o);
+ } else {
+ return false;
+ }
+ }
+
+ public boolean containsAll(Collection c) {
+ Iterator iter = c.iterator();
+ while (iter.hasNext()) {
+ if (!contains(iter.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isEmpty() {
+ return entries.isEmpty();
+ }
+
+ public Iterator iterator() {
+ return UnmodifiableIterator.decorate(entries.iterator());
+ }
+
+ public int size() {
+ return entries.size();
+ }
+
+ 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()];
+ }
+ Iterator iter = entries.iterator();
+ int i = 0;
+ while (iter.hasNext()) {
+ a[i++] = iter.next();
+ }
+ while (i < a.length) {
+ a[i++] = null;
+ }
+ return a;
+ }
+
+ public boolean add(Object o) {
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean addAll(Collection c) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void clear() {
+ 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();
+ }
+
+
+ /**
+ * An implementation of a linked list which provides access to the internal
+ * LinkNode which links the entries of the list.
+ */
+ private static final class LinkedEntries extends AbstractLinkedList {
+
+ LinkedEntries() {
+ super();
+ init();
+ }
+
+ /**
+ * Adds a child node entry to this list.
+ *
+ * @param cne the child node entry to add.
+ * @return the LinkNode which refers to the added <code>ChildNodeEntry</code>.
+ */
+ LinkNode add(ChildNodeEntry cne) {
+ LinkNode ln = (LinkNode) createNode(cne);
+ addNode(ln, header);
+ return ln;
+ }
+
+ /**
+ * Reorders an existing <code>LinkNode</code> before another existing
+ * <code>LinkNode</code>. If <code>before</code> is <code>null</code>
+ * the <code>insert</code> node is moved to the end of the list.
+ *
+ * @param insert the node to reorder.
+ * @param before the node where to reorder node <code>insert</code>.
+ */
+ void reorderNode(LinkNode insert, LinkNode before) {
+ removeNode(insert);
+ if (before == null) {
+ addNode(insert, header);
+ } else {
+ addNode(insert, before);
+ }
+ }
+
+ /**
+ * Replace the value of the given LinkNode with a new childNodeEntry
+ * value.
+ *
+ * @param node
+ * @param value
+ */
+ void replaceNode(LinkNode node, ChildNodeEntry value) {
+ updateNode(node, value);
+ }
+
+ /**
+ * Create a new <code>LinkNode</code> for a given {@link ChildNodeEntry}
+ * <code>value</code>.
+ *
+ * @param value a child node entry.
+ * @return a wrapping {@link LinkedEntries.LinkNode}.
+ */
+ protected Node createNode(Object value) {
+ return new LinkNode(value);
+ }
+
+ /**
+ * @return a new <code>LinkNode</code>.
+ */
+ protected Node createHeaderNode() {
+ return new LinkNode();
+ }
+
+ /**
+ * Returns an iterator over all
+ * @return
+ */
+ Iterator linkNodeIterator() {
+ return new Iterator() {
+
+ private LinkNode next = ((LinkNode) header).getNextLinkNode();
+
+ private int expectedModCount = modCount;
+
+ public void remove() {
+ throw new UnsupportedOperationException("remove");
+ }
+
+ public boolean hasNext() {
+ if (expectedModCount != modCount) {
+ throw new ConcurrentModificationException();
+ }
+ return next != header;
+ }
+
+ public Object next() {
+ if (expectedModCount != modCount) {
+ throw new ConcurrentModificationException();
+ }
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ LinkNode n = next;
+ next = next.getNextLinkNode();
+ return n;
+ }
+ };
+ }
+
+ //-----------------------------------------------------------------------
+
+ /**
+ * Extends the <code>AbstractLinkedList.Node</code>.
+ */
+ private final class LinkNode extends AbstractLinkedList.Node {
+
+ protected LinkNode() {
+ super();
+ }
+
+ protected LinkNode(Object value) {
+ super(value);
+ }
+
+ /**
+ * @return the wrapped <code>ChildNodeEntry</code>.
+ */
+ public ChildNodeEntry getChildNodeEntry() {
+ return (ChildNodeEntry) super.getValue();
+ }
+
+ /**
+ * Removes this <code>LinkNode</code> from the linked list.
+ */
+ public void remove() {
+ removeNode(this);
+ }
+
+ /**
+ * @return the next LinkNode.
+ */
+ public LinkNode getNextLinkNode() {
+ return (LinkNode) super.getNextNode();
+ }
+ }
+ }
+}
\ No newline at end of file
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildNodeEntries.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ChildNodeEntries.java
------------------------------------------------------------------------------
svn:keywords = author date id revision url
Modified: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java?view=diff&rev=469828&r1=469827&r2=469828
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/ItemState.java Wed Nov 1 01:04:56 2006
@@ -337,6 +337,7 @@
*/
public void statusChanged(ItemState state, int previousStatus) {
checkIsSessionState();
+ state.checkIsWorkspaceState();
// the given state is the overlayed state this state (session) is listening to.
if (state == overlayedState) {
Modified: 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?view=diff&rev=469828&r1=469827&r2=469828
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java (original)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/state/NodeState.java Wed Nov 1 01:04:56 2006
@@ -16,8 +16,6 @@
*/
package org.apache.jackrabbit.jcr2spi.state;
-import org.apache.commons.collections.list.AbstractLinkedList;
-import org.apache.commons.collections.iterators.UnmodifiableIterator;
import org.apache.commons.collections.iterators.IteratorChain;
import org.apache.jackrabbit.spi.IdFactory;
import org.apache.jackrabbit.spi.QNodeDefinition;
@@ -29,7 +27,6 @@
import org.apache.jackrabbit.jcr2spi.state.entry.ChildNodeEntry;
import org.apache.jackrabbit.jcr2spi.state.entry.ChildPropertyEntry;
import org.apache.jackrabbit.jcr2spi.state.entry.PropertyReference;
-import org.apache.jackrabbit.jcr2spi.state.entry.ChildNodeReference;
import org.apache.jackrabbit.value.QValue;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
@@ -45,9 +42,6 @@
import java.util.List;
import java.util.Set;
import java.util.Map;
-import java.util.AbstractList;
-import java.util.NoSuchElementException;
-import java.util.ConcurrentModificationException;
/**
* <code>NodeState</code> represents the state of a <code>Node</code>.
@@ -86,7 +80,7 @@
* insertion-ordered collection of ChildNodeEntry objects
* TODO: cache needs to be notified when a child node entry is traversed or NodeState is created
*/
- private ChildNodeEntries childNodeEntries = new ChildNodeEntries();
+ private ChildNodeEntries childNodeEntries = new ChildNodeEntries(this);
/**
* Map of properties. Key = {@link QName} of property. Value = {@link
@@ -176,7 +170,8 @@
Iterator it = propertyNames.iterator();
while (it.hasNext()) {
QName propName = (QName) it.next();
- addPropertyEntry(PropertyReference.create(this, propName, isf, idFactory));
+ ChildPropertyEntry pe = PropertyReference.create(this, propName, isf, idFactory);
+ properties.put(propName, pe);
}
// re-create child node entries
childNodeEntries.removeAll();
@@ -190,16 +185,12 @@
}
private void assertAvailability() {
- // TODO: improve this.
+ // TODO: TOBEFIXED. duality of creating states via ISM or via factory may result in a cached state, that is not connected to its cne.
if (uuid != null && parent != null) {
// make sure this state is connected to its childNode-entry
- ChildNodeEntry cne = parent.childNodeEntries.get(uuid);
+ ChildNodeEntry cne = parent.childNodeEntries.get(name, uuid);
if (cne != null && !cne.isAvailable()) {
- try {
- cne.getNodeState();
- } catch (ItemStateException e) {
- // ignore
- }
+ parent.childNodeEntries.replaceEntry(this);
}
}
}
@@ -636,8 +627,8 @@
// add new childNodeEntry if it has not been added by
// some earlier 'add' event
// TODO: TOBEFIXED for SNSs
- ChildNodeEntry cne = childNodeEntries.get(name, index);
- if (cne == null || ((uuid == null) ? cne.getUUID() != null : !uuid.equals(cne.getUUID()))) {
+ ChildNodeEntry cne = (uuid != null) ? childNodeEntries.get(name, uuid) : childNodeEntries.get(name, index);
+ if (cne == null) {
cne = childNodeEntries.add(name, uuid, index);
}
// and let the transiently modified session state now, that
@@ -893,11 +884,17 @@
}
// first remove all properties
for (Iterator it = properties.values().iterator(); it.hasNext(); ) {
- PropertyState propState = ((ChildPropertyEntry) it.next()).getPropertyState();
- if (propState.isValid()) {
- propState.remove();
+ ChildPropertyEntry cpe = ((ChildPropertyEntry) it.next());
+ if (cpe.isAvailable()) {
+ PropertyState pState = cpe.getPropertyState();
+ if (pState.isValid()) {
+ pState.remove();
+ } else {
+ // remove invalid property state from properties map
+ it.remove();
+ }
} else {
- // remove invalid property state from properties map
+ // remove unresolved entry from properties map
it.remove();
}
}
@@ -1070,40 +1067,26 @@
if (child.getParent() != this) {
throw new IllegalArgumentException("This NodeState is not the parent of child");
}
- ChildNodeEntry cne = ChildNodeReference.create(child, isf, idFactory);
- childNodeEntries.add(cne);
-
+ childNodeEntries.add(child);
markModified();
}
/**
* Notifies this node state that a child node state has been removed.
*
- * @param nodeState the node state that has been removed.
+ * @param childState the node state that has been removed.
* @throws IllegalArgumentException if <code>this</code> is not the parent
* of <code>nodeState</code>.
*/
- private synchronized void childNodeStateRemoved(NodeState nodeState) {
+ private synchronized void childNodeStateRemoved(NodeState childState) {
checkIsSessionState();
- if (nodeState.getParent() != this) {
+ if (childState.getParent() != this) {
throw new IllegalArgumentException("This NodeState is not the parent of nodeState");
}
// if nodeState does not exist anymore remove its child node entry
- if (nodeState.getStatus() == Status.REMOVED) {
- List entries = getChildNodeEntries(nodeState.getQName());
- for (Iterator it = entries.iterator(); it.hasNext(); ) {
- ChildNodeEntry cne = (ChildNodeEntry) it.next();
- try {
- if (cne.getNodeState() == nodeState) {
- childNodeEntries.remove(cne.getName(), cne.getIndex());
- break;
- }
- } catch (ItemStateException e) {
- // does not exist anymore? TODO: better error handling
- log.warn("child node entry does not exist anymore", e);
- }
- }
+ if (childState.getStatus() == Status.REMOVED) {
+ childNodeEntries.remove(childState);
}
markModified();
}
@@ -1438,760 +1421,4 @@
}
}
//------------------------------------------------------< 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>Collection</code> view.
- */
- private class ChildNodeEntries implements Collection {
-
- /**
- * Linked list of {@link ChildNodeEntry} instances.
- */
- private final LinkedEntries entries = new LinkedEntries();
-
- /**
- * map used for lookup by name
- * (key=name, value=either a single {@link AbstractLinkedList.Node} or a
- * list of {@link AbstractLinkedList.Node}s which are sns entries)
- */
- private final Map nameMap = new HashMap();
-
- /**
- *
- * @param name
- * @param index
- * @param uuid
- * @return
- */
- private boolean contains(QName name, int index, String uuid) {
- if (!nameMap.containsKey(name)) {
- // no matching child node entry
- return false;
- }
- Object o = nameMap.get(name);
- if (o instanceof List) {
- // SNS
- for (Iterator it = ((List) o).iterator(); it.hasNext(); ) {
- LinkedEntries.LinkNode n = (LinkedEntries.LinkNode) it.next();
- ChildNodeEntry cne = n.getChildNodeEntry();
- if (uuid == null) {
- if (cne.getIndex() == index) {
- return true;
- }
- } else if (uuid.equals(cne.getUUID())) {
- return true;
- }
- }
- // no matching entry found
- return false;
- } else {
- // single child node with this name
- ChildNodeEntry cne = ((LinkedEntries.LinkNode) o).getChildNodeEntry();
- if (uuid == null) {
- return cne.getUUID() == null;
- } else {
- return uuid.equals(cne.getUUID());
- }
- }
- }
-
- /**
- *
- * @param uuid
- * @return
- */
- private ChildNodeEntry get(String uuid) {
- for (Iterator it = entries.iterator(); it.hasNext();) {
- LinkedEntries.LinkNode ln = (LinkedEntries.LinkNode) it.next();
- ChildNodeEntry cne = ln.getChildNodeEntry();
- if (cne.getUUID() == uuid) {
- return cne;
- }
- }
- return null;
- }
-
- /**
- * Returns the <code>ChildNodeEntry</code> for the given
- * <code>nodeState</code>.
- *
- * @param nodeState the node state.
- * @return the <code>ChildNodeEntry</code> or <code>null</code> if there
- * is no <code>ChildNodeEntry</code> for <code>nodeState</code>.
- */
- ChildNodeEntry get(NodeState nodeState) {
- Object o = nameMap.get(nodeState.getQName());
- if (o == null) {
- // no matching child node entry
- return null;
- }
- if (o instanceof List) {
- // has same name sibling
- for (Iterator it = ((List) o).iterator(); it.hasNext(); ) {
- LinkedEntries.LinkNode n = (LinkedEntries.LinkNode) it.next();
- ChildNodeEntry cne = n.getChildNodeEntry();
- // only check available child node entries
- try {
- if (cne.getNodeState() == nodeState) {
- return cne;
- }
- } catch (ItemStateException e) {
- log.warn("error retrieving a child node state", e);
- }
- }
- } else {
- // single child node with this name
- ChildNodeEntry cne = ((LinkedEntries.LinkNode) o).getChildNodeEntry();
- try {
- if (cne.getNodeState() == nodeState) {
- return cne;
- }
- } catch (ItemStateException e) {
- log.warn("error retrieving a child node state", e);
- }
- }
- // not found
- return null;
- }
-
- /**
- * Returns a <code>List</code> of <code>ChildNodeEntry</code>s for the
- * given <code>nodeName</code>. This method does <b>not</b> filter out
- * removed <code>ChildNodeEntry</code>s!
- *
- * @param nodeName the child node name.
- * @return same name sibling nodes with the given <code>nodeName</code>.
- */
- List get(QName nodeName) {
- Object obj = nameMap.get(nodeName);
- if (obj == null) {
- return Collections.EMPTY_LIST;
- }
- if (obj instanceof List) {
- final List sns = (List) obj;
- // map entry is a list of siblings
- return Collections.unmodifiableList(new AbstractList() {
-
- public Object get(int index) {
- return ((LinkedEntries.LinkNode) sns.get(index)).getChildNodeEntry();
- }
-
- public int size() {
- return sns.size();
- }
-
- public Iterator iterator() {
- return new Iterator() {
-
- private Iterator iter = sns.iterator();
-
- public void remove() {
- throw new UnsupportedOperationException("remove");
- }
-
- public boolean hasNext() {
- return iter.hasNext();
- }
-
- public Object next() {
- return ((LinkedEntries.LinkNode) iter.next()).getChildNodeEntry();
- }
- };
- }
- });
- } else {
- // map entry is a single child node entry
- return Collections.singletonList(((LinkedEntries.LinkNode) obj).getChildNodeEntry());
- }
- }
-
- /**
- * Returns the <code>ChildNodeEntry</code> with the given
- * <code>nodeName</code> and <code>index</code>. This method ignores
- * <code>ChildNodeEntry</code>s which are marked removed!
- *
- * @param nodeName name of the child node entry.
- * @param index the index of the child node entry.
- * @return the <code>ChildNodeEntry</code> or <code>null</code> if there
- * is no such <code>ChildNodeEntry</code>.
- */
- 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 List) {
- // map entry is a list of siblings
- List siblings = (List) obj;
- // filter out removed states
- for (Iterator it = siblings.iterator(); it.hasNext(); ) {
- ChildNodeEntry cne = ((LinkedEntries.LinkNode) it.next()).getChildNodeEntry();
- if (cne.isAvailable()) {
- try {
- if (cne.getNodeState().isValid()) {
- index--;
- } else {
- // child node removed
- }
- } catch (ItemStateException e) {
- // should never happen, cne.isAvailable() returned true
- }
- } else {
- // then this child node entry has never been accessed
- // before and is assumed valid
- index--;
- }
- if (index == 0) {
- return cne;
- }
- }
- } else {
- // map entry is a single child node entry
- if (index == Path.INDEX_DEFAULT) {
- return ((LinkedEntries.LinkNode) obj).getChildNodeEntry();
- }
- }
- return null;
- }
-
- /**
- * Adds a <code>ChildNodeEntry</code> for a child node with the given
- * name and an optional <code>uuid</code>.
- *
- * @param nodeName the name of the child node.
- * @param uuid the UUID of the child node if it can be identified
- * with a UUID; otherwise <code>null</code>.
- * @return the created ChildNodeEntry.
- */
- ChildNodeEntry add(QName nodeName, String uuid) {
- ChildNodeEntry cne = ChildNodeReference.create(NodeState.this, nodeName, uuid, isf, idFactory);
- add(cne);
- return cne;
- }
-
- /**
- * Insert a new childnode entry at the position indicated by index.
- * @param nodeName
- * @param uuid
- * @param index
- * @return
- */
- ChildNodeEntry add(QName nodeName, String uuid, int index) {
- ChildNodeEntry cne = add(nodeName, uuid);
- // TODO: in case of SNS, move new cne to the right position.
- return cne;
- }
-
- /**
- * Adds a <code>childNode</code> to the end of the list.
- *
- * @param childState the <code>NodeState</code> to add.
- * @return the <code>ChildNodeEntry</code> which was created for
- * <code>childNode</code>.
- */
- ChildNodeEntry add(NodeState childState) {
- ChildNodeEntry cne = ChildNodeReference.create(childState, isf, idFactory);
- add(cne);
- return cne;
- }
-
- /**
- * Adds a <code>ChildNodeEntry</code> to the end of the list.
- *
- * @param cne the <code>ChildNodeEntry</code> to add.
- */
- void add(ChildNodeEntry cne) {
- QName nodeName = cne.getName();
- List siblings = null;
- Object obj = nameMap.get(nodeName);
- if (obj != null) {
- if (obj instanceof List) {
- // 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);
- }
- }
-
- LinkedEntries.LinkNode ln = entries.add(cne);
-
- if (siblings != null) {
- siblings.add(ln);
- } else {
- nameMap.put(nodeName, ln);
- }
- }
-
- /**
- * Appends a list of <code>ChildNodeEntry</code>s to this list.
- *
- * @param entriesList the list of <code>ChildNodeEntry</code>s to add.
- */
- 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.getUUID());
- }
- }
-
- /**
- * Removes the child node entry with the given <code>nodeName</code> and
- * <code>index</code>.
- *
- * @param nodeName the name of the child node entry to remove.
- * @param index the index of the child node entry to remove.
- * @return the removed <code>ChildNodeEntry</code> or <code>null</code>
- * if there is no matching <code>ChildNodeEntry</code>.
- */
- 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 LinkedEntries.LinkNode) {
- // map entry is a single child node entry
- if (index != Path.INDEX_DEFAULT) {
- return null;
- }
- LinkedEntries.LinkNode ln = (LinkedEntries.LinkNode) obj;
- nameMap.remove(nodeName);
- // remove LinkNode from entries
- ln.remove();
- return ln.getChildNodeEntry();
- }
-
- // map entry is a list of siblings
- List siblings = (List) obj;
- if (index > siblings.size()) {
- return null;
- }
-
- // remove from siblings list
- LinkedEntries.LinkNode ln = (LinkedEntries.LinkNode) siblings.remove(index - 1);
- ChildNodeEntry removedEntry = ln.getChildNodeEntry();
- // remove from ordered entries
- ln.remove();
-
- // 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 state.
- *
- * @param nodeState the node state whose entry is to be removed.
- * @return the removed entry or <code>null</code> if there is no such entry.
- */
- ChildNodeEntry remove(NodeState nodeState) {
- ChildNodeEntry entry = null;
- for (Iterator it = get(nodeState.getQName()).iterator(); it.hasNext(); ) {
- ChildNodeEntry tmp = (ChildNodeEntry) it.next();
- try {
- if (tmp.isAvailable() && tmp.getNodeState() == nodeState) {
- entry = tmp;
- break;
- }
- } catch (ItemStateException e) {
- log.warn("error accessing child node state: " + e.getMessage());
- }
- }
- if (entry != null) {
- return remove(entry.getName(), entry.getIndex());
- }
- return entry;
- }
-
- /**
- * Removes all child node entries
- */
- public void removeAll() {
- nameMap.clear();
- entries.clear();
- }
-
- /**
- * Reorders an existing <code>NodeState</code> before another
- * <code>NodeState</code>. If <code>beforeNode</code> is
- * <code>null</code> <code>insertNode</code> is moved to the end of the
- * child node entries.
- *
- * @param insertNode the node state to move.
- * @param beforeNode the node state where <code>insertNode</code> is
- * reordered to.
- * @throws NoSuchItemStateException if <code>insertNode</code> or
- * <code>beforeNode</code> does not have a <code>ChildNodeEntry</code>
- * in this <code>ChildNodeEntries</code>.
- */
- public void reorder(NodeState insertNode, NodeState beforeNode)
- throws NoSuchItemStateException {
- // the link node to move
- LinkedEntries.LinkNode insertLN;
- // the link node where insertLN is ordered before
- LinkedEntries.LinkNode beforeLN = null;
-
- Object insertObj = nameMap.get(insertNode.getQName());
- if (insertObj == null) {
- // no matching child node entry
- throw new NoSuchItemStateException(insertNode.getQName().toString());
- }
- insertLN = getLinkNode(insertObj, insertNode);
-
- // now retrieve LinkNode for beforeNode
- if (beforeNode != null) {
- Object beforeObj = nameMap.get(beforeNode.getQName());
- if (beforeObj == null) {
- throw new NoSuchItemStateException(beforeNode.getQName().toString());
- }
- beforeLN = getLinkNode(beforeObj, beforeNode);
- }
-
- if (insertObj instanceof List) {
- // adapt name lookup lists
- List insertList = (List) insertObj;
- if (beforeNode == null) {
- // simply move to end of list
- insertList.remove(insertLN);
- insertList.add(insertLN);
- } else {
- // move based on position of beforeLN
-
- // count our same name siblings until we reach beforeLN
- int snsCount = 0;
- QName insertName = insertNode.getQName();
- for (Iterator it = entries.linkNodeIterator(); it.hasNext(); ) {
- LinkedEntries.LinkNode ln = (LinkedEntries.LinkNode) it.next();
- if (ln == beforeLN) {
- insertList.remove(insertLN);
- insertList.add(snsCount, insertLN);
- break;
- } else if (ln == insertLN) {
- // do not increment snsCount for node to reorder
- } else if (ln.getChildNodeEntry().getName().equals(insertName)) {
- snsCount++;
- }
- }
- }
- } else {
- // no same name siblings -> nothing to do.
- }
-
- // reorder in linked list
- entries.reorderNode(insertLN, beforeLN);
- }
-
- /**
- * If the given child state got a (new) uuid assigned or its removed,
- * its childEntry must be adjusted.
- *
- * @param childState
- */
- private void replaceEntry(NodeState childState) {
- // NOTE: test if child-state needs to get a new entry not checked here.
- try {
- Object replaceObj = nameMap.get(childState.getQName());
- LinkedEntries.LinkNode ln = getLinkNode(replaceObj, childState);
- ChildNodeEntry newCne = ChildNodeReference.create(childState, isf, idFactory);
- entries.replaceNode(ln, newCne);
- } catch (NoSuchItemStateException e) {
- log.error("Internal error.", e);
- }
- }
-
- /**
- * Returns the matching <code>LinkNode</code> from a list or a single
- * <code>LinkNode</code>.
- *
- * @param listOrLinkNode List of <code>LinkNode</code>s or a single
- * <code>LinkNode</code>.
- * @param nodeState the <code>NodeState</code> which is the value
- * of on of the <code>LinkNode</code>s.
- * @return the matching <code>LinkNode</code>.
- * @throws NoSuchItemStateException if none of the <code>LinkNode</code>s
- * matches.
- */
- private LinkedEntries.LinkNode getLinkNode(Object listOrLinkNode,
- NodeState nodeState)
- throws NoSuchItemStateException {
- if (listOrLinkNode instanceof List) {
- // has same name sibling
- for (Iterator it = ((List) listOrLinkNode).iterator(); it.hasNext();) {
- LinkedEntries.LinkNode n = (LinkedEntries.LinkNode) it.next();
- ChildNodeEntry cne = n.getChildNodeEntry();
- // only check available child node entries
- try {
- if (cne.isAvailable() && cne.getNodeState() == nodeState) {
- return n;
- }
- } catch (ItemStateException e) {
- log.warn("error retrieving a child node state", e);
- }
- }
- } else {
- // single child node with this name
- ChildNodeEntry cne = ((LinkedEntries.LinkNode) listOrLinkNode).getChildNodeEntry();
- try {
- if (cne.isAvailable() && cne.getNodeState() == nodeState) {
- return (LinkedEntries.LinkNode) listOrLinkNode;
- }
- } catch (ItemStateException e) {
- log.warn("error retrieving a child node state", e);
- }
- }
- throw new NoSuchItemStateException(nodeState.getQName().toString());
- }
-
- //--------------------------------------< unmodifiable Collection view >
-
- public boolean contains(Object o) {
- if (o instanceof ChildNodeEntry) {
- // narrow down to same name sibling nodes and check list
- return get(((ChildNodeEntry) o).getName()).contains(o);
- } else {
- return false;
- }
- }
-
- public boolean containsAll(Collection c) {
- Iterator iter = c.iterator();
- while (iter.hasNext()) {
- if (!contains(iter.next())) {
- return false;
- }
- }
- return true;
- }
-
- public boolean isEmpty() {
- return entries.isEmpty();
- }
-
- public Iterator iterator() {
- return UnmodifiableIterator.decorate(entries.iterator());
- }
-
- public int size() {
- return entries.size();
- }
-
- 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()];
- }
- Iterator iter = entries.iterator();
- int i = 0;
- while (iter.hasNext()) {
- a[i++] = iter.next();
- }
- while (i < a.length) {
- a[i++] = null;
- }
- return a;
- }
-
- public boolean add(Object o) {
- throw new UnsupportedOperationException();
- }
-
- public boolean addAll(Collection c) {
- throw new UnsupportedOperationException();
- }
-
- public void clear() {
- 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();
- }
-
- }
-
- /**
- * An implementation of a linked list which provides access to the internal
- * LinkNode which links the entries of the list.
- */
- private static final class LinkedEntries extends AbstractLinkedList {
-
- LinkedEntries() {
- super();
- init();
- }
-
- /**
- * Adds a child node entry to this list.
- *
- * @param cne the child node entry to add.
- * @return the LinkNode which refers to the added <code>ChildNodeEntry</code>.
- */
- LinkNode add(ChildNodeEntry cne) {
- LinkNode ln = (LinkNode) createNode(cne);
- addNode(ln, header);
- return ln;
- }
-
- /**
- * Reorders an existing <code>LinkNode</code> before another existing
- * <code>LinkNode</code>. If <code>before</code> is <code>null</code>
- * the <code>insert</code> node is moved to the end of the list.
- *
- * @param insert the node to reorder.
- * @param before the node where to reorder node <code>insert</code>.
- */
- void reorderNode(LinkNode insert, LinkNode before) {
- removeNode(insert);
- if (before == null) {
- addNode(insert, header);
- } else {
- addNode(insert, before);
- }
- }
-
- /**
- * Replace the value of the given LinkNode with a new childNodeEntry
- * value.
- *
- * @param node
- * @param value
- */
- void replaceNode(LinkNode node, ChildNodeEntry value) {
- updateNode(node, value);
- }
-
- /**
- * Create a new <code>LinkNode</code> for a given {@link ChildNodeEntry}
- * <code>value</code>.
- *
- * @param value a child node entry.
- * @return a wrapping {@link LinkedEntries.LinkNode}.
- */
- protected Node createNode(Object value) {
- return new LinkNode(value);
- }
-
- /**
- * @return a new <code>LinkNode</code>.
- */
- protected Node createHeaderNode() {
- return new LinkNode();
- }
-
- /**
- * Returns an iterator over all
- * @return
- */
- Iterator linkNodeIterator() {
- return new Iterator() {
-
- private LinkNode next = ((LinkNode) header).getNextLinkNode();
-
- private int expectedModCount = modCount;
-
- public void remove() {
- throw new UnsupportedOperationException("remove");
- }
-
- public boolean hasNext() {
- if (expectedModCount != modCount) {
- throw new ConcurrentModificationException();
- }
- return next != header;
- }
-
- public Object next() {
- if (expectedModCount != modCount) {
- throw new ConcurrentModificationException();
- }
- if (!hasNext()) {
- throw new NoSuchElementException();
- }
- LinkNode n = next;
- next = next.getNextLinkNode();
- return n;
- }
- };
- }
-
- //-----------------------------------------------------------------------
-
- /**
- * Extends the <code>AbstractLinkedList.Node</code>.
- */
- private final class LinkNode extends AbstractLinkedList.Node {
-
- protected LinkNode() {
- super();
- }
-
- protected LinkNode(Object value) {
- super(value);
- }
-
- /**
- * @return the wrapped <code>ChildNodeEntry</code>.
- */
- public ChildNodeEntry getChildNodeEntry() {
- return (ChildNodeEntry) super.getValue();
- }
-
- /**
- * Removes this <code>LinkNode</code> from the linked list.
- */
- public void remove() {
- removeNode(this);
- }
-
- /**
- * @return the next LinkNode.
- */
- public LinkNode getNextLinkNode() {
- return (LinkNode) super.getNextNode();
- }
- }
- }
}