You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by st...@apache.org on 2005/02/21 19:03:35 UTC

svn commit: r154700 [2/2] - in incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core: LocalNamespaceMappings.java NodeImpl.java QName.java SessionImpl.java nodetype/NodeDefImpl.java nodetype/builtin_nodetypes.xml xml/DocViewImportHandler.java xml/ImportHandler.java xml/Importer.java xml/SessionImporter.java xml/SysViewImportHandler.java

Added: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/SessionImporter.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/SessionImporter.java?view=auto&rev=154700
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/SessionImporter.java (added)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/SessionImporter.java Mon Feb 21 10:03:31 2005
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+  *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.xml;
+
+import org.apache.jackrabbit.core.Constants;
+import org.apache.jackrabbit.core.NamespaceResolver;
+import org.apache.jackrabbit.core.NodeImpl;
+import org.apache.jackrabbit.core.QName;
+import org.apache.jackrabbit.core.SessionImpl;
+import org.apache.log4j.Logger;
+
+import javax.jcr.ItemExistsException;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.ReferenceValue;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.PropertyDef;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+
+/**
+ * <code>SessionImporter</code> ...
+ */
+public class SessionImporter implements Importer {
+
+    private static Logger log = Logger.getLogger(SessionImporter.class);
+
+    private final SessionImpl session;
+    private final NodeImpl importTargetNode;
+    private final int uuidBehavior;
+
+    private Stack parents;
+
+    /**
+     * mapping <original uuid> to <new uuid> of mix:referenceable nodes
+     */
+    private final HashMap uuidMap;
+    /**
+     * list of imported reference properties that might need correcting
+     */
+    private final ArrayList references;
+
+    public SessionImporter(NodeImpl importTargetNode,
+                           SessionImpl session,
+                           int uuidBehavior) {
+        this.importTargetNode = importTargetNode;
+        this.session = session;
+        this.uuidBehavior = uuidBehavior;
+
+        uuidMap = new HashMap();
+        references = new ArrayList();
+
+        parents = new Stack();
+        parents.push(importTargetNode);
+    }
+
+    protected NodeImpl createNode(NodeImpl parent,
+                                  QName nodeName,
+                                  QName nodeTypeName,
+                                  QName[] mixinNames,
+                                  String uuid)
+            throws RepositoryException {
+        NodeImpl node;
+        // add node
+        node = (NodeImpl) parent.addNode(nodeName, nodeTypeName, uuid);
+        // add mixins
+        if (mixinNames != null) {
+            for (int i = 0; i < mixinNames.length; i++) {
+                node.addMixin(mixinNames[i]);
+            }
+        }
+        return node;
+    }
+
+    protected NodeImpl resolveUUIDConflict(NodeImpl parent,
+                                           NodeImpl conflicting,
+                                           NodeInfo nodeInfo)
+            throws RepositoryException {
+        NodeImpl node;
+        if (uuidBehavior == IMPORT_UUID_CREATE_NEW) {
+            // create new with new uuid
+            node = createNode(parent, nodeInfo.getName(),
+                    nodeInfo.getNodeTypeName(), nodeInfo.getMixinNames(), null);
+            // remember uuid mapping
+            if (node.isNodeType(Constants.MIX_REFERENCEABLE)) {
+                uuidMap.put(nodeInfo.getUUID(), node.getUUID());
+            }
+        } else if (uuidBehavior == IMPORT_UUID_COLLISION_THROW) {
+            String msg = "a node with uuid " + nodeInfo.getUUID() + " already exists!";
+            log.debug(msg);
+            throw new ItemExistsException(msg);
+        } else if (uuidBehavior == IMPORT_UUID_COLLISION_REMOVE_EXISTING) {
+            // make sure conflicting node is not importTargetNode or an ancestor thereof
+            if (importTargetNode.getPath().startsWith(conflicting.getPath())) {
+                String msg = "cannot remove ancestor node";
+                log.debug(msg);
+                throw new RepositoryException(msg);
+            }
+            // remove conflicting
+            conflicting.remove();
+            // create new with given uuid
+            node = createNode(parent, nodeInfo.getName(),
+                    nodeInfo.getNodeTypeName(), nodeInfo.getMixinNames(),
+                    nodeInfo.getUUID());
+        } else if (uuidBehavior == IMPORT_UUID_COLLISION_REPLACE_EXISTING) {
+            if (conflicting.isRepositoryRoot()) {
+                String msg = "root node cannot be replaced";
+                log.debug(msg);
+                throw new RepositoryException(msg);
+            }
+            // @todo implement IMPORT_UUID_COLLISION_REPLACE_EXISTING behavior
+            throw new RepositoryException("uuidBehavior IMPORT_UUID_COLLISION_REPLACE_EXISTING is not yet implemented");
+        } else {
+            String msg = "unknown uuidBehavior: " + uuidBehavior;
+            log.debug(msg);
+            throw new RepositoryException(msg);
+        }
+        return node;
+    }
+
+    //-------------------------------------------------------------< Importer >
+    /**
+     * @see Importer#start()
+     */
+    public void start() throws RepositoryException {
+        // nop
+    }
+
+    /**
+     * @see Importer#startNode(NodeInfo, List, NamespaceResolver)
+     */
+    public void startNode(NodeInfo nodeInfo, List propInfos,
+                          NamespaceResolver nsContext)
+            throws RepositoryException {
+        NodeImpl parent = (NodeImpl) parents.peek();
+
+        // process node
+
+        NodeImpl node;
+        String uuid = nodeInfo.getUUID();
+        QName nodeName = nodeInfo.getName();
+        QName ntName = nodeInfo.getNodeTypeName();
+        QName[] mixins = nodeInfo.getMixinNames();
+        if (uuid == null) {
+            // no potential uuid conflict, always add new node
+            node = createNode(parent, nodeName, ntName, mixins, null);
+        } else {
+            // potential uuid conflict
+            NodeImpl conflicting;
+            try {
+                conflicting = (NodeImpl) session.getNodeByUUID(uuid);
+            } catch (ItemNotFoundException infe) {
+                conflicting = null;
+            }
+            if (conflicting != null) {
+                // resolve uuid conflict
+                node = resolveUUIDConflict(parent, conflicting, nodeInfo);
+            } else {
+                // create new with given uuid
+                node = createNode(parent, nodeName, ntName, mixins, uuid);
+            }
+        }
+
+        // process properties
+
+        Iterator iter = propInfos.iterator();
+        while (iter.hasNext()) {
+            PropInfo pi = (PropInfo) iter.next();
+            QName propName = pi.getName();
+            Value[] vals = pi.getValues();
+            int type = pi.getType();
+            if (node.hasProperty(propName)) {
+                PropertyDef def = node.getProperty(propName).getDefinition();
+                if (def.isProtected()) {
+                    // skip protected property
+                    log.debug("skipping protected property " + propName);
+                    continue;
+                }
+            }
+            // multi- or single-valued property?
+            if (vals.length == 1) {
+                // could be single- or multi-valued (n == 1)
+                try {
+                    // try setting single-value
+                    node.setProperty(propName, vals[0]);
+                } catch (ValueFormatException vfe) {
+                    // try setting value array
+                    node.setProperty(propName, vals, type);
+                } catch (ConstraintViolationException vfe) {
+                    // try setting value array
+                    node.setProperty(propName, vals, type);
+                }
+            } else {
+                // can only be multi-valued (n == 0 || n > 1)
+                node.setProperty(propName, vals, type);
+            }
+            if (type == PropertyType.REFERENCE) {
+                // store reference for later resolution
+                references.add(node.getProperty(propName));
+            }
+        }
+
+        parents.push(node);
+    }
+
+    /**
+     * @see Importer#endNode(NodeInfo)
+     */
+    public void endNode(NodeInfo node) throws RepositoryException {
+        parents.pop();
+    }
+
+    /**
+     * @see Importer#end()
+     */
+    public void end() throws RepositoryException {
+        /**
+         * adjust references that refer to uuid's which have been mapped to
+         * newly gererated uuid's on import
+         */
+        Iterator iter = references.iterator();
+        while (iter.hasNext()) {
+            Property prop = (Property) iter.next();
+            // being paranoid...
+            if (prop.getType() != PropertyType.REFERENCE) {
+                continue;
+            }
+            if (prop.getDefinition().isMultiple()) {
+                Value[] values = prop.getValues();
+                Value[] newVals = new Value[values.length];
+                for (int i = 0; i < values.length; i++) {
+                    Value val = values[i];
+                    String original = val.getString();
+                    String adjusted = (String) uuidMap.get(original);
+                    if (adjusted != null) {
+                        newVals[i] = new ReferenceValue(session.getNodeByUUID(adjusted));
+                    } else {
+                        // reference doesn't need adjusting, just copy old value
+                        newVals[i] = val;
+                    }
+                }
+                prop.setValue(newVals);
+            } else {
+                Value val = prop.getValue();
+                String original = val.getString();
+                String adjusted = (String) uuidMap.get(original);
+                if (adjusted != null) {
+                    prop.setValue(session.getNodeByUUID(adjusted));
+                }
+            }
+        }
+    }
+}

