diff --git a/source/java/org/alfresco/repo/avm/AVMNode.java b/source/java/org/alfresco/repo/avm/AVMNode.java index 7282182227..c3edd77a28 100644 --- a/source/java/org/alfresco/repo/avm/AVMNode.java +++ b/source/java/org/alfresco/repo/avm/AVMNode.java @@ -166,4 +166,9 @@ interface AVMNode * @param name The name of the property. */ public void deleteProperty(QName name); + + /** + * Delete all properties from this node. + */ + public void deleteProperties(); } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/avm/AVMNodeConverter.java b/source/java/org/alfresco/repo/avm/AVMNodeConverter.java new file mode 100644 index 0000000000..b10936c45d --- /dev/null +++ b/source/java/org/alfresco/repo/avm/AVMNodeConverter.java @@ -0,0 +1,112 @@ +/* + * 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 org.alfresco.service.cmr.repository.StoreRef; + +/** + * Utility for going back and forth between the AVM world and + * the 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 getStores() + { + // For AVM stores we fake up StoreRefs. + List stores = fAVMService.getAVMStores(); + List result = new ArrayList(); + for (AVMStoreDescriptor desc : stores) + { + String name = desc.getName(); + result.add(new StoreRef(StoreRef.PROTOCOL_AVM, name)); + } + return result; + } + + /** + * Create a new AVM store. + * @param protocol the implementation protocol + * @param identifier the protocol-specific identifier + * @return Returns a reference to the store + * @throws StoreExistsException + */ + public StoreRef createStore(String protocol, String identifier) throws StoreExistsException + { + StoreRef result = new StoreRef(StoreRef.PROTOCOL_AVM, identifier); + try + { + fAVMService.createAVMStore(identifier); + return result; + } + catch (AVMExistsException e) + { + throw new StoreExistsException("AVMStore exists", result); + } + } + + /** + * Does the indicated store exist? + * @param storeRef a reference to the store to look for + * @return Returns true if the store exists, otherwise false + */ + public boolean exists(StoreRef storeRef) + { + try + { + fAVMService.getAVMStore(storeRef.getIdentifier()); + return true; + } + catch (AVMNotFoundException e) + { + return false; + } + } + + /** + * @param nodeRef a reference to the node to look for + * @return Returns true if the node exists, otherwise false + */ + public boolean exists(NodeRef nodeRef) + { + Object [] avmInfo = AVMNodeConverter.ToAVMVersionPath(nodeRef); + int version = (Integer)avmInfo[0]; + String avmPath = (String)avmInfo[1]; + try + { + fAVMService.lookup(version, avmPath); + return true; + } + catch (AVMException e) + { + return false; + } + } + + /** + * Gets the ID of the last transaction that caused the node to change. This includes + * deletions, so it is possible that the node being referenced no longer exists. + * If the node never existed, then null is returned. + * + * @param nodeRef a reference to a current or previously existing node + * @return Returns the status of the node, or null if the node never existed + */ + public NodeRef.Status getNodeStatus(NodeRef nodeRef) + { + // TODO Need to find out if this is important and if so + // need to capture Transaction IDs. + return new NodeRef.Status("Unknown", !exists(nodeRef)); + } + + /** + * @param storeRef a reference to an existing store + * @return Returns a reference to the root node of the store + * @throws InvalidStoreRefException if the store could not be found + */ + public NodeRef getRootNode(StoreRef storeRef) throws InvalidStoreRefException + { + try + { + String storeName = storeRef.getIdentifier(); + fAVMService.getAVMStore(storeName); + return AVMNodeConverter.ToNodeRef(-1, storeName + ":/"); + } + catch (AVMNotFoundException e) + { + throw new InvalidStoreRefException(storeRef); + } + } + + /** + * @see #createNode(NodeRef, QName, QName, QName, Map) + */ + public ChildAssociationRef createNode( + NodeRef parentRef, + QName assocTypeQName, + QName assocQName, + QName nodeTypeQName) + throws InvalidNodeRefException, InvalidTypeException + { + return createNode(parentRef, + assocTypeQName, + assocQName, + nodeTypeQName, + new HashMap()); + } + + /** + * Creates a new, non-abstract, real node as a primary child of the given parent node. + * + * @param parentRef the parent 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 association + * @param nodeTypeQName a reference to the node type + * @param properties optional map of properties to keyed by their qualified names + * @return Returns a reference to the newly created child association + * @throws InvalidNodeRefException if the parent reference is invalid + * @throws InvalidTypeException if the node type reference is not recognised + * + * @see org.alfresco.service.cmr.dictionary.DictionaryService + */ + public ChildAssociationRef createNode( + NodeRef parentRef, + QName assocTypeQName, + QName assocQName, + QName nodeTypeQName, + Map properties) + throws InvalidNodeRefException, InvalidTypeException + { + // AVM stores only allow simple child associations. + if (!assocTypeQName.equals(ContentModel.ASSOC_CONTAINS)) + { + throw new InvalidTypeException(assocTypeQName); + } + String nodeName = assocQName.getLocalName(); + Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(parentRef); + int version = ((Integer)avmVersionPath[0]); + if (version >= 0) + { + throw new InvalidNodeRefException("Read only store.", parentRef); + } + String avmPath = (String)avmVersionPath[1]; + // Do the creates for supported types, or error out. + try + { + if (nodeTypeQName.equals(ContentModel.TYPE_FOLDER)) + { + fAVMService.createDirectory(avmPath, nodeName); + } + else if (nodeTypeQName.equals(ContentModel.TYPE_CONTENT)) + { + fAVMService.createFile(avmPath, nodeName); + } + else + { + throw new InvalidTypeException("Invalid node type for AVM.", nodeTypeQName); + } + } + catch (AVMNotFoundException e) + { + throw new InvalidNodeRefException("Not Found.", parentRef); + } + catch (AVMExistsException e) + { + throw new InvalidNodeRefException("Child " + nodeName + " exists", parentRef); + } + String newAVMPath = AVMNodeConverter.ExtendAVMPath(avmPath, nodeName); + NodeRef childRef = AVMNodeConverter.ToNodeRef(-1, newAVMPath); + // TODO Q? Is properties ever null? + Map props = new HashMap(); + for (QName qname : properties.keySet()) + { + props.put(qname, new PropertyValue(null, properties.get(qname))); + } + fAVMService.setNodeProperties(newAVMPath, props); + return + new ChildAssociationRef(assocTypeQName, + parentRef, + assocQName, + childRef, + true, + -1); + } + + /** + * Moves the primary location of the given node. + *

+ * 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 aspectProperties) + throws InvalidNodeRefException, InvalidAspectException + { + throw new UnsupportedOperationException("AVM Nodes cannot be advised yet."); + } + + /** + * Remove an aspect and all related properties from a node + * + * @param nodeRef + * @param aspectTypeQName the type of aspect to remove + * @throws InvalidNodeRefException if the node could not be found + * @throws InvalidAspectException if the the aspect is unknown or if the + * aspect is mandatory for the class of the node + */ + public void removeAspect(NodeRef nodeRef, QName aspectTypeQName) + throws InvalidNodeRefException, InvalidAspectException + { + throw new UnsupportedOperationException("AVM Nodes do not yet have aspects."); + } + + /** + * Determines if a given aspect is present on a node. Aspects may only be + * removed if they are NOT mandatory. + * + * @param nodeRef + * @param aspectTypeQName + * @return Returns true if the aspect has been applied to the given node, + * otherwise false + * @throws InvalidNodeRefException if the node could not be found + * @throws InvalidAspectException if the aspect reference is invalid + */ + public boolean hasAspect(NodeRef nodeRef, QName aspectTypeQName) + throws InvalidNodeRefException, InvalidAspectException + { + return false; + } + + /** + * @param nodeRef + * @return Returns a set of all aspects applied to the node, including mandatory + * aspects + * @throws InvalidNodeRefException if the node could not be found + */ + public Set getAspects(NodeRef nodeRef) throws InvalidNodeRefException + { + // TODO This is almost certainly not the right thing to do. + return new HashSet(); + } + + /** + * Deletes the given node. + *

+ * 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 getProperties(NodeRef nodeRef) throws InvalidNodeRefException + { + Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef); + Map props = null; + AVMNodeDescriptor desc = null; + try + { + props = fAVMService.getNodeProperties((Integer)avmVersionPath[0], + (String)avmVersionPath[1]); + desc = fAVMService.lookup((Integer)avmVersionPath[0], + (String)avmVersionPath[1]); + } + catch (AVMNotFoundException e) + { + throw new InvalidNodeRefException("Not Found.", nodeRef); + } + Map result = new HashMap(); + for (QName qName : props.keySet()) + { + PropertyValue value = props.get(qName); + PropertyDefinition def = fDictionaryService.getProperty(qName); + result.put(qName, value.getValue(def.getDataType().getName())); + } + // Now spoof properties that are built in. + result.put(ContentModel.PROP_CREATED, new Date(desc.getCreateDate())); + result.put(ContentModel.PROP_CREATOR, desc.getCreator()); + result.put(ContentModel.PROP_MODIFIED, new Date(desc.getModDate())); + result.put(ContentModel.PROP_MODIFIER, desc.getLastModifier()); + result.put(ContentModel.PROP_OWNER, desc.getOwner()); + return result; + } + + /** + * @param nodeRef + * @param qname the qualified name of the property + * @return Returns the value of the property, or null if not yet set + * @throws InvalidNodeRefException if the node could not be found + */ + public Serializable getProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException + { + Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef); + if (isBuiltInProperty(qname)) + { + return getBuiltInProperty(avmVersionPath, qname, nodeRef); + } + try + { + PropertyValue value = fAVMService.getNodeProperty((Integer)avmVersionPath[0], + (String)avmVersionPath[1], + qname); + if (value == null) + { + return null; + } + PropertyDefinition def = fDictionaryService.getProperty(qname); + return value.getValue(def.getDataType().getName()); + } + catch (AVMNotFoundException e) + { + throw new InvalidNodeRefException("Not Found.", nodeRef); + } + } + + /** + * A Helper to spoof built in properties. + * @param avmVersionPath The broken out version and path from a NodeRef. + * @param qName The name of the property to retrieve. + * @param nodeRef The original NodeRef (for error reporting). + * @return The property value. + */ + private Serializable getBuiltInProperty(Object [] avmVersionPath, + QName qName, + NodeRef nodeRef) + { + AVMNodeDescriptor desc = null; + try + { + desc = fAVMService.lookup((Integer)avmVersionPath[0], + (String)avmVersionPath[1]); + } + catch (AVMNotFoundException e) + { + throw new InvalidNodeRefException("Not Found.", nodeRef); + } + if (qName.equals(ContentModel.PROP_CREATED)) + { + return new Date(desc.getCreateDate()); + } + else if (qName.equals(ContentModel.PROP_CREATOR)) + { + return desc.getCreator(); + } + else if (qName.equals(ContentModel.PROP_MODIFIED)) + { + return new Date(desc.getModDate()); + } + else if (qName.equals(ContentModel.PROP_MODIFIER)) + { + return desc.getLastModifier(); + } + else if (qName.equals(ContentModel.PROP_OWNER)) + { + return desc.getOwner(); + } + else + { + fgLogger.error("Invalid Built In Property: " + qName); + return null; + } + } + + /** + * Set the values of all properties to be an Serializable instances. + * The properties given must still fulfill the requirements of the class and + * aspects relevant to the node. + *

+ * 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 properties) throws InvalidNodeRefException + { + Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef); + if ((Integer)avmVersionPath[0] >= 0) + { + throw new InvalidNodeRefException("Read only store.", nodeRef); + } + try + { + fAVMService.deleteNodeProperties((String)avmVersionPath[1]); + Map values = new HashMap(); + for (QName qName : properties.keySet()) + { + // TODO This is until modification of built-in properties + // For AVM nodes is in place. + if (isBuiltInProperty(qName)) + { + continue; + } + values.put(qName, new PropertyValue(null, properties.get(qName))); + } + fAVMService.setNodeProperties((String)avmVersionPath[1], values); + } + catch (AVMNotFoundException e) + { + throw new InvalidNodeRefException("Not Found.", nodeRef); + } + } + + /** + * Helper to distinguish built-in from generic properties. + * @param qName The name of the property to check. + * @return Whether 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)}. + *

+ * 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 getParentAssocs(NodeRef nodeRef) throws InvalidNodeRefException + { + // TODO OK, for now we'll simply return the single parent that corresponds + // to the path stuffed in the NodeRef. + Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef); + String path = (String)avmVersionPath[1]; + List result = new ArrayList(); + String [] splitPath = AVMNodeConverter.SplitBase(path); + if (splitPath[0].endsWith(":")) + { + return result; + } + result.add(new ChildAssociationRef(ContentModel.ASSOC_CONTAINS, + AVMNodeConverter.ToNodeRef((Integer)avmVersionPath[0], + splitPath[0]), + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, + splitPath[1]), + nodeRef, + true, + -1)); + return result; + } + + /** + * Gets all parent associations where the pattern of the association qualified + * name is a match + *

+ * 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 getParentAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, QNamePattern qnamePattern) + throws InvalidNodeRefException + { + if (!typeQNamePattern.isMatch(ContentModel.ASSOC_CONTAINS)) + { + return new ArrayList(); + } + List result = getParentAssocs(nodeRef); + if (result.size() == 0) + { + return result; + } + if (qnamePattern.isMatch(result.get(0).getQName())) + { + return result; + } + return new ArrayList(); + } + + /** + * Get all child associations of the given node. + *

+ * 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 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 List getChildAssocs(NodeRef nodeRef) throws InvalidNodeRefException + { + Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef); + int version = (Integer)avmVersionPath[0]; + String path = (String)avmVersionPath[1]; + List result = new ArrayList(); + SortedMap children = null; + try + { + children = + fAVMService.getDirectoryListing(version, + path); + } + catch (AVMNotFoundException e) + { + throw new InvalidNodeRefException("Not Found.", nodeRef); + } + catch (AVMWrongTypeException e) + { + return result; + } + for (String name : children.keySet()) + { + result.add(new ChildAssociationRef(ContentModel.ASSOC_CONTAINS, + nodeRef, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, + name), + AVMNodeConverter.ToNodeRef( + version, + AVMNodeConverter.ExtendAVMPath(path, name)), + true, + -1)); + } + return result; + } + + /** + * Gets all child associations where the pattern of the association qualified + * name is a match. + * + * @param nodeRef the parent node - usually a container + * @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 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 QName + * @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL + */ + @Auditable(key = Auditable.Key.ARG_0 ,parameters = {"nodeRef", "typeQNamePattern", "qnamePattern"}) + public List getChildAssocs( + NodeRef nodeRef, + QNamePattern typeQNamePattern, + QNamePattern qnamePattern) + throws InvalidNodeRefException + { + List result = new ArrayList(); + if (!typeQNamePattern.isMatch(ContentModel.ASSOC_CONTAINS)) + { + return result; + } + List all = getChildAssocs(nodeRef); + for (ChildAssociationRef child : all) + { + if (!qnamePattern.isMatch(child.getQName())) + { + continue; + } + result.add(child); + } + return result; + } + + /** + * Fetches the primary parent-child relationship. + *

+ * 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 + { + List parents = getParentAssocs(nodeRef); + if (parents.size() == 0) + { + return null; + } + return parents.get(0); + } + + /** + * + * @param sourceRef a reference to a real node + * @param targetRef a reference to a node + * @param assocTypeQName the qualified name of the association type + * @return Returns a reference to the new association + * @throws InvalidNodeRefException if either of the nodes could not be found + * @throws AssociationExistsException + */ + public AssociationRef createAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) + throws InvalidNodeRefException, AssociationExistsException + { + throw new UnsupportedOperationException("AVM does not support arbitrary associations."); + } + + /** + * + * @param sourceRef the associaton source node + * @param targetRef the association target node + * @param assocTypeQName the qualified name of the association type + * @throws InvalidNodeRefException if either of the nodes could not be found + */ + public void removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) + throws InvalidNodeRefException + { + throw new UnsupportedOperationException("AVM does not support arbitrary associations."); + } + + /** + * Fetches all associations from the given source where the associations' + * qualified names match the pattern provided. + * + * @param sourceRef the association source + * @param qnamePattern the association qname pattern to match against + * @return Returns a list of NodeAssocRef 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 List getTargetAssocs(NodeRef sourceRef, QNamePattern qnamePattern) + throws InvalidNodeRefException + { + return new ArrayList(); + } + + /** + * Fetches all associations to the given target where the associations' + * qualified names match the pattern provided. + * + * @param targetRef the association target + * @param qnamePattern the association qname pattern to match against + * @return Returns a list of NodeAssocRef instances for which the + * given node is a target + * @throws InvalidNodeRefException + * + * @see QName + * @see org.alfresco.service.namespace.RegexQNamePattern#MATCH_ALL + */ + public List getSourceAssocs(NodeRef targetRef, QNamePattern qnamePattern) + throws InvalidNodeRefException + { + return new ArrayList(); + } + + /** + * The root node has an entry in the path(s) returned. For this reason, there + * will always be at least one path element in the returned path(s). + * The first element will have a null parent reference and qname. + * + * @param nodeRef + * @return Returns the path to the node along the primary node path + * @throws InvalidNodeRefException if the node could not be found + * + * @see #getPaths(NodeRef, boolean) + */ + public Path getPath(NodeRef nodeRef) throws InvalidNodeRefException + { + // TODO Review later. This may be wrong. + Path path = new Path(); + Object [] avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef); + int version = (Integer)avmVersionPath[0]; + String currPath = (String)avmVersionPath[1]; + while (!currPath.endsWith("/")) + { + String [] splitPath = AVMNodeConverter.SplitBase(currPath); + String parentPath = splitPath[0]; + String name = splitPath[1]; + ChildAssociationRef caRef = + new ChildAssociationRef(ContentModel.ASSOC_CONTAINS, + AVMNodeConverter.ToNodeRef(version, parentPath), + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, + name), + AVMNodeConverter.ToNodeRef(version, currPath), + true, + -1); + path.prepend(new Path.ChildAssocElement(caRef)); + } + return path; + } + + /** + * The root node has an entry in the path(s) returned. For this reason, there + * will always be at least one path element in the returned path(s). + * The first element will have a null parent reference and qname. + * + * @param nodeRef + * @param primaryOnly true if only the primary path must be retrieved. If true, the + * result will have exactly one entry. + * @return Returns a List of all possible paths to the given node + * @throws InvalidNodeRefException if the node could not be found + */ + public List getPaths(NodeRef nodeRef, boolean primaryOnly) throws InvalidNodeRefException + { + List result = new ArrayList(); + result.add(getPath(nodeRef)); + return result; + } + + /** + * Get the node where archived items will have gone when deleted from the given store. + * + * @param storeRef the store that items were deleted from + * @return Returns the archive node parent + */ + public NodeRef getStoreArchiveNode(StoreRef storeRef) + { + throw new UnsupportedOperationException("AVM does not support this operation."); + } + + /** + * Restore an individual node (along with its sub-tree nodes) to the target location. + * The archived node must have the {@link org.alfresco.model.ContentModel#ASPECT_ARCHIVED archived aspect} + * set against it. + * + * @param archivedNodeRef the archived node + * @param destinationParentNodeRef the parent to move the node into + * or null to use the original + * @param assocTypeQName the primary association type name to use in the new location + * or null to use the original + * @param assocQName the primary association name to use in the new location + * or null to use the original + * @return Returns the reference to the newly created node + */ + public NodeRef restoreNode( + NodeRef archivedNodeRef, + NodeRef destinationParentNodeRef, + QName assocTypeQName, + QName assocQName) + { + throw new UnsupportedOperationException("AVM does not support this operation."); + } +} diff --git a/source/java/org/alfresco/repo/avm/AVMPathConverter.java b/source/java/org/alfresco/repo/avm/AVMPathConverter.java deleted file mode 100644 index 2e9fa0311f..0000000000 --- a/source/java/org/alfresco/repo/avm/AVMPathConverter.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * 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; - -/** - * Utility class with static methods to convert between - * a NodeRef style id string and an ordinary AVM path. - * @author britt - */ -public class AVMPathConverter -{ - /** - * Converts to a string to stuff into a NodeRef. Version = 3 - * of main:/snot/efluvium/biggle would be converted into - * 3/main/snot/efluvium/biggle - * @param version The version id. - * @param avmPath The full AVM path. - * @return A mangled string appropriate for stuffing into a NodeRef. - */ - public static String toNodeRefStyle(int version, String avmPath) - { - String [] pathParts = avmPath.split(":"); - if (pathParts.length != 2) - { - throw new AVMException("Malformed path."); - } - return version + "/" + pathParts[0] + pathParts[1]; - } - - /** - * Convert from a NodeRef packed form to a version number - * and standard AVM path. - * @param nrID The NodeRef packed for of an AVM path. - * @return The version number and the standard AVM path. - */ - public static Object[] toAVMStyle(String nrID) - { - Object [] result = new Object[2]; - String [] pathParts = nrID.split("/"); - result[0] = new Integer(pathParts[0]); - StringBuilder builder = new StringBuilder(); - builder.append(pathParts[1]); - builder.append(':'); - for (int i = 2; i < pathParts.length; i++) - { - builder.append('/'); - builder.append(pathParts[i]); - } - if (pathParts.length == 2) - { - builder.append('/'); - } - result[1] = builder.toString(); - return result; - } -} diff --git a/source/java/org/alfresco/repo/avm/AVMPathConverterTest.java b/source/java/org/alfresco/repo/avm/AVMPathConverterTest.java deleted file mode 100644 index cb7b76478c..0000000000 --- a/source/java/org/alfresco/repo/avm/AVMPathConverterTest.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 junit.framework.TestCase; - -/** - * Test of AVMPathConverter. - * @author britt - */ -public class AVMPathConverterTest extends TestCase -{ - /** - * Test conversions. - */ - public void testConversions() - { - try - { - String start = "main:/"; - String nrRep = AVMPathConverter.toNodeRefStyle(-1, start); - assertEquals("-1/main/", nrRep); - Object[] back = AVMPathConverter.toAVMStyle(nrRep); - assertEquals(((Integer)back[0]).intValue(), -1); - assertEquals((String)back[1], "main:/"); - start = "main:/foo/bar/baz"; - nrRep = AVMPathConverter.toNodeRefStyle(2, start); - assertEquals("2/main/foo/bar/baz", nrRep); - back = AVMPathConverter.toAVMStyle(nrRep); - assertEquals(((Integer)back[0]).intValue(), 2); - assertEquals((String)back[1], "main:/foo/bar/baz"); - - } - catch (Exception e) - { - e.printStackTrace(System.err); - fail(); - } - } -} diff --git a/source/java/org/alfresco/repo/avm/AVMRepository.java b/source/java/org/alfresco/repo/avm/AVMRepository.java index 9e60afe32e..066ad220b5 100644 --- a/source/java/org/alfresco/repo/avm/AVMRepository.java +++ b/source/java/org/alfresco/repo/avm/AVMRepository.java @@ -992,6 +992,18 @@ class AVMRepository store.deleteNodeProperty(pathParts[1], name); } + /** + * Delete all properties on a node. + * @param path The path to the node. + */ + public void deleteNodeProperties(String path) + { + fLookupCount.set(1); + String [] pathParts = SplitPath(path); + AVMStore store = getAVMStoreByName(pathParts[0]); + store.deleteNodeProperties(pathParts[1]); + } + /** * Set a property on a store. Overwrites if property exists. * @param store The AVMStore. diff --git a/source/java/org/alfresco/repo/avm/AVMService.java b/source/java/org/alfresco/repo/avm/AVMService.java index 54c06f6801..92be632dcc 100644 --- a/source/java/org/alfresco/repo/avm/AVMService.java +++ b/source/java/org/alfresco/repo/avm/AVMService.java @@ -493,6 +493,12 @@ public interface AVMService */ public void deleteNodeProperty(String path, QName name); + /** + * Delete all the properties attached to an AVM node. + * @param path The path to the node. + */ + public void deleteNodeProperties(String path); + /** * Set a property on a store. If the property exists it will be overwritten. * @param store The store to set the property on. diff --git a/source/java/org/alfresco/repo/avm/AVMServiceImpl.java b/source/java/org/alfresco/repo/avm/AVMServiceImpl.java index 83f7ca4c9c..d84a6d90df 100644 --- a/source/java/org/alfresco/repo/avm/AVMServiceImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMServiceImpl.java @@ -1183,7 +1183,28 @@ class AVMServiceImpl implements AVMService TxnCallback doit = new TxnCallback(); fTransaction.perform(doit, true); } - + + /** + * Delete all the properties attached to an AVM node. + * @param path The path to the node. + */ + public void deleteNodeProperties(final String path) + { + if (path == null) + { + throw new AVMBadArgumentException("Null path."); + } + class TxnCallback implements RetryingTransactionCallback + { + public void perform() + { + fAVMRepository.deleteNodeProperties(path); + } + } + TxnCallback doit = new TxnCallback(); + fTransaction.perform(doit, true); + } + /** * Set a property on a store. If the property exists it will be overwritten. * @param store The store to set the property on. diff --git a/source/java/org/alfresco/repo/avm/AVMServiceTest.java b/source/java/org/alfresco/repo/avm/AVMServiceTest.java index e166439794..99c0cdc764 100644 --- a/source/java/org/alfresco/repo/avm/AVMServiceTest.java +++ b/source/java/org/alfresco/repo/avm/AVMServiceTest.java @@ -2142,6 +2142,10 @@ public class AVMServiceTest extends AVMServiceTestBase assertEquals(2, props.size()); assertEquals(p2.toString(), props.get(n2).toString()); assertEquals(p3.toString(), props.get(n3).toString()); + fService.deleteNodeProperties("main:/a/b/c/bar"); + fService.createSnapshot("main"); + props = fService.getNodeProperties(-1, "main:/a/b/c/bar"); + assertEquals(0, props.size()); } catch (Exception e) { diff --git a/source/java/org/alfresco/repo/avm/AVMStore.java b/source/java/org/alfresco/repo/avm/AVMStore.java index 0daf524ddb..3591863abb 100644 --- a/source/java/org/alfresco/repo/avm/AVMStore.java +++ b/source/java/org/alfresco/repo/avm/AVMStore.java @@ -291,6 +291,12 @@ interface AVMStore */ public void deleteNodeProperty(String path, QName name); + /** + * Delete all properties from a node. + * @param path The path to the node. + */ + public void deleteNodeProperties(String path); + /** * Get all the properties associated with a node. * @param version The version to look under. diff --git a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java index 0b716c7b00..2f4c1a06fd 100644 --- a/source/java/org/alfresco/repo/avm/AVMStoreImpl.java +++ b/source/java/org/alfresco/repo/avm/AVMStoreImpl.java @@ -893,6 +893,17 @@ class AVMStoreImpl implements AVMStore, Serializable node.deleteProperty(name); } + /** + * Delete all properties from a node. + * @param path The path to the node. + */ + public void deleteNodeProperties(String path) + { + Lookup lPath = lookup(-1, path, true); + AVMNode node = lPath.getCurrentNode(); + node.deleteProperties(); + } + /** * Set a property on this store. Replaces if property already exists. * @param name The QName of the property.