You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by kw...@apache.org on 2021/09/23 13:32:58 UTC

[jackrabbit-filevault] 01/01: JCRVLT-357 split up parser and importer for DocView files

This is an automated email from the ASF dual-hosted git repository.

kwin pushed a commit to branch feature/JCRVLT-357-split-up-docviewsaximporter
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git

commit e08c755c6e71149f7c1ca62a2aeaf2bf5cfdc60f
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Thu Sep 23 15:32:47 2021 +0200

    JCRVLT-357 split up parser and importer for DocView files
---
 .../vault/fs/impl/io/AbstractArtifactHandler.java  |   15 +-
 .../vault/fs/impl/io/DocViewAdapter.java           |   12 +-
 .../vault/fs/impl/io/DocViewAnalyzer.java          |    1 +
 ...ocViewSAXImporter.java => DocViewImporter.java} | 1107 ++++++++------------
 .../vault/fs/impl/io/DocViewSAXHandler.java        |  265 +++++
 .../vault/fs/impl/io/FileArtifactHandler.java      |   20 +-
 .../vault/fs/impl/io/GenericArtifactHandler.java   |   14 +-
 .../vault/fs/impl/io/JackrabbitACLImporter.java    |   87 +-
 .../vault/fs/impl/io/JcrSysViewTransformer.java    |   92 +-
 .../jackrabbit/vault/fs/io/DocViewParser.java      |   47 +
 .../vault/fs/io/DocViewParserHandler.java          |   29 +
 .../apache/jackrabbit/vault/util/DocViewNode.java  |   13 +-
 .../apache/jackrabbit/vault/util/DocViewNode2.java |   80 ++
 .../jackrabbit/vault/util/DocViewProperty.java     |    3 +-
 ...{DocViewProperty.java => DocViewProperty2.java} |  115 +-
 .../jackrabbit/vault/util/EffectiveNodeType.java   |    1 +
 16 files changed, 1065 insertions(+), 836 deletions(-)

diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AbstractArtifactHandler.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AbstractArtifactHandler.java
index 0f0ded9..ef13192 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AbstractArtifactHandler.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AbstractArtifactHandler.java
@@ -32,10 +32,13 @@ import org.apache.jackrabbit.vault.fs.api.ArtifactHandler;
 import org.apache.jackrabbit.vault.fs.api.ArtifactSet;
 import org.apache.jackrabbit.vault.fs.api.DumpContext;
 import org.apache.jackrabbit.vault.fs.api.Dumpable;
+import org.apache.jackrabbit.vault.fs.api.IdConflictPolicy;
 import org.apache.jackrabbit.vault.fs.api.ImportInfo;
 import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
 import org.apache.jackrabbit.vault.fs.impl.ArtifactSetImpl;
 import org.apache.jackrabbit.vault.fs.io.AccessControlHandling;
+import org.apache.jackrabbit.vault.fs.io.DocViewParser;
+import org.apache.jackrabbit.vault.fs.io.DocViewParserHandler;
 import org.apache.jackrabbit.vault.fs.io.ImportOptions;
 import org.apache.jackrabbit.vault.fs.spi.ACLManagement;
 import org.apache.jackrabbit.vault.fs.spi.ServiceProviderFactory;
@@ -160,13 +163,9 @@ public abstract class AbstractArtifactHandler implements ArtifactHandler, Dumpab
         ctx.println(isLast, getClass().getSimpleName());
     }
 
-    protected void parseXmlWithSaxHandler(InputSource source, DefaultHandler handler) throws ParserConfigurationException, SAXException, IOException {
-        SAXParserFactory factory = SAXParserFactory.newInstance();
-        factory.setNamespaceAware(true);
-        factory.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
-        SAXParser parser = factory.newSAXParser();
-        parser.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
-        parser.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
-        parser.parse(source, handler);
+    protected ImportInfoImpl importDocView(InputSource source, Node parentNode, String rootNodeName, ArtifactSetImpl artifacts, WorkspaceFilter wspFilter, IdConflictPolicy idConflictPolicy) throws IOException, RepositoryException {
+        DocViewImporter handler = new DocViewImporter(parentNode, rootNodeName, artifacts, wspFilter, idConflictPolicy, getAcHandling(), getCugHandling());
+        new DocViewParser().parse(rootNodeName, source, handler);
+        return handler.getInfo();
     }
 }
\ No newline at end of file
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewAdapter.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewAdapter.java
index 8980aa9..f690dec 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewAdapter.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewAdapter.java
@@ -20,13 +20,15 @@ import java.util.List;
 
 import javax.jcr.RepositoryException;
 