Propchange: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/SessionImporter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/SysViewImportHandler.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/SysViewImportHandler.java?view=diff&r1=154699&r2=154700
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/SysViewImportHandler.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/xml/SysViewImportHandler.java Mon Feb 21 10:03:31 2005
@@ -16,8 +16,12 @@
  */
 package org.apache.jackrabbit.core.xml;
 
-import org.apache.jackrabbit.core.*;
-import org.apache.jackrabbit.core.nodetype.NodeTypeImpl;
+import org.apache.jackrabbit.core.BaseException;
+import org.apache.jackrabbit.core.Constants;
+import org.apache.jackrabbit.core.IllegalNameException;
+import org.apache.jackrabbit.core.NamespaceResolver;
+import org.apache.jackrabbit.core.QName;
+import org.apache.jackrabbit.core.UnknownPrefixException;
 import org.apache.jackrabbit.core.util.Base64;
 import org.apache.jackrabbit.core.util.ValueHelper;
 import org.apache.log4j.Logger;
@@ -26,21 +30,25 @@
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.DefaultHandler;
 
-import javax.jcr.*;
-import javax.jcr.nodetype.ConstraintViolationException;
-import javax.jcr.nodetype.NodeDef;
-import javax.jcr.nodetype.PropertyDef;
+import javax.jcr.BinaryValue;
+import javax.jcr.InvalidSerializedDataException;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Stack;
 
 /**
  * <code>SysViewImportHandler</code>  ...
  */
 class SysViewImportHandler extends DefaultHandler implements Constants {
+
     private static Logger log = Logger.getLogger(SysViewImportHandler.class);
 
-    private SessionImpl session;
+    private final Importer importer;
+    private final NamespaceResolver nsContext;
 
     /**
      * stack of ImportState instances; an instance is pushed onto the stack
@@ -48,134 +56,156 @@
      * the same instance is popped from the stack in the endElement method
      * when the corresponding sv:node element is encountered.
      */
-    private Stack stateStack = new Stack();
-
-    /**
-     * list of all reference properties that need to be adjusted
-     */
-    private LinkedList references = new LinkedList();
-
-    /**
-     * mapping <original uuid> to <new uuid> of mix:referenceable nodes
-     */
-    private HashMap referees = new HashMap();
+    private final Stack stack = new Stack();
 
     /**
      * fields used temporarily while processing sv:property and sv:value elements
      */
     private QName currentPropName;
     private int currentPropType = PropertyType.UNDEFINED;
-    private ArrayList currentPropValues;
+    private ArrayList currentPropValues = new ArrayList();
     private StringBuffer currentPropValue;
 
-    SysViewImportHandler(NodeImpl importTargetNode, SessionImpl session) {
-        ImportState state = new ImportState();
-        state.node = importTargetNode;
-        this.session = session;
-        stateStack.push(state);
+    SysViewImportHandler(Importer importer, NamespaceResolver nsContext) {
+        this.importer = importer;
+        this.nsContext = nsContext;
+    }
+
+    private void processNode(ImportState state, boolean start, boolean end)
+            throws SAXException {
+        if (!start && !end) {
+            return;
+        }
+        Importer.NodeInfo node = new Importer.NodeInfo();
+        node.setName(state.nodeName);
+        node.setNodeTypeName(state.nodeTypeName);
+        if (state.mixinNames != null) {
+            QName[] mixins = (QName[]) state.mixinNames.toArray(new QName[state.mixinNames.size()]);
+            node.setMixinNames(mixins);
+        }
+        node.setUUID(state.uuid);
+        // call Importer
+        try {
+            if (start) {
+                importer.startNode(node, state.props, nsContext);
+            }
+            if (end) {
+                importer.endNode(node);
+            }
+        } catch (RepositoryException re) {
+            throw new SAXException(re);
+        }
     }
 
     //-------------------------------------------------------< ContentHandler >
     /**
+     * @see ContentHandler#startDocument()
+     */
+    public void startDocument() throws SAXException {
+        try {
+            importer.start();
+        } catch (RepositoryException re) {
+            throw new SAXException(re);
+        }
+    }
+
+    /**
      * @see ContentHandler#startElement(String, String, String, Attributes)
      */
     public void startElement(String namespaceURI, String localName,
                              String qName, Attributes atts)
             throws SAXException {
-        try {
-            String elemName;
-            String nsURI;
-            if (namespaceURI != null && !"".equals(namespaceURI)) {
-                nsURI = namespaceURI;
-                elemName = localName;
-            } else {
-                try {
-                    nsURI = QName.fromJCRName(qName, session.getNamespaceResolver()).getNamespaceURI();
-                    elemName = QName.fromJCRName(qName, session.getNamespaceResolver()).getLocalName();
-                } catch (BaseException e) {
-                    // should never happen...
-                    String msg = "internal error: failed to parse/resolve element name " + qName;
-                    log.debug(msg);
-                    throw new SAXException(msg, e);
-                }
+        String elemName;
+        String nsURI;
+        if (namespaceURI != null && !"".equals(namespaceURI)) {
+            nsURI = namespaceURI;
+            elemName = localName;
+        } else {
+            try {
+                nsURI = QName.fromJCRName(qName, nsContext).getNamespaceURI();
+                elemName = QName.fromJCRName(qName, nsContext).getLocalName();
+            } catch (BaseException e) {
+                // should never happen...
+                String msg = "internal error: failed to parse/resolve element name " + qName;
+                log.debug(msg);
+                throw new SAXException(msg, e);
             }
-            // check namespace
-            if (!NS_SV_URI.equals(nsURI)) {
-                throw new SAXException(new InvalidSerializedDataException("invalid namespace for element in system view xml document: " + nsURI));
+        }
+        // check namespace
+        if (!NS_SV_URI.equals(nsURI)) {
+            throw new SAXException(new InvalidSerializedDataException("invalid namespace for element in system view xml document: " + nsURI));
+        }
+        // check element name
+        if (SysViewSAXEventGenerator.NODE_ELEMENT.equals(elemName)) {
+            // sv:node element
+
+            // node name (value of sv:name attribute)
+            String name = atts.getValue(SysViewSAXEventGenerator.NS_SV_URI, SysViewSAXEventGenerator.NAME_ATTRIBUTE);
+            if (name == null) {
+                // try qualified name
+                name = atts.getValue(SysViewSAXEventGenerator.NS_SV_PREFIX + ":" + SysViewSAXEventGenerator.NAME_ATTRIBUTE);
+            }
+            if (name == null) {
+                throw new SAXException(new InvalidSerializedDataException("missing mandatory sv:name attributeof element sv:node"));
             }
-            // check element name
-            if (SysViewSAXEventGenerator.NODE_ELEMENT.equals(elemName)) {
-                // sv:node element
-
-                // node name (value of sv:name attribute)
-                String name = atts.getValue(SysViewSAXEventGenerator.NS_SV_URI, SysViewSAXEventGenerator.NAME_ATTRIBUTE);
-                if (name == null) {
-                    // try qualified name
-                    name = atts.getValue(SysViewSAXEventGenerator.NS_SV_PREFIX + ":" + SysViewSAXEventGenerator.NAME_ATTRIBUTE);
-                }
-                if (name == null) {
-                    throw new SAXException(new InvalidSerializedDataException("missing mandatory sv:name attributeof element sv:node"));
-                }
-
-                ImportState current = (ImportState) stateStack.peek();
-                if (current.node == null) {
-                    // need to create current node first
-                    createNode(current);
-                }
-
-                // push new ImportState instance onto the stack
-                ImportState state = new ImportState();
-                state.parent = current.node;
-                try {
-                    state.nodeName = QName.fromJCRName(name, session.getNamespaceResolver());
-                } catch (IllegalNameException ine) {
-                    throw new SAXException(new InvalidSerializedDataException("illegal node name: " + name, ine));
-                } catch (UnknownPrefixException upe) {
-                    throw new SAXException(new InvalidSerializedDataException("illegal node name: " + name, upe));
-                }
-                stateStack.push(state);
-            } else if (SysViewSAXEventGenerator.PROPERTY_ELEMENT.equals(elemName)) {
-                // sv:property element
-
-                // reset temp fields
-                currentPropValues = new ArrayList();
 
-                // property name (value of sv:name attribute)
-                String name = atts.getValue(SysViewSAXEventGenerator.NS_SV_URI, SysViewSAXEventGenerator.NAME_ATTRIBUTE);
-                if (name == null) {
-                    // try qualified name
-                    name = atts.getValue(SysViewSAXEventGenerator.NS_SV_PREFIX + ":" + SysViewSAXEventGenerator.NAME_ATTRIBUTE);
-                }
-                if (name == null) {
-                    throw new SAXException(new InvalidSerializedDataException("missing mandatory sv:name attributeof element sv:property"));
-                }
-                try {
-                    currentPropName = QName.fromJCRName(name, session.getNamespaceResolver());
-                } catch (IllegalNameException ine) {
-                    throw new SAXException(new InvalidSerializedDataException("illegal property name: " + name, ine));
-                } catch (UnknownPrefixException upe) {
-                    throw new SAXException(new InvalidSerializedDataException("illegal property name: " + name, upe));
-                }
-                // property type (sv:type attribute)
-                String type = atts.getValue(SysViewSAXEventGenerator.NS_SV_URI, SysViewSAXEventGenerator.TYPE_ATTRIBUTE);
-                if (type == null) {
-                    // try qualified name
-                    type = atts.getValue(SysViewSAXEventGenerator.NS_SV_PREFIX + ":" + SysViewSAXEventGenerator.TYPE_ATTRIBUTE);
-                }
-                if (type == null) {
-                    throw new SAXException(new InvalidSerializedDataException("missing mandatory sv:type attributeof element sv:property"));
-                }
-                currentPropType = PropertyType.valueFromName(type);
-            } else if (SysViewSAXEventGenerator.VALUE_ELEMENT.equals(elemName)) {
-                // sv:value element
+            if (!stack.isEmpty()) {
+                // process current node first
+                ImportState current = (ImportState) stack.peek();
+                // need to start current node
+                processNode(current, true, false);
+                current.started = true;
+            }
 
-                // reset temp fields
-                currentPropValue = new StringBuffer();
-            } else {
-                throw new SAXException(new InvalidSerializedDataException("unexpected element found in system view xml document: " + elemName));
+            // push new ImportState instance onto the stack
+            ImportState state = new ImportState();
+            try {
+                state.nodeName = QName.fromJCRName(name, nsContext);
+            } catch (IllegalNameException ine) {
+                throw new SAXException(new InvalidSerializedDataException("illegal node name: " + name, ine));
+            } catch (UnknownPrefixException upe) {
+                throw new SAXException(new InvalidSerializedDataException("illegal node name: " + name, upe));
             }
-        } catch (RepositoryException re) {
-            throw new SAXException(re);
+            stack.push(state);
+        } else if (SysViewSAXEventGenerator.PROPERTY_ELEMENT.equals(elemName)) {
+            // sv:property element
+
+            // reset temp fields
+            currentPropValues.clear();
+
+            // property name (value of sv:name attribute)
+            String name = atts.getValue(SysViewSAXEventGenerator.NS_SV_URI, SysViewSAXEventGenerator.NAME_ATTRIBUTE);
+            if (name == null) {
+                // try qualified name
+                name = atts.getValue(SysViewSAXEventGenerator.NS_SV_PREFIX + ":" + SysViewSAXEventGenerator.NAME_ATTRIBUTE);
+            }
+            if (name == null) {
+                throw new SAXException(new InvalidSerializedDataException("missing mandatory sv:name attributeof element sv:property"));
+            }
+            try {
+                currentPropName = QName.fromJCRName(name, nsContext);
+            } catch (IllegalNameException ine) {
+                throw new SAXException(new InvalidSerializedDataException("illegal property name: " + name, ine));
+            } catch (UnknownPrefixException upe) {
+                throw new SAXException(new InvalidSerializedDataException("illegal property name: " + name, upe));
+            }
+            // property type (sv:type attribute)
+            String type = atts.getValue(SysViewSAXEventGenerator.NS_SV_URI, SysViewSAXEventGenerator.TYPE_ATTRIBUTE);
+            if (type == null) {
+                // try qualified name
+                type = atts.getValue(SysViewSAXEventGenerator.NS_SV_PREFIX + ":" + SysViewSAXEventGenerator.TYPE_ATTRIBUTE);
+            }
+            if (type == null) {
+                throw new SAXException(new InvalidSerializedDataException("missing mandatory sv:type attributeof element sv:property"));
+            }
+            currentPropType = PropertyType.valueFromName(type);
+        } else if (SysViewSAXEventGenerator.VALUE_ELEMENT.equals(elemName)) {
+            // sv:value element
+
+            // reset temp fields
+            currentPropValue = new StringBuffer();
+        } else {
+            throw new SAXException(new InvalidSerializedDataException("unexpected element found in system view xml document: " + elemName));
         }
     }
 
@@ -199,7 +229,7 @@
                 elemName = localName;
             } else {
                 try {
-                    elemName = QName.fromJCRName(qName, session.getNamespaceResolver()).getLocalName();
+                    elemName = QName.fromJCRName(qName, nsContext).getLocalName();
                 } catch (BaseException e) {
                     // should never happen...
                     String msg = "internal error: failed to parse/resolve element name " + qName;
@@ -208,15 +238,19 @@
                 }
             }
             // check element name
-            ImportState current = (ImportState) stateStack.peek();
+            ImportState state = (ImportState) stack.peek();
             if (SysViewSAXEventGenerator.NODE_ELEMENT.equals(elemName)) {
                 // sv:node element
-                if (current.node == null) {
-                    // need to create current node first
-                    createNode(current);
+                if (!state.started) {
+                    // need to start & end current node
+                    processNode(state, true, true);
+                    state.started = true;
+                } else {
+                    // need to end current node
+                    processNode(state, false, true);
                 }
                 // pop current state from stack
-                stateStack.pop();
+                stack.pop();
             } else if (SysViewSAXEventGenerator.PROPERTY_ELEMENT.equals(elemName)) {
                 // sv:property element
 
@@ -224,20 +258,20 @@
                 // have been collected and create node as necessary
                 if (currentPropName.equals(JCR_PRIMARYTYPE)) {
                     try {
-                        current.primaryType = QName.fromJCRName((String) currentPropValues.get(0), session.getNamespaceResolver());
+                        state.nodeTypeName = QName.fromJCRName((String) currentPropValues.get(0), nsContext);
                     } catch (IllegalNameException ine) {
                         throw new SAXException(new InvalidSerializedDataException("illegal node type name: " + currentPropValues.get(0), ine));
                     } catch (UnknownPrefixException upe) {
                         throw new SAXException(new InvalidSerializedDataException("illegal node type name: " + currentPropValues.get(0), upe));
                     }
                 } else if (currentPropName.equals(JCR_MIXINTYPES)) {
-                    if (current.mixinTypes == null) {
-                        current.mixinTypes = new ArrayList(currentPropValues.size());
+                    if (state.mixinNames == null) {
+                        state.mixinNames = new ArrayList(currentPropValues.size());
                     }
                     for (int i = 0; i < currentPropValues.size(); i++) {
                         try {
-                            QName mixin = QName.fromJCRName((String) currentPropValues.get(i), session.getNamespaceResolver());
-                            current.mixinTypes.add(mixin);
+                            QName mixin = QName.fromJCRName((String) currentPropValues.get(i), nsContext);
+                            state.mixinNames.add(mixin);
                         } catch (IllegalNameException ine) {
                             throw new SAXException(new InvalidSerializedDataException("illegal mixin type name: " + currentPropValues.get(i), ine));
                         } catch (UnknownPrefixException upe) {
@@ -245,31 +279,8 @@
                         }
                     }
                 } else if (currentPropName.equals(JCR_UUID)) {
-                    current.uuid = (String) currentPropValues.get(0);
-                    // jcr:uuid is the last system property; we can assume that all
-                    // required system properties have been collected by now
-                    if (current.node == null) {
-                        // now that we've collected all required system properties
-                        // we're ready to create the node
-                        createNode(current);
-                    }
-                } else if (currentPropName.equals(JCR_BASEVERSION)) {
-                    // ignore so far
-                } else if (currentPropName.equals(JCR_VERSIONHISTORY)) {
-                    // ignore so far
-                } else if (currentPropName.equals(JCR_PREDECESSORS)) {
-                    // ignore so far
-                } else if (currentPropName.equals(JCR_ISCHECKEDOUT)) {
-                    // ignore so far
+                    state.uuid = (String) currentPropValues.get(0);
                 } else {
-                    // non-system property encountered; we can assume that all
-                    // required system properties have been collected by now
-                    if (current.node == null) {
-                        // now that we've collected all required system properties
-                        // we're ready to create the node
-                        createNode(current);
-                    }
-
                     // convert values to native type and set property
                     Value[] vals = new Value[currentPropValues.size()];
                     for (int i = 0; i < currentPropValues.size(); i++) {
@@ -288,40 +299,16 @@
                             vals[i] = ValueHelper.convert(value, currentPropType);
                         }
                     }
-                    if (current.node.hasProperty(currentPropName)) {
-                        PropertyDef def = current.node.getProperty(currentPropName).getDefinition();
-                        if (def.isProtected()) {
-                            // ignore protected property
-                            // reset temp fields and get outta here
-                            currentPropValues = null;
-                            return;
-                        }
-                    }
-                    // multi- or single-valued property?
-                    if (vals.length == 1) {
-                        // could be single- or multi-valued (n == 1)
-                        try {
-                            // try setting single-value
-                            current.node.setProperty(currentPropName, vals[0]);
-                        } catch (ValueFormatException vfe) {
-                            // try setting value array
-                            current.node.setProperty(currentPropName, vals);
-                        } catch (ConstraintViolationException vfe) {
-                            // try setting value array
-                            current.node.setProperty(currentPropName, vals);
-                        }
-                    } else {
-                        // can only be multi-valued (n == 0 || n > 1)
-                        current.node.setProperty(currentPropName, vals);
-                    }
-                    if (currentPropType == PropertyType.REFERENCE) {
-                        // store reference for later resolution
-                        references.add(current.node.getProperty(currentPropName));
-                    }
+                    Importer.PropInfo prop = new Importer.PropInfo();
+                    prop.setName(currentPropName);
+                    prop.setType(currentPropType);
+                    prop.setValues(vals);
+
+                    state.props.add(prop);
                 }
 
                 // reset temp fields
-                currentPropValues = null;
+                currentPropValues.clear();
             } else if (SysViewSAXEventGenerator.VALUE_ELEMENT.equals(elemName)) {
                 // sv:value element
                 currentPropValues.add(currentPropValue.toString());
@@ -340,91 +327,39 @@
      */
     public void endDocument() throws SAXException {
         try {
-            // adjust all reference properties
-            Iterator iter = references.iterator();
-            while (iter.hasNext()) {
-                Property prop = (Property) iter.next();
-                if (prop.getDefinition().isMultiple()) {
-                    Value[] values = prop.getValues();
-                    Value[] newVals = new Value[values.length];
-                    for (int i = 0; i < values.length; i++) {
-                        Value val = values[i];
-                        if (val.getType() == PropertyType.REFERENCE) {
-                            String original = val.getString();
-                            String adjusted = (String) referees.get(original);
-                            if (adjusted == null) {
-                                log.warn("Reference " + original + " of property can not be adjusted! " + prop.getPath());
-                                newVals[i] = val;
-                            } else {
-                                newVals[i] = new ReferenceValue(session.getNodeByUUID(adjusted));
-                            }
-                        } else {
-                            newVals[i] = val;
-                        }
-                    }
-                    prop.setValue(newVals);
-                } else {
-                    Value val = prop.getValue();
-                    if (val.getType() == PropertyType.REFERENCE) {
-                        String original = val.getString();
-                        String adjusted = (String) referees.get(original);
-                        if (adjusted == null) {
-                            log.warn("Reference " + original + " of property can not be adjusted! " + prop.getPath());
-                        } else {
-                            prop.setValue(session.getNodeByUUID(adjusted));
-                        }
-                    }
-                }
-            }
+            importer.end();
         } catch (RepositoryException re) {
-            throw new SAXException("failed to adjust REFERENCE properties", re);
-        }
-    }
-
-    private void createNode(ImportState state) throws RepositoryException {
-        if (state.primaryType == null) {
-            throw new InvalidSerializedDataException("missing mandatory jcr:primaryType property");
-        }
-        if (state.uuid != null) {
-            // @todo what are the semantics of the uuid with respect to import?
-            // move existing node with given uuid to current position?
-            // create with new uuid? what about reference values refering to given uuid?
-        }
-        if (state.parent.hasNode(state.nodeName)) {
-            state.node = state.parent.getNode(state.nodeName);
-            NodeDef def = state.node.getDefinition();
-            if (def.isProtected() || def.isAutoCreate()) {
-                // @todo how to handle protected/auto-created child node?
-                state.node = (NodeImpl) state.parent.getNode(state.nodeName);
-            } else if (!def.allowSameNameSibs()) {
-                throw new ItemExistsException(state.parent.safeGetJCRPath() + "/" + state.nodeName);
-            }
-        }
-        if (state.node == null) {
-            state.node = (NodeImpl) state.parent.addNode(state.nodeName, state.primaryType);
-            if (state.mixinTypes != null) {
-                for (int i = 0; i < state.mixinTypes.size(); i++) {
-                    NodeTypeImpl mixin = session.getNodeTypeManager().getNodeType((QName) state.mixinTypes.get(i));
-                    state.node.addMixin(mixin.getName());
-                }
-            }
-        }
-
-        // check for mix:referenceable
-        if (state.node.isNodeType(MIX_REFERENCEABLE)) {
-            log.info("adding refereee: ori=" + state.uuid + " new=" + state.node.getUUID());
-            referees.put(state.uuid, state.node.getUUID());
+            throw new SAXException(re);
         }
     }
 
     //--------------------------------------------------------< inner classes >
     class ImportState {
-        QName primaryType = null;
-        ArrayList mixinTypes = null;
-        String uuid = null;
-
-        QName nodeName = null;
-        NodeImpl parent = null;
-        NodeImpl node = null;
+        /**
+         * name of current node
+         */
+        QName nodeName;
+        /**
+         * primary type of current node
+         */
+        QName nodeTypeName;
+        /**
+         * list of mixin types of current node
+         */
+        ArrayList mixinNames;
+        /**
+         * uuid of current node
+         */
+        String uuid;
+
+        /**
+         * list of PropInfo instances representing properties of current node
+         */
+        ArrayList props = new ArrayList();
+
+        /**
+         * flag indicating whether startNode() has been called for current node
+         */
+        boolean started = false;
     }
 }