StoreRef
, NodeRef
world.
+ * @author britt
+ */
+class AVMNodeConverter
+{
+ /**
+ * Get a NodeRef corresponding to the given path and version.
+ * @param version The version id.
+ * @param avmPath The AVM path.
+ * @return A NodeRef with AVM info stuffed inside.
+ */
+ public static NodeRef ToNodeRef(int version, String avmPath)
+ {
+ String [] pathParts = avmPath.split(":");
+ if (pathParts.length != 2)
+ {
+ throw new AVMException("Malformed AVM Path.");
+ }
+ StoreRef storeRef = ToStoreRef(pathParts[0]);
+ String translated = version + pathParts[1];
+ return new NodeRef(storeRef, translated);
+ }
+
+ /**
+ * Get a StoreRef that corresponds to a given AVMStore name.
+ * @param avmStore The name of the AVMStore.
+ * @return A working StoreRef.
+ */
+ public static StoreRef ToStoreRef(String avmStore)
+ {
+ return new StoreRef(StoreRef.PROTOCOL_AVM, avmStore);
+ }
+
+ /**
+ * Convert a NodeRef into a version, AVMPath pair.
+ * @param nodeRef The NodeRef to convert.
+ * @return An Integer, String array.
+ */
+ public static Object[] ToAVMVersionPath(NodeRef nodeRef)
+ {
+ StoreRef store = nodeRef.getStoreRef();
+ String translated = nodeRef.getId();
+ int off = translated.indexOf("/");
+ int version = Integer.parseInt(translated.substring(0, off));
+ String path = translated.substring(off);
+ Object [] result = new Object[2];
+ result[0] = new Integer(version);
+ result[1] = store.getIdentifier() + ":" + path;
+ return result;
+ }
+
+ /**
+ * Extend an already valid AVM path by one more component.
+ * @param path The starting AVM path.
+ * @param name The name to add to it.
+ * @return The extended path.
+ */
+ public static String ExtendAVMPath(String path, String name)
+ {
+ if (path.endsWith("/"))
+ {
+ return path + name;
+ }
+ else
+ {
+ return path + "/" + name;
+ }
+ }
+
+ /**
+ * Split a path into its parent path and its base name.
+ * @param path The initial AVM path.
+ * @return An array of 2 Strings containing the parent path and the base
+ * name.
+ */
+ public static String [] SplitBase(String path)
+ {
+ int off = path.lastIndexOf("/");
+ if (off == -1)
+ {
+ throw new AVMException("Invalid Path.");
+ }
+ String [] decomposed = new String[2];
+ decomposed[0] = path.substring(0, off);
+ decomposed[1] = path.substring(off + 1);
+ return decomposed;
+ }
+}
diff --git a/source/java/org/alfresco/repo/avm/AVMNodeConverterTest.java b/source/java/org/alfresco/repo/avm/AVMNodeConverterTest.java
new file mode 100644
index 0000000000..449c21fbc6
--- /dev/null
+++ b/source/java/org/alfresco/repo/avm/AVMNodeConverterTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2006 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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.alfresco.repo.avm;
+
+import org.alfresco.service.cmr.repository.NodeRef;
+
+import junit.framework.TestCase;
+
+/**
+ * Tester of the converter from NodeRef, StoreRef space to AVM space.
+ * @author britt
+ */
+public class AVMNodeConverterTest extends TestCase
+{
+ /**
+ * Test Going betwwen a NodeRef and a version, path pair.
+ */
+ public void testTranslate()
+ {
+ String avmPath = "main:/";
+ int version = 2;
+ NodeRef nodeRef = AVMNodeConverter.ToNodeRef(version, avmPath);
+ System.out.println(nodeRef);
+ Object [] backOut = AVMNodeConverter.ToAVMVersionPath(nodeRef);
+ assertEquals(2, ((Integer)backOut[0]).intValue());
+ assertEquals(avmPath, (String)backOut[1]);
+ avmPath = "main:/fista/mista/wisticuff";
+ version = -1;
+ nodeRef = AVMNodeConverter.ToNodeRef(version, avmPath);
+ System.out.println(nodeRef);
+ backOut = AVMNodeConverter.ToAVMVersionPath(nodeRef);
+ assertEquals(-1, ((Integer)backOut[0]).intValue());
+ assertEquals(avmPath, (String)backOut[1]);
+ }
+}
diff --git a/source/java/org/alfresco/repo/avm/AVMNodeImpl.java b/source/java/org/alfresco/repo/avm/AVMNodeImpl.java
index f246840cb5..2565c6e0d3 100644
--- a/source/java/org/alfresco/repo/avm/AVMNodeImpl.java
+++ b/source/java/org/alfresco/repo/avm/AVMNodeImpl.java
@@ -356,4 +356,12 @@ abstract class AVMNodeImpl implements AVMNode, Serializable
{
AVMContext.fgInstance.fAVMNodePropertyDAO.delete(this, name);
}
+
+ /**
+ * Delete all properties from this node.
+ */
+ public void deleteProperties()
+ {
+ AVMContext.fgInstance.fAVMNodePropertyDAO.deleteAll(this);
+ }
}
diff --git a/source/java/org/alfresco/repo/avm/AVMNodeService.java b/source/java/org/alfresco/repo/avm/AVMNodeService.java
new file mode 100644
index 0000000000..1f66f1b9de
--- /dev/null
+++ b/source/java/org/alfresco/repo/avm/AVMNodeService.java
@@ -0,0 +1,1146 @@
+/*
+ * Copyright (C) 2006 Alfresco, Inc.
+ *
+ * Licensed under the Mozilla Public License version 1.1
+ * with a permitted attribution clause. You may obtain a
+ * copy of the License at
+ *
+ * http://www.alfresco.org/legal/license.txt
+ *
+ * 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.alfresco.repo.avm;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+
+import javax.naming.OperationNotSupportedException;
+
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.domain.PropertyValue;
+import org.alfresco.service.Auditable;
+import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.cmr.dictionary.InvalidAspectException;
+import org.alfresco.service.cmr.dictionary.InvalidTypeException;
+import org.alfresco.service.cmr.dictionary.PropertyDefinition;
+import org.alfresco.service.cmr.repository.AssociationExistsException;
+import org.alfresco.service.cmr.repository.AssociationRef;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.CyclicChildRelationshipException;
+import org.alfresco.service.cmr.repository.InvalidChildAssociationRefException;
+import org.alfresco.service.cmr.repository.InvalidNodeRefException;
+import org.alfresco.service.cmr.repository.InvalidStoreRefException;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.repository.Path;
+import org.alfresco.service.cmr.repository.StoreExistsException;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.namespace.NamespaceService;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.namespace.QNamePattern;
+import org.apache.log4j.Logger;
+
+/**
+ * NodeService implementing facade over AVMService.
+ * @author britt
+ */
+public class AVMNodeService implements NodeService
+{
+ private static Logger fgLogger = Logger.getLogger(AVMNodeService.class);
+
+ /**
+ * Reference to AVMService.
+ */
+ private AVMService fAVMService;
+
+ /**
+ * Reference to DictionaryService.
+ */
+ private DictionaryService fDictionaryService;
+
+ /**
+ * Set the AVMService. For Spring.
+ * @param service The AVMService instance.
+ */
+ public void setAvmService(AVMService service)
+ {
+ fAVMService = service;
+ }
+
+ /**
+ * Default constructor.
+ */
+ public AVMNodeService()
+ {
+ }
+
+ /**
+ * Gets a list of all available node store references
+ *
+ * @return Returns a list of store references
+ */
+ public List+ * This involves changing the node's primary parent and possibly the name of the + * association referencing it. + *
+ * If the new parent is in a different store from the original, then the entire
+ * node hierarchy is moved to the new store. Inter-store associations are not
+ * affected.
+ *
+ * @param nodeToMoveRef the node to move
+ * @param newParentRef the new parent of the moved node
+ * @param assocTypeQName the type of the association to create. This is used
+ * for verification against the data dictionary.
+ * @param assocQName the qualified name of the new child association
+ * @return Returns a reference to the newly created child association
+ * @throws InvalidNodeRefException if either the parent node or move node reference is invalid
+ * @throws CyclicChildRelationshipException if the child partakes in a cyclic relationship after the add
+ *
+ * @see #getPrimaryParent(NodeRef)
+ */
+ public ChildAssociationRef moveNode(
+ NodeRef nodeToMoveRef,
+ NodeRef newParentRef,
+ QName assocTypeQName,
+ QName assocQName)
+ throws InvalidNodeRefException
+ {
+ // AVM stores only allow simple child associations.
+ if (!assocTypeQName.equals(ContentModel.ASSOC_CONTAINS))
+ {
+ throw new InvalidTypeException(assocTypeQName);
+ }
+ // Extract the parts from the source.
+ Object [] src = AVMNodeConverter.ToAVMVersionPath(nodeToMoveRef);
+ int srcVersion = (Integer)src[0];
+ if (srcVersion >= 0)
+ {
+ throw new InvalidNodeRefException("Read Only Store.", nodeToMoveRef);
+ }
+ String srcPath = (String)src[0];
+ String [] splitSrc = null;
+ try
+ {
+ splitSrc = AVMNodeConverter.SplitBase(srcPath);
+ }
+ catch (AVMException e)
+ {
+ throw new InvalidNodeRefException("Invalid src path.", nodeToMoveRef);
+ }
+ String srcParent = splitSrc[0];
+ if (srcParent.endsWith(":"))
+ {
+ throw new InvalidNodeRefException("Cannot rename root node.", nodeToMoveRef);
+ }
+ String srcName = splitSrc[1];
+ // Extract and setup the parts of the destination.
+ Object[] dst = AVMNodeConverter.ToAVMVersionPath(newParentRef);
+ if ((Integer)dst[0] >= 0)
+ {
+ throw new InvalidNodeRefException("Read Only Store.", newParentRef);
+ }
+ String dstParent = (String)dst[1];
+ String dstName = assocQName.getLocalName();
+ // Actually perform the rename and return a pseudo
+ // ChildAssociationRef.
+ try
+ {
+ fAVMService.rename(srcParent, srcName, dstParent, dstName);
+ String dstPath = AVMNodeConverter.ExtendAVMPath(dstParent, dstName);
+ return new ChildAssociationRef(assocTypeQName,
+ newParentRef,
+ assocQName,
+ AVMNodeConverter.ToNodeRef(-1, dstPath),
+ true,
+ -1);
+ }
+ catch (AVMNotFoundException e)
+ {
+ throw new InvalidNodeRefException("Non existent node.", nodeToMoveRef);
+ }
+ catch (AVMExistsException e)
+ {
+ throw new InvalidNodeRefException("Target already exists.", newParentRef);
+ }
+ catch (AVMException e)
+ {
+ throw new InvalidNodeRefException("Illegal move.", nodeToMoveRef);
+ }
+ }
+
+ /**
+ * Set the ordering index of the child association. This affects the ordering of
+ * of the return values of methods that return a set of children or child
+ * associations.
+ *
+ * @param childAssocRef the child association that must be moved in the order
+ * @param index an arbitrary index that will affect the return order
+ *
+ * @see #getChildAssocs(NodeRef)
+ * @see #getChildAssocs(NodeRef, QNamePattern, QNamePattern)
+ * @see ChildAssociationRef#getNthSibling()
+ */
+ public void setChildAssociationIndex(
+ ChildAssociationRef childAssocRef,
+ int index)
+ throws InvalidChildAssociationRefException
+ {
+ // TODO We'll keep this a no-op unless there's a
+ // compelling reason to implement this capability
+ // for the AVM repository.
+ }
+
+ /**
+ * @param nodeRef
+ * @return Returns the type name
+ * @throws InvalidNodeRefException if the node could not be found
+ *
+ * @see org.alfresco.service.cmr.dictionary.DictionaryService
+ */
+ public QName getType(NodeRef nodeRef) throws InvalidNodeRefException
+ {
+ Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef);
+ try
+ {
+ AVMNodeDescriptor desc = fAVMService.lookup((Integer)avmVersionPath[0],
+ (String)avmVersionPath[1]);
+ if (desc.isDirectory())
+ {
+ return ContentModel.TYPE_FOLDER;
+ }
+ else
+ {
+ return ContentModel.TYPE_CONTENT;
+ }
+ }
+ catch (AVMNotFoundException e)
+ {
+ throw new InvalidNodeRefException("Not Found.", nodeRef);
+ }
+ }
+
+ /**
+ * Re-sets the type of the node. Can be called in order specialise a node to a sub-type.
+ *
+ * This should be used with caution since calling it changes the type of the node and thus
+ * implies a different set of aspects, properties and associations. It is the calling codes
+ * responsibility to ensure that the node is in a approriate state after changing the type.
+ *
+ * @param nodeRef the node reference
+ * @param typeQName the type QName
+ *
+ * @since 1.1
+ */
+ public void setType(NodeRef nodeRef, QName typeQName) throws InvalidNodeRefException
+ {
+ throw new UnsupportedOperationException("AVM Types are immutable.");
+ }
+
+ /**
+ * Applies an aspect to the given node. After this method has been called,
+ * the node with have all the aspect-related properties present
+ *
+ * @param nodeRef
+ * @param aspectTypeQName the aspect to apply to the node
+ * @param aspectProperties a minimum of the mandatory properties required for
+ * the aspect
+ * @throws InvalidNodeRefException
+ * @throws InvalidAspectException if the class reference is not to a valid aspect
+ *
+ * @see org.alfresco.service.cmr.dictionary.DictionaryService#getAspect(QName)
+ * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getProperties()
+ */
+ public void addAspect(
+ NodeRef nodeRef,
+ QName aspectTypeQName,
+ Map
+ * All associations (both children and regular node associations)
+ * will be deleted, and where the given node is the primary parent,
+ * the children will also be cascade deleted.
+ *
+ * @param nodeRef reference to a node within a store
+ * @throws InvalidNodeRefException if the reference given is invalid
+ */
+ public void deleteNode(NodeRef nodeRef) throws InvalidNodeRefException
+ {
+ Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef);
+ if ((Integer)avmVersionPath[0] != -1)
+ {
+ throw new InvalidNodeRefException("Read only store.", nodeRef);
+ }
+ String [] avmPathBase = AVMNodeConverter.SplitBase((String)avmVersionPath[1]);
+ if (avmPathBase[0].endsWith(":"))
+ {
+ throw new InvalidNodeRefException("Cannot delete root node.", nodeRef);
+ }
+ try
+ {
+ fAVMService.removeNode(avmPathBase[0], avmPathBase[1]);
+ }
+ catch (AVMNotFoundException e)
+ {
+ throw new InvalidNodeRefException("Not Found.", nodeRef);
+ }
+ }
+
+ /**
+ * Makes a parent-child association between the given nodes. Both nodes must belong to the same store.
+ *
+ *
+ * @param parentRef
+ * @param childRef
+ * @param assocTypeQName the qualified name of the association type as defined in the datadictionary
+ * @param qname the qualified name of the association
+ * @return Returns a reference to the newly created child association
+ * @throws InvalidNodeRefException if the parent or child nodes could not be found
+ * @throws CyclicChildRelationshipException if the child partakes in a cyclic relationship after the add
+ */
+ public ChildAssociationRef addChild(
+ NodeRef parentRef,
+ NodeRef childRef,
+ QName assocTypeQName,
+ QName qname) throws InvalidNodeRefException
+ {
+ throw new UnsupportedOperationException("addChild: unsupported");
+ }
+
+ /**
+ * Severs all parent-child relationships between two nodes.
+ *
+ * The child node will be cascade deleted if one of the associations was the
+ * primary association, i.e. the one with which the child node was created.
+ *
+ * @param parentRef the parent end of the association
+ * @param childRef the child end of the association
+ * @throws InvalidNodeRefException if the parent or child nodes could not be found
+ */
+ public void removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException
+ {
+ Object [] parentVersionPath = AVMNodeConverter.ToAVMVersionPath(parentRef);
+ if ((Integer)parentVersionPath[0] >= 0)
+ {
+ throw new InvalidNodeRefException("Read only store.", parentRef);
+ }
+ Object [] childVersionPath = AVMNodeConverter.ToAVMVersionPath(parentRef);
+ if ((Integer)childVersionPath[0] >= 0)
+ {
+ throw new InvalidNodeRefException("Read only store.", childRef);
+ }
+ String parentPath = (String)parentVersionPath[1];
+ String childPath = (String)childVersionPath[1];
+ String [] childPathBase = AVMNodeConverter.SplitBase(childPath);
+ if (!childPathBase[0].equals(parentPath))
+ {
+ throw new InvalidNodeRefException("Not a child.", childRef);
+ }
+ try
+ {
+ fAVMService.removeNode(childPathBase[0], childPathBase[1]);
+ }
+ catch (AVMNotFoundException e)
+ {
+ throw new InvalidNodeRefException("Not found.", childRef);
+ }
+ }
+
+ /**
+ * @param nodeRef
+ * @return Returns all properties keyed by their qualified name
+ * @throws InvalidNodeRefException if the node could not be found
+ */
+ public Map
+ * NOTE: Null values are allowed.
+ *
+ * @param nodeRef
+ * @param properties all the properties of the node keyed by their qualified names
+ * @throws InvalidNodeRefException if the node could not be found
+ */
+ public void setProperties(NodeRef nodeRef, Map
+ * NOTE: Null values are allowed.
+ *
+ * @param nodeRef
+ * @param qname the fully qualified name of the property
+ * @param propertyValue the value of the property - never null
+ * @throws InvalidNodeRefException if the node could not be found
+ */
+ public void setProperty(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException
+ {
+ // TODO Just until we can set built in properties on AVM Nodes.
+ if (isBuiltInProperty(qname))
+ {
+ return;
+ }
+ Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef);
+ if ((Integer)avmVersionPath[0] >= 0)
+ {
+ throw new InvalidNodeRefException("Read only store.", nodeRef);
+ }
+ try
+ {
+ fAVMService.setNodeProperty((String)avmVersionPath[1], qname, new PropertyValue(null, value));
+ }
+ catch (AVMNotFoundException e)
+ {
+ throw new InvalidNodeRefException("Not Found.", nodeRef);
+ }
+ }
+
+ /**
+ * @param nodeRef the child node
+ * @return Returns a list of all parent-child associations that exist where the given
+ * node is the child
+ * @throws InvalidNodeRefException if the node could not be found
+ *
+ * @see #getParentAssocs(NodeRef, QNamePattern, QNamePattern)
+ */
+ public List
+ * The resultant list is ordered by (a) explicit index and (b) association creation time.
+ *
+ * @param nodeRef the child node
+ * @param typeQNamePattern the pattern that the type qualified name of the association must match
+ * @param qnamePattern the pattern that the qnames of the assocs must match
+ * @return Returns a list of all parent-child associations that exist where the given
+ * node is the child
+ * @throws InvalidNodeRefException if the node could not be found
+ *
+ * @see ChildAssociationRef#getNthSibling()
+ * @see #setChildAssociationIndex(ChildAssociationRef, int)
+ * @see QName
+ * @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL
+ */
+ public List
+ * The resultant list is ordered by (a) explicit index and (b) association creation time.
+ *
+ * @param nodeRef the parent node - usually a container
+ * @return Returns a collection of
+ * For a root node, the parent node reference will be null.
+ *
+ * @param nodeRef
+ * @return Returns the primary parent-child association of the node
+ * @throws InvalidNodeRefException if the node could not be found
+ */
+ public ChildAssociationRef getPrimaryParent(NodeRef nodeRef) throws InvalidNodeRefException
+ {
+ ListSerializable
instances.
+ * The properties given must still fulfill the requirements of the class and
+ * aspects relevant to the node.
+ * qName
is a built-in propety.
+ */
+ private boolean isBuiltInProperty(QName qName)
+ {
+ return qName.equals(ContentModel.PROP_CREATED) ||
+ qName.equals(ContentModel.PROP_CREATOR) ||
+ qName.equals(ContentModel.PROP_MODIFIED) ||
+ qName.equals(ContentModel.PROP_MODIFIER) ||
+ qName.equals(ContentModel.PROP_OWNER);
+ }
+
+ /**
+ * Sets the value of a property to be any Serializable
instance.
+ * To remove a property value, use {@link #getProperties(NodeRef)}, remove the
+ * value and call {@link #setProperties(NodeRef, Map)}.
+ * ChildAssocRef
instances. If the
+ * node is not a container then the result will be empty.
+ * @throws InvalidNodeRefException if the node could not be found
+ *
+ * @see #getChildAssocs(NodeRef, QNamePattern, QNamePattern)
+ * @see #setChildAssociationIndex(ChildAssociationRef, int)
+ * @see ChildAssociationRef#getNthSibling()
+ */
+ public ListChildAssocRef
instances. If the
+ * node is not a container then the result will be empty.
+ * @throws InvalidNodeRefException if the node could not be found
+ *
+ * @see QName
+ * @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL
+ */
+ @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "typeQNamePattern", "qnamePattern"})
+ public ListNodeAssocRef
instances for which the
+ * given node is a source
+ * @throws InvalidNodeRefException if the source node could not be found
+ *
+ * @see QName
+ * @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL
+ */
+ public ListNodeAssocRef
instances for which the
+ * given node is a target
+ * @throws InvalidNodeRefException
+ *
+ * @see QName
+ * @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL
+ */
+ public List