From c0524bd3cc5679217c8509cf292ab650c55ad56b Mon Sep 17 00:00:00 2001 From: David Caruana Date: Sat, 21 Jan 2006 19:14:16 +0000 Subject: [PATCH] Addition of Import references; allows... - secondary child links (or multiple parents) - update of existing items git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2171 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../alfresco/repo/importer/ImportNode.java | 7 +- .../repo/importer/ImporterBootstrap.java | 12 +- .../repo/importer/ImporterComponent.java | 160 ++++++-- .../repo/importer/ImporterComponentTest.java | 7 +- .../repo/importer/importercomponent_test.xml | 53 ++- .../repo/importer/view/NodeContext.java | 49 ++- .../repo/importer/view/ParentContext.java | 26 +- .../repo/importer/view/ViewParser.java | 359 ++++++++++++------ .../service/cmr/view/ImporterProgress.java | 10 + source/java/org/alfresco/tools/Import.java | 8 + 10 files changed, 505 insertions(+), 186 deletions(-) diff --git a/source/java/org/alfresco/repo/importer/ImportNode.java b/source/java/org/alfresco/repo/importer/ImportNode.java index 01e671dcf4..28975c559f 100644 --- a/source/java/org/alfresco/repo/importer/ImportNode.java +++ b/source/java/org/alfresco/repo/importer/ImportNode.java @@ -46,6 +46,11 @@ public interface ImportNode */ public TypeDefinition getTypeDefinition(); + /** + * @return is this a node reference + */ + public boolean isReference(); + /** * @return the node ref */ @@ -55,7 +60,7 @@ public interface ImportNode * @return node uuid to create node with */ public String getUUID(); - + /** * @return the child name */ diff --git a/source/java/org/alfresco/repo/importer/ImporterBootstrap.java b/source/java/org/alfresco/repo/importer/ImporterBootstrap.java index 0ff5e80c74..78f2a45bc5 100644 --- a/source/java/org/alfresco/repo/importer/ImporterBootstrap.java +++ b/source/java/org/alfresco/repo/importer/ImporterBootstrap.java @@ -378,7 +378,7 @@ public class ImporterBootstrap /** * Bootstrap Binding */ - private class BootstrapBinding implements ImporterBinding + private static class BootstrapBinding implements ImporterBinding { private Properties configuration = null; private ResourceBundle resourceBundle = null; @@ -455,6 +455,16 @@ public class ImporterBootstrap logger.debug("Created node " + nodeRef + " (child name: " + childName + ") within parent " + parentRef + " (association type: " + assocName + ")"); } + /* + * (non-Javadoc) + * @see org.alfresco.service.cmr.view.ImporterProgress#nodeLinked(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName) + */ + public void nodeLinked(NodeRef nodeRef, NodeRef parentRef, QName assocName, QName childName) + { + if (logger.isDebugEnabled()) + logger.debug("Linked node " + nodeRef + " (child name: " + childName + ") within parent " + parentRef + " (association type: " + assocName + ")"); + } + /* (non-Javadoc) * @see org.alfresco.repo.importer.Progress#contentCreated(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) */ diff --git a/source/java/org/alfresco/repo/importer/ImporterComponent.java b/source/java/org/alfresco/repo/importer/ImporterComponent.java index 05cc574019..b22b4b5803 100644 --- a/source/java/org/alfresco/repo/importer/ImporterComponent.java +++ b/source/java/org/alfresco/repo/importer/ImporterComponent.java @@ -368,8 +368,11 @@ public class ImporterComponent private ImporterBinding binding; private ImporterProgress progress; private ImportPackageHandler streamHandler; - private List nodeRefs = new ArrayList(); private NodeImporterStrategy importStrategy; + private UpdateExistingNodeImporterStrategy updateStrategy; + + // Import tracking + private List nodeRefs = new ArrayList(); // Flush threshold private int flushThreshold = 500; @@ -392,6 +395,7 @@ public class ImporterComponent this.progress = progress; this.streamHandler = streamHandler; this.importStrategy = createNodeImporterStrategy(binding == null ? null : binding.getUUIDBinding()); + this.updateStrategy = new UpdateExistingNodeImporterStrategy(); } /** @@ -472,14 +476,22 @@ public class ImporterComponent } } } - + /* (non-Javadoc) * @see org.alfresco.repo.importer.Importer#importNode(org.alfresco.repo.importer.ImportNode) */ public NodeRef importNode(ImportNode context) { // import node - NodeRef nodeRef = importStrategy.importNode(context); + NodeRef nodeRef; + if (context.isReference()) + { + nodeRef = linkNode(context); + } + else + { + nodeRef = importStrategy.importNode(context); + } // apply aspects for (QName aspect : context.getNodeAspects()) @@ -511,6 +523,55 @@ public class ImporterComponent return nodeRef; } + + /** + * Link an existing Node + * + * @param context node to link in + * @return node reference of child linked in + */ + private NodeRef linkNode(ImportNode context) + { + ImportParent parentContext = context.getParentContext(); + NodeRef parentRef = parentContext.getParentRef(); + + // determine the child node reference + String uuid = context.getUUID(); + if (uuid == null) + { + throw new ImporterException("Node reference does not resolve to a node"); + } + NodeRef childRef = new NodeRef(parentRef.getStoreRef(), uuid); + + // Note: do not link references that are defined in the root of the import + if (!parentRef.equals(getRootRef())) + { + // determine child assoc type + QName assocType = getAssocType(context); + + // determine child name + QName childQName = getChildName(context); + if (childQName == null) + { + String name = (String)nodeService.getProperty(childRef, ContentModel.PROP_NAME); + if (name == null || name.length() == 0) + { + throw new ImporterException("Cannot determine node reference child name"); + } + String localName = QName.createValidLocalName(name); + childQName = QName.createQName(assocType.getNamespaceURI(), localName); + } + + // create the link + nodeService.addChild(parentRef, childRef, assocType, childQName); + reportNodeLinked(childRef, parentRef, assocType, childQName); + } + + // second, perform any specified udpates to the node + updateStrategy.importNode(context); + + return childRef; + } /** * Import Node Content. @@ -625,11 +686,7 @@ public class ImporterComponent { for (QName disabledBehaviour: disabledBehaviours) { - boolean alreadyDisabled = behaviourFilter.disableBehaviour(importedRef.context.getNodeRef(), disabledBehaviour); - if (alreadyDisabled) - { - disabledBehaviours.remove(disabledBehaviour); - } + behaviourFilter.disableBehaviour(importedRef.context.getNodeRef(), disabledBehaviour); } nodeService.setProperty(importedRef.context.getNodeRef(), importedRef.property, nodeRef); } @@ -648,6 +705,40 @@ public class ImporterComponent { behaviourFilter.enableAllBehaviours(); } + + /** + * Get the child name to import node under + * + * @param context the node + * @return the child name + */ + private QName getChildName(ImportNode context) + { + QName assocType = getAssocType(context); + QName childQName = null; + + // Determine child name + String childName = context.getChildName(); + if (childName != null) + { + childName = bindPlaceHolder(childName, binding); + String[] qnameComponents = QName.splitPrefixedQName(childName); + childQName = QName.createQName(qnameComponents[0], QName.createValidLocalName(qnameComponents[1]), namespaceService); + } + else + { + Map typeProperties = context.getProperties(); + String name = (String)typeProperties.get(ContentModel.PROP_NAME); + if (name != null && name.length() > 0) + { + name = bindPlaceHolder(name, binding); + String localName = QName.createValidLocalName(name); + childQName = QName.createQName(assocType.getNamespaceURI(), localName); + } + } + + return childQName; + } /** * Get appropriate child association type for node to import under @@ -830,7 +921,7 @@ public class ImporterComponent } return objValue; } - + /** * Helper to report node created progress * @@ -845,6 +936,20 @@ public class ImporterComponent } } + /** + * Helper to report node linked progress + * + * @param progress + * @param childAssocRef + */ + private void reportNodeLinked(NodeRef childRef, NodeRef parentRef, QName assocType, QName childName) + { + if (progress != null) + { + progress.nodeLinked(childRef, parentRef, assocType, childName); + } + } + /** * Helper to report content created progress * @@ -938,30 +1043,12 @@ public class ImporterComponent TypeDefinition nodeType = node.getTypeDefinition(); NodeRef parentRef = node.getParentContext().getParentRef(); QName assocType = getAssocType(node); - QName childQName = null; + QName childQName = getChildName(node); + if (childQName == null) + { + throw new ImporterException("Cannot determine child name of node (type: " + nodeType.getName() + ")"); + } - // Determine child name - String childName = node.getChildName(); - if (childName != null) - { - childName = bindPlaceHolder(childName, binding); - String[] qnameComponents = QName.splitPrefixedQName(childName); - childQName = QName.createQName(qnameComponents[0], QName.createValidLocalName(qnameComponents[1]), namespaceService); - } - else - { - Map typeProperties = node.getProperties(); - String name = (String)typeProperties.get(ContentModel.PROP_NAME); - if (name == null || name.length() == 0) - { - throw new ImporterException("Cannot import node of type " + nodeType.getName() + " - it does not have a name"); - } - - name = bindPlaceHolder(name, binding); - String localName = QName.createValidLocalName(name); - childQName = QName.createQName(assocType.getNamespaceURI(), localName); - } - // Create initial node (but, first disable behaviour for the node to be created) Set disabledBehaviours = getDisabledBehaviours(node); for (QName disabledBehaviour: disabledBehaviours) @@ -972,7 +1059,7 @@ public class ImporterComponent disabledBehaviours.remove(disabledBehaviour); } } - + // Build initial map of properties Map initialProperties = bindProperties(node); @@ -1154,8 +1241,11 @@ public class ImporterComponent // do the update Map existingProperties = nodeService.getProperties(existingNodeRef); Map updateProperties = bindProperties(node); - existingProperties.putAll(updateProperties); - nodeService.setProperties(existingNodeRef, existingProperties); + if (updateProperties != null) + { + existingProperties.putAll(updateProperties); + nodeService.setProperties(existingNodeRef, existingProperties); + } // Apply permissions boolean inheritPermissions = node.getInheritPermissions(); diff --git a/source/java/org/alfresco/repo/importer/ImporterComponentTest.java b/source/java/org/alfresco/repo/importer/ImporterComponentTest.java index 79d70fb672..0caa22c05e 100644 --- a/source/java/org/alfresco/repo/importer/ImporterComponentTest.java +++ b/source/java/org/alfresco/repo/importer/ImporterComponentTest.java @@ -58,7 +58,6 @@ public class ImporterComponentTest extends BaseSpringTest // Create the store this.storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); } - @Override protected void onTearDownInTransaction() throws Exception @@ -98,6 +97,12 @@ public class ImporterComponentTest extends BaseSpringTest " (association " + assocName + ")"); } + public void nodeLinked(NodeRef nodeRef, NodeRef parentRef, QName assocName, QName childName) + { + System.out.println("TestProgress: linked node " + nodeRef + " within parent " + parentRef + " named " + childName + + " (association " + assocName + ")"); + } + public void contentCreated(NodeRef nodeRef, String sourceUrl) { System.out.println("TestProgress: created content " + nodeRef + " from url " + sourceUrl); diff --git a/source/java/org/alfresco/repo/importer/importercomponent_test.xml b/source/java/org/alfresco/repo/importer/importercomponent_test.xml index f56600a266..27b6dd6200 100644 --- a/source/java/org/alfresco/repo/importer/importercomponent_test.xml +++ b/source/java/org/alfresco/repo/importer/importercomponent_test.xml @@ -4,7 +4,7 @@ + xmlns:sys="http://www.alfresco.org/model/system/1.0"> unknown @@ -13,7 +13,7 @@ /system - + System true @@ -32,43 +32,58 @@ - + - ${username} - Fred - Bloggs - ../../cm:people_x0020_folder + ${username} + Fred + Bloggs + ../../cm:people_x0020_folder + + + + + + + + Raymond + + + + - Some Content + Categorised Content ../cm:people_x0020_folder ../cm:people_x0020_folder - - - - Translation of Some Content - - - - - Real content - contentUrl=classpath:org/alfresco/repo/importer/importercomponent_testfile.txt|mimetype=text|size=|encoding= - + + + Some content + contentUrl=classpath:org/alfresco/repo/importer/importercomponent_testfile.txt|mimetype=text|size=|encoding= + + + + + + + + + \ No newline at end of file diff --git a/source/java/org/alfresco/repo/importer/view/NodeContext.java b/source/java/org/alfresco/repo/importer/view/NodeContext.java index 37ce1100e8..879cbfbd55 100644 --- a/source/java/org/alfresco/repo/importer/view/NodeContext.java +++ b/source/java/org/alfresco/repo/importer/view/NodeContext.java @@ -51,15 +51,17 @@ public class NodeContext extends ElementContext implements ImportNode { private ParentContext parentContext; + private boolean isReference = false; private NodeRef nodeRef; - private String uuid; + private String importId; // unique identifier within import (optional) + private String uuid; // unique identifier within repository private TypeDefinition typeDef; private String childName; private Map nodeAspects = new HashMap(); private Map nodeChildAssocs = new HashMap(); private Map nodeProperties = new HashMap(); private Map propertyDatatypes = new HashMap(); - + // permissions private boolean inherit = true; private List accessControlEntries = new ArrayList(); @@ -79,7 +81,7 @@ public class NodeContext extends ElementContext this.typeDef = typeDef; this.uuid = null; } - + /* (non-Javadoc) * @see org.alfresco.repo.importer.ImportNode#getParentContext() */ @@ -96,6 +98,23 @@ public class NodeContext extends ElementContext return typeDef; } + /* + * (non-Javadoc) + * @see org.alfresco.repo.importer.ImportNode#isReference() + */ + public boolean isReference() + { + return isReference; + } + + /** + * @param isReference true => this is a node reference + */ + public void setReference(boolean isReference) + { + this.isReference = isReference; + } + /** * Set Type Definition * @@ -139,6 +158,23 @@ public class NodeContext extends ElementContext this.uuid = uuid; } + /* + * (non-Javadoc) + * @see org.alfresco.repo.importer.ImportNode#getImportId() + */ + public String getImportId() + { + return importId; + } + + /** + * @param importId import scoped id + */ + public void setImportId(String importId) + { + this.importId = importId; + } + /* (non-Javadoc) * @see org.alfresco.repo.importer.ImportNode#getChildName() */ @@ -369,11 +405,14 @@ public class NodeContext extends ElementContext PropertyDefinition def = null; if (nodeProperties.containsKey(defName) == false) { - def = getDictionaryService().getProperty(typeDef.getName(), defName); + def = (typeDef == null) ? null : getDictionaryService().getProperty(typeDef.getName(), defName); if (def == null) { Set allAspects = new HashSet(); - allAspects.addAll(typeDef.getDefaultAspects()); + if (typeDef != null) + { + allAspects.addAll(typeDef.getDefaultAspects()); + } allAspects.addAll(nodeAspects.values()); for (AspectDefinition aspectDef : allAspects) { diff --git a/source/java/org/alfresco/repo/importer/view/ParentContext.java b/source/java/org/alfresco/repo/importer/view/ParentContext.java index 5e18336192..25b14a1ed2 100644 --- a/source/java/org/alfresco/repo/importer/view/ParentContext.java +++ b/source/java/org/alfresco/repo/importer/view/ParentContext.java @@ -84,18 +84,22 @@ public class ParentContext extends ElementContext { this(elementName, parent); - // Ensure association is valid for node - Set allAspects = new HashSet(); - for (AspectDefinition typeAspect : parent.getTypeDefinition().getDefaultAspects()) + TypeDefinition typeDef = parent.getTypeDefinition(); + if (typeDef != null) { - allAspects.add(typeAspect.getName()); - } - allAspects.addAll(parent.getNodeAspects()); - TypeDefinition anonymousType = getDictionaryService().getAnonymousType(parent.getTypeDefinition().getName(), allAspects); - Map nodeAssociations = anonymousType.getChildAssociations(); - if (nodeAssociations.containsKey(childDef.getName()) == false) - { - throw new ImporterException("Association " + childDef.getName() + " is not valid for node " + parent.getTypeDefinition().getName()); + // Ensure association type is valid for node parent + Set allAspects = new HashSet(); + for (AspectDefinition typeAspect : parent.getTypeDefinition().getDefaultAspects()) + { + allAspects.add(typeAspect.getName()); + } + allAspects.addAll(parent.getNodeAspects()); + TypeDefinition anonymousType = getDictionaryService().getAnonymousType(parent.getTypeDefinition().getName(), allAspects); + Map nodeAssociations = anonymousType.getChildAssociations(); + if (nodeAssociations.containsKey(childDef.getName()) == false) + { + throw new ImporterException("Association " + childDef.getName() + " is not valid for node " + parent.getTypeDefinition().getName()); + } } parentRef = parent.getNodeRef(); diff --git a/source/java/org/alfresco/repo/importer/view/ViewParser.java b/source/java/org/alfresco/repo/importer/view/ViewParser.java index 4da7c2072d..f5cdb8bd0d 100644 --- a/source/java/org/alfresco/repo/importer/view/ViewParser.java +++ b/source/java/org/alfresco/repo/importer/view/ViewParser.java @@ -18,18 +18,21 @@ package org.alfresco.repo.importer.view; import java.io.IOException; import java.io.Reader; +import java.util.HashMap; +import java.util.Map; import java.util.Stack; import org.alfresco.repo.importer.Importer; import org.alfresco.repo.importer.Parser; import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.view.ImporterException; import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.view.ImporterException; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.apache.commons.logging.Log; @@ -55,6 +58,8 @@ public class ViewParser implements Parser private static final String VIEW_ISNULL_ATTR = "isNull"; private static final String VIEW_INHERIT_PERMISSIONS_ATTR = "inherit"; private static final String VIEW_ACCESS_STATUS_ATTR = "access"; + private static final String VIEW_ID_ATTR = "id"; + private static final String VIEW_IDREF_ATTR = "idref"; private static final QName VIEW_METADATA = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "metadata"); private static final QName VIEW_VALUE_QNAME = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "value"); private static final QName VIEW_VALUES_QNAME = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "values"); @@ -65,6 +70,7 @@ public class ViewParser implements Parser private static final QName VIEW_ACE = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "ace"); private static final QName VIEW_AUTHORITY = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "authority"); private static final QName VIEW_PERMISSION = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "permission"); + private static final QName VIEW_REFERENCE = QName.createQName(NamespaceService.REPOSITORY_VIEW_1_0_URI, "reference"); // XML Pull Parser Factory @@ -73,8 +79,17 @@ public class ViewParser implements Parser // Supporting services private NamespaceService namespaceService; private DictionaryService dictionaryService; + + // Parser Context maintained during each parse + private class ParserContext + { + Importer importer; + DictionaryService dictionaryService; + Stack elementStack; + Map importIds = new HashMap(); + } - + /** * Construct */ @@ -92,6 +107,7 @@ public class ViewParser implements Parser } } + /** * @param namespaceService the namespace service */ @@ -117,7 +133,11 @@ public class ViewParser implements Parser { XmlPullParser xpp = factory.newPullParser(); xpp.setInput(viewReader); - Stack contextStack = new Stack(); + + ParserContext parserContext = new ParserContext(); + parserContext.importer = importer; + parserContext.dictionaryService = dictionaryService; + parserContext.elementStack = new Stack(); try { @@ -129,17 +149,17 @@ public class ViewParser implements Parser { if (xpp.getDepth() == 1) { - processRoot(xpp, importer, contextStack); + processRoot(xpp, parserContext); } else { - processStartElement(xpp, contextStack); + processStartElement(xpp, parserContext); } break; } case XmlPullParser.END_TAG: { - processEndElement(xpp, contextStack); + processEndElement(xpp, parserContext); break; } } @@ -164,61 +184,69 @@ public class ViewParser implements Parser * @throws XmlPullParserException * @throws IOException */ - private void processStartElement(XmlPullParser xpp, Stack contextStack) + private void processStartElement(XmlPullParser xpp, ParserContext parserContext) throws XmlPullParserException, IOException { // Extract qualified name QName defName = getName(xpp); // Process the element - Object context = contextStack.peek(); + Object element = parserContext.elementStack.peek(); // Handle special view directives if (defName.equals(VIEW_METADATA)) { - contextStack.push(new MetaDataContext(defName, (ElementContext)context)); + parserContext.elementStack.push(new MetaDataContext(defName, (ElementContext)element)); } else if (defName.equals(VIEW_ASPECTS) || defName.equals(VIEW_PROPERTIES) || defName.equals(VIEW_ASSOCIATIONS) || defName.equals(VIEW_ACL)) { - if (context instanceof NodeItemContext) + if (element instanceof NodeItemContext) { - throw new ImporterException("Cannot nest element " + defName + " within " + ((NodeItemContext)context).getElementName()); + throw new ImporterException("Cannot nest element " + defName + " within " + ((NodeItemContext)element).getElementName()); } - if (!(context instanceof NodeContext)) + if (!(element instanceof NodeContext)) { throw new ImporterException("Element " + defName + " can only be declared within a node"); } - NodeContext nodeContext = (NodeContext)context; - contextStack.push(new NodeItemContext(defName, nodeContext)); + NodeContext node = (NodeContext)element; + parserContext.elementStack.push(new NodeItemContext(defName, node)); // process ACL specific attributes if (defName.equals(VIEW_ACL)) { - processACL(xpp, contextStack); + processACL(xpp, parserContext); } } else { - if (context instanceof MetaDataContext) + if (element instanceof MetaDataContext) { - processMetaData(xpp, defName, contextStack); + processMetaData(xpp, defName, parserContext); } - else if (context instanceof ParentContext) + else if (element instanceof ParentContext) { - // Process type definition - TypeDefinition typeDef = dictionaryService.getType(defName); - if (typeDef == null) + if (defName.equals(VIEW_REFERENCE)) { - throw new ImporterException("Type " + defName + " has not been defined in the Repository dictionary"); + // Process reference + processStartReference(xpp, defName, parserContext); + } + else + { + // Process type definition + TypeDefinition typeDef = dictionaryService.getType(defName); + if (typeDef == null) + { + throw new ImporterException("Type " + defName + " has not been defined in the Repository dictionary"); + } + processStartType(xpp, typeDef, parserContext); } - processStartType(xpp, typeDef, contextStack); return; } - else if (context instanceof NodeContext) + else if (element instanceof NodeContext) { // Process children of node // Note: Process in the following order: aspects, properties and associations - Object def = ((NodeContext)context).determineDefinition(defName); + Object def = ((NodeContext)element).determineDefinition(defName); if (def == null) { throw new ImporterException("Definition " + defName + " is not valid; cannot find in Repository dictionary"); @@ -226,17 +254,17 @@ public class ViewParser implements Parser if (def instanceof AspectDefinition) { - processAspect(xpp, (AspectDefinition)def, contextStack); + processAspect(xpp, (AspectDefinition)def, parserContext); return; } else if (def instanceof PropertyDefinition) { - processProperty(xpp, ((PropertyDefinition)def).getName(), contextStack); + processProperty(xpp, ((PropertyDefinition)def).getName(), parserContext); return; } else if (def instanceof ChildAssociationDefinition) { - processStartChildAssoc(xpp, (ChildAssociationDefinition)def, contextStack); + processStartChildAssoc(xpp, (ChildAssociationDefinition)def, parserContext); return; } else @@ -244,38 +272,42 @@ public class ViewParser implements Parser // TODO: general association } } - else if (context instanceof NodeItemContext) + else if (element instanceof NodeItemContext) { - NodeItemContext nodeItemContext = (NodeItemContext)context; - NodeContext nodeContext = nodeItemContext.getNodeContext(); - QName itemName = nodeItemContext.getElementName(); + NodeItemContext nodeItem = (NodeItemContext)element; + NodeContext node = nodeItem.getNodeContext(); + QName itemName = nodeItem.getElementName(); if (itemName.equals(VIEW_ASPECTS)) { - AspectDefinition def = nodeContext.determineAspect(defName); + AspectDefinition def = node.determineAspect(defName); if (def == null) { throw new ImporterException("Aspect name " + defName + " is not valid; cannot find in Repository dictionary"); } - processAspect(xpp, def, contextStack); + processAspect(xpp, def, parserContext); } else if (itemName.equals(VIEW_PROPERTIES)) { // Note: Allow properties which do not have a data dictionary definition - processProperty(xpp, defName, contextStack); + processProperty(xpp, defName, parserContext); } else if (itemName.equals(VIEW_ASSOCIATIONS)) { - // TODO: Handle general associations... - ChildAssociationDefinition def = (ChildAssociationDefinition)nodeContext.determineAssociation(defName); + AssociationDefinition def = (AssociationDefinition)node.determineAssociation(defName); if (def == null) { throw new ImporterException("Association name " + defName + " is not valid; cannot find in Repository dictionary"); } - processStartChildAssoc(xpp, def, contextStack); + // TODO: Handle general associations... + if (!(def instanceof ChildAssociationDefinition)) + { + throw new ImporterException("Unsupported operation: The association " + defName + " cannot be imported - only child associations are supported at this time"); + } + processStartChildAssoc(xpp, (ChildAssociationDefinition)def, parserContext); } else if (itemName.equals(VIEW_ACL)) { - processAccessControlEntry(xpp, contextStack); + processAccessControlEntry(xpp, parserContext); } } } @@ -293,14 +325,14 @@ public class ViewParser implements Parser * @throws XmlPullParserException * @throws IOException */ - private void processRoot(XmlPullParser xpp, Importer importer, Stack contextStack) + private void processRoot(XmlPullParser xpp, ParserContext parserContext) throws XmlPullParserException, IOException { - ParentContext parentContext = new ParentContext(getName(xpp), dictionaryService, importer); - contextStack.push(parentContext); + ParentContext parent = new ParentContext(getName(xpp), parserContext.dictionaryService, parserContext.importer); + parserContext.elementStack.push(parent); if (logger.isDebugEnabled()) - logger.debug(indentLog("Pushed " + parentContext, contextStack.size() -1)); + logger.debug(indentLog("Pushed " + parent, parserContext.elementStack.size() -1)); } /** @@ -312,10 +344,10 @@ public class ViewParser implements Parser * @throws XmlPullParserException * @throws IOException */ - private void processMetaData(XmlPullParser xpp, QName metaDataName, Stack contextStack) + private void processMetaData(XmlPullParser xpp, QName metaDataName, ParserContext parserContext) throws XmlPullParserException, IOException { - MetaDataContext context = (MetaDataContext)contextStack.peek(); + MetaDataContext metaData = (MetaDataContext)parserContext.elementStack.peek(); String value = null; @@ -331,7 +363,7 @@ public class ViewParser implements Parser throw new ImporterException("Meta data element " + metaDataName + " is missing end tag"); } - context.setProperty(metaDataName, value); + metaData.setProperty(metaDataName, value); } /** @@ -343,25 +375,88 @@ public class ViewParser implements Parser * @throws XmlPullParserException * @throws IOException */ - private void processStartType(XmlPullParser xpp, TypeDefinition typeDef, Stack contextStack) + private void processStartType(XmlPullParser xpp, TypeDefinition typeDef, ParserContext parserContext) throws XmlPullParserException, IOException { - ParentContext parentContext = (ParentContext)contextStack.peek(); - NodeContext context = new NodeContext(typeDef.getName(), parentContext, typeDef); + ParentContext parent = (ParentContext)parserContext.elementStack.peek(); + NodeContext node = new NodeContext(typeDef.getName(), parent, typeDef); + + // Extract child name if explicitly defined + String childName = xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_CHILD_NAME_ATTR); + if (childName != null && childName.length() > 0) + { + node.setChildName(childName); + } + + // Extract import id if explicitly defined + String importId = xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_ID_ATTR); + if (importId != null && importId.length() > 0) + { + node.setImportId(importId); + } + + parserContext.elementStack.push(node); + + if (logger.isDebugEnabled()) + logger.debug(indentLog("Pushed " + node, parserContext.elementStack.size() -1)); + } + + /** + * Process start reference + * + * @param xpp + * @param typeDef + * @param contextStack + * @throws XmlPullParserException + * @throws IOException + */ + private void processStartReference(XmlPullParser xpp, QName refName, ParserContext parserContext) + throws XmlPullParserException, IOException + { + ParentContext parent = (ParentContext)parserContext.elementStack.peek(); + + // Extract Import scoped reference Id if explicitly defined + String uuid = null; + String idRef = xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_IDREF_ATTR); + if (idRef != null && idRef.length() > 0) + { + // retrieve uuid from previously imported node + NodeRef nodeRef = getImportReference(parserContext, idRef); + if (nodeRef == null) + { + throw new ImporterException("Cannot find node referenced by id " + idRef); + } + uuid = nodeRef.getId(); + } + + // Create reference + NodeContext node = new NodeContext(refName, parent, null); + node.setReference(true); + if (uuid != null) + { + node.setUUID(uuid); + } // Extract child name if explicitly defined String childName = xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_CHILD_NAME_ATTR); if (childName != null && childName.length() > 0) { - context.setChildName(childName); + node.setChildName(childName); } - contextStack.push(context); + // Extract import id if explicitly defined + String importId = xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_ID_ATTR); + if (importId != null && importId.length() > 0) + { + node.setImportId(importId); + } + + parserContext.elementStack.push(node); if (logger.isDebugEnabled()) - logger.debug(indentLog("Pushed " + context, contextStack.size() -1)); + logger.debug(indentLog("Pushed Reference " + node, parserContext.elementStack.size() -1)); } - + /** * Process aspect definition * @@ -371,11 +466,11 @@ public class ViewParser implements Parser * @throws XmlPullParserException * @throws IOException */ - private void processAspect(XmlPullParser xpp, AspectDefinition aspectDef, Stack contextStack) + private void processAspect(XmlPullParser xpp, AspectDefinition aspectDef, ParserContext parserContext) throws XmlPullParserException, IOException { - NodeContext context = peekNodeContext(contextStack); - context.addAspect(aspectDef); + NodeContext node = peekNodeContext(parserContext.elementStack); + node.addAspect(aspectDef); int eventType = xpp.next(); if (eventType != XmlPullParser.END_TAG) @@ -384,7 +479,7 @@ public class ViewParser implements Parser } if (logger.isDebugEnabled()) - logger.debug(indentLog("Processed aspect " + aspectDef.getName(), contextStack.size())); + logger.debug(indentLog("Processed aspect " + aspectDef.getName(), parserContext.elementStack.size())); } /** @@ -393,9 +488,9 @@ public class ViewParser implements Parser * @param xpp * @param contextStack */ - private void processACL(XmlPullParser xpp, Stack contextStack) + private void processACL(XmlPullParser xpp, ParserContext parserContext) { - NodeContext context = peekNodeContext(contextStack); + NodeContext node = peekNodeContext(parserContext.elementStack); String strInherit = xpp.getAttributeValue(NamespaceService.REPOSITORY_VIEW_1_0_URI, VIEW_INHERIT_PERMISSIONS_ATTR); if (strInherit != null) @@ -403,7 +498,7 @@ public class ViewParser implements Parser Boolean inherit = Boolean.valueOf(strInherit); if (!inherit) { - context.setInheritPermissions(false); + node.setInheritPermissions(false); } } } @@ -416,10 +511,10 @@ public class ViewParser implements Parser * @throws XmlPullParserException * @throws IOException */ - private void processAccessControlEntry(XmlPullParser xpp, Stack contextStack) + private void processAccessControlEntry(XmlPullParser xpp, ParserContext parserContext) throws XmlPullParserException, IOException { - NodeContext context = peekNodeContext(contextStack); + NodeContext node = peekNodeContext(parserContext.elementStack); QName defName = getName(xpp); if (!defName.equals(VIEW_ACE)) @@ -500,7 +595,7 @@ public class ViewParser implements Parser } // update node context - context.addAccessControlEntry(accessStatus, authority, permission); + node.addAccessControlEntry(accessStatus, authority, permission); } /** @@ -512,10 +607,10 @@ public class ViewParser implements Parser * @throws XmlPullParserException * @throws IOException */ - private void processProperty(XmlPullParser xpp, QName propertyName, Stack contextStack) + private void processProperty(XmlPullParser xpp, QName propertyName, ParserContext parserContext) throws XmlPullParserException, IOException { - NodeContext context = peekNodeContext(contextStack); + NodeContext node = peekNodeContext(parserContext.elementStack); // Extract single value String value = ""; @@ -527,11 +622,10 @@ public class ViewParser implements Parser } if (eventType == XmlPullParser.END_TAG) { - context.addProperty(propertyName, value); + node.addProperty(propertyName, value); } else { - // Extract collection, if specified boolean isCollection = false; if (eventType == XmlPullParser.START_TAG) @@ -539,7 +633,7 @@ public class ViewParser implements Parser QName name = getName(xpp); if (name.equals(VIEW_VALUES_QNAME)) { - context.addPropertyCollection(propertyName); + node.addPropertyCollection(propertyName); isCollection = true; eventType = xpp.next(); if (eventType == XmlPullParser.TEXT) @@ -568,10 +662,10 @@ public class ViewParser implements Parser } if (eventType == XmlPullParser.END_TAG) { - context.addProperty(propertyName, decoratedValue); + node.addProperty(propertyName, decoratedValue); if (datatype != null) { - context.addDatatype(propertyName, dictionaryService.getDataType(datatype)); + node.addDatatype(propertyName, dictionaryService.getDataType(datatype)); } } else @@ -604,11 +698,10 @@ public class ViewParser implements Parser throw new ImporterException("Invalid view structure - property " + propertyName + " definition is invalid"); } } - } if (logger.isDebugEnabled()) - logger.debug(indentLog("Processed property " + propertyName, contextStack.size())); + logger.debug(indentLog("Processed property " + propertyName, parserContext.elementStack.size())); } /** @@ -620,54 +713,48 @@ public class ViewParser implements Parser * @throws XmlPullParserException * @throws IOException */ - private void processStartChildAssoc(XmlPullParser xpp, ChildAssociationDefinition childAssocDef, Stack contextStack) + private void processStartChildAssoc(XmlPullParser xpp, ChildAssociationDefinition childAssocDef, ParserContext parserContext) throws XmlPullParserException, IOException { - NodeContext context = peekNodeContext(contextStack); + NodeContext node = peekNodeContext(parserContext.elementStack); + importNode(parserContext, node); - if (context.getNodeRef() == null) - { - // Create Node - NodeRef nodeRef = context.getImporter().importNode(context); - context.setNodeRef(nodeRef); - } - // Construct Child Association Context - ParentContext parentContext = new ParentContext(childAssocDef.getName(), context, childAssocDef); - contextStack.push(parentContext); + ParentContext parent = new ParentContext(childAssocDef.getName(), node, childAssocDef); + parserContext.elementStack.push(parent); if (logger.isDebugEnabled()) - logger.debug(indentLog("Pushed " + parentContext, contextStack.size() -1)); + logger.debug(indentLog("Pushed " + parent, parserContext.elementStack.size() -1)); } - + /** * Process end of xml element * * @param xpp * @param contextStack */ - private void processEndElement(XmlPullParser xpp, Stack contextStack) + private void processEndElement(XmlPullParser xpp, ParserContext parserContext) { - ElementContext context = contextStack.peek(); - if (context.getElementName().getLocalName().equals(xpp.getName()) && - context.getElementName().getNamespaceURI().equals(xpp.getNamespace())) + ElementContext element = parserContext.elementStack.peek(); + if (element.getElementName().getLocalName().equals(xpp.getName()) && + element.getElementName().getNamespaceURI().equals(xpp.getNamespace())) { - context = contextStack.pop(); + element = parserContext.elementStack.pop(); if (logger.isDebugEnabled()) - logger.debug(indentLog("Popped " + context, contextStack.size())); + logger.debug(indentLog("Popped " + element, parserContext.elementStack.size())); - if (context instanceof NodeContext) + if (element instanceof NodeContext) { - processEndType((NodeContext)context); + processEndType(parserContext, (NodeContext)element); } - else if (context instanceof ParentContext) + else if (element instanceof ParentContext) { - processEndChildAssoc((ParentContext)context); + processEndChildAssoc(parserContext, (ParentContext)element); } - else if (context instanceof MetaDataContext) + else if (element instanceof MetaDataContext) { - processEndMetaData((MetaDataContext)context); + processEndMetaData(parserContext, (MetaDataContext)element); } } } @@ -675,17 +762,13 @@ public class ViewParser implements Parser /** * Process end of the type definition * - * @param context + * @param node */ - private void processEndType(NodeContext context) + private void processEndType(ParserContext parserContext, NodeContext node) { - NodeRef nodeRef = context.getNodeRef(); - if (nodeRef == null) - { - nodeRef = context.getImporter().importNode(context); - context.setNodeRef(nodeRef); - } - context.getImporter().childrenImported(nodeRef); + NodeRef nodeRef = node.getNodeRef(); + importNode(parserContext, node); + node.getImporter().childrenImported(nodeRef); } /** @@ -693,7 +776,7 @@ public class ViewParser implements Parser * * @param context */ - private void processEndChildAssoc(ParentContext context) + private void processEndChildAssoc(ParserContext parserContext, ParentContext parent) { } @@ -702,11 +785,61 @@ public class ViewParser implements Parser * * @param context */ - private void processEndMetaData(MetaDataContext context) + private void processEndMetaData(ParserContext parserContext, MetaDataContext context) { context.getImporter().importMetaData(context.getProperties()); } + + /** + * Import node + * + * @param parserContext parser context + * @param node node context + */ + private void importNode(ParserContext parserContext, NodeContext node) + { + if (node.getNodeRef() == null) + { + // Import Node + NodeRef nodeRef = node.getImporter().importNode(node); + node.setNodeRef(nodeRef); + + // Maintain running list of "import" scoped ids + String importId = node.getImportId(); + if (importId != null && importId.length() > 0) + { + createImportReference(parserContext, importId, nodeRef); + } + } + } + + /** + * Maps an Import Id to a Node Reference + * + * @param importId import Id + * @param nodeRef node reference + */ + private void createImportReference(ParserContext parserContext, String importId, NodeRef nodeRef) + { + if (parserContext.importIds.containsKey(importId)) + { + throw new ImporterException("Import id " + importId + " already specified within import file"); + } + parserContext.importIds.put(importId, nodeRef); + } + + /** + * Gets the Node Reference for the specified Import Id + * + * @param importId the import id + * @return the node reference + */ + private NodeRef getImportReference(ParserContext parserContext, String importId) + { + return parserContext.importIds.get(importId); + } + /** * Get parent Node Context * @@ -715,14 +848,14 @@ public class ViewParser implements Parser */ private NodeContext peekNodeContext(Stack contextStack) { - ElementContext context = contextStack.peek(); - if (context instanceof NodeContext) + ElementContext element = contextStack.peek(); + if (element instanceof NodeContext) { - return (NodeContext)context; + return (NodeContext)element; } - else if (context instanceof NodeItemContext) + else if (element instanceof NodeItemContext) { - return ((NodeItemContext)context).getNodeContext(); + return ((NodeItemContext)element).getNodeContext(); } throw new ImporterException("Internal error: Failed to retrieve node context"); } diff --git a/source/java/org/alfresco/service/cmr/view/ImporterProgress.java b/source/java/org/alfresco/service/cmr/view/ImporterProgress.java index 8af53bf3ad..2a1fe9fb6f 100644 --- a/source/java/org/alfresco/service/cmr/view/ImporterProgress.java +++ b/source/java/org/alfresco/service/cmr/view/ImporterProgress.java @@ -41,6 +41,16 @@ public interface ImporterProgress */ public void nodeCreated(NodeRef nodeRef, NodeRef parentRef, QName assocName, QName childName); + /** + * Report creation of a node link. + * + * @param nodeRef the node ref + * @param parentRef the parent ref + * @param assocName the child association type name + * @param childName the child association name + */ + public void nodeLinked(NodeRef nodeRef, NodeRef parentRef, QName assocName, QName childName); + /** * Report creation of content * diff --git a/source/java/org/alfresco/tools/Import.java b/source/java/org/alfresco/tools/Import.java index 58aeb4ac7d..ea1283187a 100644 --- a/source/java/org/alfresco/tools/Import.java +++ b/source/java/org/alfresco/tools/Import.java @@ -286,6 +286,14 @@ public class Import extends Tool logVerbose("Imported node " + nodeRef + " (parent=" + parentRef + ", childname=" + childName + ", association=" + assocName + ")"); } + /* + * (non-Javadoc) + * @see org.alfresco.service.cmr.view.ImporterProgress#nodeLinked(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName) + */ + public void nodeLinked(NodeRef nodeRef, NodeRef parentRef, QName assocName, QName childName) + { + } + /* (non-Javadoc) * @see org.alfresco.service.cmr.view.ImporterProgress#contentCreated(org.alfresco.service.cmr.repository.NodeRef, java.lang.String) */