-import org.apache.jackrabbit.vault.util.DocViewNode;
+import org.apache.jackrabbit.vault.util.DocViewNode2;
 import org.xml.sax.SAXException;
 
 /**
- * {@code DocViewAdapter} is used by the {@link org.apache.jackrabbit.vault.fs.impl.io.DocViewSAXImporter} to handle
+ * {@code DocViewAdapter} is used by the {@link org.apache.jackrabbit.vault.fs.impl.io.DocViewSAXHandler} to handle
  * special content that is not importable using "normal" JCR calls. For example users and other protected content
  * needs to be imported using the {@link javax.jcr.Session#importXML(String, java.io.InputStream, int)} or similar.
+ * 
+ * TODO: check overlap with DocViewParserHandler
  */
 public interface DocViewAdapter {
 
@@ -36,14 +38,14 @@ public interface DocViewAdapter {
      * @throws SAXException if a parsing error occurs.
      * @throws RepositoryException if a import exception occurs.
      */
-    void startNode(DocViewNode node) throws SAXException, RepositoryException;
+    void startNode(DocViewNode2 node) throws RepositoryException;
 
     /**
      * Ends node is invoked when the importer ascends from an element.
      * @throws SAXException if a parsing error occurs.
      * @throws RepositoryException if a import exception occurs.
      */
-    void endNode() throws SAXException, RepositoryException;
+    void endNode() throws RepositoryException;
 
     /**
      * Is called by the importer if the adapter is no longer used and must finalize the import.
@@ -51,6 +53,6 @@ public interface DocViewAdapter {
      * @throws RepositoryException if a import exception occurs.
      * @return The paths that were created.
      */
-    List<String> close() throws SAXException, RepositoryException;
+    List<String> close() throws RepositoryException;
 
 }
\ No newline at end of file
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewAnalyzer.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewAnalyzer.java
index 68b5dac..9caebe7 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewAnalyzer.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewAnalyzer.java
@@ -42,6 +42,7 @@ import org.xml.sax.SAXException;
 
 /**
  * Implements a docview analyzer that scans the XML for nodes.
+ * TODO: not really necessary, replace with implementation of DocViewParserHandler....
  */
 public class DocViewAnalyzer extends RejectingEntityDefaultHandler implements NamespaceResolver {
 
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXImporter.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewImporter.java
similarity index 70%
rename from vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXImporter.java
rename to vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewImporter.java
index 9f29a60..f57621a 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXImporter.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewImporter.java
@@ -1,4 +1,4 @@
-/*
+/*************************************************************************
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
  * this work for additional information regarding copyright ownership.
@@ -13,8 +13,7 @@
  * 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.vault.fs.impl.io;
 
 import java.io.IOException;
@@ -32,10 +31,12 @@ import java.util.UUID;
 
 import javax.jcr.ImportUUIDBehavior;
 import javax.jcr.Item;
+import javax.jcr.ItemExistsException;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.NamespaceException;
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
+import javax.jcr.PathNotFoundException;
 import javax.jcr.Property;
 import javax.jcr.PropertyIterator;
 import javax.jcr.PropertyType;
@@ -45,15 +46,18 @@ import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.Value;
 import javax.jcr.ValueFactory;
+import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
 import javax.jcr.nodetype.NodeType;
 import javax.jcr.nodetype.PropertyDefinition;
+import javax.jcr.version.VersionException;
 
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver;
+import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
 import org.apache.jackrabbit.spi.commons.name.NameConstants;
-import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
-import org.apache.jackrabbit.util.ISO9075;
+import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
 import org.apache.jackrabbit.util.Text;
 import org.apache.jackrabbit.vault.fs.api.Artifact;
 import org.apache.jackrabbit.vault.fs.api.ArtifactType;
@@ -66,88 +70,56 @@ import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
 import org.apache.jackrabbit.vault.fs.impl.ArtifactSetImpl;
 import org.apache.jackrabbit.vault.fs.impl.PropertyValueArtifact;
 import org.apache.jackrabbit.vault.fs.io.AccessControlHandling;
+import org.apache.jackrabbit.vault.fs.io.DocViewParserHandler;
 import org.apache.jackrabbit.vault.fs.spi.ACLManagement;
 import org.apache.jackrabbit.vault.fs.spi.ServiceProviderFactory;
 import org.apache.jackrabbit.vault.fs.spi.UserManagement;
 import org.apache.jackrabbit.vault.fs.spi.impl.jcr20.JcrNamespaceHelper;
-import org.apache.jackrabbit.vault.util.DocViewNode;
+import org.apache.jackrabbit.vault.util.DocViewNode2;
 import org.apache.jackrabbit.vault.util.DocViewProperty;
+import org.apache.jackrabbit.vault.util.DocViewProperty2;
 import org.apache.jackrabbit.vault.util.EffectiveNodeType;
 import org.apache.jackrabbit.vault.util.JcrConstants;
 import org.apache.jackrabbit.vault.util.MimeTypes;
-import org.apache.jackrabbit.vault.util.RejectingEntityDefaultHandler;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.xml.sax.Attributes;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 
-/**
- * Implements an importer that processes SAX events from a (modified) document
- * view. The behaviour for existing nodes works as follows:
- * TODO: better description
- * <pre>
- *
- * - extended docview always includes SNS indexes
- * - label is the last element of the path
- * - uuid "child" means uuid of a direct child node matches
- * - uuid "desc" means uuid of a descendant child node matches
- * - uuid "other" means uuid of a node outside of the node tree matches
- *
- * uuid  | label | nt  | result
- * -     | no    | -   | create
- * -     | yes   | no  | replace
- * -     | yes   | yes | reuse
- * no    | no    | -   | create
- * no    | yes   | no  | replace
- * no    | yes   | yes | reuse
- * child | no    | no  | replace
- * child | no    | yes | replace
- * child | yes   | no  | replace
- * child | yes   | yes | reuse
- * desc  | -     | -   | *error*
- * other | -     | -   | *error*
- * </pre>
- */
-public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements NamespaceResolver {
-
-    private static final String PROP_OAK_COUNTER = "oak:counter";
+public class DocViewImporter implements DocViewParserHandler {
 
-    /**
-     * the default logger
-     */
-    static final Logger log = LoggerFactory.getLogger(DocViewSAXImporter.class);
+    private static final Name NAME_REP_CUG_POLICY = NameFactoryImpl.getInstance().create(Name.NS_REP_URI, "principalNames");
+    private static final Name NAME_REP_MEMBERS = NameFactoryImpl.getInstance().create(Name.NS_REP_URI, "members");
 
+    private static final String NAMESPACE_OAK = "http://jackrabbit.apache.org/oak/ns/1.0";
+    private static final Name NAME_OAK_COUNTER = NameFactoryImpl.getInstance().create(NAMESPACE_OAK, "counter");
+    
     /**
-     * empty attributes
+     * the default logger
      */
-    static final Attributes EMPTY_ATTRIBUTES = new AttributesImpl();
+    static final Logger log = LoggerFactory.getLogger(DocViewImporter.class);
 
     /**
      * these properties are protected but can be set nevertheless via system view xml import
      */
-    static final Set<String> PROTECTED_PROPERTIES;
+    static final Set<Name> PROTECTED_PROPERTIES;
 
     static {
-        Set<String> props = new HashSet<>();
-        props.add(JcrConstants.JCR_PRIMARYTYPE);
-        props.add(JcrConstants.JCR_MIXINTYPES);
-        props.add(JcrConstants.JCR_UUID);
-        props.add(JcrConstants.JCR_ISCHECKEDOUT);
-        props.add(JcrConstants.JCR_BASEVERSION);
-        props.add(JcrConstants.JCR_PREDECESSORS);
-        props.add(JcrConstants.JCR_SUCCESSORS);
-        props.add(JcrConstants.JCR_VERSIONHISTORY);
+        Set<Name> props = new HashSet<>();
+        props.add(NameConstants.JCR_PRIMARYTYPE);
+        props.add(NameConstants.JCR_MIXINTYPES);
+        props.add(NameConstants.JCR_UUID);
+        props.add(NameConstants.JCR_ISCHECKEDOUT);
+        props.add(NameConstants.JCR_BASEVERSION);
+        props.add(NameConstants.JCR_PREDECESSORS);
+        props.add(NameConstants.JCR_SUCCESSORS);
+        props.add(NameConstants.JCR_VERSIONHISTORY);
         PROTECTED_PROPERTIES = Collections.unmodifiableSet(props);
     }
 
-    /**
-     * the current namespace state
-     */
-    private NameSpace nsStack = null;
 
     /**
      * the importing session
@@ -160,19 +132,9 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
     private final Node parentNode;
 
     /**
-     * the name of the root node
-     */
-    private final String rootNodeName;
-
-    /**
-     * the depth of the root node
-     */
-    private final int rootDepth;
-
-    /**
-     * current stack
+     * final import information
      */
-    private DocViewSAXImporter.StackElement stack;
+    private ImportInfoImpl importInfo = new ImportInfoImpl();
 
     /**
      * Specified the filter that is used to check if child nodes are contained
@@ -188,7 +150,7 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
     /**
      * a map of binaries (attachments)
      */
-    private Map<String, Map<String, DocViewSAXImporter.BlobInfo>> binaries
+    private Map<String, Map<String, BlobInfo>> binaries
             = new HashMap<>();
 
     /**
@@ -202,16 +164,6 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
     private Set<String> preserveProperties = new HashSet<>();
 
     /**
-     * the default name path resolver
-     */
-    private final DefaultNamePathResolver npResolver = new DefaultNamePathResolver(this);
-
-    /**
-     * final import information
-     */
-    private ImportInfoImpl importInfo = new ImportInfoImpl();
-
-    /**
      * acl management
      */
     private final ACLManagement aclManagement;
@@ -224,13 +176,13 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
     /**
      * the acl handling to apply
      */
-    private AccessControlHandling aclHandling = AccessControlHandling.IGNORE;
+    private final AccessControlHandling aclHandling;
 
     /**
      * Closed user group handling to apply by default (when set to <code>null</code>)
      * falls back to using aclHandling
      */
-    private AccessControlHandling cugHandling = null;
+    private final @Nullable AccessControlHandling cugHandling;
 
     /**
      * flag indicating if SNS are supported by the underlying repository
@@ -245,7 +197,16 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
     private final IdConflictPolicy idConflictPolicy;
 
     /**
-     * Creates a new importer that will receive SAX events and imports the
+     * current stack
+     */
+    private StackElement stack;
+
+    private int rootDepth;
+
+    private final NamePathResolver npResolver;
+    
+    /**
+     * Creates a new importer that will imports the
      * items below the given root.
      *
      * @param parentNode   the (parent) node of the import
@@ -254,22 +215,27 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
      * @param wspFilter    workspace filter
      * @throws RepositoryException if an error occurs.
      */
-    public DocViewSAXImporter(Node parentNode, String rootNodeName,
-                              ArtifactSetImpl artifacts, WorkspaceFilter wspFilter, IdConflictPolicy idConflictPolicy)
-            throws RepositoryException {
+    public DocViewImporter(Node parentNode, String rootNodeName,
+                              ArtifactSetImpl artifacts, WorkspaceFilter wspFilter, IdConflictPolicy idConflictPolicy) throws RepositoryException {
+        this(parentNode, rootNodeName, artifacts, wspFilter, idConflictPolicy, AccessControlHandling.IGNORE, null);
+    }
+
+    public DocViewImporter(Node parentNode, String rootNodeName,
+            ArtifactSetImpl artifacts, WorkspaceFilter wspFilter, IdConflictPolicy idConflictPolicy, AccessControlHandling aclHandling, AccessControlHandling cugHandling) throws RepositoryException {
         this.filter = artifacts.getCoverage();
         this.wspFilter = wspFilter;
         this.parentNode = parentNode;
         this.rootDepth = parentNode.getDepth() + 1;
         this.session = parentNode.getSession();
-        this.rootNodeName = rootNodeName;
         this.aclManagement = ServiceProviderFactory.getProvider().getACLManagement();
         this.userManagement = ServiceProviderFactory.getProvider().getUserManagement();
         this.snsSupported = session.getRepository().
                 getDescriptorValue(Repository.NODE_TYPE_MANAGEMENT_SAME_NAME_SIBLINGS_SUPPORTED).getBoolean();
         this.nsHelper = new JcrNamespaceHelper(session, null);
         this.idConflictPolicy = idConflictPolicy;
-
+        this.aclHandling = aclHandling;
+        this.cugHandling = cugHandling;
+    
         String rootPath = parentNode.getPath();
         if (!rootPath.equals("/")) {
             rootPath += "/";
@@ -285,116 +251,181 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
         for (Artifact a : artifacts.values(ArtifactType.HINT)) {
             hints.add(rootPath + a.getRelativePath());
         }
+        
+        stack = new StackElement(parentNode, parentNode.isNew());
+        npResolver = new DefaultNamePathResolver(parentNode.getSession());
     }
 
-    public AccessControlHandling getAclHandling() {
-        return aclHandling;
-    }
-
-    public void setAclHandling(AccessControlHandling aclHandling) {
-        this.aclHandling = aclHandling;
-    }
-
-    /**
-     * returns closed user group handling
-     * @return either current cugHandling value or <code>null</code>
-     * if undefined and aclHandling is used instead
-     */
-    public AccessControlHandling getCugHandling() {
-        return cugHandling;
-    }
-
-    /**
-     * Sets closed user group handling for this importer
-     * For backwards compatibility, if <code>null</code> is specified
-     * then importer falls back to using aclHandling value instead.
-     * @param cugHandling
-     */
-    public void setCugHandling(AccessControlHandling cugHandling) {
-        this.cugHandling = cugHandling;
-    }
+    @Override
+    public void startDocViewNode(String parentNodePath, DocViewNode2 docViewNode) throws IOException, RepositoryException {
 
-    private void registerBinary(Artifact a, String rootPath)
-            throws RepositoryException {
-        String path = rootPath + a.getRelativePath();
-        int idx = -1;
-        int pos = path.indexOf('[', path.lastIndexOf('/'));
-        if (pos > 0) {
-            idx = Integer.parseInt(path.substring(pos + 1, path.length() - 1));
-            path = path.substring(0, pos);
-        }
-        if (a.getType() == ArtifactType.FILE && a instanceof PropertyValueArtifact) {
-            // hack, mark "file" properties just as present
-            String parentPath = ((PropertyValueArtifact) a).getProperty().getParent().getPath();
-            preserveProperties.add(parentPath + "/" + JcrConstants.JCR_DATA);
-            preserveProperties.add(parentPath + "/" + JcrConstants.JCR_LASTMODIFIED);
-        } else {
-            preserveProperties.add(path);
-            // hack, mark "file" properties just as present
-            preserveProperties.add(path + "/jcr:content/jcr:data");
-            preserveProperties.add(path + "/jcr:content/jcr:lastModified");
-            preserveProperties.add(path + "/jcr:content/jcr:mimeType");
-            String parentPath = Text.getRelativeParent(path, 1);
-            String name = Text.getName(path);
-            Map<String, DocViewSAXImporter.BlobInfo> infoSet = binaries.get(parentPath);
-            if (infoSet == null) {
-                infoSet = new HashMap<String, DocViewSAXImporter.BlobInfo>();
-                binaries.put(parentPath, infoSet);
+        stack.addName(docViewNode.getName());
+        Node node = stack.getNode();
+        if (node == null) {
+            stack = stack.push();
+            DocViewAdapter xform = stack.getAdapter();
+            if (xform != null) {
+                xform.startNode(docViewNode);
+            } else {
+                log.trace("Skipping ignored node {}", docViewNode); // TODO: clarify what this means
             }
-            DocViewSAXImporter.BlobInfo info = infoSet.get(name);
-            if (info == null) {
-                info = new DocViewSAXImporter.BlobInfo(idx >= 0);
-                infoSet.put(name, info);
+        } else {
+            final String path = parentNodePath + "/" + docViewNode.getName();
+            if (docViewNode.getProperties().isEmpty()) {
+                // only ordering node. skip
+                log.trace("Skipping empty node {}", path);
+                stack = stack.push();
+                return;
+            } else if (docViewNode.getIndex() > 1) {
+                // TODO: skip SNS nodes with index > 1
+                log.warn("Skipping unsupported SNS node with index > 1. Some content will be missing after import: {}", path);
+                stack = stack.push();
+                return;
             }
-            if (idx >= 0) {
-                info.add(idx, a);
-            } else {
-                info.add(a);
+            try {
+                // is policy node?
+                    if (aclManagement.isACLNodeType(docViewNode.getPrimaryType())) {
+                        AccessControlHandling acHandling = getAcHandling(docViewNode.getName());
+                        if (acHandling != AccessControlHandling.CLEAR && acHandling != AccessControlHandling.IGNORE) {
+                            log.trace("Access control policy element detected. starting special transformation {}/{}", node.getPath(), docViewNode.getName());
+                            if (aclManagement.ensureAccessControllable(node, npResolver.getJCRName(docViewNode.getName()))) {
+                                log.debug("Adding access control policy element to non access-controllable parent - adding mixin: {}", node.getPath());
+                            }
+                            stack = stack.push();
+                            if (NameConstants.REP_REPO_POLICY.equals(docViewNode.getName())) {
+                                if (node.getDepth() == 0) {
+                                    stack.adapter = new JackrabbitACLImporter(session, acHandling);
+                                    stack.adapter.startNode(docViewNode);
+                                } else {
+                                    log.debug("ignoring invalid location for repository level ACL: {}", node.getPath());
+                                }
+                            } else {
+                                stack.adapter = new JackrabbitACLImporter(node, acHandling);
+                                stack.adapter.startNode(docViewNode);
+                            }
+                        } else {
+                            stack = stack.push();
+                        }
+                    } else if (userManagement != null && userManagement.isAuthorizableNodeType(docViewNode.getPrimaryType())) {
+                        // is authorizable node?
+                        handleAuthorizable(node, docViewNode);
+                    } else {
+                        // regular node
+                        stack = stack.push(addNode(docViewNode));
+                    }
+                } catch (RepositoryException | IOException e) {
+                    if (e instanceof ConstraintViolationException && wspFilter.getImportMode(path) != ImportMode.REPLACE) {
+                        // only warn in case of constraint violations for mode != replace (as best effort is used in that case)
+                        log.warn("Error during processing of {}: {}, skip node due to import mode {}", path, e.toString(), wspFilter.getImportMode(path));
+                        importInfo.onNop(path);
+                    } else {
+                        log.error("Error during processing of {}: {}", path, e.toString());
+                        importInfo.onError(path, e);
+                    }
+                    stack = stack.push();
+                }
             }
-        }
-        log.trace("scheduling binary: {}{}", rootPath, a.getRelativePath() + a.getExtension());
-    }
-
-    private boolean isIncluded(Item item, int depth) throws RepositoryException {
-        String path = importInfo.getRemapped().map(item.getPath());
-        return wspFilter.contains(path) && (depth == 0 || filter.contains(item, path, depth));
     }
 
-    public ImportInfoImpl getInfo() {
-        return importInfo;
-    }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public void startDocument() throws SAXException {
-        try {
-            stack = new StackElement(parentNode, parentNode.isNew());
-        } catch (RepositoryException e) {
-            throw new SAXException(e);
-        }
+    public void endDocViewNode(String parentNodePath, DocViewNode2 docViewNode) throws IOException, RepositoryException {
+        
+            // currentNode's import is finished, check if any child nodes
+            // need to be removed
+            NodeNameList childNames = stack.getChildNames();
+            Node node = stack.getNode();
+            int numChildren = 0;
+            if (node == null) {
+                DocViewAdapter adapter = stack.getAdapter();
+                if (adapter != null) {
+                    adapter.endNode();
+                }
+                // close transformer if last in stack
+                if (stack.adapter != null) {
+                    List<String> createdPaths = stack.adapter.close();
+                    for (String createdPath : createdPaths) {
+                        importInfo.onCreated(createdPath);
+                    }
+                    stack.adapter = null;
+                    log.trace("Sysview transformation complete.");
+                }
+            } else {
+                NodeIterator iter = node.getNodes();
+                while (iter.hasNext()) {
+                    numChildren++;
+                    Node child = iter.nextNode();
+                    String path = child.getPath();
+                    String label = Text.getName(path);
+                    AccessControlHandling acHandling = getAcHandling(npResolver.getQName(child.getName()));
+                    if (!childNames.contains(label)
+                            && !hints.contains(path)
+                            && isIncluded(child, child.getDepth() - rootDepth)) {
+                        // if the child is in the filter, it belongs to
+                        // this aggregate and needs to be removed
+                        if (aclManagement.isACLNode(child)) {
+                            if (acHandling == AccessControlHandling.OVERWRITE
+                                    || acHandling == AccessControlHandling.CLEAR) {
+                                importInfo.onDeleted(path);
+                                aclManagement.clearACL(node);
+                            }
+                        } else {
+                            if (wspFilter.getImportMode(path) == ImportMode.REPLACE) {
+                                importInfo.onDeleted(path);
+                                // check if child is not protected
+                                if (child.getDefinition().isProtected()) {
+                                    log.debug("Refuse to delete protected child node: {}", path);
+                                } else if (child.getDefinition().isMandatory()) {
+                                    log.debug("Refuse to delete mandatory child node: {}", path);
+                                } else {
+                                    child.remove();
+                                }
+                            }
+                        }
+                    } else if (acHandling == AccessControlHandling.CLEAR
+                            && aclManagement.isACLNode(child)
+                            && isIncluded(child, child.getDepth() - rootDepth)) {
+                        importInfo.onDeleted(path);
+                        aclManagement.clearACL(node);
+                    }
+                }
+                if (isIncluded(node, node.getDepth() - rootDepth)) {
+                    // ensure order
+                    stack.restoreOrder();
+                }
+            }
+            stack = stack.pop();
+            if (node != null && (numChildren == 0 && !childNames.isEmpty() || stack.isRoot())) {
+                importInfo.addNameList(node.getPath(), childNames);
+            }
     }
 
     /**
      * {@inheritDoc}
+     * @throws RepositoryException 
+     * @throws IOException 
+     * @throws ConstraintViolationException 
+     * @throws VersionException 
+     * @throws LockException 
+     * @throws NoSuchNodeTypeException 
+     * @throws PathNotFoundException 
+     * @throws ItemExistsException 
      */
     @Override
-    public void endDocument() throws SAXException {
+    public void endDocument() throws RepositoryException, IOException {
         if (!stack.isRoot()) {
             throw new IllegalStateException("stack mismatch");
         }
 
         // process binaries
         for (String parentPath : binaries.keySet()) {
-            Map<String, DocViewSAXImporter.BlobInfo> blobs = binaries.get(parentPath);
+            Map<String, BlobInfo> blobs = binaries.get(parentPath);
             // check for node
             log.trace("processing binaries at {}", parentPath);
-            try {
-                if (session.nodeExists(parentPath)) {
+            if (session.nodeExists(parentPath)) {
                     Node node = session.getNode(parentPath);
                     for (String propName : blobs.keySet()) {
-                        DocViewSAXImporter.BlobInfo info = blobs.get(propName);
+                        BlobInfo info = blobs.get(propName);
                         if (node.hasNode(propName)) {
                             handleBinNode(node.getNode(propName), info, true);
                         } else if (info.isFile()) {
@@ -417,7 +448,7 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
                     // assume below 'this' root.
                     Node node = null;
                     for (String propName : blobs.keySet()) {
-                        DocViewSAXImporter.BlobInfo info = blobs.get(propName);
+                        BlobInfo info = blobs.get(propName);
                         if (info.isFile()) {
                             if (node == null) {
                                 node = createNodeDeep(parentPath);
@@ -428,12 +459,60 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
                         }
                     }
                 }
-            } catch (Exception e) {
-                throw new SAXException(e);
+        }
+    }
+    
+    private void registerBinary(Artifact a, String rootPath)
+            throws RepositoryException {
+        String path = rootPath + a.getRelativePath();
+        int idx = -1;
+        int pos = path.indexOf('[', path.lastIndexOf('/'));
+        if (pos > 0) {
+            idx = Integer.parseInt(path.substring(pos + 1, path.length() - 1));
+            path = path.substring(0, pos);
+        }
+        if (a.getType() == ArtifactType.FILE && a instanceof PropertyValueArtifact) {
+            // hack, mark "file" properties just as present
+            String parentPath = ((PropertyValueArtifact) a).getProperty().getParent().getPath();
+            preserveProperties.add(parentPath + "/" + JcrConstants.JCR_DATA);
+            preserveProperties.add(parentPath + "/" + JcrConstants.JCR_LASTMODIFIED);
+        } else {
+            preserveProperties.add(path);
+            // hack, mark "file" properties just as present
+            preserveProperties.add(path + "/jcr:content/jcr:data");
+            preserveProperties.add(path + "/jcr:content/jcr:lastModified");
+            preserveProperties.add(path + "/jcr:content/jcr:mimeType");
+            String parentPath = Text.getRelativeParent(path, 1);
+            String name = Text.getName(path);
+            Map<String, BlobInfo> infoSet = binaries.get(parentPath);
+            if (infoSet == null) {
+                infoSet = new HashMap<String, BlobInfo>();
+                binaries.put(parentPath, infoSet);
+            }
+            BlobInfo info = infoSet.get(name);
+            if (info == null) {
+                info = new BlobInfo(idx >= 0);
+                infoSet.put(name, info);
+            }
+            if (idx >= 0) {
+                info.add(idx, a);
+            } else {
+                info.add(a);
             }
         }
+        log.trace("scheduling binary: {}{}", rootPath, a.getRelativePath() + a.getExtension());
     }
 
+    private boolean isIncluded(Item item, int depth) throws RepositoryException {
+        String path = importInfo.getRemapped().map(item.getPath());
+        return wspFilter.contains(path) && (depth == 0 || filter.contains(item, path, depth));
+    }
+
+    public ImportInfoImpl getInfo() {
+        return importInfo;
+    }
+
+
     private Node createNodeDeep(String path) throws RepositoryException {
         if (session.nodeExists(path)) {
             return session.getNode(path);
@@ -456,7 +535,7 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
         return node;
     }
 
-    private void handleBinNode(Node node, DocViewSAXImporter.BlobInfo info, boolean checkIfNtFileOk)
+    private void handleBinNode(Node node, BlobInfo info, boolean checkIfNtFileOk)
             throws RepositoryException, IOException {
         log.trace("handling binary file at {}", node.getPath());
         if (info.isMulti) {
@@ -520,218 +599,30 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
         }
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void characters(char ch[], int start, int length) throws SAXException {
-        // can be ignored in docview
-    }
 
     /**
-     * {@inheritDoc}
-     * <p>
-     * Pushes the mapping to the stack and updates the namespace mapping in the
-     * session.
+     * Handle an authorizable node
+     *
+     * @param node the parent node
+     * @param docViewNode   doc view node of the authorizable
+     * @throws RepositoryException if an error accessing the repository occurrs.
+     * @throws SAXException        if an XML parsing error occurrs.
      */
-    @Override
-    public void startPrefixMapping(String prefix, String uri) throws SAXException {
-        log.trace("-> prefixMapping for {}:{}", prefix, uri);
-        NameSpace ns = new NameSpace(prefix, uri);
-        // push on stack
-        ns.next = nsStack;
-        nsStack = ns;
-        // check if uri is already registered
-        String oldPrefix;
-        try {
-            oldPrefix = session.getNamespacePrefix(uri);
-        } catch (NamespaceException e) {
-            // assume uri never registered
-            try {
-                oldPrefix = nsHelper.registerNamespace(prefix, uri);
-            } catch (RepositoryException e1) {
-                throw new SAXException(e);
-            }
-        } catch (RepositoryException e) {
-            throw new SAXException(e);
-        }
-        // update mapping
-        if (!oldPrefix.equals(prefix)) {
-            try {
-                session.setNamespacePrefix(prefix, uri);
-            } catch (RepositoryException e) {
-                throw new SAXException(e);
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * Pops the mapping from the stack and updates the namespace mapping in the
-     * session if necessary.
-     */
-    @Override
-    public void endPrefixMapping(String prefix) throws SAXException {
-        log.trace("<- prefixMapping for {}", prefix);
-        NameSpace ns = nsStack;
-        NameSpace prev = null;
-        while (ns != null && !ns.prefix.equals(prefix)) {
-            prev = ns;
-            ns = ns.next;
-        }
-        if (ns == null) {
-            throw new SAXException("Illegal state: prefix " + prefix + " never mapped.");
-        }
-        // remove from stack
-        if (prev == null) {
-            nsStack = ns.next;
-        } else {
-            prev.next = ns.next;
-        }
-        // find old prefix
-        ns = ns.next;
-        while (ns != null && !ns.prefix.equals(prefix)) {
-            ns = ns.next;
-        }
-        // update mapping
-        if (ns != null) {
-            try {
-                session.setNamespacePrefix(prefix, ns.uri);
-            } catch (RepositoryException e) {
-                throw new SAXException(e);
-            }
-            log.trace("   remapped: {}:{}", prefix, ns.uri);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException {
-        // special handling for root node
-        String label = ISO9075.decode(qName);
-        if (stack.isRoot()
-                && localName.equals(NameConstants.JCR_ROOT.getLocalName())
-                && uri.equals(NameConstants.JCR_ROOT.getNamespaceURI())) {
-                label = rootNodeName;
-        }
-        String name = label;
-        log.trace("-> element {}", label);
-        boolean snsNode = false;
-        int idx = name.lastIndexOf('[');
-        if (idx > 0) {
-            if (!snsSupported) {
-                int idx2 = name.indexOf(']', idx);
-                if (idx2 > 0) {
-                    try {
-                        if (Integer.valueOf(name.substring(idx + 1, idx2)) > 1) {
-                            snsNode = true;
-                        }
-                    } catch (NumberFormatException e) {
-                        // ignore
-                    }
-                }
-            }
-            name = name.substring(0, idx);
-        }
-        try {
-            stack.addName(label);
-            Node node = stack.getNode();
-            if (node == null) {
-                stack = stack.push();
-                DocViewAdapter xform = stack.getAdapter();
-                if (xform != null) {
-                    DocViewNode ni = new DocViewNode(name, label, attributes, npResolver);
-                    xform.startNode(ni);
-                } else {
-                    log.trace("Skipping ignored element {}", name);
-                }
-            } else {
-                final String path = (!node.getPath().equals("/") ? node.getPath() : "")+ "/" + name;
-                if (attributes.getLength() == 0) {
-                    // only ordering node. skip
-                    log.trace("Skipping empty node {}", path);
-                    stack = stack.push();
-                } else if (snsNode) {
-                    // skip SNS nodes with index > 1
-                    log.warn("Skipping unsupported SNS node with index > 1. Some content will be missing after import: {}", path);
-                    stack = stack.push();
-                } else {
-                    try {
-                        DocViewNode ni = new DocViewNode(name, label, attributes, npResolver);
-                        // is policy node?
-                        if (aclManagement.isACLNodeType(ni.primary)) {
-                            AccessControlHandling acHandling = getAcHandling(label);
-                            if (acHandling != AccessControlHandling.CLEAR && acHandling != AccessControlHandling.IGNORE) {
-                                log.trace("Access control policy element detected. starting special transformation {}/{}", node.getPath(), name);
-                                if (aclManagement.ensureAccessControllable(node, ni.primary)) {
-                                    log.debug("Adding access control policy element to non access-controllable parent - adding mixin: {}", node.getPath());
-                                }
-                                stack = stack.push();
-                                if ("rep:repoPolicy".equals(name)) {
-                                    if (node.getDepth() == 0) {
-                                        stack.adapter = new JackrabbitACLImporter(session, acHandling);
-                                        stack.adapter.startNode(ni);
-                                    } else {
-                                        log.debug("ignoring invalid location for repository level ACL: {}", node.getPath());
-                                    }
-                                } else {
-                                    stack.adapter = new JackrabbitACLImporter(node, acHandling);
-                                    stack.adapter.startNode(ni);
-                                }
-                            } else {
-                                stack = stack.push();
-                            }
-                        } else if (userManagement != null && userManagement.isAuthorizableNodeType(ni.primary)) {
-                            // is authorizable node?
-                            handleAuthorizable(node, ni);
-                        } else {
-                            // regular node
-                            stack = stack.push(addNode(ni));
-                        }
-                    } catch (RepositoryException | IOException e) {
-                        if (e instanceof ConstraintViolationException && wspFilter.getImportMode(path) != ImportMode.REPLACE) {
-                            // only warn in case of constraint violations for mode != replace (as best effort is used in that case)
-                            log.warn("Error during processing of {}: {}, skip node due to import mode {}", path, e.toString(), wspFilter.getImportMode(path));
-                            importInfo.onNop(path);
-                        } else {
-                            log.error("Error during processing of {}: {}", path, e.toString());
-                            importInfo.onError(path, e);
-                        }
-                        stack = stack.push();
-                    }
-                }
-            }
-        }  catch (RepositoryException e) {
-            throw new SAXException("Fatal exception while parsing", e);
-        } 
-    }
-
-    /**
-     * Handle an authorizable node
-     *
-     * @param node the parent node
-     * @param ni   doc view node of the authorizable
-     * @throws RepositoryException if an error accessing the repository occurrs.
-     * @throws SAXException        if an XML parsing error occurrs.
-     */
-    private void handleAuthorizable(Node node, DocViewNode ni) throws RepositoryException, SAXException {
-        String id = userManagement.getAuthorizableId(ni);
-        String newPath = node.getPath() + "/" + ni.name;
-        boolean isIncluded = wspFilter.contains(newPath);
-        String oldPath = userManagement.getAuthorizablePath(this.session, id);
-        if (oldPath == null) {
-            if (!isIncluded) {
-                log.trace("auto-creating authorizable node not in filter {}", newPath);
+    private void handleAuthorizable(Node node, DocViewNode2 docViewNode) throws RepositoryException {
+        String id = userManagement.getAuthorizableId(docViewNode);
+        String newPath = node.getPath() + "/" + docViewNode.name;
+        boolean isIncluded = wspFilter.contains(newPath);
+        String oldPath = userManagement.getAuthorizablePath(this.session, id);
+        if (oldPath == null) {
+            if (!isIncluded) {
+                log.trace("auto-creating authorizable node not in filter {}", newPath);
             }
 
             // just import the authorizable node
             log.trace("Authorizable element detected. starting sysview transformation {}", newPath);
             stack = stack.push();
             stack.adapter = new JcrSysViewTransformer(node, wspFilter.getImportMode(newPath));
-            stack.adapter.startNode(ni);
+            stack.adapter.startNode(docViewNode);
             importInfo.onCreated(newPath);
             return;
         }
@@ -760,7 +651,7 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
             case MERGE_PROPERTIES:
                 // remember desired memberships.
                 // todo: how to deal with multi-node memberships? see JCRVLT-69
-                DocViewProperty prop = ni.props.get("rep:members");
+                DocViewProperty2 prop = docViewNode.getProperties().get(NAME_REP_MEMBERS);
                 if (prop != null) {
                     importInfo.registerMemberships(id, prop.values);
                 }
@@ -775,7 +666,7 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
                 log.trace("Authorizable element detected. starting sysview transformation {}", newPath);
                 stack = stack.push();
                 stack.adapter = new JcrSysViewTransformer(node, mode);
-                stack.adapter.startNode(ni);
+                stack.adapter.startNode(docViewNode);
                 importInfo.onReplaced(newPath);
                 break;
 
@@ -787,13 +678,10 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
                 // we need to tweak the ni.name so that the sysview import does not
                 // rename the authorizable node name
                 String newName = Text.getName(oldPath);
-                DocViewNode mapped = new DocViewNode(
-                        newName,
-                        newName,
-                        ni.uuid,
-                        ni.props,
-                        ni.mixins,
-                        ni.primary
+                DocViewNode2 mapped = new DocViewNode2(
+                        npResolver.getQName(newName),
+                        -1,
+                        docViewNode.getProperties()
                 );
                 // but we need to augment with a potential rep:authorizableId
                 if (authNode.hasProperty("rep:authorizableId")) {
@@ -811,34 +699,34 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
         }
     }
 
-    private StackElement addNode(DocViewNode ni) throws RepositoryException, IOException {
+    private StackElement addNode(DocViewNode2 docViewNode) throws RepositoryException, IOException {
         final Node currentNode = stack.getNode();
 
         Node existingNode = null;
-        if ("".equals(ni.label)) {
+        if ("".equals(docViewNode.label)) {
             // special case for root node update
             existingNode = currentNode;
         } else {
-            if (stack.checkForNode() && currentNode.hasNode(ni.label)) {
-                existingNode = currentNode.getNode(ni.label);
+            if (stack.checkForNode() && currentNode.hasNode(docViewNode.getName().toString())) {
+                existingNode = currentNode.getNode(docViewNode.getName().toString());
             }
-            if (ni.uuid != null && idConflictPolicy == IdConflictPolicy.FAIL) {
+            if (docViewNode.getIdentifier() != null && idConflictPolicy == IdConflictPolicy.FAIL) {
                 try {
                     // does uuid already exist in the repo?
-                    Node sameIdNode = session.getNodeByIdentifier(ni.uuid);
+                    Node sameIdNode = session.getNodeByIdentifier(docViewNode.getIdentifier());
                     // edge-case: same node path -> uuid is kept
                     if (existingNode != null && existingNode.getPath().equals(sameIdNode.getPath())) {
-                        log.debug("Node with existing identifier {} at {} is being updated without modifying its uuid", ni.uuid, existingNode.getPath());
+                        log.debug("Node with existing identifier {} at {} is being updated without modifying its uuid", docViewNode.getIdentifier(), existingNode.getPath());
                     } else {
                         // uuid found in path covered by filter
                         if (isIncluded(sameIdNode, 0)) {
-                            log.warn("Node identifier {} for to-be imported node {}/{} already taken by {}, trying to release it.", ni.uuid, currentNode.getPath(), ni.label, sameIdNode.getPath());
+                            log.warn("Node identifier {} for to-be imported node {}/{} already taken by {}, trying to release it.", docViewNode.getIdentifier(), currentNode.getPath(), docViewNode.getName(), sameIdNode.getPath());
                             removeReferences(sameIdNode);
                             session.removeItem(sameIdNode.getPath());
                             existingNode = null;
                         } else {
                             // uuid found in path not-covered by filter
-                            throw new ReferentialIntegrityException("UUID " + ni.uuid + " already taken by node " + sameIdNode.getPath());
+                            throw new ReferentialIntegrityException("UUID " + docViewNode.getIdentifier() + " already taken by node " + sameIdNode.getPath());
                         }
                     }
                 } catch (ItemNotFoundException e) {
@@ -848,20 +736,20 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
         }
 
         // check if new node needs to be checked in
-        DocViewProperty coProp = ni.props.remove(JcrConstants.JCR_ISCHECKEDOUT);
+        DocViewProperty coProp = docViewNode.props.remove(JcrConstants.JCR_ISCHECKEDOUT);
         boolean isCheckedIn = coProp != null && "false".equals(coProp.values[0]);
 
         // create or update node
         boolean isNew = existingNode == null;
         if (isNew) {
             // workaround for bug in jcr2spi if mixins are empty
-            if (!ni.props.containsKey(JcrConstants.JCR_MIXINTYPES)) {
-                ni.props.put(JcrConstants.JCR_MIXINTYPES,
+            if (!docViewNode.props.containsKey(JcrConstants.JCR_MIXINTYPES)) {
+                docViewNode.props.put(JcrConstants.JCR_MIXINTYPES,
                         new DocViewProperty(JcrConstants.JCR_MIXINTYPES, new String[0], true, PropertyType.NAME));
             }
 
             stack.ensureCheckedOut();
-            existingNode = createNewNode(currentNode, ni);
+            existingNode = createNewNode(currentNode, docViewNode);
             if (existingNode.getDefinition() == null) {
                 throw new RepositoryException("Child node not allowed.");
             }
@@ -881,7 +769,7 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
                 importInfo.registerToVersion(existingNode.getPath());
             }
             ImportMode importMode = wspFilter.getImportMode(existingNode.getPath());
-            Node updatedNode = updateExistingNode(existingNode, ni, importMode);
+            Node updatedNode = updateExistingNode(existingNode, docViewNode, importMode);
             if (updatedNode != null) {
                 if (updatedNode.isNodeType(JcrConstants.NT_RESOURCE) && !updatedNode.hasProperty(JcrConstants.JCR_DATA)) {
                     importInfo.onMissing(existingNode.getPath() + "/" + JcrConstants.JCR_DATA);
@@ -904,7 +792,7 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
      * @throws ReferentialIntegrityException in case some references can not be removed (outside filters)
      * @throws RepositoryException in case some other error occurs
      */
-    public void removeReferences(@NotNull Node node) throws ReferentialIntegrityException, RepositoryException {
+    private void removeReferences(@NotNull Node node) throws ReferentialIntegrityException, RepositoryException {
         Collection<String> removableReferencePaths = new ArrayList<>();
         PropertyIterator pIter = node.getReferences();
         while (pIter.hasNext()) {
@@ -921,11 +809,11 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
         }
     }
 
-    private @Nullable Node updateExistingNode(@NotNull Node node, @NotNull DocViewNode ni, @NotNull ImportMode importMode) throws RepositoryException {
+    private @Nullable Node updateExistingNode(@NotNull Node node, @NotNull DocViewNode2 ni, @NotNull ImportMode importMode) throws RepositoryException {
         VersioningState vs = new VersioningState(stack, node);
         Node updatedNode = null;
         // try to set uuid via sysview import if it differs from existing one
-        if (ni.uuid != null && !node.getIdentifier().equals(ni.uuid) && !"rep:root".equals(ni.primary)) {
+        if (!node.getIdentifier().equals(ni.getIdentifier()) && !"rep:root".equals(ni.getPrimaryType())) {
             NodeStash stash = new NodeStash(session, node.getPath());
             stash.stash();
             Node parent = node.getParent();
@@ -936,23 +824,21 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
         } else {
             // TODO: is this faster than using sysview import?
             // set new primary type (but never set rep:root)
-            if (importMode == ImportMode.REPLACE && !"rep:root".equals(ni.primary)) {
-                if (!node.getPrimaryNodeType().getName().equals(ni.primary)) {
+            if (importMode == ImportMode.REPLACE && !"rep:root".equals(ni.getPrimaryType())) {
+                if (!node.getPrimaryNodeType().getName().equals(ni.getPrimaryType())) {
                     vs.ensureCheckedOut();
-                    node.setPrimaryType(ni.primary);
+                    node.setPrimaryType(ni.getPrimaryType());
                     updatedNode = node;
                 }
             }
             // calculate mixins to be added
             Set<String> newMixins = new HashSet<>();
-            AccessControlHandling acHandling = getAcHandling(ni.name);
-            if (ni.mixins != null) {
-                for (String mixin : ni.mixins) {
-                    // omit name if mix:AccessControllable and CLEAR
-                    if (!aclManagement.isAccessControllableMixin(mixin)
-                            || acHandling != AccessControlHandling.CLEAR) {
-                        newMixins.add(mixin);
-                    }
+            AccessControlHandling acHandling = getAcHandling(ni.getName());
+            for (String mixin : ni.getMixinTypes()) {
+                // omit name if mix:AccessControllable and CLEAR
+                if (!aclManagement.isAccessControllableMixin(mixin)
+                        || acHandling != AccessControlHandling.CLEAR) {
+                    newMixins.add(mixin);
                 }
             }
             // remove mixins not in package (only for mode = replace)
@@ -1011,7 +897,7 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
      * @return the newly created node
      * @throws RepositoryException
      */
-    private @NotNull Node createNewNode(Node parentNode, DocViewNode ni)
+    private @NotNull Node createNewNode(Node parentNode, DocViewNode2 ni)
             throws RepositoryException {
         final int importUuidBehavior;
         switch(idConflictPolicy) {
@@ -1038,48 +924,48 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
                 handler.startPrefixMapping(prefix, session.getNamespaceURI(prefix));
             }
             AttributesImpl attrs = new AttributesImpl();
-            attrs.addAttribute(Name.NS_SV_URI, "name", "sv:name", "CDATA", ni.name);
+            attrs.addAttribute(Name.NS_SV_URI, "name", "sv:name", "CDATA", ni.getName().toString());
             handler.startElement(Name.NS_SV_URI, "node", "sv:node", attrs);
     
             // check if SNS and a helper uuid if needed
             boolean addMixRef = false;
-            if (!ni.label.equals(ni.name) && ni.uuid == null) {
-                ni.uuid = UUID.randomUUID().toString();
-                ni.props.put(JcrConstants.JCR_UUID, new DocViewProperty(
-                        JcrConstants.JCR_UUID, new String[]{ni.uuid}, false, PropertyType.STRING));
+            Map<Name, DocViewProperty2> properties = ni.getProperties();
+            
+            if (ni.getIndex() > 0 && ni.getIdentifier() == null) {
+                properties.put(NameConstants.JCR_UUID, new DocViewProperty2(
+                        NameConstants.JCR_UUID, new String[]{UUID.randomUUID().toString()}, false, PropertyType.STRING));
                 // check mixins
-                DocViewProperty mix = ni.props.get(JcrConstants.JCR_MIXINTYPES);
+                DocViewProperty2 mix = properties.get(NameConstants.JCR_MIXINTYPES);
                 addMixRef = true;
                 if (mix == null) {
-                    mix = new DocViewProperty(JcrConstants.JCR_MIXINTYPES, new String[]{JcrConstants.MIX_REFERENCEABLE}, true, PropertyType.NAME);
-                    ni.props.put(mix.name, mix);
+                    mix = new DocViewProperty2(NameConstants.JCR_MIXINTYPES, new String[]{JcrConstants.MIX_REFERENCEABLE}, true, PropertyType.NAME);
+                    properties.put(mix.getName(), mix);
                 } else {
-                    for (String v : mix.values) {
+                    for (String v : mix.getStringValues()) {
                         if (v.equals(JcrConstants.MIX_REFERENCEABLE)) {
                             addMixRef = false;
                             break;
                         }
                     }
                     if (addMixRef) {
-                        String[] vs = new String[mix.values.length + 1];
-                        System.arraycopy(mix.values, 0, vs, 0, mix.values.length);
-                        vs[mix.values.length] = JcrConstants.MIX_REFERENCEABLE;
-                        mix = new DocViewProperty(JcrConstants.JCR_MIXINTYPES, vs, true, PropertyType.NAME);
-                        ni.props.put(mix.name, mix);
+                        Collection<String> mixinValues = mix.getStringValues();
+                        mixinValues.add(JcrConstants.MIX_REFERENCEABLE);
+                        mix = new DocViewProperty2(NameConstants.JCR_MIXINTYPES, mixinValues.toArray(new String[0]), true, PropertyType.NAME);
+                        properties.put(mix.getName(), mix);
                     }
                 }
             }
             // add the properties
-            for (DocViewProperty p : ni.props.values()) {
+            for (DocViewProperty2 p : properties.values()) {
                 if (p != null && p.values != null) {
                     // only pass 'protected' properties to the import
-                    if (PROTECTED_PROPERTIES.contains(p.name)) {
+                    if (PROTECTED_PROPERTIES.contains(p.getName())) {
                         attrs = new AttributesImpl();
-                        attrs.addAttribute(Name.NS_SV_URI, "name", "sv:name", "CDATA", p.name);
-                        attrs.addAttribute(Name.NS_SV_URI, "type", "sv:type", "CDATA", PropertyType.nameFromValue(p.type));
+                        attrs.addAttribute(Name.NS_SV_URI, "name", "sv:name", "CDATA", p.getName().toString());
+                        attrs.addAttribute(Name.NS_SV_URI, "type", "sv:type", "CDATA", PropertyType.nameFromValue(p.getType()));
                         handler.startElement(Name.NS_SV_URI, "property", "sv:property", attrs);
-                        for (String v : p.values) {
-                            handler.startElement(Name.NS_SV_URI, "value", "sv:value", EMPTY_ATTRIBUTES);
+                        for (String v : p.getStringValues()) {
+                            handler.startElement(Name.NS_SV_URI, "value", "sv:value", DocViewSAXHandler.EMPTY_ATTRIBUTES);
                             handler.characters(v.toCharArray(), 0, v.length());
                             handler.endElement(Name.NS_SV_URI, "value", "sv:value");
                         }
@@ -1128,51 +1014,50 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
      * @return{@code true} in case the property is protected, {@code false} otherwise
      * @throws RepositoryException 
      */
-    private static boolean isPropertyProtected(@NotNull EffectiveNodeType effectiveNodeType, @NotNull DocViewProperty docViewProperty) throws RepositoryException {
-        return effectiveNodeType.getApplicablePropertyDefinition(docViewProperty.name, docViewProperty.isMulti, docViewProperty.type).map(PropertyDefinition::isProtected).orElse(false);
+    private boolean isPropertyProtected(@NotNull EffectiveNodeType effectiveNodeType, @NotNull DocViewProperty2 docViewProperty) throws RepositoryException {
+        return effectiveNodeType.getApplicablePropertyDefinition(npResolver.getJCRName(docViewProperty.getName()), docViewProperty.isMultiValue(), docViewProperty.getType()).map(PropertyDefinition::isProtected).orElse(false);
     }
 
-    private Node getNodeByUUIDLabelOrName(@NotNull Node currentNode, @NotNull DocViewNode ni, boolean isUuidNewlyAssigned) throws RepositoryException {
+    private Node getNodeByUUIDLabelOrName(@NotNull Node currentNode, @NotNull DocViewNode2 ni, boolean isUuidNewlyAssigned) throws RepositoryException {
         Node node = null;
-        if (ni.uuid != null && !isUuidNewlyAssigned) {
+        if (ni.getIdentifier() != null && !isUuidNewlyAssigned) {
             try {
-                node = currentNode.getSession().getNodeByUUID(ni.uuid);
+                node = currentNode.getSession().getNodeByUUID(ni.getIdentifier());
             } catch (RepositoryException e) {
-                log.warn("Newly created node not found by uuid {}: {}", currentNode.getPath() + "/" + ni.name, e.toString());
+                log.warn("Newly created node not found by uuid {}: {}", currentNode.getPath() + "/" + ni.getName(), e.toString());
             }
         }
         if (node == null) {
             try {
-                node = currentNode.getNode(ni.label);
+                node = currentNode.getNode(ni.getName().toString());
             } catch (RepositoryException e) {
-                log.warn("Newly created node not found by label {}: {}", currentNode.getPath() + "/" + ni.name, e.toString());
+                log.warn("Newly created node not found by label {}: {}", currentNode.getPath() + "/" + ni.getName(), e.toString());
             }
         }
         if (node == null) {
             try {
-                node = currentNode.getNode(ni.name);
+                node = currentNode.getNode(ni.getName().toString());
             } catch (RepositoryException e) {
-                log.warn("Newly created node not found by name {}: {}", currentNode.getPath() + "/" + ni.name, e.toString());
+                log.warn("Newly created node not found by name {}: {}", currentNode.getPath() + "/" + ni.getName(), e.toString());
                 throw e;
             }
         }
         return node;
     }
 
-    private boolean setUnprotectedProperties(@NotNull Node node, @NotNull DocViewNode ni, boolean overwriteExistingProperties, @Nullable VersioningState vs) throws RepositoryException {
+    private boolean setUnprotectedProperties(@NotNull Node node, @NotNull DocViewNode2 ni, boolean overwriteExistingProperties, @Nullable VersioningState vs) throws RepositoryException {
         boolean isAtomicCounter = false;
-        if (ni.mixins != null) {
-            for (String mixin : ni.mixins) {
+        for (String mixin : ni.getMixinTypes()) {
                 if ("mix:atomicCounter".equals(mixin)) {
                     isAtomicCounter = true;
                 }
             }
-        }
+        
         EffectiveNodeType effectiveNodeType = EffectiveNodeType.ofNode(node);
         boolean modified = false;
         // add properties
-        for (DocViewProperty prop : ni.props.values()) {
-            if (prop != null && !isPropertyProtected(effectiveNodeType, prop) && (overwriteExistingProperties || !node.hasProperty(prop.name)) && wspFilter.includesProperty(node.getPath() + "/" + prop.name)) {
+        for (DocViewProperty2 prop : ni.getProperties().values()) {
+            if (prop != null && !isPropertyProtected(effectiveNodeType, prop) && (overwriteExistingProperties || !node.hasProperty(prop.getName().toString())) && wspFilter.includesProperty(node.getPath() + "/" + npResolver.getJCRName(prop.getName()))) {
                 // check if property is allowed
                 try {
                     modified |= prop.apply(node);
@@ -1187,7 +1072,7 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
                     } catch (RepositoryException e1) {
                         // be lenient in case of mode != replace
                         if (wspFilter.getImportMode(node.getPath()) != ImportMode.REPLACE) {
-                            log.warn("Error while setting property {} (ignore due to mode {}): {}", prop.name, wspFilter.getImportMode(node.getPath()), e1);
+                            log.warn("Error while setting property {} (ignore due to mode {}): {}", prop.getName(), wspFilter.getImportMode(node.getPath()), e1);
                         } else {
                             throw e;
                         }
@@ -1197,14 +1082,14 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
         }
 
         // adjust oak atomic counter
-        if (isAtomicCounter && wspFilter.includesProperty(node.getPath() + "/" + PROP_OAK_COUNTER)) {
+        if (isAtomicCounter && wspFilter.includesProperty(node.getPath() + "/" + npResolver.getJCRName(NAME_OAK_COUNTER))) {
             long previous = 0;
-            if (node.hasProperty(PROP_OAK_COUNTER)) {
-                previous = node.getProperty(PROP_OAK_COUNTER).getLong();
+            if (node.hasProperty(NAME_OAK_COUNTER.toString())) {
+                previous = node.getProperty(NAME_OAK_COUNTER.toString()).getLong();
             }
             long counter = 0;
             try {
-                counter = Long.valueOf(ni.getValue(PROP_OAK_COUNTER));
+                counter = Long.valueOf(ni.getPropertyValue(NAME_OAK_COUNTER));
             } catch (NumberFormatException e) {
                 // ignore
             }
@@ -1219,85 +1104,6 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
     /**
      * {@inheritDoc}
      */
-    @Override
-    public void endElement(String uri, String localName, String qName) throws SAXException {
-        log.trace("<- element {}", qName);
-        try {
-            // currentNode's import is finished, check if any child nodes
-            // need to be removed
-            NodeNameList childNames = stack.getChildNames();
-            Node node = stack.getNode();
-            int numChildren = 0;
-            if (node == null) {
-                DocViewAdapter adapter = stack.getAdapter();
-                if (adapter != null) {
-                    adapter.endNode();
-                }
-                // close transformer if last in stack
-                if (stack.adapter != null) {
-                    List<String> createdPaths = stack.adapter.close();
-                    for (String createdPath : createdPaths) {
-                        importInfo.onCreated(createdPath);
-                    }
-                    stack.adapter = null;
-                    log.trace("Sysview transformation complete.");
-                }
-            } else {
-                NodeIterator iter = node.getNodes();
-                while (iter.hasNext()) {
-                    numChildren++;
-                    Node child = iter.nextNode();
-                    String path = child.getPath();
-                    String label = Text.getName(path);
-                    AccessControlHandling acHandling = getAcHandling(child.getName());
-                    if (!childNames.contains(label)
-                            && !hints.contains(path)
-                            && isIncluded(child, child.getDepth() - rootDepth)) {
-                        // if the child is in the filter, it belongs to
-                        // this aggregate and needs to be removed
-                        if (aclManagement.isACLNode(child)) {
-                            if (acHandling == AccessControlHandling.OVERWRITE
-                                    || acHandling == AccessControlHandling.CLEAR) {
-                                importInfo.onDeleted(path);
-                                aclManagement.clearACL(node);
-                            }
-                        } else {
-                            if (wspFilter.getImportMode(path) == ImportMode.REPLACE) {
-                                importInfo.onDeleted(path);
-                                // check if child is not protected
-                                if (child.getDefinition().isProtected()) {
-                                    log.debug("Refuse to delete protected child node: {}", path);
-                                } else if (child.getDefinition().isMandatory()) {
-                                    log.debug("Refuse to delete mandatory child node: {}", path);
-                                } else {
-                                    child.remove();
-                                }
-                            }
-                        }
-                    } else if (acHandling == AccessControlHandling.CLEAR
-                            && aclManagement.isACLNode(child)
-                            && isIncluded(child, child.getDepth() - rootDepth)) {
-                        importInfo.onDeleted(path);
-                        aclManagement.clearACL(node);
-                    }
-                }
-                if (isIncluded(node, node.getDepth() - rootDepth)) {
-                    // ensure order
-                    stack.restoreOrder();
-                }
-            }
-            stack = stack.pop();
-            if (node != null && (numChildren == 0 && !childNames.isEmpty() || stack.isRoot())) {
-                importInfo.addNameList(node.getPath(), childNames);
-            }
-        } catch (RepositoryException e) {
-            throw new SAXException(e);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
     public String getURI(String prefix) throws NamespaceException {
         try {
             return session.getNamespaceURI(prefix);
@@ -1325,117 +1131,20 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
      * everything else
      */
     @NotNull
-    private AccessControlHandling getAcHandling(@NotNull String nodeName) {
-        if (cugHandling != null && "rep:cugPolicy".equals(nodeName)) {
+    private AccessControlHandling getAcHandling(@NotNull Name nodeName) {
+        if (cugHandling != null && NAME_REP_CUG_POLICY.equals(nodeName)) {
             return cugHandling;
         } else {
             return aclHandling;
         }
     }
 
-    /**
-     * Helper class that stores information about attachments
-     */
-    private static class BlobInfo {
-
-        private final boolean isMulti;
-
-        private final List<Artifact> artifacts = new ArrayList<Artifact>();
-
-        public BlobInfo(boolean multi) {
-            isMulti = multi;
-        }
-
-        public boolean isFile() {
-            return artifacts.size() > 0 && artifacts.get(0).getType() == ArtifactType.FILE;
-        }
-
-        public void add(Artifact a) {
-            assert artifacts.isEmpty();
-            artifacts.add(a);
-        }
-
-        public void add(int idx, Artifact a) {
-            while (idx >= artifacts.size()) {
-                artifacts.add(null);
-            }
-            artifacts.set(idx, a);
-        }
-
-        public Value[] getValues(Session session)
-                throws RepositoryException, IOException {
-            Value[] values = new Value[artifacts.size()];
-            for (int i = 0; i < values.length; i++) {
-                Artifact a = artifacts.get(i);
-                try (InputStream input = a.getInputStream()) {
-                    values[i] = session.getValueFactory().createValue(input);
-                }
-            }
-            return values;
-        }
-
-        public Value getValue(Session session)
-                throws RepositoryException, IOException {
-            Artifact a = artifacts.get(0);
-            try (InputStream input = a.getInputStream()) {
-                return session.getValueFactory().createValue(input);
-            }
-        }
-
-        public void detach() {
-            for (Artifact a : artifacts) {
-                if (a instanceof PropertyValueArtifact) {
-                    try {
-                        ((PropertyValueArtifact) a).detach();
-                    } catch (IOException e) {
-                        log.warn("error while detaching property artifact", e);
-                    } catch (RepositoryException e) {
-                        log.warn("error while detaching property artifact", e);
-                    }
-                }
-            }
-        }
-    }
-
-    private class VersioningState {
-
-        private final StackElement stack;
-
-        private final Node node;
-
-        private boolean isCheckedOut;
-
-        private boolean isParentCheckedOut;
-
-        private VersioningState(StackElement stack, Node node) throws RepositoryException {
-            this.stack = stack;
-            this.node = node;
-            isCheckedOut = node == null || !node.isNodeType(JcrConstants.MIX_VERSIONABLE) || node.isCheckedOut();
-            isParentCheckedOut = stack.isCheckedOut();
-        }
-
-        public void ensureCheckedOut() throws RepositoryException {
-            if (!isCheckedOut) {
-                importInfo.registerToVersion(node.getPath());
-                try {
-                    node.checkout();
-                } catch (RepositoryException e) {
-                    log.warn("error while checkout node (ignored)", e);
-                }
-                isCheckedOut = true;
-            }
-            if (!isParentCheckedOut) {
-                stack.ensureCheckedOut();
-                isParentCheckedOut = true;
-            }
-        }
-    }
 
     private class StackElement {
 
         private final Node node;
 
-        private DocViewSAXImporter.StackElement parent;
+        private StackElement parent;
 
         private final NodeNameList childNames = new NodeNameList();
 
@@ -1486,7 +1195,7 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
             return !isNew || parent == null;
         }
 
-        public void addName(String name) {
+        public void addName(Name name) {
             childNames.addName(name);
         }
 
@@ -1524,40 +1233,102 @@ public class DocViewSAXImporter extends RejectingEntityDefaultHandler implements
 
     }
 
-}
+    /**
+     * Helper class that stores information about attachments
+     */
+    private static class BlobInfo {
 
+        private final boolean isMulti;
 
-/**
- * A representation of a namespace.  One of these will
- * be pushed on the namespace stack for each
- * element.
- */
-class NameSpace {
+        private final List<Artifact> artifacts = new ArrayList<Artifact>();
 
-    /**
-     * Next NameSpace element on the stack.
-     */
-    public NameSpace next = null;
+        public BlobInfo(boolean multi) {
+            isMulti = multi;
+        }
 
-    /**
-     * Prefix of this NameSpace element.
-     */
-    public String prefix;
+        public boolean isFile() {
+            return artifacts.size() > 0 && artifacts.get(0).getType() == ArtifactType.FILE;
+        }
 
-    /**
-     * Namespace URI of this NameSpace element.
-     */
-    public String uri;  // if null, then Element namespace is empty.
+        public void add(Artifact a) {
+            assert artifacts.isEmpty();
+            artifacts.add(a);
+        }
 
-    /**
-     * Construct a namespace for placement on the
-     * result tree namespace stack.
-     *
-     * @param prefix Prefix of this element
-     * @param uri    URI of  this element
-     */
-    public NameSpace(String prefix, String uri) {
-        this.prefix = prefix;
-        this.uri = uri;
+        public void add(int idx, Artifact a) {
+            while (idx >= artifacts.size()) {
+                artifacts.add(null);
+            }
+            artifacts.set(idx, a);
+        }
+
+        public Value[] getValues(Session session)
+                throws RepositoryException, IOException {
+            Value[] values = new Value[artifacts.size()];
+            for (int i = 0; i < values.length; i++) {
+                Artifact a = artifacts.get(i);
+                try (InputStream input = a.getInputStream()) {
+                    values[i] = session.getValueFactory().createValue(input);
+                }
+            }
+            return values;
+        }
+
+        public Value getValue(Session session)
+                throws RepositoryException, IOException {
+            Artifact a = artifacts.get(0);
+            try (InputStream input = a.getInputStream()) {
+                return session.getValueFactory().createValue(input);
+            }
+        }
+
+        public void detach() {
+            for (Artifact a : artifacts) {
+                if (a instanceof PropertyValueArtifact) {
+                    try {
+                        ((PropertyValueArtifact) a).detach();
+                    } catch (IOException e) {
+                        log.warn("error while detaching property artifact", e);
+                    } catch (RepositoryException e) {
+                        log.warn("error while detaching property artifact", e);
+                    }
+                }
+            }
+        }
     }
+
+    private class VersioningState {
+
+        private final StackElement stack;
+
+        private final Node node;
+
+        private boolean isCheckedOut;
+
+        private boolean isParentCheckedOut;
+
+        private VersioningState(StackElement stack, Node node) throws RepositoryException {
+            this.stack = stack;
+            this.node = node;
+            isCheckedOut = node == null || !node.isNodeType(JcrConstants.MIX_VERSIONABLE) || node.isCheckedOut();
+            isParentCheckedOut = stack.isCheckedOut();
+        }
+
+        public void ensureCheckedOut() throws RepositoryException {
+            if (!isCheckedOut) {
+                importInfo.registerToVersion(node.getPath());
+                try {
+                    node.checkout();
+                } catch (RepositoryException e) {
+                    log.warn("error while checkout node (ignored)", e);
+                }
+                isCheckedOut = true;
+            }
+            if (!isParentCheckedOut) {
+                stack.ensureCheckedOut();
+                isParentCheckedOut = true;
+            }
+        }
+    }
+
 }
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXHandler.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXHandler.java
new file mode 100644
index 0000000..572cb04
--- /dev/null
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/DocViewSAXHandler.java
@@ -0,0 +1,265 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.fs.impl.io;
+
+import java.io.IOException;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.jcr.NamespaceException;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.NameFactory;
+import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver;
+import org.apache.jackrabbit.spi.commons.conversion.IllegalNameException;
+import org.apache.jackrabbit.spi.commons.name.NameConstants;
+import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
+import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
+import org.apache.jackrabbit.util.ISO9075;
+import org.apache.jackrabbit.util.Text;
+import org.apache.jackrabbit.vault.fs.io.DocViewParserHandler;
+import org.apache.jackrabbit.vault.util.DocViewNode2;
+import org.apache.jackrabbit.vault.util.DocViewProperty2;
+import org.apache.jackrabbit.vault.util.RejectingEntityDefaultHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+import org.xml.sax.helpers.NamespaceSupport;
+
+/**
+ * Implements an importer that processes SAX events from a (modified) document
+ * view. The behaviour for existing nodes works as follows:
+ * TODO: better description
+ * <pre>
+ *
+ * - extended docview always includes SNS indexes
+ * - label is the last element of the path
+ * - uuid "child" means uuid of a direct child node matches
+ * - uuid "desc" means uuid of a descendant child node matches
+ * - uuid "other" means uuid of a node outside of the node tree matches
+ *
+ * uuid  | label | nt  | result
+ * -     | no    | -   | create
+ * -     | yes   | no  | replace
+ * -     | yes   | yes | reuse
+ * no    | no    | -   | create
+ * no    | yes   | no  | replace
+ * no    | yes   | yes | reuse
+ * child | no    | no  | replace
+ * child | no    | yes | replace
+ * child | yes   | no  | replace
+ * child | yes   | yes | reuse
+ * desc  | -     | -   | *error*
+ * other | -     | -   | *error*
+ * </pre>
+ */
+public class DocViewSAXHandler extends RejectingEntityDefaultHandler implements NamespaceResolver {
+
+
+    private static final NameFactory FACTORY = NameFactoryImpl.getInstance();
+
+    /**
+     * the default logger
+     */
+    static final Logger log = LoggerFactory.getLogger(DocViewSAXHandler.class);
+
+    /**
+     * empty attributes
+     */
+    static final Attributes EMPTY_ATTRIBUTES = new AttributesImpl();
+
+    /**
+     * the current namespace state
+     */
+    private final NamespaceSupport nsSupport;
+
+    /**
+     * the default name path resolver
+     */
+   private final DefaultNamePathResolver npResolver = new DefaultNamePathResolver(this);
+
+
+    private final DocViewParserHandler handler;
+
+    private final String rootNodePath;
+    
+    private boolean hasVisitedFirstElement = false;
+
+    public DocViewSAXHandler(DocViewParserHandler handler, String rootNodePath) {
+        super();
+        this.handler = handler;
+        this.rootNodePath = rootNodePath;
+        nsSupport = new NamespaceSupport();
+    }
+
+    
+    @Override
+    public void setDocumentLocator(Locator locator) {
+        // TODO Auto-generated method stub
+        super.setDocumentLocator(locator);
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void startDocument() throws SAXException {
+        
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void endDocument() throws SAXException {
+        try {
+            handler.endDocument();
+        } catch (RepositoryException | IOException e) {
+            throw new SAXException(e);
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void characters(char ch[], int start, int length) throws SAXException {
+        // can be ignored in docview
+    }
+
+    @Override
+    public void startPrefixMapping(String prefix, String uri) throws SAXException {
+        log.trace("-> prefixMapping for {}:{}", prefix, uri);
+        nsSupport.declarePrefix(prefix, uri);
+    }
+
+    @Override
+    public void endPrefixMapping(String prefix) throws SAXException {
+        log.trace("<- prefixMapping for {}", prefix);
+        nsSupport.popContext();
+    }
+
+    
+    /** 
+     * Extracts the index and the original item name from a name according to <a href="https://docs.adobe.com/content/docs/en/spec/jcr/2.0/22_Same-Name_Siblings.html#22.2%20Addressing%20Same-Name%20Siblings%20by%20Path>JCR 2.0 22.2</a>
+     * @param name
+     * @return
+     */
+    private Map.Entry<String, Integer> getNameAndIndex(String name) {
+        int idx = name.lastIndexOf('[');
+        int index = -1;
+        if (idx > 0) {
+            // extract index
+            int idx2 = name.indexOf(']', idx);
+            if (idx2 > 0) {
+                try {
+                    index = Integer.valueOf(name.substring(idx + 1, idx2));
+                } catch (NumberFormatException e) {
+                    // ignore
+                }
+            }
+            name = name.substring(0, idx);
+        }
+        return new SimpleEntry<>(name, index);
+    }
+    
+   
+    @Override
+    public String getURI(String prefix) throws NamespaceException {
+        String uri = nsSupport.getURI(prefix);
+        if (uri == null) {
+            throw new NamespaceException("Unknown prefix " + prefix);
+        }
+        return uri;
+    }
+
+    @Override
+    public String getPrefix(String uri) throws NamespaceException {
+        String prefix = nsSupport.getPrefix(uri);
+        if (prefix == null) {
+            throw new NamespaceException("Unmapped URL " + uri);
+        }
+        return prefix;
+    }
+
+    public void startElement(final String uri, final String localName, final String qName, final Attributes attributes) throws SAXException {
+        // special handling for root node
+        log.trace("-> element {}", qName);
+        Name name;
+        final int index;
+        if (!hasVisitedFirstElement
+                && localName.equals(NameConstants.JCR_ROOT.getLocalName())
+                && uri.equals(NameConstants.JCR_ROOT.getNamespaceURI())) {
+            Map.Entry<String, Integer> nameAndIndex = getNameAndIndex(Text.getName(rootNodePath));
+            index = nameAndIndex.getValue();
+            try {
+                name = npResolver.getQName(nameAndIndex.getKey());
+            } catch (NamespaceException | IllegalNameException e) {
+                throw new SAXException("Invalid name space", e);
+            }
+        } else {
+            Map.Entry<String, Integer> nameAndIndex = getNameAndIndex(ISO9075.decode(localName));
+            index = nameAndIndex.getValue();
+            name = FACTORY.create(uri, nameAndIndex.getKey());
+        }
+        hasVisitedFirstElement = true;
+        try {
+            Map<Name, DocViewProperty2> props = new HashMap<>();
+            for (int i = 0; i < attributes.getLength(); i++) {
+                // ignore non CDATA attributes
+                if (!attributes.getType(i).equals("CDATA")) {
+                    continue;
+                }
+                Name pName = FACTORY.create(
+                        attributes.getURI(i),
+                        ISO9075.decode(attributes.getLocalName(i)));
+                DocViewProperty2 property = DocViewProperty2.parse(
+                        pName.toString(),
+                        attributes.getValue(i));
+                props.put(property.name, property);
+                
+            }
+            DocViewNode2 ni = new DocViewNode2(name, index, props);
+            // TODO: path
+            handler.startDocViewNode(null, ni);
+            
+        }  catch (RepositoryException|IOException e) {
+            throw new SAXException("Error while processing element " + qName, e);
+        } 
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void endElement(String uri, String localName, String qName) throws SAXException {
+        log.trace("<- element {}", qName);
+        try {
+            handler.endDocViewNode(qName, null);
+        } catch (RepositoryException|IOException e) {
+            throw new SAXException(e);
+        }
+    }
+}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FileArtifactHandler.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FileArtifactHandler.java
index 7486ad7..9d14429 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FileArtifactHandler.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FileArtifactHandler.java
@@ -227,15 +227,7 @@ public class FileArtifactHandler extends AbstractArtifactHandler  {
                         mode = wspFilter.getImportMode(path);
                     }
                     if (mode != ImportMode.MERGE) {
-                        try {
-                            DocViewSAXImporter handler = new DocViewSAXImporter(newParent, newName, newSet, wspFilter, options.getIdConflictPolicy());
-                            handler.setAclHandling(getAcHandling());
-                            handler.setCugHandling(getCugHandling());
-                            parseXmlWithSaxHandler(file.getInputSource(), handler);
-                            info.merge(handler.getInfo());
-                        } catch (ParserConfigurationException|SAXException e) {
-                            throw new RepositoryException(e);
-                        }
+                        info.merge(importDocView(file.getInputSource(), newParent, newName, newSet, wspFilter, options.getIdConflictPolicy()));
                     } else {
                         info.onNop(path);
                     }
@@ -322,15 +314,7 @@ public class FileArtifactHandler extends AbstractArtifactHandler  {
         if (idx > 0) {
             rootName = rootName.substring(0, idx);
         }
-        DocViewSAXImporter handler = new DocViewSAXImporter(parent, rootName, artifacts, wspFilter, options.getIdConflictPolicy());
-        handler.setAclHandling(getAcHandling());
-        handler.setCugHandling(getCugHandling());
-        try {
-            parseXmlWithSaxHandler(source, handler);
-            return handler.getInfo();
-        } catch (ParserConfigurationException|SAXException e) {
-            throw new RepositoryException(e);
-        } 
+        return importDocView(source, parent, rootName, artifacts, wspFilter, options.getIdConflictPolicy());
     }
 
     private boolean importNtResource(ImportInfo info, Node content, Artifact artifact)
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/GenericArtifactHandler.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/GenericArtifactHandler.java
index 518bbac..de38b78 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/GenericArtifactHandler.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/GenericArtifactHandler.java
@@ -88,19 +88,7 @@ public class GenericArtifactHandler extends AbstractArtifactHandler {
                     }
                 }
             }
-            try {
-                DocViewSAXImporter handler = new DocViewSAXImporter(parent, name, artifacts, wspFilter, options.getIdConflictPolicy());
-                handler.setAclHandling(getAcHandling());
-                handler.setCugHandling(getCugHandling());
-                parseXmlWithSaxHandler(source, handler);
-                info.merge(handler.getInfo());
-            } catch (ParserConfigurationException e) {
-                throw new RepositoryException(e);
-            } catch (SAXException e) {
-                info = new ImportInfoImpl();
-                info.onError(path, e);
-                log.error("Error while parsing {}: {}", source.getSystemId(), e);
-            }
+            info.merge(importDocView(source, parent, name, artifacts, wspFilter, options.getIdConflictPolicy()));
         }
         return info;
     }
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/JackrabbitACLImporter.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/JackrabbitACLImporter.java
index 964aa86..829ebbb 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/JackrabbitACLImporter.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/JackrabbitACLImporter.java
@@ -18,6 +18,7 @@ package org.apache.jackrabbit.vault.fs.impl.io;
 
 import java.security.Principal;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -45,11 +46,15 @@ import org.apache.jackrabbit.api.security.authorization.PrincipalSetPolicy;
 import org.apache.jackrabbit.api.security.principal.PrincipalManager;
 import org.apache.jackrabbit.api.security.user.Authorizable;
 import org.apache.jackrabbit.commons.jackrabbit.authorization.AccessControlUtils;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.commons.name.NameConstants;
+import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
 import org.apache.jackrabbit.vault.fs.io.AccessControlHandling;
 import org.apache.jackrabbit.vault.util.DocViewNode;
+import org.apache.jackrabbit.vault.util.DocViewNode2;
 import org.apache.jackrabbit.vault.util.DocViewProperty;
+import org.apache.jackrabbit.vault.util.DocViewProperty2;
 import org.slf4j.Logger;
-import org.xml.sax.SAXException;
 
 /**
  * Implements a doc view adapter that reads the ACL information of the docview hierarchy and applies it to the
@@ -57,10 +62,13 @@ import org.xml.sax.SAXException;
  */
 public class JackrabbitACLImporter implements DocViewAdapter {
 
+    private static final Name NAME_REP_EFFECTIVE_PATH = NameFactoryImpl.getInstance().create(Name.NS_REP_URI, "effectivePath");
+    private static final Name NAME_REP_PRINCIPAL_NAMES = NameFactoryImpl.getInstance().create(Name.NS_REP_URI, "principalNames");
+
     /**
      * default logger
      */
-    private static final Logger log = DocViewSAXImporter.log;
+    private static final Logger log = DocViewSAXHandler.log;
 
     private final JackrabbitSession session;
 
@@ -108,21 +116,21 @@ public class JackrabbitACLImporter implements DocViewAdapter {
         this.states.push(State.INITIAL);
     }
 
-    public void startNode(DocViewNode node) {
+    public void startNode(DocViewNode2 node) {
         State state = states.peek();
         switch (state) {
             case INITIAL:
-                if ("rep:ACL".equals(node.primary)) {
+                if ("rep:ACL".equals(node.getPrimaryType())) {
                     importPolicy = new ImportedAcList();
                     state = State.ACL;
-                } else if ("rep:CugPolicy".equals(node.primary)) {
+                } else if ("rep:CugPolicy".equals(node.getPrimaryType())) {
                     importPolicy = new ImportedPrincipalSet(node);
                     state = State.PRINCIPAL_SET_POLICY;
-                } else if ("rep:PrincipalPolicy".equals(node.primary)) {
+                } else if ("rep:PrincipalPolicy".equals(node.getPrimaryType())) {
                     importPolicy = new ImportedPrincipalAcList(node);
                     state = State.ACL;
                 } else {
-                    log.error("Error while reading access control content: Expected rep:ACL or rep:CugPolicy but was: {}", node.primary);
+                    log.error("Error while reading access control content: Expected rep:ACL or rep:CugPolicy but was: {}", node.getPrimaryType());
                     state = State.ERROR;
                 }
                 break;
@@ -141,12 +149,12 @@ public class JackrabbitACLImporter implements DocViewAdapter {
         states.push(state);
     }
 
-    public void endNode() throws SAXException {
+    public void endNode() {
         State state = states.pop();
         importPolicy.endNode(state);
     }
 
-    public List<String> close() throws SAXException, RepositoryException {
+    public List<String> close() throws RepositoryException {
         if (states.peek() != State.INITIAL) {
             log.error("Unexpected end state: {}", states.peek());
         }
@@ -163,7 +171,7 @@ public class JackrabbitACLImporter implements DocViewAdapter {
 
     private abstract class ImportedPolicy<T extends AccessControlPolicy> {
 
-        abstract State append(State state, DocViewNode childNode);
+        abstract State append(State state, DocViewNode2 node);
 
         abstract void endNode(State state);
 
@@ -235,7 +243,7 @@ public class JackrabbitACLImporter implements DocViewAdapter {
         }
 
         @Override
-        State append(State state, DocViewNode childNode) {
+        State append(State state, DocViewNode2 childNode) {
             if (state == State.ACL) {
                 try {
                     currentACE = new ACE(childNode);
@@ -249,7 +257,7 @@ public class JackrabbitACLImporter implements DocViewAdapter {
                 currentACE.addRestrictions(childNode);
                 return State.RESTRICTION;
             } else {
-                log.error("Error while reading access control content: Unexpected node: {} for state {}", childNode.primary, state);
+                log.error("Error while reading access control content: Unexpected node: {} for state {}", childNode.getPrimaryType(), state);
                 return State.ERROR;
             }
         }
@@ -322,18 +330,18 @@ public class JackrabbitACLImporter implements DocViewAdapter {
 
     private final class ImportedPrincipalSet extends ImportedPolicy<PrincipalSetPolicy> {
 
-        private final String[] principalNames;
+        private final Collection<String> principalNames;
 
-        private ImportedPrincipalSet(DocViewNode node) {
+        private ImportedPrincipalSet(DocViewNode2 node) {
             // don't change the status as a cug policy may not have child nodes.
             // just collect the rep:principalNames property
             // any subsequent state would indicate an error
-            principalNames = node.getValues("rep:principalNames");
+            principalNames = node.getPropertyValues(NAME_REP_PRINCIPAL_NAMES);
         }
 
         @Override
-        State append(State state, DocViewNode childNode) {
-            log.error("Error while reading access control content: Unexpected node: {} for state {}", childNode.primary, state);
+        State append(State state, DocViewNode2 childNode) {
+            log.error("Error while reading access control content: Unexpected node: {} for state {}", childNode.getPrimaryType(), state);
             return State.ERROR;
         }
 
@@ -356,10 +364,7 @@ public class JackrabbitACLImporter implements DocViewAdapter {
             }
 
             // TODO: correct behavior for MERGE and MERGE_PRESERVE?
-            Principal[] principals = new Principal[principalNames.length];
-            for (int i = 0; i < principals.length; i++) {
-                principals[i] = getPrincipal(principalNames[i]);
-            }
+            Principal[] principals = principalNames.stream().map(name -> getPrincipal(name)).toArray(Principal[]::new);
 
             psPolicy.addPrincipals(principals);
             acMgr.setPolicy(accessControlledPath, psPolicy);
@@ -378,8 +383,8 @@ public class JackrabbitACLImporter implements DocViewAdapter {
         private final List<PrincipalEntry> entries = new ArrayList<>();
         private PrincipalEntry currentEntry;
 
-        private ImportedPrincipalAcList(DocViewNode node) {
-            String principalName = node.getValue("rep:principalName");
+        private ImportedPrincipalAcList(DocViewNode2 node) {
+            String principalName = node.getPropertyValue(NameConstants.REP_PRINCIPAL_NAME);
              Principal p = pMgr.getPrincipal(principalName);
              if (p == null) {
                  try {
@@ -398,10 +403,10 @@ public class JackrabbitACLImporter implements DocViewAdapter {
         }
 
         @Override
-        State append(State state, DocViewNode childNode) {
+        State append(State state, DocViewNode2 childNode) {
             if (state == State.ACL) {
-                if (!"rep:PrincipalEntry".equals(childNode.primary)) {
-                    log.error("Unexpected node type of access control entry: {}", childNode.primary);
+                if (!"rep:PrincipalEntry".equals(childNode.getPrimaryType())) {
+                    log.error("Unexpected node type of access control entry: {}", childNode.getPrimaryType());
                     return State.ERROR;
                 }
                 currentEntry = new PrincipalEntry(childNode);
@@ -411,7 +416,7 @@ public class JackrabbitACLImporter implements DocViewAdapter {
                 currentEntry.addRestrictions(childNode);
                 return State.RESTRICTION;
             } else {
-                log.error("Error while reading access control content: Unexpected node: {} for state {}", childNode.primary, state);
+                log.error("Error while reading access control content: Unexpected node: {} for state {}", childNode.getPrimaryType(), state);
                 return State.ERROR;
             }
         }
@@ -462,16 +467,16 @@ public class JackrabbitACLImporter implements DocViewAdapter {
 
     private static class AbstractEntry {
 
-        private final String[] privileges;
-        private final Map<String, DocViewProperty> restrictions = new HashMap<String, DocViewProperty>();
+        private final Collection<String> privileges;
+        private final Map<Name, DocViewProperty2> restrictions = new HashMap<>();
 
-        private AbstractEntry(DocViewNode node) {
-            privileges = node.getValues("rep:privileges");
+        private AbstractEntry(DocViewNode2 node) {
+            privileges = node.getPropertyValues(NameConstants.REP_PRIVILEGES);
             addRestrictions(node);
         }
 
-        void addRestrictions(DocViewNode node) {
-            restrictions.putAll(node.props);
+        void addRestrictions(DocViewNode2 node) {
+            restrictions.putAll(node.getProperties());
         }
 
         void convertRestrictions(JackrabbitAccessControlList acl, ValueFactory vf, Map<String, Value> svRestrictions, Map<String, Value[]> mvRestrictions) throws RepositoryException {
@@ -502,16 +507,16 @@ public class JackrabbitACLImporter implements DocViewAdapter {
         private final boolean allow;
         private final String principalName;
 
-        private ACE(DocViewNode node) {
-            super(node);
-            if ("rep:GrantACE".equals(node.primary)) {
+        private ACE(DocViewNode2 childNode) {
+            super(childNode);
+            if ("rep:GrantACE".equals(childNode.getPrimaryType())) {
                 allow = true;
-            } else if ("rep:DenyACE".equals(node.primary)) {
+            } else if ("rep:DenyACE".equals(childNode.getPrimaryType())) {
                 allow = false;
             } else {
-                throw new IllegalArgumentException("Unexpected node ACE type: " + node.primary);
+                throw new IllegalArgumentException("Unexpected node ACE type: " + childNode.getPrimaryType());
             }
-            principalName = node.getValue("rep:principalName");
+            principalName = childNode.getPropertyValue(NameConstants.REP_PRINCIPAL_NAME);
         }
     }
 
@@ -519,9 +524,9 @@ public class JackrabbitACLImporter implements DocViewAdapter {
 
         private final String effectivePath;
 
-        private PrincipalEntry(DocViewNode node) {
+        private PrincipalEntry(DocViewNode2 node) {
             super(node);
-            String v = node.getValue("rep:effectivePath");
+            String v = node.getPropertyValue(NAME_REP_EFFECTIVE_PATH);
             if (v.isEmpty()) {
                 effectivePath = null;
             } else {
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/JcrSysViewTransformer.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/JcrSysViewTransformer.java
index 11dea93..129784f 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/JcrSysViewTransformer.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/JcrSysViewTransformer.java
@@ -22,6 +22,7 @@ import java.util.List;
 import java.util.Set;
 
 import javax.jcr.ImportUUIDBehavior;
+import javax.jcr.InvalidSerializedDataException;
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
 import javax.jcr.PropertyType;
@@ -31,7 +32,9 @@ import javax.jcr.Session;
 import org.apache.jackrabbit.spi.Name;
 import org.apache.jackrabbit.vault.fs.api.ImportMode;
 import org.apache.jackrabbit.vault.util.DocViewNode;
+import org.apache.jackrabbit.vault.util.DocViewNode2;
 import org.apache.jackrabbit.vault.util.DocViewProperty;
+import org.apache.jackrabbit.vault.util.DocViewProperty2;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
@@ -75,11 +78,11 @@ public class JcrSysViewTransformer implements DocViewAdapter {
 
     private long ignoreLevel = 0;
 
-    public JcrSysViewTransformer(@NotNull Node node, @NotNull ImportMode importMode) throws SAXException, RepositoryException {
+    public JcrSysViewTransformer(@NotNull Node node, @NotNull ImportMode importMode) throws RepositoryException {
         this(node, null, importMode);
     }
 
-    JcrSysViewTransformer(@NotNull Node node, @Nullable String existingPath, @NotNull ImportMode importMode) throws RepositoryException, SAXException {
+    JcrSysViewTransformer(@NotNull Node node, @Nullable String existingPath, @NotNull ImportMode importMode) throws RepositoryException {
         Session session = node.getSession();
         parent = node;
         handler = session.getImportContentHandler(
@@ -90,9 +93,13 @@ public class JcrSysViewTransformer implements DocViewAdapter {
         );
         // first define the current namespaces
         String[] prefixes = session.getNamespacePrefixes();
-        handler.startDocument();
-        for (String prefix: prefixes) {
-            handler.startPrefixMapping(prefix, session.getNamespaceURI(prefix));
+        try {
+            handler.startDocument();
+            for (String prefix: prefixes) {
+                handler.startPrefixMapping(prefix, session.getNamespaceURI(prefix));
+            }
+        } catch (SAXException e) {
+            throw new RepositoryException("Can not use sysview handler", e);
         }
 
         this.existingPath = existingPath;
@@ -105,8 +112,13 @@ public class JcrSysViewTransformer implements DocViewAdapter {
         this.importMode = importMode;
     }
 
-    public List<String> close() throws SAXException {
-        handler.endDocument();
+    @Override
+    public List<String> close() throws InvalidSerializedDataException {
+        try {
+            handler.endDocument();
+        } catch (SAXException e) {
+            throw new InvalidSerializedDataException("Invalid sysview", e);
+        }
 
         // get list of created paths
         List<String> paths = new ArrayList<String>();
@@ -142,43 +154,48 @@ public class JcrSysViewTransformer implements DocViewAdapter {
         }
     }
 
-    public void startNode(DocViewNode ni) throws SAXException {
+    @Override
+    public void startNode(DocViewNode2 ni) throws RepositoryException {
         if (ignoreLevel > 0) {
-            DocViewSAXImporter.log.trace("ignoring child node of excluded node: {}", ni.name);
+            DocViewSAXHandler.log.trace("ignoring child node of excluded node: {}", ni.getName());
             ignoreLevel++;
             return;
         }
-        if (excludedNodeNames.contains(ni.name)) {
-            DocViewSAXImporter.log.trace("Ignoring excluded node {}", ni.name);
+        if (excludedNodeNames.contains(ni.getName())) {
+            DocViewSAXHandler.log.trace("Ignoring excluded node {}", ni.getName());
             ignoreLevel = 1;
             return;
         }
 
-        DocViewSAXImporter.log.trace("Transforming element to sysview {}", ni.name);
-
-        AttributesImpl attrs = new AttributesImpl();
-
-        attrs.addAttribute(Name.NS_SV_URI, "name", "sv:name", "CDATA", ni.name);
-        handler.startElement(Name.NS_SV_URI, "node", "sv:node", attrs);
+        DocViewSAXHandler.log.trace("Transforming element to sysview {}", ni.getName());
 
-        // add the properties
-        for (DocViewProperty p: ni.props.values()) {
-            if (p != null && p.values != null) {
-                attrs = new AttributesImpl();
-                attrs.addAttribute(Name.NS_SV_URI, "name", "sv:name", "CDATA", p.name);
-                attrs.addAttribute(Name.NS_SV_URI, "type", "sv:type", "CDATA", PropertyType.nameFromValue(p.type));
-                if (p.isMulti) {
-                    attrs.addAttribute(Name.NS_SV_URI, "multiple", "sv:multiple", "CDATA", "true");
-                }
-
-                handler.startElement(Name.NS_SV_URI, "property", "sv:property", attrs);
-                for (String v: p.values) {
-                    handler.startElement(Name.NS_SV_URI, "value", "sv:value", DocViewSAXImporter.EMPTY_ATTRIBUTES);
-                    handler.characters(v.toCharArray(), 0, v.length());
-                    handler.endElement(Name.NS_SV_URI, "value", "sv:value");
+        try {
+            AttributesImpl attrs = new AttributesImpl();
+
+            attrs.addAttribute(Name.NS_SV_URI, "name", "sv:name", "CDATA", ni.getName().toString());
+            handler.startElement(Name.NS_SV_URI, "node", "sv:node", attrs);
+
+            // add the properties
+            for (DocViewProperty2 p: ni.getProperties().values()) {
+                if (p != null && p.values != null) {
+                    attrs = new AttributesImpl();
+                    attrs.addAttribute(Name.NS_SV_URI, "name", "sv:name", "CDATA", p.name);
+                    attrs.addAttribute(Name.NS_SV_URI, "type", "sv:type", "CDATA", PropertyType.nameFromValue(p.type));
+                    if (p.isMulti) {
+                        attrs.addAttribute(Name.NS_SV_URI, "multiple", "sv:multiple", "CDATA", "true");
+                    }
+
+                    handler.startElement(Name.NS_SV_URI, "property", "sv:property", attrs);
+                    for (String v: p.values) {
+                        handler.startElement(Name.NS_SV_URI, "value", "sv:value", DocViewSAXHandler.EMPTY_ATTRIBUTES);
+                        handler.characters(v.toCharArray(), 0, v.length());
+                        handler.endElement(Name.NS_SV_URI, "value", "sv:value");
+                    }
+                    handler.endElement(Name.NS_SV_URI, "property", "sv:property");
                 }
-                handler.endElement(Name.NS_SV_URI, "property", "sv:property");
             }
+        } catch (SAXException e) {
+            throw new InvalidSerializedDataException("Invalid sysview", e);
         }
 
         if (rootName == null) {
@@ -186,12 +203,17 @@ public class JcrSysViewTransformer implements DocViewAdapter {
         }
     }
 
-    public void endNode() throws SAXException {
+    @Override
+    public void endNode() throws RepositoryException {
         if (ignoreLevel > 0) {
             ignoreLevel--;
             return;
         }
-        handler.endElement(Name.NS_SV_URI, "node", "sv:node");
+        try {
+            handler.endElement(Name.NS_SV_URI, "node", "sv:node");
+        } catch (SAXException e) {
+            throw new InvalidSerializedDataException("Invalid sysview", e);
+        }
     }
 
     public JcrSysViewTransformer excludeNode(String name) {
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/DocViewParser.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/DocViewParser.java
new file mode 100644
index 0000000..34a4c03
--- /dev/null
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/DocViewParser.java
@@ -0,0 +1,47 @@
+package org.apache.jackrabbit.vault.fs.io;
+
+import java.io.IOException;
+
+import javax.jcr.RepositoryException;
+import javax.xml.XMLConstants;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.jackrabbit.vault.fs.impl.io.DocViewSAXHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+public class DocViewParser {
+
+    private SAXParser createSaxParser() throws ParserConfigurationException, SAXException {
+        SAXParserFactory factory = SAXParserFactory.newInstance();
+        factory.setNamespaceAware(true);
+        factory.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
+        SAXParser parser = factory.newSAXParser();
+        parser.setProperty(XMLConstants.ACCESS_EXTERNAL_DTD, "");
+        parser.setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
+        return parser;
+    }
+
+    /**
+     * 
+     * @param inputSource
+     * @param handler
+     * @throws IOException 
+     */
+    public void parse(String rootNodePath, InputSource inputSource, DocViewParserHandler handler) throws IOException, RepositoryException {
+        final SAXParser parser;
+        try {
+            parser = createSaxParser();
+        } catch (ParserConfigurationException|SAXException e) {
+            throw new IllegalStateException("Could not create SAX parser" + e.getMessage(), e);
+        }
+        try {
+            parser.parse(inputSource, new DocViewSAXHandler(handler, rootNodePath));
+        } catch (SAXException e) {
+            // all other exception just pass on as IOException
+            throw new IllegalArgumentException("Could not parse " + inputSource, e);
+        }
+    }
+}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/DocViewParserHandler.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/DocViewParserHandler.java
new file mode 100644
index 0000000..8fcebc2
--- /dev/null
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/DocViewParserHandler.java
@@ -0,0 +1,29 @@
+/*************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ************************************************************************/
+package org.apache.jackrabbit.vault.fs.io;
+
+import java.io.IOException;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.util.DocViewNode2;
+
+public interface DocViewParserHandler {
+    void startDocViewNode(String parentNodePath, DocViewNode2 docViewNode) throws IOException, RepositoryException;
+    void endDocViewNode(String parentNodePath, DocViewNode2 docViewNode) throws IOException, RepositoryException; // TODO: how to access stack (i.e. 
+    void endDocument() throws RepositoryException, IOException; // only called once
+}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewNode.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewNode.java
index 09c0771..98a742e 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewNode.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewNode.java
@@ -32,12 +32,14 @@ import org.jetbrains.annotations.Nullable;
 import org.xml.sax.Attributes;
 
 /**
- * Helper class that represents a (jcr) node abstraction based on
+ * Helper class that represents a JCR node abstraction encapsulating multiple
  * {@link org.apache.jackrabbit.vault.util.DocViewProperty properties}.
+ * @deprecated Use {@link DocViewNode2} instead.
  */
+@Deprecated
 public class DocViewNode {
 
-    public final @NotNull String name;
+    public final @NotNull String name; // always use expanded names here
     /** usually equal to {@link #name} except when this node has a same name sibling, in that case label has format {@code <name>[index]}, https://docs.adobe.com/content/docs/en/spec/jcr/2.0/22_Same-Name_Siblings.html#22.2%20Addressing%20Same-Name%20Siblings%20by%20Path */
     public final @NotNull String label;
     public final @NotNull Map<String, DocViewProperty> props = new HashMap<>();
@@ -54,8 +56,13 @@ public class DocViewNode {
         this.props.putAll(props);
     }
 
+    @Deprecated
     public DocViewNode(@NotNull String name, @NotNull String label, Attributes attributes, NamePathResolver npResolver)
             throws NamespaceException {
+        this(name, label, attributes);
+    }
+    public DocViewNode(@NotNull String name, @NotNull String label, Attributes attributes)
+            throws NamespaceException {
         this.name = name;
         this.label = label;
         String uuid = null;
@@ -70,7 +77,7 @@ public class DocViewNode {
                     attributes.getURI(i),
                     ISO9075.decode(attributes.getLocalName(i)));
             DocViewProperty info = DocViewProperty.parse(
-                    npResolver.getJCRName(pName),
+                    pName.toString(),
                     attributes.getValue(i));
             props.put(info.name, info);
             if (pName.equals(NameConstants.JCR_UUID)) {
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewNode2.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewNode2.java
new file mode 100644
index 0000000..d19610e
--- /dev/null
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewNode2.java
@@ -0,0 +1,80 @@
+/*************************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ ************************************************************************/
+package org.apache.jackrabbit.vault.util;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.commons.name.NameConstants;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Helper class that represents an immutable JCR node abstraction encapsulating multiple
+ * {@link org.apache.jackrabbit.vault.util.DocViewProperty2 properties}.
+ */
+public class DocViewNode2 {
+
+    private final @NotNull Name name;
+    /** usually equal to {@link #name} except when this node has a same name sibling, in that case label has format {@code <name>[index]}, https://docs.adobe.com/content/docs/en/spec/jcr/2.0/22_Same-Name_Siblings.html#22.2%20Addressing%20Same-Name%20Siblings%20by%20Path */
+    private final int index; // either 0 if no same-name sibling or 1 and above for SNS
+    private final @NotNull Map<Name, DocViewProperty2> properties = new HashMap<>();
+
+    public DocViewNode2(@NotNull Name name, int index, @NotNull Map<Name, DocViewProperty2> props) {
+        this.name = name;
+        this.index = index;
+        this.properties.putAll(props);
+    }
+
+    public Name getName() {
+        return name;
+    }
+
+    public int getIndex() {
+        return index;
+    }
+
+    public @NotNull Map<Name, DocViewProperty2> getProperties() {
+        return properties;
+    }
+
+    public @NotNull Collection<String> getPropertyValues(@NotNull Name name) {
+        DocViewProperty2 prop = properties.get(name);
+        return prop == null ? null : prop.getStringValues();
+    }
+
+    public @Nullable String getPropertyValue(@NotNull Name name) {
+        DocViewProperty2 prop = properties.get(name);
+        return prop == null ? null : prop.getStringValue();
+    }
+
+
+    public @Nullable String getPrimaryType() {
+        return getPropertyValue(NameConstants.JCR_PRIMARYTYPE);
+    }
+
+    public @NotNull Collection<String> getMixinTypes() {
+        return getPropertyValues(NameConstants.JCR_MIXINTYPES);
+    }
+
+    public @Nullable String getIdentifier() {
+        return getPropertyValue(NameConstants.JCR_UUID);
+    }
+    
+}
\ No newline at end of file
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty.java
index 0c9b065..3a6621a 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty.java
@@ -55,8 +55,9 @@ import org.jetbrains.annotations.NotNull;
  * value := is a string representation of the value where the following characters are escaped: ',\[{' with a leading '\'
  * </code>
  * </pre>
- * 
+ * @deprecated Use {@link DocViewProperty2} instead.
  */
+@Deprecated
 public class DocViewProperty {
 
     public static final String BINARY_REF = "BinaryRef";
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty2.java
similarity index 85%
copy from vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty.java
copy to vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty2.java
index 0c9b065..37ccfce 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DocViewProperty2.java
@@ -19,6 +19,7 @@ package org.apache.jackrabbit.vault.util;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -38,10 +39,15 @@ import javax.jcr.version.VersionException;
 
 import org.apache.jackrabbit.api.ReferenceBinary;
 import org.apache.jackrabbit.commons.jackrabbit.SimpleReferenceBinary;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.NameFactory;
+import org.apache.jackrabbit.spi.commons.name.NameConstants;
+import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl;
 import org.apache.jackrabbit.util.Text;
 import org.apache.jackrabbit.util.XMLChar;
 import org.apache.jackrabbit.value.ValueHelper;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 /**
  * Helper class that represents a JCR property in the FileVault (enhanced) document view format.
@@ -57,43 +63,45 @@ import org.jetbrains.annotations.NotNull;
  * </pre>
  * 
  */
-public class DocViewProperty {
+public class DocViewProperty2 {
 
     public static final String BINARY_REF = "BinaryRef";
 
+    private static final NameFactory FACTORY = NameFactoryImpl.getInstance();
+
     /**
      * name of the property
      */
-    public final String name;
+    private final Name name;
 
     /**
      * value(s) of the property. always contains at least one value if this is
      * not a mv property.
      */
-    public final String[] values;
+    private final String[] values;
 
     /**
      * indicates a multi-value property
      */
-    public final boolean isMulti;
+    private final boolean isMultiValue;
 
     /**
      * type of this property (can be undefined)
      */
-    public final int type;
+    private final int type;
 
     /**
      * indicates a binary ref property
      */
-    public final boolean isReferenceProperty;
+    private final boolean isReferenceProperty;
 
     /**
      * set of unambigous property names (which never need an explicit type descriptor as the types are defined by the spec)
      */
-    private static final Set<String> UNAMBIGOUS = new HashSet<>();
+    private static final Set<Name> UNAMBIGOUS = new HashSet<>();
     static {
-        UNAMBIGOUS.add("jcr:primaryType");
-        UNAMBIGOUS.add("jcr:mixinTypes");
+        UNAMBIGOUS.add(NameConstants.JCR_PRIMARYTYPE);
+        UNAMBIGOUS.add(NameConstants.JCR_MIXINTYPES);
     }
 
     /**
@@ -107,7 +115,7 @@ public class DocViewProperty {
      * @return the new property
      * @throws RepositoryException
      */
-    public static DocViewProperty fromValues(@NotNull String name, @NotNull Value[] values, int type, boolean isMulti, boolean sort, boolean useBinaryReferences) throws RepositoryException {
+    public static DocViewProperty2 fromValues(@NotNull Name name, @NotNull Value[] values, int type, boolean isMulti, boolean sort, boolean useBinaryReferences) throws RepositoryException {
         List<String> strValues = new ArrayList<>();
         if (isMulti) {
             if (sort) {
@@ -135,7 +143,7 @@ public class DocViewProperty {
         if (isBinaryRef == null) {
             isBinaryRef = false;
         }
-        return new DocViewProperty(name, strValues.toArray(new String[0]), isMulti, type, isBinaryRef);
+        return new DocViewProperty2(name, strValues.toArray(new String[0]), isMulti, type, isBinaryRef);
     }
 
     /**
@@ -147,7 +155,7 @@ public class DocViewProperty {
      * @throws IllegalArgumentException if single value property and not exactly 1 value is given.
      * @throws RepositoryException if another error occurs
      */
-    public static DocViewProperty fromProperty(@NotNull Property prop, boolean sort, boolean useBinaryReferences) throws RepositoryException {
+    public static DocViewProperty2 fromProperty(@NotNull Property prop, boolean sort, boolean useBinaryReferences) throws RepositoryException {
         boolean isMultiValue = prop.getDefinition().isMultiple();
         final Value[] values;
         if (isMultiValue) {
@@ -155,7 +163,7 @@ public class DocViewProperty {
         } else {
             values = new Value[] { prop.getValue() };
         }
-        return fromValues(prop.getName(), values, prop.getType(), isMultiValue, sort, useBinaryReferences);
+        return fromValues(FACTORY.create(prop.getName()), values, prop.getType(), isMultiValue, sort, useBinaryReferences);
     }
 
     static String serializeValue(Value value, boolean useBinaryReferences) throws RepositoryException {
@@ -186,7 +194,7 @@ public class DocViewProperty {
      * @param type type of the property
      * @throws IllegalArgumentException if single value property and not exactly 1 value is given.
      */
-    public DocViewProperty(String name, String[] values, boolean multi, int type) {
+    public DocViewProperty2(Name name, String[] values, boolean multi, int type) {
         this(name, values, multi, type, false);
     }
 
@@ -194,23 +202,23 @@ public class DocViewProperty {
      * Creates a new property.
      * @param name name of the property
      * @param values string representation of values.
-     * @param multi indicates if this is a multi-value property
+     * @param isMultiValue indicates if this is a multi-value property
      * @param type type of the property
      * @param isRef {@code true} to indicate that this is a binary reference property
      * @throws IllegalArgumentException if single value property and not exactly 1 value is given.
      */
-    public DocViewProperty(String name, String[] values, boolean multi, int type, boolean isRef) {
+    public DocViewProperty2(Name name, String[] values, boolean isMultiValue, int type, boolean isRef) {
         this.name = name;
         this.values = values;
-        isMulti = multi;
+        this.isMultiValue = isMultiValue;
         // validate type
         if (type == PropertyType.UNDEFINED) {
-            if ("jcr:primaryType".equals(name) || "jcr:mixinTypes".equals(name)) {
+            if (NameConstants.JCR_PRIMARYTYPE.equals(name) || NameConstants.JCR_MIXINTYPES.equals(name)) {
                 type = PropertyType.NAME;
             }
         }
         this.type = type;
-        if (!isMulti && values.length != 1) {
+        if (!isMultiValue && values.length != 1) {
             throw new IllegalArgumentException("Single value property needs exactly 1 value.");
         }
         this.isReferenceProperty = isRef;
@@ -222,7 +230,7 @@ public class DocViewProperty {
      * @param value (attribute) value
      * @return a property
      */
-    public static DocViewProperty parse(String name, String value) {
+    public static DocViewProperty2 parse(String name, String value) {
         boolean isMulti = false;
         boolean isBinaryRef = false;
         int type = PropertyType.UNDEFINED;
@@ -337,7 +345,7 @@ public class DocViewProperty {
         } else {
             values = new String[]{tmp.toString()};
         }
-        return new DocViewProperty(name, values, isMulti, type, isBinaryRef);
+        return new DocViewProperty2(FACTORY.create(name), values, isMulti, type, isBinaryRef);
     }
     /**
      * Formats (serializes) the given JCR property value according to the enhanced docview syntax.
@@ -378,7 +386,7 @@ public class DocViewProperty {
             }
             attrValue.append('{').append(strType).append('}');
         }
-        if (isMulti) {
+        if (isMultiValue) {
             attrValue.append('[');
         }
         for (int i=0;i<values.length;i++) {
@@ -394,14 +402,14 @@ public class DocViewProperty {
                     case PropertyType.STRING:
                     case PropertyType.NAME:
                     case PropertyType.PATH:
-                        attrValue.append(escape(value, isMulti));
+                        attrValue.append(escape(value, isMultiValue));
                         break;
                     default:
                         attrValue.append(value);
                 }
             }
         }
-        if (isMulti) {
+        if (isMultiValue) {
             attrValue.append(']');
         }
         return attrValue.toString();
@@ -411,29 +419,29 @@ public class DocViewProperty {
      * Escapes the value
      * @param buf buffer to append to
      * @param value value to escape
-     * @param isMulti indicates multi value property
+     * @param isMultiValue indicates multi-value property
      * @deprecated Rather use {@link #escape(String, boolean)}
      */
     @Deprecated
-    protected static void escape(StringBuffer buf, String value, boolean isMulti) {
-        buf.append(escape(value, isMulti));
+    protected static void escape(StringBuffer buf, String value, boolean isMultiValue) {
+        buf.append(escape(value, isMultiValue));
     }
 
     /**
      * Escapes the value
      * @param value value to escape
-     * @param isMulti indicates multi value property
+     * @param isMultiValue indicates multi-value property
      * @return the escaped value
      */
-    protected static String escape(String value, boolean isMulti) {
+    protected static String escape(String value, boolean isMultiValue) {
         StringBuilder buf = new StringBuilder();
         for (int i=0; i<value.length(); i++) {
             char c = value.charAt(i);
             if (c == '\\') {
                 buf.append("\\\\");
-            } else if (c == ',' && isMulti) {
+            } else if (c == ',' && isMultiValue) {
                 buf.append("\\,");
-            } else if (i == 0 && !isMulti && (c == '[' || c == '{')) {
+            } else if (i == 0 && !isMultiValue && (c == '[' || c == '{')) {
                 buf.append('\\').append(c);
             } else if ( XMLChar.isInvalid(c)) {
                 buf.append("\\u");
@@ -472,7 +480,7 @@ public class DocViewProperty {
      * @param name the name
      * @return {@code true} if type information should be emitted, otherwise {@code false}
      */
-    private static boolean isAmbiguous(int type, String name) {
+    private static boolean isAmbiguous(int type, Name name) {
         return type != PropertyType.STRING && !UNAMBIGOUS.contains(name);
     }
 
@@ -484,7 +492,7 @@ public class DocViewProperty {
      * @throws RepositoryException if a repository error occurs
      */
     public boolean apply(Node node) throws RepositoryException {
-        Property prop = node.hasProperty(name) ? node.getProperty(name) : null;
+        Property prop = node.hasProperty(name.toString()) ? node.getProperty(name.toString()) : null;
         // check if multiple flags are equal
         if (prop != null && isMulti != prop.getDefinition().isMultiple()) {
             prop.remove();
@@ -515,9 +523,9 @@ public class DocViewProperty {
                 }
             }
             if (type == PropertyType.UNDEFINED) {
-                node.setProperty(name, values);
+                node.setProperty(name.toString(), values);
             } else {
-                node.setProperty(name, values, type);
+                node.setProperty(name.toString(), values, type);
             }
             // assume modified
             return true;
@@ -529,13 +537,13 @@ public class DocViewProperty {
             if (v == null || !v.getString().equals(values[0])) {
                 try {
                     if (type == PropertyType.UNDEFINED) {
-                        node.setProperty(name, values[0]);
+                        node.setProperty(name.toString(), values[0]);
                     } else {
-                        node.setProperty(name, values[0], type);
+                        node.setProperty(name.toString(), values[0], type);
                     }
                 } catch (ValueFormatException e) {
                     // forcing string
-                    node.setProperty(name, values[0], PropertyType.STRING);
+                    node.setProperty(name.toString(), values[0], PropertyType.STRING);
                 }
                 return true;
             }
@@ -576,9 +584,9 @@ public class DocViewProperty {
                 return false;
             }
             if (isMulti) {
-                node.setProperty(name, binaryValues.toArray(new Value[0]));
+                node.setProperty(name.toString(), binaryValues.toArray(new Value[0]));
             } else {
-                node.setProperty(name, binaryValues.get(0));
+                node.setProperty(name.toString(), binaryValues.get(0));
             }
             // the binary property is always modified (TODO: check if still correct with JCRVLT-110)
             return true;
@@ -593,7 +601,7 @@ public class DocViewProperty {
     public int hashCode() {
         final int prime = 31;
         int result = 1;
-        result = prime * result + (isMulti ? 1231 : 1237);
+        result = prime * result + (isMultiValue ? 1231 : 1237);
         result = prime * result + (isReferenceProperty ? 1231 : 1237);
         result = prime * result + ((name == null) ? 0 : name.hashCode());
         result = prime * result + type;
@@ -609,8 +617,8 @@ public class DocViewProperty {
             return false;
         if (getClass() != obj.getClass())
             return false;
-        DocViewProperty other = (DocViewProperty) obj;
-        if (isMulti != other.isMulti)
+        DocViewProperty2 other = (DocViewProperty2) obj;
+        if (isMultiValue != other.isMultiValue)
             return false;
         if (isReferenceProperty != other.isReferenceProperty)
             return false;
@@ -632,8 +640,27 @@ public class DocViewProperty {
      */
     @Override
     public String toString() {
-        return "DocViewProperty [name=" + name + ", values=" + Arrays.toString(values) + ", isMulti=" + isMulti + ", type=" + PropertyType.nameFromValue(type)
+        return "DocViewProperty [name=" + name + ", values=" + Arrays.toString(values) + ", isMultiValue=" + isMultiValue + ", type=" + PropertyType.nameFromValue(type)
                 + ", isReferenceProperty=" + isReferenceProperty + "]";
     }
 
+    public @NotNull Name getName() {
+        return name;
+    }
+
+    public boolean isMultiValue() {
+        return isMultiValue;
+    }
+
+    public int getType() {
+        return type;
+    }
+
+    public @Nullable String getStringValue() {
+        
+    }
+    
+    public @NotNull Collection<String> getStringValues() {
+        
+    }
 }
\ No newline at end of file
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/EffectiveNodeType.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/EffectiveNodeType.java
index 7ac2488..dad5090 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/EffectiveNodeType.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/EffectiveNodeType.java
@@ -143,6 +143,7 @@ public final class EffectiveNodeType {
     private static <T extends ItemDefinition> Optional<T> getApplicableItemDefinition(List<T> itemDefinitions, Predicate<T> predicate, @Nullable String name) {
         final Predicate<ItemDefinition> namePredicate;
         if (name != null) {
+            // TODO: support expanded and qualified names!
             namePredicate = pd -> name.equals(pd.getName());
         } else {
             namePredicate = pd -> "*".equals(pd.getName());