jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From tri...@apache.org
Subject svn commit: r1512568 [13/39] - in /jackrabbit/commons/filevault/trunk: ./ parent/ vault-cli/ vault-cli/src/ vault-cli/src/main/ vault-cli/src/main/appassembler/ vault-cli/src/main/assembly/ vault-cli/src/main/java/ vault-cli/src/main/java/org/ vault-cl...
Date Sat, 10 Aug 2013 05:53:54 GMT
Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/FileAggregator.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/FileAggregator.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/FileAggregator.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/FileAggregator.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,267 @@
+/*
+ * 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.aggregator;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+import org.apache.jackrabbit.vault.fs.DirectoryArtifact;
+import org.apache.jackrabbit.vault.fs.api.Aggregate;
+import org.apache.jackrabbit.vault.fs.api.Aggregator;
+import org.apache.jackrabbit.vault.fs.api.Artifact;
+import org.apache.jackrabbit.vault.fs.api.ArtifactSet;
+import org.apache.jackrabbit.vault.fs.api.ArtifactType;
+import org.apache.jackrabbit.vault.fs.api.DumpContext;
+import org.apache.jackrabbit.vault.fs.api.Dumpable;
+import org.apache.jackrabbit.vault.fs.api.ImportInfo;
+import org.apache.jackrabbit.vault.fs.api.ItemFilterSet;
+import org.apache.jackrabbit.vault.fs.impl.AggregateManagerImpl;
+import org.apache.jackrabbit.vault.fs.impl.ArtifactSetImpl;
+import org.apache.jackrabbit.vault.fs.impl.io.DocViewSerializer;
+import org.apache.jackrabbit.vault.fs.impl.io.ImportInfoImpl;
+import org.apache.jackrabbit.vault.fs.io.Serializer;
+import org.apache.jackrabbit.vault.util.Constants;
+import org.apache.jackrabbit.vault.util.JcrConstants;
+import org.apache.jackrabbit.vault.util.MimeTypes;
+import org.apache.jackrabbit.vault.util.PathUtil;
+import org.apache.jackrabbit.vault.util.Text;
+
+/**
+ * A file aggregate contains nt:file or nt:resource nodes.
+ * It always produces a primary FileArtifact that represents the jcr:data,
+ * and jcr:lastModified. if the jcr:mimeType is correctly detectable from the
+ * file's extension it's also included in that artifact.
+ * if the mapping is ambiguous or if the file or content contains additional
+ * information, an additional directory artifact ".dir" and a .content.xml
+ * is created.
+ */
+public class FileAggregator implements Aggregator, Dumpable {
+
+    /**
+     * filter that regulates what to be included in the aggregate
+     */
+    private final ItemFilterSet contentFilter = new ItemFilterSet();
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean matches(Node node, String path) throws RepositoryException {
+        if (node.isNodeType(JcrConstants.NT_FILE)) {
+            return node.hasProperty(JcrConstants.JCR_CONTENT + "/" + JcrConstants.JCR_DATA);
+        } else {
+            return node.isNodeType(JcrConstants.NT_RESOURCE);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean includes(Node root, Node node, Property prop, String path)
+            throws RepositoryException {
+        // we reject auto-generated properties so that they don't get
+        // included in the .dir/.content.xml
+        if (node.getName().equals(JcrConstants.JCR_CONTENT)) {
+            String name = path == null ? prop.getName() : Text.getName(path);
+            if (name.equals(JcrConstants.JCR_DATA)) {
+                return false;
+            } else if (name.equals(JcrConstants.JCR_LASTMODIFIED)) {
+                return false;
+            } else if (name.equals(JcrConstants.JCR_UUID)) {
+                return false;
+            } else if (name.equals(JcrConstants.JCR_MIMETYPE)) {
+                String expected = MimeTypes.getMimeType(root.getName(), MimeTypes.APPLICATION_OCTET_STREAM);
+                return !expected.equals(prop.getString());
+            } else if (name.equals(JcrConstants.JCR_ENCODING)) {
+                if (prop.getString().equals("utf-8")) {
+                    String mimeType = MimeTypes.getMimeType(root.getName(), MimeTypes.APPLICATION_OCTET_STREAM);
+                    return MimeTypes.isBinary(mimeType);
+                }
+            }
+        } else if (node == root && node.isNodeType(JcrConstants.NT_RESOURCE)) {
+            // for nt:resource nodes, we only reject data and last modified
+            // we don't want to risk to the entire node gets rejected.
+            String name = path == null ? prop.getName() : Text.getName(path);
+            if (name.equals(JcrConstants.JCR_DATA)) {
+                return false;
+            } else if (name.equals(JcrConstants.JCR_LASTMODIFIED)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean includes(Node root, Node node, String path) throws RepositoryException {
+        if (path == null) {
+            path = node.getPath();
+        }
+        int depth = PathUtil.getDepth(path) - root.getDepth();
+        boolean isFile = root.isNodeType(JcrConstants.NT_FILE);
+        if (depth == 0) {
+            // should not happen, but ok.
+            return true;
+        } else if (depth == 1) {
+            // on root level, we only allow "jcr:content" if it's a file
+            if (isFile && node.getName().equals(JcrConstants.JCR_CONTENT)) {
+                return true;
+            }
+        } else {
+            // no sub nodes.
+        }
+        // only respect content filter if not empty
+        return !contentFilter.isEmpty() && contentFilter.contains(node, path, depth);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return <code>false</code> always.
+     */
+    public boolean hasFullCoverage() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return <code>false</code> always.
+     */
+    public boolean isDefault() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param aggregate
+     */
+    public ArtifactSet createArtifacts(Aggregate aggregate) throws RepositoryException {
+        ArtifactSetImpl artifacts = new ArtifactSetImpl();
+        Node node = aggregate.getNode();
+        ((AggregateManagerImpl) aggregate.getManager()).addNodeTypes(node);
+        Node content = node;
+        if (content.isNodeType(JcrConstants.NT_FILE)) {
+            content = node.getNode(JcrConstants.JCR_CONTENT);
+            ((AggregateManagerImpl) aggregate.getManager()).addNodeTypes(content);
+        }
+        // retrieve basic properties
+        long lastModified = 0;
+        String encoding = null;
+        try {
+            lastModified = content.getProperty(JcrConstants.JCR_LASTMODIFIED).getDate().getTimeInMillis();
+        } catch (RepositoryException e) {
+            // ignore
+        }
+        String mimeType = null;
+        if (content.hasProperty(JcrConstants.JCR_MIMETYPE)) {
+            try {
+                mimeType = content.getProperty(JcrConstants.JCR_MIMETYPE).getString();
+            } catch (RepositoryException e) {
+                // ignore
+            }
+        }
+        if (mimeType == null) {
+            // guess mime type from name
+            mimeType = MimeTypes.getMimeType(node.getName(), MimeTypes.APPLICATION_OCTET_STREAM);
+        }
+
+        if (content.hasProperty(JcrConstants.JCR_ENCODING)) {
+            try {
+                encoding = content.getProperty(JcrConstants.JCR_ENCODING).getString();
+            } catch (RepositoryException e) {
+                // ignore
+            }
+        }
+        // check needs .dir artifact nt:file. we trust that the repository does
+        // not add other properties than the one defined in JCR.
+        boolean needsDir = !node.getPrimaryNodeType().getName().equals(JcrConstants.NT_FILE);
+        if (!needsDir) {
+            // suppress mix:lockable (todo: make configurable)
+            if (node.hasProperty(JcrConstants.JCR_MIXINTYPES)) {
+                for (Value v: node.getProperty(JcrConstants.JCR_MIXINTYPES).getValues()) {
+                    if (!v.getString().equals(JcrConstants.MIX_LOCKABLE)) {
+                        needsDir = true;
+                        break;
+                    }
+                }
+            }
+        }
+        if (!needsDir) {
+            needsDir = !content.getPrimaryNodeType().getName().equals(JcrConstants.NT_RESOURCE);
+        }
+        if (!needsDir) {
+            if (content.hasProperty(JcrConstants.JCR_MIXINTYPES)) {
+                for (Value v: content.getProperty(JcrConstants.JCR_MIXINTYPES).getValues()) {
+                    if (!v.getString().equals(JcrConstants.MIX_LOCKABLE)) {
+                        needsDir = true;
+                        break;
+                    }
+                }
+            }
+        }
+        if (!needsDir) {
+            needsDir = !MimeTypes.matches(node.getName(), mimeType, MimeTypes.APPLICATION_OCTET_STREAM);
+        }
+        if (!needsDir && encoding != null) {
+            needsDir = !"utf-8".equals(encoding) || MimeTypes.isBinary(mimeType);
+        }
+
+        // create file artifact
+        String name = aggregate.getName();
+        artifacts.add(null, name, "", ArtifactType.FILE,
+                content.getProperty(JcrConstants.JCR_DATA), lastModified);
+
+        // create .dir artifact
+        if (needsDir) {
+            // in this case, we create a directory artifact
+            Artifact parent = new DirectoryArtifact(name, ".dir");
+            artifacts.add(parent);
+            // and extra
+            Serializer ser = new DocViewSerializer(aggregate);
+            // hack: do better
+            artifacts.add(parent, "", Constants.DOT_CONTENT_XML, ArtifactType.PRIMARY, ser, 0);
+        }
+        return artifacts;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ImportInfo remove(Node node, boolean recursive, boolean trySave)
+            throws RepositoryException {
+        ImportInfo info = new ImportInfoImpl();
+        info.onDeleted(node.getPath());
+        Node parent = node.getParent();
+        node.remove();
+        if (trySave) {
+            parent.save();
+        }
+        return info;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void dump(DumpContext ctx, boolean isLast) {
+        ctx.println(isLast, getClass().getSimpleName());
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/FileFolderAggregator.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/FileFolderAggregator.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/FileFolderAggregator.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/FileFolderAggregator.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,40 @@
+/*
+ * 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.aggregator;
+
+import org.apache.jackrabbit.vault.fs.filter.FileFolderNodeFilter;
+import org.apache.jackrabbit.vault.fs.filter.NodeTypeItemFilter;
+import org.apache.jackrabbit.vault.util.JcrConstants;
+
+/**
+ * Defines an aggregator that handles file/folder like nodes. It matches
+ * all nt:hierarchyNode nodes that have or define a jcr:content
+ * child node and excludes child nodes that are nt:hierarchyNodes.
+ */
+public class FileFolderAggregator extends GenericAggregator {
+
+    public FileFolderAggregator() {
+        getMatchFilter().addInclude(
+                new FileFolderNodeFilter()
+        ).seal();
+        getContentFilter().addExclude(
+                new NodeTypeItemFilter(JcrConstants.NT_HIERARCHYNODE, true, 1, Integer.MAX_VALUE)
+        ).seal();
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/FullCoverageAggregator.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/FullCoverageAggregator.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/FullCoverageAggregator.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/FullCoverageAggregator.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,34 @@
+/*
+ * 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.aggregator;
+
+import org.apache.jackrabbit.vault.fs.api.ItemFilter;
+
+/**
+ * <code>FullCoverageAggregator</code>...
+ */
+public class FullCoverageAggregator extends GenericAggregator {
+
+    public FullCoverageAggregator() {
+        getContentFilter().addInclude(ItemFilter.ALL).seal();
+    }
+
+    public boolean hasFullCoverage() {
+        return true;
+    }
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/GenericAggregator.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/GenericAggregator.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/GenericAggregator.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/GenericAggregator.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,281 @@
+/*
+ * 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.aggregator;
+
+import java.util.Collection;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.fs.DirectoryArtifact;
+import org.apache.jackrabbit.vault.fs.api.Aggregate;
+import org.apache.jackrabbit.vault.fs.api.Aggregator;
+import org.apache.jackrabbit.vault.fs.api.Artifact;
+import org.apache.jackrabbit.vault.fs.api.ArtifactSet;
+import org.apache.jackrabbit.vault.fs.api.ArtifactType;
+import org.apache.jackrabbit.vault.fs.api.DumpContext;
+import org.apache.jackrabbit.vault.fs.api.Dumpable;
+import org.apache.jackrabbit.vault.fs.api.ImportInfo;
+import org.apache.jackrabbit.vault.fs.api.ItemFilterSet;
+import org.apache.jackrabbit.vault.fs.impl.ArtifactSetImpl;
+import org.apache.jackrabbit.vault.fs.impl.io.DocViewSerializer;
+import org.apache.jackrabbit.vault.fs.impl.io.ImportInfoImpl;
+import org.apache.jackrabbit.vault.fs.io.Serializer;
+import org.apache.jackrabbit.vault.fs.spi.ACLManagement;
+import org.apache.jackrabbit.vault.fs.spi.ServiceProviderFactory;
+import org.apache.jackrabbit.vault.util.Constants;
+import org.apache.jackrabbit.vault.util.PathUtil;
+
+/**
+ */
+public class GenericAggregator implements Aggregator, Dumpable {
+
+    /**
+     * name hint for the aggregator
+     */
+    private String name;
+
+    /**
+     * flag indicating if this aggregator aggregates children
+     */
+    private boolean fullCoverage = false;
+
+    /**
+     * filter that regulates what to be included in the aggregate
+     */
+    private final ItemFilterSet contentFilter = new ItemFilterSet();
+
+    /**
+     * filter that regulates if this aggregator is to be used
+     */
+    private final ItemFilterSet matchFilter = new ItemFilterSet();
+
+    /**
+     * the 'default' aggregator flag
+     */
+    private boolean isDefault = false;
+
+    /**
+     * acl management
+     */
+    private ACLManagement aclManagement;
+
+    private ACLManagement getAclManagement() {
+        if (aclManagement == null) {
+            aclManagement = ServiceProviderFactory.getProvider().getACLManagement();
+        }
+        return aclManagement;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean hasFullCoverage() {
+        return fullCoverage;
+    }
+
+    public void setHasFullCoverage(String hasFullCoverage) {
+        this.fullCoverage = Boolean.valueOf(hasFullCoverage);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isDefault() {
+        return isDefault;
+    }
+
+    /**
+     * Sets the default flag
+     * @param aDefault the default flag
+     */
+    public void setIsDefault(String aDefault) {
+        isDefault = Boolean.valueOf(aDefault);
+    }
+
+    /**
+     * Returns the name of this aggregator
+     * @return the name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Sets the name of this aggregator
+     * @param name the name
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * Sets the flag indicating if this aggregator aggregates children
+     *
+     * @param fullCoverage <code>true</code> if this aggregator aggregates
+     *        children
+     */
+    public void setFullCoverage(String fullCoverage) {
+        this.fullCoverage = Boolean.valueOf(fullCoverage);
+    }
+
+
+    /**
+     * Returns the content filter
+     * @return the content filter
+     */
+    public ItemFilterSet getContentFilter() {
+        return contentFilter;
+    }
+
+    /**
+     * Returns the match filter
+     * @return the match filter
+     */
+    public ItemFilterSet getMatchFilter() {
+        return matchFilter;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean includes(Node root, Node node, String path) throws RepositoryException {
+        if (path == null) {
+            path = node.getPath();
+        }
+        return contentFilter.contains(node, path, PathUtil.getDepth(path) - root.getDepth());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean matches(Node node, String path) throws RepositoryException {
+        return matchFilter.contains(node, path, 0);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean includes(Node root, Node parent, Property property, String path) throws RepositoryException {
+        if (path == null) {
+            path = property.getPath();
+        }
+        return contentFilter.contains(property, path, PathUtil.getDepth(path) - root.getDepth());
+    }
+
+    /**
+     * {@inheritDoc}
+     * @param aggregate
+     */
+    public ArtifactSet createArtifacts(Aggregate aggregate) throws RepositoryException {
+        ArtifactSetImpl artifacts = new ArtifactSetImpl();
+
+        // set coverage filter to artifacts
+        artifacts.setCoverage(getContentFilter());
+
+        // add directory
+        ArtifactType type = ArtifactType.PRIMARY;
+        String name = aggregate.getName();
+        String ext = ".xml";
+        Artifact parent = null;
+        if (!hasFullCoverage()) {
+            parent = new DirectoryArtifact(name);
+            artifacts.add(parent);
+            name = "";
+            ext = Constants.DOT_CONTENT_XML;
+            // special optimization if only nt:folder
+            if (aggregate.getNode().getPrimaryNodeType().getName().equals("nt:folder")
+                    && aggregate.getNode().getMixinNodeTypes().length == 0) {
+                return artifacts;
+            }
+        }
+
+        // add extra
+        Serializer ser = new DocViewSerializer(aggregate);
+        artifacts.add(parent, name, ext, type, ser, 0);
+
+        // add binaries
+        Collection<Property> bins = aggregate.getBinaries();
+        if (bins != null && !bins.isEmpty()) {
+            if (hasFullCoverage()) {
+                assert parent == null;
+                parent = new DirectoryArtifact(aggregate.getName());
+                artifacts.add(parent);
+            }
+            int pathLen = aggregate.getPath().length();
+            if (pathLen > 1) {
+                pathLen++;
+            }
+            for (Property prop: bins) {
+                String relPath = prop.getPath().substring(pathLen);
+                artifacts.add(parent, relPath, ".binary", ArtifactType.BINARY, prop, 0);
+            }
+
+        }
+        return artifacts;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Throws an exception if this aggregator allows children but
+     * <code>recursive</code> is <code>false</code>.
+     */
+    public ImportInfo remove(Node node, boolean recursive, boolean trySave)
+            throws RepositoryException {
+        if (fullCoverage && !recursive) {
+            // todo: allow smarter removal
+            throw new RepositoryException("Unable to remove content since aggregation has children and recursive is not set.");
+        }
+        ImportInfo info = new ImportInfoImpl();
+        info.onDeleted(node.getPath());
+        Node parent = node.getParent();
+        if (getAclManagement().isACLNode(node)) {
+            getAclManagement().clearACL(parent);
+        } else {
+            node.remove();
+        }
+        if (trySave) {
+            parent.save();
+        }
+        return info;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void dump(DumpContext ctx, boolean isLast) {
+        ctx.println(isLast, getClass().getSimpleName());
+        ctx.indent(isLast);
+        ctx.printf(false, "name: %s", getName());
+        ctx.printf(false, "fullCoverage: %b", hasFullCoverage());
+        ctx.printf(false, "default: %b", isDefault());
+        ctx.println(false, "Content Filter");
+        ctx.indent(false);
+        getContentFilter().dump(ctx, true);
+        ctx.outdent();
+        ctx.println(true, "Match Filter");
+        ctx.indent(true);
+        getMatchFilter().dump(ctx, true);
+        ctx.outdent();
+        ctx.outdent();
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/NodeTypeAggregator.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/NodeTypeAggregator.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/NodeTypeAggregator.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/NodeTypeAggregator.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,95 @@
+/*
+ * 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.aggregator;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.fs.api.ArtifactType;
+import org.apache.jackrabbit.vault.fs.api.DumpContext;
+import org.apache.jackrabbit.vault.fs.api.ImportInfo;
+import org.apache.jackrabbit.vault.fs.filter.NodeTypeItemFilter;
+import org.apache.jackrabbit.vault.fs.impl.AggregateImpl;
+import org.apache.jackrabbit.vault.fs.impl.ArtifactSetImpl;
+import org.apache.jackrabbit.vault.fs.impl.io.CNDSerializer;
+import org.apache.jackrabbit.vault.fs.impl.io.ImportInfoImpl;
+import org.apache.jackrabbit.vault.fs.io.Serializer;
+import org.apache.jackrabbit.vault.util.JcrConstants;
+
+/**
+ * Implements an aggregator that serializes nt:nodeType nodes into a .cnd files.
+ */
+public class NodeTypeAggregator extends GenericAggregator {
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return <code>true</code> always.
+     */
+    public boolean hasFullCoverage() {
+        return true;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * If no match filter is defined, add the nt:nodeType as node type filter.
+     */
+    public boolean matches(Node node, String path) throws RepositoryException {
+        if (getMatchFilter().isEmpty()) {
+            getMatchFilter().addInclude(
+                    new NodeTypeItemFilter(JcrConstants.NT_NODETYPE, true)
+            );
+        }
+        return super.matches(node, path);
+    }
+
+
+    /**
+     * {@inheritDoc}
+     * @param aggregate
+     */
+    public ArtifactSetImpl createArtifacts(AggregateImpl aggregate) throws RepositoryException {
+        ArtifactSetImpl artifacts = new ArtifactSetImpl();
+        Serializer ser = new CNDSerializer(aggregate);
+        artifacts.add(null, aggregate.getRelPath(), ".xcnd", ArtifactType.PRIMARY, ser, 0);
+        return artifacts;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ImportInfo remove(Node node, boolean recursive, boolean trySave) throws RepositoryException {
+        ImportInfo info = new ImportInfoImpl();
+        info.onDeleted(node.getPath());
+        Node parent = node.getParent();
+        node.remove();
+        if (trySave) {
+            parent.save();
+        }
+        return info;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void dump(DumpContext ctx, boolean isLast) {
+        ctx.println(isLast, getClass().getSimpleName());
+    }
+    
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/RootAggregator.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/RootAggregator.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/RootAggregator.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/aggregator/RootAggregator.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,77 @@
+/*
+ * 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.aggregator;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.fs.api.ImportInfo;
+import org.apache.jackrabbit.vault.fs.filter.IsNodeFilter;
+
+/**
+ */
+public class RootAggregator extends GenericAggregator {
+
+    /**
+     * Default constructor that initializes the filters.
+     */
+    public RootAggregator() {
+        getContentFilter().addExclude(new IsNodeFilter()).seal();
+        getMatchFilter().seal();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return <code>false</code> always
+     */
+    public boolean hasFullCoverage() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return <code>false</code> always
+     */
+    public boolean isDefault() {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return "root" always
+     */
+    public String getName() {
+        return "root";
+    }
+
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws UnsupportedOperationException since removing the root is not
+     *         valid.
+     */
+    public ImportInfo remove(Node node, boolean recursive, boolean trySave)
+            throws RepositoryException {
+        throw new UnsupportedOperationException("Cannot remove root node.");
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AbstractArtifactHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AbstractArtifactHandler.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AbstractArtifactHandler.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AbstractArtifactHandler.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,128 @@
+/*
+ * 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 javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.jackrabbit.vault.fs.api.Aggregate;
+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.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.spi.ACLManagement;
+import org.apache.jackrabbit.vault.fs.spi.ServiceProviderFactory;
+
+/**
+ * <code>AbstractArtifactHandler</code>...
+ *
+ */
+public abstract class AbstractArtifactHandler implements ArtifactHandler, Dumpable {
+
+    /**
+     * access control handling.
+     * todo: would be better to pass via some kind of import context
+     */
+    protected AccessControlHandling acHandling = AccessControlHandling.OVERWRITE;
+
+    /**
+     * acl management
+     */
+    private ACLManagement aclManagement;
+
+    /**
+     * Returns the access control handling defined for this handler
+     * @return the access control handling.
+     */
+    public AccessControlHandling getAcHandling() {
+        return acHandling;
+    }
+
+    /**
+     * Sets the access control handling used for importing.
+     * @param acHandling the access control handling
+     */
+    public void setAcHandling(AccessControlHandling acHandling) {
+        this.acHandling = acHandling;
+    }
+
+    /**
+     * Returns the ACL management
+     * @return the ACL management
+     */
+    public ACLManagement getAclManagement() {
+        if (aclManagement == null) {
+            aclManagement = ServiceProviderFactory.getProvider().getACLManagement();
+        }
+        return aclManagement;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ImportInfo accept(Session session, Aggregate file,
+                             ArtifactSet artifacts)
+            throws RepositoryException, IOException {
+        Node node = file.getNode();
+        String name = node.getName();
+        return accept(file.getManager().getWorkspaceFilter(),
+                name.length() == 0 ? node : node.getParent(),
+                name, (ArtifactSetImpl) artifacts);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ImportInfo accept(Session session, Aggregate parent, String name,
+                             ArtifactSet artifacts)
+            throws RepositoryException, IOException {
+        Node node = parent.getNode();
+        return accept(parent.getManager().getWorkspaceFilter(),
+                node, name, (ArtifactSetImpl) artifacts);
+    }
+
+    /**
+     * Imports an artifact set below the node.
+     *
+     * @param wspFilter the workspace filter
+     * @param parent the parent node
+     * @param name the name of the (new) import
+     * @param artifacts the artifact set
+     * @return the import info on successful import, <code>null</code> otherwise
+     * @throws RepositoryException if an error occurs.
+     * @throws IOException if an I/O error occurs.
+     */
+    protected abstract ImportInfoImpl accept(WorkspaceFilter wspFilter, Node parent,
+                                         String name, ArtifactSetImpl artifacts)
+            throws RepositoryException, IOException;
+
+    /**
+     * {@inheritDoc}
+     */
+    public void dump(DumpContext ctx, boolean isLast) {
+        ctx.println(isLast, getClass().getSimpleName());
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AbstractSAXFormatter.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AbstractSAXFormatter.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AbstractSAXFormatter.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AbstractSAXFormatter.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,208 @@
+/*
+ * 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 javax.jcr.NamespaceException;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver;
+import org.apache.jackrabbit.spi.commons.name.NameConstants;
+import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
+import org.apache.jackrabbit.spi.commons.namespace.SessionNamespaceResolver;
+import org.apache.jackrabbit.vault.fs.api.Aggregate;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * <code>AbstractSaxFormatter</code>...
+ *
+ */
+public abstract class AbstractSAXFormatter implements AggregateWalkListener {
+
+    /**
+     * The XML elements and attributes used in serialization
+     */
+    public static final String NODE_ELEMENT = "node";
+    public static final String PREFIXED_NODE_ELEMENT =
+        Name.NS_SV_PREFIX + ":" + NODE_ELEMENT;
+
+    public static final String PROPERTY_ELEMENT = "property";
+    public static final String PREFIXED_PROPERTY_ELEMENT =
+        Name.NS_SV_PREFIX + ":" + PROPERTY_ELEMENT;
+
+    public static final String VALUE_ELEMENT = "value";
+    public static final String PREFIXED_VALUE_ELEMENT =
+        Name.NS_SV_PREFIX + ":" + VALUE_ELEMENT;
+
+    public static final String NAME_ATTRIBUTE = "name";
+    public static final String PREFIXED_NAME_ATTRIBUTE =
+        Name.NS_SV_PREFIX + ":" + NAME_ATTRIBUTE;
+
+    public static final String TYPE_ATTRIBUTE = "type";
+    public static final String PREFIXED_TYPE_ATTRIBUTE =
+        Name.NS_SV_PREFIX + ":" + TYPE_ATTRIBUTE;
+
+    public static final String CDATA_TYPE = "CDATA";
+    public static final String ENUMERATION_TYPE = "ENUMERATION";
+
+    /**
+     * indicates if binaries are to be excluded from the serialization
+     */
+    protected boolean skipBinary = true;
+
+    /**
+     * the session to be used for resolving namespace mappings
+     */
+    protected final Session session;
+
+    /**
+     * the session's namespace resolver
+     */
+    protected final NamespaceResolver nsResolver;
+
+    /**
+     * the content handler to feed the SAX events to
+     */
+    protected final ContentHandler contentHandler;
+
+    /**
+     * The jcr:primaryType property name (allowed for session-local prefix mappings)
+     */
+    protected final String jcrPrimaryType;
+
+    /**
+     * The nt:unstructured name (allowed for session-local prefix mappings)
+     */
+    protected final String ntUnstructured;
+
+    /**
+     * The jcr:mixinTypes property name (allowed for session-local prefix mappings)
+     */
+    protected final String jcrMixinTypes;
+
+    /**
+     * The jcr:uuid property name (allowed for session-local prefix mappings)
+     */
+    protected final String jcrUUID;
+
+    /**
+     * The jcr:root node name (allowed for session-local prefix mappings)
+     */
+    protected final String jcrRoot;
+
+    /**
+     * The jcr:xmltext node name (allowed for session-local prefix mappings)
+     */
+    protected final String jcrXMLText;
+
+    /**
+     * The jcr:xmlCharacters property name (allowed for session-local prefix mappings)
+     */
+    protected final String jcrXMLCharacters;
+
+    /**
+     * the export context
+     */
+    protected final Aggregate aggregate;
+
+    protected AbstractSAXFormatter(Aggregate aggregate, ContentHandler contentHandler)
+            throws RepositoryException {
+
+        this.aggregate = aggregate;
+        this.session = aggregate.getNode().getSession();
+        nsResolver = new SessionNamespaceResolver(session);
+
+        this.contentHandler = contentHandler;
+
+        DefaultNamePathResolver npResolver = new DefaultNamePathResolver(nsResolver);
+
+        // resolve the names of some well known properties
+        // allowing for session-local prefix mappings
+        try {
+            jcrPrimaryType = npResolver.getJCRName(NameConstants.JCR_PRIMARYTYPE);
+            jcrMixinTypes = npResolver.getJCRName(NameConstants.JCR_MIXINTYPES);
+            jcrUUID = npResolver.getJCRName(NameConstants.JCR_UUID);
+            jcrRoot = npResolver.getJCRName(NameConstants.JCR_ROOT);
+            jcrXMLText = npResolver.getJCRName(NameConstants.JCR_XMLTEXT);
+            jcrXMLCharacters = npResolver.getJCRName(NameConstants.JCR_XMLCHARACTERS);
+            ntUnstructured = npResolver.getJCRName(NameConstants.NT_UNSTRUCTURED);
+        } catch (NamespaceException e) {
+            // should never get here...
+            String msg = "internal error: failed to resolve namespace mappings";
+            throw new RepositoryException(msg, e);
+        }
+    }
+
+    public void onWalkBegin(Node root) throws RepositoryException {
+        try {
+            contentHandler.startDocument();
+            startNamespaceDeclarations();
+        } catch (SAXException e) {
+            throw new RepositoryException(e);
+        }
+    }
+
+    public void onWalkEnd(Node root) throws RepositoryException {
+        try {
+            // clear namespace declarations and end document
+            endNamespaceDeclarations();
+            contentHandler.endDocument();
+        } catch (SAXException e) {
+            throw new RepositoryException(e);
+        }
+    }
+
+    /**
+     * Starts namespace declarations
+     *
+     * @throws RepositoryException if a repository error occurs
+     * @throws SAXException if the underlying content handler throws a sax exception
+     */
+    protected void startNamespaceDeclarations()
+            throws RepositoryException, SAXException {
+        // start namespace declarations
+        for (String prefix: aggregate.getNamespacePrefixes()) {
+            if (Name.NS_XML_PREFIX.equals(prefix)) {
+                // skip 'xml' prefix as this would be an illegal namespace declaration
+                continue;
+            }
+            contentHandler.startPrefixMapping(prefix, aggregate.getNamespaceURI(prefix));
+        }
+    }
+
+    /**
+     * Ends namespace declarations
+     *
+     * @throws RepositoryException if a repository error occurs
+     * @throws SAXException if the underlying content handler throws a sax exception
+      */
+    protected void endNamespaceDeclarations()
+            throws RepositoryException, SAXException {
+        // end namespace declarations
+        for (String prefix: aggregate.getNamespacePrefixes()) {
+            if (Name.NS_XML_PREFIX.equals(prefix)) {
+                // skip 'xml' prefix as this would be an illegal namespace declaration
+                continue;
+            }
+            contentHandler.endPrefixMapping(prefix);
+        }
+    }
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AggregateWalkListener.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AggregateWalkListener.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AggregateWalkListener.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/AggregateWalkListener.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,99 @@
+/*
+ * 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 javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.fs.impl.AggregateImpl;
+
+/**
+ * Receives walk event from the {@link AggregateImpl#walk(AggregateWalkListener)}.
+ *
+ */
+public interface AggregateWalkListener {
+
+    /**
+     * Invoked when a tree walk begins
+     *
+     * @param root the root node of the tree walk
+     * @throws RepositoryException if a repository error occurs.
+     */
+    public void onWalkBegin(Node root) throws RepositoryException;
+
+    /**
+     * Invoked when a node is traversed
+     *
+     * @param node     the node that is traversed
+     * @param included indicates if the node is included in the aggregate. If
+     *                 <code>false</code> it's just a traversed intermediate node.
+     * @param depth    the relative depth of the node in respect to the tree root node.
+     * @throws RepositoryException if a repository error occurs.
+     */
+    public void onNodeBegin(Node node, boolean included, int depth)
+            throws RepositoryException;
+
+    /**
+     * Invoked when a property is included in the aggregate.
+     *
+     * @param prop  the property
+     * @param depth the depth relative to the tree root
+     * @throws RepositoryException if a repository error occurs.
+     */
+    public void onProperty(Property prop, int depth) throws RepositoryException;
+
+    /**
+     * Invoked when the child nodes are to be traversed.
+     *
+     * @param node  the node of which the children are to be traversed.
+     * @param depth the depth of that node
+     * @throws RepositoryException if a repository error occurs.
+     */
+    public void onChildren(Node node, int depth) throws RepositoryException;
+
+    /**
+     * Invoked when a node finished traversing
+     *
+     * @param node     the node that is finished traversing
+     * @param included indicates if the node is included in the aggregate. If
+     *                 <code>false</code> it's just a traversed intermediate node.
+     * @param depth    the relative depth of the node in respect to the tree root node.
+     * @throws RepositoryException if a repository error occurs.
+     */
+    public void onNodeEnd(Node node, boolean included, int depth)
+            throws RepositoryException;
+
+    /**
+     * Invoked when a traversed node is ignored due to a filter.
+     *
+     * @param node the node that is ignored
+     * @param depth the relative depth of the node in respect to the tree root node.
+     * @throws RepositoryException if a repository error occurs
+     */
+    public void onNodeIgnored(Node node, int depth) throws RepositoryException;
+
+    /**
+     * Invoked when a tree walk begins
+     *
+     * @param root the root node of the tree walk
+     * @throws RepositoryException if a repository error occurs.
+     */
+    public void onWalkEnd(Node root) throws RepositoryException;
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/CNDImporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/CNDImporter.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/CNDImporter.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/CNDImporter.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,482 @@
+/*
+ * 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.Reader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.jcr.Node;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+
+import org.apache.jackrabbit.util.ISO9075;
+import org.apache.jackrabbit.vault.fs.impl.io.legacycnd.Lexer;
+import org.apache.jackrabbit.vault.fs.impl.io.legacycnd.ParseException;
+import org.apache.jackrabbit.vault.util.Constants;
+import org.apache.jackrabbit.vault.util.JcrConstants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * <code>CNDImporter</code>...
+ *
+ */
+public class CNDImporter {
+
+    /**
+     * default logger
+     */
+    private static final Logger log = LoggerFactory.getLogger(CNDImporter.class);
+
+    /**
+     * the underlying lexer
+     */
+    private Lexer lexer;
+
+    /**
+     * the current token
+     */
+    private String currentToken;
+
+    /**
+     * old namespace mappings that need to be reverted
+     */
+    private Map<String, String> oldMappings = new HashMap<String, String>();
+
+    public ImportInfoImpl doImport(Node parent, String name, Reader r, String systemId)
+            throws RepositoryException {
+        try {
+            lexer = new Lexer(r, systemId);
+            nextToken();
+            ImportInfoImpl info = parse(parent, name);
+            // reset name spaces
+            for (String prefix: oldMappings.keySet()) {
+                String uri = oldMappings.get(prefix);
+                try {
+                    parent.getSession().setNamespacePrefix(prefix, uri);
+                } catch (RepositoryException e) {
+                    // ignore
+                }
+            }
+            return info;
+        } catch (ParseException e) {
+            log.error("Error while parsing.", e);
+            return null;
+        }
+    }
+
+    private ImportInfoImpl parse(Node parent, String name) throws ParseException, RepositoryException {
+        while (!currentTokenEquals(Lexer.EOF)) {
+            if (!doNameSpace(parent.getSession())) {
+                break;
+            }
+        }
+        ImportInfoImpl info = new ImportInfoImpl();
+        while (!currentTokenEquals(Lexer.EOF)) {
+            String ntName = doNodeTypeName();
+            if (name == null) {
+                name = ntName;
+            }
+            if (parent.hasNode(name)) {
+                parent.getNode(name).remove();
+            }
+            Node node;
+            if (parent.hasNode(name)) {
+                parent.getNode(name).remove();
+                node = parent.addNode(name, JcrConstants.NT_NODETYPE);
+                info.onReplaced(node.getPath());
+            } else {
+                node = parent.addNode(name, JcrConstants.NT_NODETYPE);
+                info.onCreated(node.getPath());
+            }
+            node.setProperty(JcrConstants.JCR_NODETYPENAME, name);
+            
+            // init mandatory props
+            node.setProperty(JcrConstants.JCR_HASORDERABLECHILDNODES, false);
+            node.setProperty(JcrConstants.JCR_ISMIXIN, false);
+            doSuperTypes(node);
+            doOptions(node);
+            doItemDefs(node);
+            name = null;
+        }
+        return info;
+    }
+
+    private boolean doNameSpace(Session session) throws ParseException {
+        if (!currentTokenEquals('<')) {
+            return false;
+        }
+        nextToken();
+        String prefix = currentToken;
+        nextToken();
+        if (!currentTokenEquals('=')) {
+            lexer.fail("Missing = in namespace decl.");
+        }
+        nextToken();
+        String uri = currentToken;
+        nextToken();
+        if (!currentTokenEquals('>')) {
+            lexer.fail("Missing > in namespace decl.");
+        }
+        String oldPrefix = null;
+        try {
+            oldPrefix = session.getNamespacePrefix(uri);
+        } catch (RepositoryException e) {
+            // assume does not exist yet, so register
+        }
+        try {
+            if (oldPrefix == null) {
+                session.getWorkspace().getNamespaceRegistry().registerNamespace(prefix, uri);
+            } else if (!oldPrefix.equals(prefix)) {
+                // remap
+                oldMappings.put(oldPrefix, uri);
+                session.setNamespacePrefix(prefix, uri);
+            }
+        } catch (RepositoryException e) {
+            lexer.fail("unable to remap namespace", e);
+        }
+        nextToken();
+        return true;
+    }
+
+    private String doNodeTypeName() throws ParseException {
+        String name;
+        if (!currentTokenEquals(Lexer.BEGIN_NODE_TYPE_NAME)) {
+            lexer.fail("Missing '" + Lexer.BEGIN_NODE_TYPE_NAME + "' delimiter for beginning of node type name");
+        }
+        nextToken();
+        name = ISO9075.decode(currentToken);
+
+        nextToken();
+        if (!currentTokenEquals(Lexer.END_NODE_TYPE_NAME)) {
+            lexer.fail("Missing '" + Lexer.END_NODE_TYPE_NAME + "' delimiter for end of node type name, found " + currentToken);
+        }
+        nextToken();
+
+        return name;
+    }
+
+    private static void setProperty(Node node, String name, List<String> values)
+            throws RepositoryException {
+        node.setProperty(name, values.toArray(new String[values.size()]));
+    }
+
+    private static void setProperty(Node node, String name, List<String> values, int type)
+            throws RepositoryException {
+        node.setProperty(name, values.toArray(new String[values.size()]), type);
+    }
+
+    private void doSuperTypes(Node ntd) throws ParseException, RepositoryException {
+        // a set would be nicer here, in case someone defines a super type twice.
+        // but due to issue [JCR-333], the resulting node type definition is
+        // not symmetric anymore and the tests will fail.
+        List<String> supertypes = new ArrayList<String>();
+        if (!currentTokenEquals(Lexer.EXTENDS)) {
+            return;
+        }
+        do {
+            nextToken();
+            supertypes.add(ISO9075.decode(currentToken));
+            nextToken();
+        } while (currentTokenEquals(Lexer.LIST_DELIMITER));
+        setProperty(ntd, JcrConstants.JCR_SUPERTYPES, supertypes);
+    }
+
+    private void doOptions(Node ntd) throws ParseException, RepositoryException {
+        if (currentTokenEquals(Lexer.ORDERABLE)) {
+            ntd.setProperty(JcrConstants.JCR_HASORDERABLECHILDNODES, true);
+            nextToken();
+            if (currentTokenEquals(Lexer.MIXIN)) {
+                ntd.setProperty(JcrConstants.JCR_ISMIXIN, true);
+                nextToken();
+            }
+        } else if (currentTokenEquals(Lexer.MIXIN)) {
+            ntd.setProperty(JcrConstants.JCR_ISMIXIN, true);
+            nextToken();
+            if (currentTokenEquals(Lexer.ORDERABLE)) {
+                ntd.setProperty(JcrConstants.JCR_HASORDERABLECHILDNODES, true);
+                nextToken();
+            }
+        }
+    }
+
+    private void doItemDefs(Node ntd) throws ParseException, RepositoryException {
+        while (currentTokenEquals(Lexer.PROPERTY_DEFINITION) || currentTokenEquals(Lexer.CHILD_NODE_DEFINITION)) {
+            if (currentTokenEquals(Lexer.PROPERTY_DEFINITION)) {
+                Node pdi = ntd.addNode(JcrConstants.JCR_PROPERTYDEFINITION);
+                nextToken();
+                doPropertyDefinition(pdi);
+
+            } else if (currentTokenEquals(Lexer.CHILD_NODE_DEFINITION)) {
+                Node ndi = ntd.addNode(JcrConstants.JCR_CHILDNODEDEFINITION);
+
+                nextToken();
+                doChildNodeDefinition(ndi);
+            }
+        }
+    }
+
+    private void doPropertyDefinition(Node pdi) throws ParseException, RepositoryException {
+        String name = ISO9075.decode(currentToken);
+        if (!name.equals("") && !name.equals("*")) {
+            pdi.setProperty(JcrConstants.JCR_NAME, name);
+        }
+        // init mandatory props
+        pdi.setProperty(JcrConstants.JCR_AUTOCREATED, false);
+        pdi.setProperty(JcrConstants.JCR_MANDATORY, false);
+        pdi.setProperty(JcrConstants.JCR_MULTIPLE, false);
+        pdi.setProperty(JcrConstants.JCR_ONPARENTVERSION, "COPY");
+        pdi.setProperty(JcrConstants.JCR_PROTECTED, false);
+        pdi.setProperty(JcrConstants.JCR_REQUIREDTYPE, "UNDEFINED");
+
+        nextToken();
+        int type = doPropertyType(pdi);
+        doPropertyDefaultValue(pdi, type);
+        doPropertyAttributes(pdi);
+        doPropertyValueConstraints(pdi);
+    }
+
+    private int doPropertyType(Node pdi) throws ParseException, RepositoryException {
+        if (!currentTokenEquals(Lexer.BEGIN_TYPE)) {
+            return PropertyType.UNDEFINED;
+        }
+        nextToken();
+        int type = PropertyType.UNDEFINED;
+        if (currentTokenEquals(Lexer.STRING)) {
+            type = PropertyType.STRING;
+        } else if (currentTokenEquals(Lexer.BINARY)) {
+            type = PropertyType.BINARY;
+        } else if (currentTokenEquals(Lexer.LONG)) {
+            type = PropertyType.LONG;
+        } else if (currentTokenEquals(Lexer.DOUBLE)) {
+            type = PropertyType.DOUBLE;
+        } else if (currentTokenEquals(Lexer.BOOLEAN)) {
+            type = PropertyType.BOOLEAN;
+        } else if (currentTokenEquals(Lexer.DATE)) {
+            type = PropertyType.DATE;
+        } else if (currentTokenEquals(Lexer.NAME)) {
+            type = PropertyType.NAME;
+        } else if (currentTokenEquals(Lexer.PATH)) {
+            type = PropertyType.PATH;
+        } else if (currentTokenEquals(Lexer.REFERENCE)) {
+            type = PropertyType.REFERENCE;
+        } else if (currentTokenEquals(Lexer.UNDEFINED)) {
+            type = PropertyType.UNDEFINED;
+        } else {
+            lexer.fail("Unknown property type '" + currentToken + "' specified");
+        }
+        pdi.setProperty(JcrConstants.JCR_REQUIREDTYPE, PropertyType.nameFromValue(type).toUpperCase());
+        nextToken();
+        if (!currentTokenEquals(Lexer.END_TYPE)) {
+            lexer.fail("Missing '" + Lexer.END_TYPE + "' delimiter for end of property type");
+        }
+        nextToken();
+        return type;
+    }
+
+    private void doPropertyAttributes(Node pdi) throws ParseException, RepositoryException {
+        while (currentTokenEquals(Lexer.ATTRIBUTE)) {
+            if (currentTokenEquals(Lexer.PRIMARY)) {
+                Value name = pdi.getProperty(JcrConstants.JCR_NAME).getValue();
+                if (pdi.getParent().hasProperty(JcrConstants.JCR_PRIMARYITEMNAME)) {
+                    lexer.fail("More than one primary item specified in node type '" + name.getString() + "'");
+                }
+                pdi.getParent().setProperty(JcrConstants.JCR_PRIMARYITEMNAME, name);
+            } else if (currentTokenEquals(Lexer.AUTOCREATED)) {
+                pdi.setProperty(JcrConstants.JCR_AUTOCREATED, true);
+            } else if (currentTokenEquals(Lexer.MANDATORY)) {
+                pdi.setProperty(JcrConstants.JCR_MANDATORY, true);
+            } else if (currentTokenEquals(Lexer.PROTECTED)) {
+                pdi.setProperty(JcrConstants.JCR_PROTECTED, true);
+            } else if (currentTokenEquals(Lexer.MULTIPLE)) {
+                pdi.setProperty(JcrConstants.JCR_MULTIPLE, true);
+            } else if (currentTokenEquals(Lexer.COPY)) {
+                pdi.setProperty(JcrConstants.JCR_ONPARENTVERSION, "COPY");
+            } else if (currentTokenEquals(Lexer.VERSION)) {
+                pdi.setProperty(JcrConstants.JCR_ONPARENTVERSION, "VERSION");
+            } else if (currentTokenEquals(Lexer.INITIALIZE)) {
+                pdi.setProperty(JcrConstants.JCR_ONPARENTVERSION, "INITIALIZE");
+            } else if (currentTokenEquals(Lexer.COMPUTE)) {
+                pdi.setProperty(JcrConstants.JCR_ONPARENTVERSION, "COMPUTE");
+            } else if (currentTokenEquals(Lexer.IGNORE)) {
+                pdi.setProperty(JcrConstants.JCR_ONPARENTVERSION, "IGNORE");
+            } else if (currentTokenEquals(Lexer.ABORT)) {
+                pdi.setProperty(JcrConstants.JCR_ONPARENTVERSION, "ABORT");
+            }
+            nextToken();
+        }
+    }
+
+    private void doPropertyDefaultValue(Node pdi, int type)
+            throws ParseException, RepositoryException {
+        if (!currentTokenEquals(Lexer.DEFAULT)) {
+            return;
+        }
+        List<String> defaultValues = new ArrayList<String>();
+        do {
+            nextToken();
+            defaultValues.add(currentToken);
+            nextToken();
+        } while (currentTokenEquals(Lexer.LIST_DELIMITER));
+        // use required type
+        setProperty(pdi, JcrConstants.JCR_DEFAULTVALUES, defaultValues, type);
+    }
+
+    private void doPropertyValueConstraints(Node pdi) throws ParseException, RepositoryException {
+        if (!currentTokenEquals(Lexer.CONSTRAINT)) {
+            return;
+        }
+        List<String> constraints = new ArrayList<String>();
+        do {
+            nextToken();
+            constraints.add(currentToken);
+            nextToken();
+        } while (currentTokenEquals(Lexer.LIST_DELIMITER));
+        setProperty(pdi, JcrConstants.JCR_VALUECONSTRAINTS, constraints);
+    }
+
+    private void doChildNodeDefinition(Node ndi) throws ParseException, RepositoryException {
+        String name = ISO9075.decode(currentToken);
+        if (!name.equals("") && !name.equals("*")) {
+            ndi.setProperty(JcrConstants.JCR_NAME, name);
+        }
+        // init mandatory props
+        ndi.setProperty(JcrConstants.JCR_AUTOCREATED, false);
+        ndi.setProperty(JcrConstants.JCR_MANDATORY, false);
+        ndi.setProperty(JcrConstants.JCR_SAMENAMESIBLINGS, false);
+        ndi.setProperty(JcrConstants.JCR_ONPARENTVERSION, "COPY");
+        ndi.setProperty(JcrConstants.JCR_PROTECTED, false);
+        ndi.setProperty(JcrConstants.JCR_REQUIREDPRIMARYTYPES, Constants.EMPTY_STRING_ARRAY);
+
+        nextToken();
+        doChildNodeRequiredTypes(ndi);
+        doChildNodeDefaultType(ndi);
+        doChildNodeAttributes(ndi);
+    }
+
+    private void doChildNodeRequiredTypes(Node ndi) throws ParseException, RepositoryException {
+        if (!currentTokenEquals(Lexer.BEGIN_TYPE)) {
+            return;
+        }
+        List<String> types = new ArrayList<String>();
+        do {
+            nextToken();
+            types.add(ISO9075.decode(currentToken));
+            nextToken();
+        } while (currentTokenEquals(Lexer.LIST_DELIMITER));
+        setProperty(ndi, JcrConstants.JCR_REQUIREDPRIMARYTYPES, types);
+        nextToken();
+    }
+
+    private void doChildNodeDefaultType(Node ndi) throws ParseException, RepositoryException {
+        if (!currentTokenEquals(Lexer.DEFAULT)) {
+            return;
+        }
+        nextToken();
+        ndi.setProperty(JcrConstants.JCR_DEFAULTPRIMARYTYPE, ISO9075.decode(currentToken));
+        nextToken();
+    }
+
+    private void doChildNodeAttributes(Node ndi) throws ParseException, RepositoryException {
+        while (currentTokenEquals(Lexer.ATTRIBUTE)) {
+            if (currentTokenEquals(Lexer.PRIMARY)) {
+                Value name = ndi.getProperty(JcrConstants.JCR_NAME).getValue();
+                if (ndi.getParent().hasProperty(JcrConstants.JCR_PRIMARYITEMNAME)) {
+                    lexer.fail("More than one primary item specified in node type '" + name.getString() + "'");
+                }
+                ndi.getParent().setProperty(JcrConstants.JCR_PRIMARYITEMNAME, name);
+            } else if (currentTokenEquals(Lexer.AUTOCREATED)) {
+                ndi.setProperty(JcrConstants.JCR_AUTOCREATED, true);
+            } else if (currentTokenEquals(Lexer.MANDATORY)) {
+                ndi.setProperty(JcrConstants.JCR_MANDATORY, true);
+            } else if (currentTokenEquals(Lexer.PROTECTED)) {
+                ndi.setProperty(JcrConstants.JCR_PROTECTED, true);
+            } else if (currentTokenEquals(Lexer.MULTIPLE)) {
+                ndi.setProperty(JcrConstants.JCR_SAMENAMESIBLINGS, true);
+            } else if (currentTokenEquals(Lexer.COPY)) {
+                ndi.setProperty(JcrConstants.JCR_ONPARENTVERSION, "COPY");
+            } else if (currentTokenEquals(Lexer.VERSION)) {
+                ndi.setProperty(JcrConstants.JCR_ONPARENTVERSION, "VERSION");
+            } else if (currentTokenEquals(Lexer.INITIALIZE)) {
+                ndi.setProperty(JcrConstants.JCR_ONPARENTVERSION, "INITIALIZE");
+            } else if (currentTokenEquals(Lexer.COMPUTE)) {
+                ndi.setProperty(JcrConstants.JCR_ONPARENTVERSION, "COMPUTE");
+            } else if (currentTokenEquals(Lexer.IGNORE)) {
+                ndi.setProperty(JcrConstants.JCR_ONPARENTVERSION, "IGNORE");
+            } else if (currentTokenEquals(Lexer.ABORT)) {
+                ndi.setProperty(JcrConstants.JCR_ONPARENTVERSION, "ABORT");
+            }
+            nextToken();
+        }
+    }
+
+    /**
+     * Gets the next token from the underlying lexer.
+     *
+     * @see Lexer#getNextToken()
+     * @throws ParseException if the lexer fails to get the next token.
+     */
+    private void nextToken() throws ParseException {
+        currentToken = lexer.getNextToken();
+    }
+
+    /**
+     * Checks if the {@link #currentToken} is semantically equal to the given
+     * argument.
+     *
+     * @param s the tokens to compare with
+     * @return <code>true</code> if equals; <code>false</code> otherwise.
+     */
+    private boolean currentTokenEquals(String[] s) {
+        for (String value : s) {
+            if (currentToken.equals(value)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Checks if the {@link #currentToken} is semantically equal to the given
+     * argument.
+     *
+     * @param c the tokens to compare with
+     * @return <code>true</code> if equals; <code>false</code> otherwise.
+     */
+    private boolean currentTokenEquals(char c) {
+        return currentToken.length() == 1 && currentToken.charAt(0) == c;
+    }
+
+    /**
+     * Checks if the {@link #currentToken} is semantically equal to the given
+     * argument.
+     *
+     * @param s the tokens to compare with
+     * @return <code>true</code> if equals; <code>false</code> otherwise.
+     */
+    private boolean currentTokenEquals(String s) {
+        return currentToken.equals(s);
+    }
+
+    
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/CNDSerializer.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/CNDSerializer.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/CNDSerializer.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/CNDSerializer.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,279 @@
+/*
+ * 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.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+import org.apache.jackrabbit.vault.fs.api.Aggregate;
+import org.apache.jackrabbit.vault.fs.api.SerializationType;
+import org.apache.jackrabbit.vault.fs.io.Serializer;
+import org.apache.jackrabbit.vault.util.JcrConstants;
+
+/**
+ * <code>DocViewSerializer</code>...
+*
+*/
+public class CNDSerializer implements Serializer {
+
+    /**
+     * the indention string
+     */
+    private static final String INDENT = "  ";
+
+    /**
+     * the export context
+     */
+    private final Aggregate aggregate;
+
+    /**
+     * Creates a new doc view serializer
+     * @param aggregate the export context
+     */
+    public CNDSerializer(Aggregate aggregate) {
+        this.aggregate = aggregate;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void writeContent(OutputStream out) throws IOException, RepositoryException {
+        Writer w = new OutputStreamWriter(out, "utf-8");
+        for (String prefix: aggregate.getNamespacePrefixes()) {
+            w.write("<'");
+            w.write(prefix);
+            w.write("'='");
+            w.write(escape(aggregate.getNamespaceURI(prefix)));
+            w.write("'>\n");
+        }
+        w.write("\n");
+        
+        writeNodeTypeDef(w, aggregate.getNode());
+        w.close();
+        out.flush();
+    }
+
+    private void writeNodeTypeDef(Writer out, Node node) throws IOException, RepositoryException {
+        writeName(out, node);
+        writeSupertypes(out, node);
+        writeOptions(out, node);
+        writeDefs(out, node);
+        out.write("\n");
+    }
+
+    private void writeName(Writer out, Node node)
+            throws IOException, RepositoryException {
+        out.write("[");
+        out.write(node.getProperty(JcrConstants.JCR_NODETYPENAME).getString());
+        out.write("]");
+    }
+
+    private void writeSupertypes(Writer out, Node node) throws IOException, RepositoryException {
+        Value[] types = node.getProperty(JcrConstants.JCR_SUPERTYPES).getValues();
+        String delim = " > ";
+        for (Value s: types) {
+            out.write(delim);
+            out.write(s.getString());
+            delim = ", ";
+        }
+    }
+
+    private void writeOptions(Writer out, Node node) throws IOException, RepositoryException {
+        String delim = "\n " + INDENT;
+        if (node.getProperty(JcrConstants.JCR_HASORDERABLECHILDNODES).getBoolean()) {
+            out.write(delim);
+            out.write("orderable");
+            delim = " ";
+        }
+        if (node.getProperty(JcrConstants.JCR_ISMIXIN).getBoolean()) {
+            out.write(delim);
+            out.write("mixin");
+        }
+    }
+
+    private void writeDefs(Writer out, Node node) throws IOException, RepositoryException {
+        NodeIterator iter = node.getNodes();
+        String primary = null;
+        if (node.hasProperty(JcrConstants.JCR_PRIMARYITEMNAME)) {
+            primary = node.getProperty(JcrConstants.JCR_PRIMARYITEMNAME).getString();
+        }
+        while (iter.hasNext()) {
+            Node child = iter.nextNode();
+            if (child.getPrimaryNodeType().getName().equals(JcrConstants.NT_PROPERTYDEFINITION)) {
+                writePropDef(out, child, primary);
+            }
+        }
+        iter = node.getNodes();
+        while (iter.hasNext()) {
+            Node child = iter.nextNode();
+            if (child.getPrimaryNodeType().getName().equals(JcrConstants.NT_CHILDNODEDEFINITION)) {
+                writeNodeDef(out, child, primary);
+            }
+        }
+    }
+
+    private void writePropDef(Writer out, Node node, String primary) throws IOException, RepositoryException {
+        out.write("\n" + INDENT + "- ");
+        String name = "*";
+        if (node.hasProperty(JcrConstants.JCR_NAME)) {
+            name = node.getProperty(JcrConstants.JCR_NAME).getString();
+        }
+        out.write(name);
+        out.write(" (");
+        out.write(node.getProperty(JcrConstants.JCR_REQUIREDTYPE).getString().toLowerCase());
+        out.write(")");
+        if (node.hasProperty(JcrConstants.JCR_DEFAULTVALUES)) {
+            writeDefaultValues(out, node.getProperty(JcrConstants.JCR_DEFAULTVALUES).getValues());
+        }
+        if (primary != null && primary.equals(name)) {
+            out.write(" primary");
+        }
+        if (node.getProperty(JcrConstants.JCR_MANDATORY).getBoolean()) {
+            out.write(" mandatory");
+        }
+        if (node.getProperty(JcrConstants.JCR_AUTOCREATED).getBoolean()) {
+            out.write(" autocreated");
+        }
+        if (node.getProperty(JcrConstants.JCR_PROTECTED).getBoolean()) {
+            out.write(" protected");
+        }
+        if (node.getProperty(JcrConstants.JCR_MULTIPLE).getBoolean()) {
+            out.write(" multiple");
+        }
+        String opv = node.getProperty(JcrConstants.JCR_ONPARENTVERSION).getString().toLowerCase();
+        if (!opv.equals("copy")) {
+            out.write(" ");
+            out.write(opv);
+        }
+        if (node.hasProperty(JcrConstants.JCR_VALUECONSTRAINTS)) {
+            writeValueConstraints(out, node.getProperty(JcrConstants.JCR_VALUECONSTRAINTS).getValues());
+        }
+    }
+
+    private void writeDefaultValues(Writer out, Value[] dva) throws IOException, RepositoryException {
+        if (dva != null && dva.length > 0) {
+            String delim = " = '";
+            for (Value value : dva) {
+                out.write(delim);
+                out.write(escape(value.getString()));
+                out.write("'");
+                delim = ", '";
+            }
+        }
+    }
+
+    private void writeValueConstraints(Writer out, Value[] vca) throws IOException, RepositoryException {
+        String delim = "\n" + INDENT  + "  < ";
+        for (Value v: vca) {
+            out.write(delim);
+            out.write("'");
+            out.write(escape(v.getString()));
+            out.write("'");
+            delim = ", ";
+        }
+    }
+
+    private void writeNodeDef(Writer out, Node node, String primary) throws IOException, RepositoryException {
+        out.write("\n" + INDENT + "+ ");
+        String name = "*";
+        if (node.hasProperty(JcrConstants.JCR_NAME)) {
+            name = node.getProperty(JcrConstants.JCR_NAME).getString();
+        }
+        out.write(name);
+
+        writeRequiredTypes(out, node.getProperty(JcrConstants.JCR_REQUIREDPRIMARYTYPES).getValues());
+        if (node.hasProperty(JcrConstants.JCR_DEFAULTPRIMARYTYPE)) {
+            writeDefaultType(out, node.getProperty(JcrConstants.JCR_DEFAULTPRIMARYTYPE).getString());
+        }
+        if (primary != null && primary.equals(name)) {
+            out.write(" primary");
+        }
+        if (node.getProperty(JcrConstants.JCR_MANDATORY).getBoolean()) {
+            out.write(" mandatory");
+        }
+        if (node.getProperty(JcrConstants.JCR_AUTOCREATED).getBoolean()) {
+            out.write(" autocreated");
+        }
+        if (node.getProperty(JcrConstants.JCR_PROTECTED).getBoolean()) {
+            out.write(" protected");
+        }
+        if (node.getProperty(JcrConstants.JCR_SAMENAMESIBLINGS).getBoolean()) {
+            out.write(" multiple");
+        }
+        String opv = node.getProperty(JcrConstants.JCR_ONPARENTVERSION).getString().toLowerCase();
+        if (!opv.equals("copy")) {
+            out.write(" ");
+            out.write(opv);
+        }
+    }
+
+    private void writeRequiredTypes(Writer out, Value[] reqTypes) throws IOException, RepositoryException {
+        if (reqTypes.length > 0) {
+            String delim = " (";
+            for (Value value : reqTypes) {
+                out.write(delim);
+                out.write(value.getString());
+                delim = ", ";
+            }
+            out.write(")");
+        }
+    }
+
+    private void writeDefaultType(Writer out, String defType) throws IOException {
+        if (!defType.equals("*")) {
+            out.write(" = ");
+            out.write(defType);
+        }
+    }
+
+    /**
+     * escape
+     * @param s the string to escape
+     * @return the escaped string
+     */
+    private String escape(String s) {
+        StringBuffer sb = new StringBuffer(s);
+        for (int i = 0; i < sb.length(); i++) {
+            if (sb.charAt(i) == '\\') {
+                sb.insert(i, '\\');
+                i++;
+            } else if (sb.charAt(i) == '\'') {
+                sb.insert(i, '\'');
+                i++;
+            }
+        }
+        return sb.toString();
+    }
+
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return {@link SerializationType#CND}
+     */
+    public SerializationType getType() {
+        return SerializationType.CND;
+    }
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/ContentAnalyzer.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/ContentAnalyzer.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/ContentAnalyzer.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/ContentAnalyzer.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,145 @@
+/*
+ * 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.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+
+/**
+ * The content analyzer traverses the tree and checks the following:
+ * - retrieves all namespaces used in this tree
+ * - retrieve all binary properties in this tree
+ *
+ */
+public class ContentAnalyzer implements AggregateWalkListener {
+
+    private final HashMap<String, String> namespaces = new HashMap<String, String>();
+
+    private final List<Property> binaries = new LinkedList<Property>();
+
+    private final List<String> ignoredPaths = new LinkedList<String>();
+
+    private boolean isEmpty = true;
+
+    public String[] getNamespaceURIs() {
+        return namespaces.keySet().toArray(new String[namespaces.keySet().size()]);
+    }
+
+    public String getNamespacePrefix(String uri) {
+        return namespaces.get(uri);
+    }
+
+    public Collection<Property> getBinaries() {
+        return binaries;
+    }
+
+    public Collection<String> getIgnoredPaths() {
+        return ignoredPaths;
+    }
+
+    public boolean isEmpty() {
+        return isEmpty;
+    }
+
+    public void onNodeBegin(Node node, boolean included, int depth)
+            throws RepositoryException {
+        if (included) {
+            isEmpty = false;
+        }
+        addNamespace(node.getSession(), node.getName());
+    }
+
+    public void onNodeEnd(Node node, boolean included, int depth)
+            throws RepositoryException {
+        // ignore
+    }
+
+    public void onProperty(Property prop, int depth) throws RepositoryException {
+        isEmpty = false;
+        addNamespace(prop.getSession(), prop.getName());
+        switch (prop.getType()) {
+            case PropertyType.BINARY:
+                binaries.add(prop);
+                break;
+            case PropertyType.NAME:
+                if (prop.getDefinition().isMultiple()) {
+                    Value[] values = prop.getValues();
+                    for (Value value: values) {
+                        addNamespace(prop.getSession(), value.getString());
+                    }
+                } else {
+                    addNamespace(prop.getSession(), prop.getValue().getString());
+                }
+                break;
+            case PropertyType.PATH:
+                if (prop.getDefinition().isMultiple()) {
+                    Value[] values = prop.getValues();
+                    for (Value value: values) {
+                        addNamespacePath(prop.getSession(), value.getString());
+                    }
+                } else {
+                    addNamespacePath(prop.getSession(), prop.getValue().getString());
+                }
+                break;
+
+        }
+    }
+
+    public void onNodeIgnored(Node node, int depth) throws RepositoryException {
+        ignoredPaths.add(node.getPath());
+    }
+
+    public void onWalkBegin(Node root) throws RepositoryException {
+        // ignore
+    }
+
+    public void onChildren(Node node, int depth) throws RepositoryException {
+        // ignore
+    }
+
+    public void onWalkEnd(Node root) throws RepositoryException {
+        // ignore
+    }
+
+    private void addNamespace(Session s, String name) throws RepositoryException {
+        int idx = name.indexOf(':');
+        if (idx > 0) {
+            String prefix = name.substring(0, idx);
+            String uri = s.getNamespaceURI(prefix);
+            namespaces.put(uri, prefix);
+        }
+    }
+
+    private void addNamespacePath(Session s, String path) throws RepositoryException {
+        String[] names = path.split("/");
+        for (String name: names) {
+            addNamespace(s, name);
+        }
+    }
+
+
+}
\ No newline at end of file



Mime
View raw message