You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by tr...@apache.org on 2013/08/10 07:53:54 UTC

svn commit: r1512568 [15/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...

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FileArtifactHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FileArtifactHandler.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FileArtifactHandler.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FileArtifactHandler.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,370 @@
+/*
+ * 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.InputStream;
+import java.util.Calendar;
+import java.util.Collection;
+
+import javax.jcr.ImportUUIDBehavior;
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.jackrabbit.vault.fs.api.Artifact;
+import org.apache.jackrabbit.vault.fs.api.ArtifactType;
+import org.apache.jackrabbit.vault.fs.api.ImportArtifact;
+import org.apache.jackrabbit.vault.fs.api.ImportInfo;
+import org.apache.jackrabbit.vault.fs.api.ImportMode;
+import org.apache.jackrabbit.vault.fs.api.ItemFilterSet;
+import org.apache.jackrabbit.vault.fs.api.SerializationType;
+import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
+import org.apache.jackrabbit.vault.fs.impl.ArtifactSetImpl;
+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;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * Creates nt:file structures from  {@link SerializationType#XML_GENERIC} or
+ * {@link SerializationType#GENERIC} artifacts.
+ *
+ */
+public class FileArtifactHandler extends AbstractArtifactHandler  {
+
+    /**
+     * The node type for xml deserialization
+     */
+    private String xmlNodeType = "nt:xmlDocument";
+
+    /**
+     * Indicates if xml should be deserialized
+     */
+    private boolean explodeXml = false;
+
+    /**
+     * Returns the node type that is used for generic xml deserialization.
+     * This has only an effect if {@link #isExplodeXml()} is <code>true</code>.
+     *
+     * @return the xml node type.
+     */
+    public String getXmlNodeType() {
+        return xmlNodeType;
+    }
+
+    /**
+     * Sets the node type that is used for generic xml deserialization.
+     * This has only an effect if {@link #isExplodeXml()} is <code>true</code>.
+     * <p/>
+     * Default is <code>nt:xmlDocument</code>
+     *
+     * @param xmlNodeType the xml node type name
+     */
+    public void setXmlNodeType(String xmlNodeType) {
+        this.xmlNodeType = xmlNodeType;
+    }
+
+    /**
+     * Checks if this handler explodes the xml for a generic xml deserialization.
+     *
+     * @return <code>true</code> if this handler explodes the xml
+     */
+    public boolean isExplodeXml() {
+        return explodeXml;
+    }
+
+    /**
+     * Sets whether this handler should explode the xml of a generic xml
+     * serialization.
+     * <p/>
+     * Default is <code>false</code>.
+     *
+     * @param explodeXml <code>true</code> if to explode the xml
+     */
+    public void setExplodeXml(boolean explodeXml) {
+        this.explodeXml = explodeXml;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Handles generic artifact sets
+     */
+    public ImportInfoImpl accept(WorkspaceFilter wspFilter, Node parent,
+                                String name, ArtifactSetImpl artifacts)
+            throws RepositoryException, IOException {
+        // check if any file artifacts was removed
+        ImportInfoImpl info = null;
+        Collection<Artifact> removed = artifacts.removed();
+        for (Artifact a: removed) {
+            if (a.getType() == ArtifactType.FILE) {
+                if (parent.hasNode(a.getRelativePath())) {
+                    Node file = parent.getNode(a.getRelativePath());
+                    String path = file.getPath();
+                    // check wsp filter, only remove if 'REPLACE'
+                    if (info == null) {
+                        info = new ImportInfoImpl();
+                    }
+                    if (wspFilter.getImportMode(path) == ImportMode.REPLACE) {
+                        info.onDeleted(path);
+                        file.remove();
+                    } else {
+                        info.onNop(path);
+                    }
+                }
+            }
+        }
+
+        // need at least a file or binary artifact
+        if (artifacts.size(ArtifactType.FILE) > 0 || artifacts.size(ArtifactType.BINARY) > 0) {
+            // check if the generic handler can import something
+            Artifact primary = artifacts.getPrimaryData();
+            if (primary != null) {
+                if (info == null) {
+                    info = new ImportInfoImpl();
+                }
+                // check import mode
+                ImportMode mode = ImportMode.REPLACE;
+                String path = PathUtil.getPath(parent, primary.getRelativePath());
+                if (primary.getRelativePath().length() == 0 || parent.hasNode(primary.getRelativePath())) {
+                    mode = wspFilter.getImportMode(path);
+                }
+                // only update if not MERGE (i.e. is REPLACE or UPDATE)
+                if (mode != ImportMode.MERGE) {
+                    InputSource source = primary.getInputSource();
+                    if (source != null) {
+                        info.merge(importDocView(parent, source, artifacts, wspFilter));
+                    }
+                } else {
+                    info.onNop(path);
+                }
+            }
+            // handle files
+            for (Artifact file: artifacts.values(ArtifactType.FILE)) {
+                if (info == null) {
+                    info = new ImportInfoImpl();
+                }
+                // check type of file artifact
+                if (file.getSerializationType() == SerializationType.GENERIC
+                        || file.getSerializationType() == SerializationType.XML_GENERIC) {
+                    // case 1: new file
+                    if (!parent.hasNode(file.getRelativePath())) {
+                        importFile(info, parent, file);
+                    } else {
+                        // case 2: same structure, new data
+                        if (file instanceof ImportArtifact) {
+                            Node fileNode = parent.getNode(file.getRelativePath());
+                            // check import mode, only replace if not MERGE
+                            if (wspFilter.getImportMode(fileNode.getPath()) != ImportMode.MERGE) {
+                                if (!fileNode.hasNode(JcrConstants.JCR_CONTENT)) {
+                                    // apparently no nt:file, recreate file node
+                                    fileNode.remove();
+                                    importFile(info, parent, file);
+                                } else {
+                                    if (!importNtResource(info, fileNode.getNode(JcrConstants.JCR_CONTENT), file)) {
+                                        info.onNop(fileNode.getPath());
+                                    }
+                                }
+                            } else {
+                                info.onNop(fileNode.getPath());
+                            }
+                        } else {
+                            // do nothing
+                        }
+                    }
+                } else if (file.getSerializationType() == SerializationType.XML_DOCVIEW) {
+                    // special case for full coverage files below a intermediate node
+                    String relPath = Text.getRelativeParent(file.getRelativePath(), 1);
+                    String newName = Text.getName(file.getRelativePath());
+                    Node newParent = parent;
+                    if (relPath.length() > 0) {
+                        if (parent.hasNode(relPath)) {
+                            newParent = parent.getNode(relPath);
+                        } else {
+                            throw new IllegalArgumentException("Special docview file can't be imported. parent does not exist: " + parent.getPath() + "/" + relPath);
+                        }
+                    }
+                    ArtifactSetImpl newSet = new ArtifactSetImpl();
+                    newSet.setCoverage(ItemFilterSet.INCLUDE_ALL);
+
+                    // check import mode
+                    ImportMode mode = ImportMode.REPLACE;
+                    String path = PathUtil.getPath(newParent, newName);
+                    if (newName.length() == 0 || newParent.hasNode(newName)) {
+                        mode = wspFilter.getImportMode(path);
+                    }
+                    if (mode != ImportMode.MERGE) {
+                        try {
+                            DocViewSAXImporter handler = new DocViewSAXImporter(newParent, newName, newSet, wspFilter);
+                            handler.setAclHandling(getAcHandling());
+                            SAXParserFactory factory = SAXParserFactory.newInstance();
+                            factory.setNamespaceAware(true);
+                            factory.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
+                            SAXParser parser = factory.newSAXParser();
+                            parser.parse(file.getInputSource(), handler);
+                            info.merge(handler.getInfo());
+                        } catch (ParserConfigurationException e) {
+                            throw new RepositoryException(e);
+                        } catch (SAXException e) {
+                            throw new RepositoryException(e);
+                        }
+                    } else {
+                        info.onNop(path);
+                    }
+                } else {
+                    throw new IllegalArgumentException("Files of type " + file.getSerializationType() + " can't be handled by this handler " + this);
+                }
+            }
+            ValueFactory factory = parent.getSession().getValueFactory();
+            for (Artifact binary: artifacts.values(ArtifactType.BINARY)) {
+                // get parent node
+                Node parentNode = parent;
+                String path = binary.getRelativePath();
+                int idx = path.lastIndexOf('/');
+                if (idx > 0) {
+                    parentNode = parent.getNode(path.substring(0, idx));
+                    path = path.substring(idx + 1);
+                }
+                // only update binary if import mode is not MERGE
+                if (wspFilter.getImportMode(parentNode.getPath()) != ImportMode.MERGE) {
+                    Value value = factory.createValue(binary.getInputStream());
+                    if (!parentNode.hasProperty(path)
+                            || !value.equals(parentNode.getProperty(path).getValue())) {
+                        parent.setProperty(path, value);
+                        if (info == null) {
+                            info = new ImportInfoImpl();
+                        }
+                        info.onModified(path);
+                        info.onModified(parentNode.getPath());
+                    }
+                }
+            }
+        }
+        return info;
+    }
+
+    private Node importFile(ImportInfo info, Node parent, Artifact primary)
+            throws RepositoryException, IOException {
+        String name = primary.getRelativePath();
+        Node fileNode;
+        Node contentNode;
+        if (parent.hasNode(name)) {
+            fileNode = parent.getNode(name);
+            if (!fileNode.isNodeType(JcrConstants.NT_FILE)) {
+                parent.refresh(false);
+                throw new IOException("Incompatible content. Expected a nt:file but was " + fileNode.getPrimaryNodeType().getName());
+            }
+            contentNode = fileNode.getNode(JcrConstants.JCR_CONTENT);
+            info.onNop(fileNode.getPath());
+        } else {
+            fileNode = parent.addNode(name, JcrConstants.NT_FILE);
+            String contentNodeType = primary.getSerializationType() == SerializationType.XML_GENERIC
+                    && isExplodeXml() ? getXmlNodeType() : JcrConstants.NT_RESOURCE;
+            contentNode = fileNode.addNode(JcrConstants.JCR_CONTENT, contentNodeType);
+            info.onCreated(fileNode.getPath());
+            info.onCreated(contentNode.getPath());
+        }
+        importNtResource(info, contentNode, primary);
+        return fileNode;
+    }
+
+    private ImportInfoImpl importDocView(Node parent, InputSource source,
+                                     ArtifactSetImpl artifacts, WorkspaceFilter wspFilter)
+            throws RepositoryException, IOException {
+        String rootName = artifacts.getPrimaryData().getRelativePath();
+        int idx = rootName.indexOf('/');
+        if (idx > 0) {
+            rootName = rootName.substring(0, idx);
+        }
+        DocViewSAXImporter handler = new DocViewSAXImporter(parent, rootName, artifacts, wspFilter);
+        handler.setAclHandling(getAcHandling());
+        try {
+            SAXParserFactory factory = SAXParserFactory.newInstance();
+            factory.setNamespaceAware(true);
+            factory.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
+            SAXParser parser = factory.newSAXParser();
+            parser.parse(source, handler);
+            return handler.getInfo();
+        } catch (ParserConfigurationException e) {
+            throw new RepositoryException(e);
+        } catch (SAXException e) {
+            throw new RepositoryException(e);
+        }
+    }
+
+    private boolean importNtResource(ImportInfo info, Node content, Artifact artifact)
+            throws RepositoryException, IOException {
+
+        String path = content.getPath();
+        boolean modified = false;
+        if (explodeXml && !content.isNodeType(JcrConstants.NT_RESOURCE)) {
+            // explode xml
+            InputStream in = artifact.getInputStream();
+            try {
+                content.getSession().importXML(path, in, ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW);
+                // can't really augment info here
+            } finally {
+                in.close();
+            }
+            modified = true;
+        } else {
+            ValueFactory factory = content.getSession().getValueFactory();
+            Value value = factory.createValue(artifact.getInputStream());
+            if (!content.hasProperty(JcrConstants.JCR_DATA)
+                    || !value.equals(content.getProperty(JcrConstants.JCR_DATA).getValue())) {
+                content.setProperty(JcrConstants.JCR_DATA, value);
+                modified = true;
+            }
+
+            // always update last modified if binary was modified (bug #22969)
+            if (!content.hasProperty(JcrConstants.JCR_LASTMODIFIED) || modified) {
+                Calendar lastMod = Calendar.getInstance();
+                content.setProperty(JcrConstants.JCR_LASTMODIFIED, lastMod);
+                modified = true;
+            }
+
+            if (!content.hasProperty(JcrConstants.JCR_MIMETYPE)) {
+                String mimeType = artifact.getContentType();
+                if (mimeType == null) {
+                    mimeType = Text.getName(artifact.getRelativePath(), '.');
+                    mimeType = MimeTypes.getMimeType(mimeType, MimeTypes.APPLICATION_OCTET_STREAM);
+                }
+                content.setProperty(JcrConstants.JCR_MIMETYPE, mimeType);
+                modified = true;
+            }
+            if (content.isNew()) {
+                // mark binary data as modified
+                info.onCreated(path + "/" + JcrConstants.JCR_DATA);
+                info.onNop(path);
+            } else if (modified) {
+                // mark binary data as modified
+                info.onModified(path + "/" + JcrConstants.JCR_DATA);
+                info.onModified(path);
+            }
+        }
+        return modified;
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FilteredXMLSerializer.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FilteredXMLSerializer.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FilteredXMLSerializer.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FilteredXMLSerializer.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,151 @@
+/*
+ * 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.util.HashSet;
+import java.util.Set;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.util.xml.serialize.OutputFormat;
+import org.apache.jackrabbit.vault.util.xml.serialize.XMLSerializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * FilteredXMLSerializer extends {@link XMLSerializer} by adding the possibility to
+ * set an element filter for specific namespace URIs and/or raw names.
+ *
+ */
+public class FilteredXMLSerializer extends XMLSerializer {
+
+    private static Logger log = LoggerFactory.getLogger(FilteredXMLSerializer.class);
+
+    private Filter filter = new DefaultFilter();
+
+    public FilteredXMLSerializer(OutputStream outputStream, OutputFormat outputFormat) throws RepositoryException {
+        super(outputStream, outputFormat);
+    }
+
+    public void setFilter(Filter filter) {
+        this.filter = filter;
+    }
+
+    public Filter getFilter() {
+        return filter;
+    }
+
+    public void startElement(String namespaceURI, String localName, String rawName, Attributes attrs) throws SAXException {
+        if (filter.isFilteredNamespace(namespaceURI) || filter.isFilteredRawName(rawName)) {
+            log.debug("Filtering: {" + namespaceURI + "}" + localName + " -> " + rawName);
+        } else {
+            AttributesImpl fAttrs = new AttributesImpl(attrs);
+            for (int i = 0; i < fAttrs.getLength(); i++) {
+                String uri = fAttrs.getURI(i);
+                if (filter.isFilteredNamespace(uri)) {
+                    fAttrs.removeAttribute(i);
+                }
+            }
+            super.startElement(namespaceURI, localName, rawName, fAttrs);
+        }
+    }
+
+    public void endElement(String namespaceURI, String localName, String rawName) throws SAXException {
+        if (filter.isFilteredNamespace(namespaceURI) || filter.isFilteredRawName(rawName)) {
+            log.debug("Filtering: {" + namespaceURI + "}" + localName + " -> " + rawName);
+        } else {
+            super.endElement(namespaceURI, localName, rawName);
+        }
+    }
+
+    public void endElementIO(String namespaceURI, String localName, String rawName) throws IOException {
+        if (filter.isFilteredNamespace(namespaceURI) || filter.isFilteredRawName(rawName)) {
+            log.debug("Filtering: {" + namespaceURI + "}" + localName + " -> " + rawName);
+        } else {
+            super.endElementIO(namespaceURI, localName, rawName);
+        }
+    }
+
+    public interface Filter {
+
+        public void addNamespaceFilter(String namespaceURI);
+
+        public void removeNamespaceFilter(String namespaceURI);
+
+        public String[] getNamespaceFilters();
+
+        public void addRawNameFilter(String rawName);
+
+        public void removeRawNameFilter(String rawName);
+
+        public String[] getRawNameFilters();
+
+        public boolean isFilteredNamespace(String namespaceURI);
+
+        public boolean isFilteredRawName(String rawName);
+    }
+
+    /**
+     * Default filter with no entries.
+     */
+    public static class DefaultFilter implements Filter {
+
+        private Set<String> namespaces = new HashSet<String>();
+        private Set<String> rawNames = new HashSet<String>();
+
+        protected DefaultFilter() {
+        }
+
+        public void addNamespaceFilter(String namespaceURI) {
+            namespaces.add(namespaceURI);
+        }
+
+        public void removeNamespaceFilter(String namespaceURI) {
+            namespaces.remove(namespaceURI);
+        }
+
+        public String[] getNamespaceFilters() {
+            return namespaces.toArray(new String[namespaces.size()]);
+        }
+
+        public void addRawNameFilter(String rawName) {
+            rawNames.add(rawName);
+        }
+
+        public void removeRawNameFilter(String rawName) {
+            rawNames.remove(rawName);
+        }
+
+        public String[] getRawNameFilters() {
+            return rawNames.toArray(new String[rawNames.size()]);
+        }
+
+        public boolean isFilteredNamespace(String namespaceURI) {
+            return namespaces.contains(namespaceURI);
+        }
+
+        public boolean isFilteredRawName(String rawName) {
+            return rawNames.contains(rawName);
+        }
+    }
+}

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FolderArtifactHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FolderArtifactHandler.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FolderArtifactHandler.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/FolderArtifactHandler.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,127 @@
+/*
+ * 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.HashSet;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.fs.api.Artifact;
+import org.apache.jackrabbit.vault.fs.api.ArtifactType;
+import org.apache.jackrabbit.vault.fs.api.ImportMode;
+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.util.JcrConstants;
+
+/**
+ * Handles artifact sets with just a directory.
+ *
+ */
+public class FolderArtifactHandler extends AbstractArtifactHandler {
+
+    /**
+     * node type to use for the folders
+     */
+    private String nodeType = JcrConstants.NT_FOLDER;
+
+    /**
+     * Returns the node type that is used to create folders.
+     * @return the node type name.
+     */
+    public String getNodeType() {
+        return nodeType;
+    }
+
+    /**
+     * Sets the node type name that is used to create folders. Default is "nt:folder"
+     * @param nodeType the node type name
+     */
+    public void setNodeType(String nodeType) {
+        this.nodeType = nodeType;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Handles generic artifact sets
+     */
+    public ImportInfoImpl accept(WorkspaceFilter wspFilter, Node parent, String name,
+                             ArtifactSetImpl artifacts)
+            throws RepositoryException, IOException {
+        Artifact dir = artifacts.getDirectory();
+        if (dir == null || artifacts.size() != 1) {
+            return null;
+        }
+        ImportInfoImpl info = new ImportInfoImpl();
+        if (dir.getRelativePath().length() == 0) {
+            // special check if artifact addresses itself
+            return info;
+        }
+        if (!parent.hasNode(dir.getRelativePath())) {
+            Node node = parent.addNode(dir.getRelativePath(), nodeType);
+            info.onCreated(node.getPath());
+        } else {
+            // sync nodes
+            Set<String> hints = new HashSet<String>();
+            String rootPath = parent.getPath();
+            if (!rootPath.equals("/")) {
+                rootPath += "/";
+            }
+            for (Artifact a: artifacts.values(ArtifactType.HINT)) {
+                hints.add(rootPath + a.getRelativePath());
+            }
+
+            Node node = parent.getNode(dir.getRelativePath());
+            NodeIterator iter = node.getNodes();
+            while (iter.hasNext()) {
+                Node child = iter.nextNode();
+                String path = child.getPath();
+                if (wspFilter.contains(path)) {
+                    if (wspFilter.getImportMode(path) == ImportMode.REPLACE) {
+                        if (!hints.contains(path)) {
+                            // if the child is in the filter, it belongs to
+                            // this aggregate and needs to be removed
+                            if (getAclManagement().isACLNode(child)) {
+                                if (acHandling == AccessControlHandling.OVERWRITE
+                                        || acHandling == AccessControlHandling.CLEAR) {
+                                    info.onDeleted(path);
+                                    getAclManagement().clearACL(node);
+                                }
+                            } else {
+                                info.onDeleted(path);
+                                child.remove();
+                            }
+                        } else if (acHandling == AccessControlHandling.CLEAR
+                                && getAclManagement().isACLNode(child)) {
+                            info.onDeleted(path);
+                            getAclManagement().clearACL(node);
+                        }
+                    }
+                }
+            }
+
+        }
+        return info;
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/GenericArtifactHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/GenericArtifactHandler.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/GenericArtifactHandler.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/GenericArtifactHandler.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,112 @@
+/*
+ * 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.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.jackrabbit.vault.fs.api.Artifact;
+import org.apache.jackrabbit.vault.fs.api.ImportMode;
+import org.apache.jackrabbit.vault.fs.api.SerializationType;
+import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
+import org.apache.jackrabbit.vault.fs.impl.ArtifactSetImpl;
+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.util.PathUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * Handles docview artifacts.
+ *
+ */
+public class GenericArtifactHandler extends AbstractArtifactHandler {
+
+    /**
+     * default logger
+     */
+    private static final Logger log = LoggerFactory.getLogger(GenericArtifactHandler.class);
+
+    private UserManagement userManagement = ServiceProviderFactory.getProvider().getUserManagement();
+
+    private ACLManagement aclManagement = ServiceProviderFactory.getProvider().getACLManagement();
+
+    /**
+     * {@inheritDoc}
+     *
+     * Handles generic artifact sets
+     */
+    public ImportInfoImpl accept(WorkspaceFilter wspFilter, Node parent,
+                                 String name, ArtifactSetImpl artifacts)
+            throws RepositoryException, IOException {
+        Artifact primary = artifacts.getPrimaryData();
+        if (primary == null) {
+            return null;
+        }
+
+        // check type of primary artifact
+        ImportInfoImpl info = null;
+        InputSource source = primary.getInputSource();
+        if (source != null && primary.getSerializationType() == SerializationType.XML_DOCVIEW) {
+            // primary docview artifact. don't except to have additional
+            // extra content artifacts
+            info = new ImportInfoImpl();
+            String path = PathUtil.getPath(parent, name);
+            if (name.length() == 0 || parent.hasNode(name)) {
+                if (wspFilter.getImportMode(path) == ImportMode.MERGE) {
+                    // do import the content if node is an authorizable or ACL
+                    Node newNode = parent.getNode(name);
+                    if (userManagement.isAuthorizableNodeType(newNode.getPrimaryNodeType().getName())) {
+                        log.debug("don't skip authorizable node on MERGE: {}", path);
+                    } else if (aclManagement.isACLNode(newNode)) {
+                        log.debug("don't skip policy node on MERGE: {}", path);
+                    } else {
+                        info.onNop(path);
+                        return info;
+                    }
+                }
+            }
+            try {
+                DocViewSAXImporter handler = new DocViewSAXImporter(parent, name, artifacts, wspFilter);
+                handler.setAclHandling(getAcHandling());
+                SAXParserFactory factory = SAXParserFactory.newInstance();
+                factory.setNamespaceAware(true);
+                factory.setFeature("http://xml.org/sax/features/namespace-prefixes", false);
+                SAXParser parser = factory.newSAXParser();
+                parser.parse(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);
+            }
+        }
+        return info;
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/ImportInfoImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/ImportInfoImpl.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/ImportInfoImpl.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/ImportInfoImpl.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,241 @@
+/*
+ * 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.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.version.Version;
+
+import org.apache.jackrabbit.vault.fs.api.ImportInfo;
+import org.apache.jackrabbit.vault.fs.api.NodeNameList;
+import org.apache.jackrabbit.vault.util.JcrConstants;
+import org.apache.jackrabbit.vault.util.PathComparator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * <code>ImportInfo</code>...
+ *
+ */
+public class ImportInfoImpl implements ImportInfo {
+
+    /**
+     * the default logger
+     */
+    static final Logger log = LoggerFactory.getLogger(ImportInfoImpl.class);
+
+    private final TreeMap<String, Type> mods = new TreeMap<String, Type>(new PathComparator());
+
+    private final Map<String, Exception> errorMap =  new HashMap<String, Exception>();
+
+    private Map<String, String> remapped;
+
+    /**
+     * list of uuids of nodes that need to be checked-in after the import
+     */
+    private final Set<String> toVersion = new LinkedHashSet<String>();
+
+    private NodeNameList nameList;
+
+    private Node node;
+
+    private int numModified;
+
+    private Map<String, String[]> memberships;
+
+    public static ImportInfo create(ImportInfo base) {
+        if (base == null) {
+            return new ImportInfoImpl();
+        } else {
+            return base;
+        }
+    }
+
+    public ImportInfoImpl() {
+    }
+
+    public ImportInfoImpl merge(ImportInfo base) {
+        if (base instanceof ImportInfoImpl) {
+            ImportInfoImpl baseImpl = (ImportInfoImpl) base;
+            mods.putAll(baseImpl.mods);
+            errorMap.putAll(baseImpl.errorMap);
+            numModified +=baseImpl.numModified;
+            toVersion.addAll(baseImpl.toVersion);
+            // also merge node and name list if not set yet
+            if (node == null) {
+                node = baseImpl.node;
+                nameList = baseImpl.nameList;
+            }
+            if (remapped == null) {
+                remapped = baseImpl.remapped;
+            } else {
+                remapped.putAll(baseImpl.getRemapped());
+            }
+            if (memberships == null) {
+                memberships = baseImpl.memberships;
+            } else {
+                memberships.putAll(baseImpl.getMemberships());
+            }
+        }
+        return this;
+    }
+
+    public NodeNameList getNameList() {
+        return nameList;
+    }
+
+    public void setNameList(NodeNameList nameList) {
+        this.nameList = nameList;
+    }
+
+    /**
+     * Returns the root node of this import.
+     * @return root node if this import or <code>null</code>
+     */
+    public Node getNode() {
+        return node;
+    }
+
+    public void setNode(Node node) {
+        this.node = node;
+    }
+
+    public void onModified(String path) {
+        Type prev = mods.get(path);
+        if (prev == null || prev != Type.CRE) {
+            addMod(path, Type.MOD);
+        }
+    }
+
+    public void onNop(String path) {
+        if (!mods.containsKey(path)) {
+            addMod(path, Type.NOP);
+        }
+    }
+
+    public void onCreated(String path) {
+        addMod(path, Type.CRE);
+    }
+
+    public void onDeleted(String path) {
+        addMod(path, Type.DEL);
+    }
+
+    public void onReplaced(String path) {
+        addMod(path, Type.REP);
+    }
+
+    public void onMissing(String path) {
+        addMod(path, Type.MIS);
+    }
+
+    public void onError(String path, Exception e) {
+        addMod(path, Type.ERR);
+        errorMap.put(path, e);
+    }
+
+    public void onRemapped(String oldPath, String newPath) {
+        if (remapped == null) {
+            remapped = new HashMap<String, String>();
+        }
+        remapped.put(oldPath, newPath);
+    }
+
+    public Map<String, String> getRemapped() {
+        return remapped == null ? Collections.<String, String>emptyMap() : remapped;
+    }
+
+    private void addMod(String path, Type mod) {
+        // don't overwrite errors
+        if (mods.get(path) != Type.ERR) {
+            mods.put(path, mod);
+            if (mod != Type.NOP) {
+                numModified++;
+            }
+        }
+        log.debug("{} {}", mod, path);
+    }
+
+    public TreeMap<String, Type> getModifications() {
+        return mods;
+    }
+
+    public Exception getError(String path) {
+        return errorMap.get(path);
+    }
+
+    public Collection<String> getToVersion() {
+        return toVersion;
+    }
+    
+    public void registerToVersion(String path) {
+        toVersion.add(path);
+    }
+
+    public void checkinNodes(Session session) {
+        Iterator<String> iter = toVersion.iterator();
+        while (iter.hasNext()) {
+            String path = iter.next();
+            iter.remove();
+            try {
+                Node node = session.getNode(path);
+                // check if node is really versionable. SPI might not have known it at the time
+                // this node was registered for versioning
+                if (node.isNodeType(JcrConstants.MIX_VERSIONABLE)) {
+                    try {
+                        Version v = node.checkin();
+                        log.info("Checked in versionable node {} (v={})", path, v.getName());
+                    } catch (RepositoryException e) {
+                        log.error("Error while checkin node {}: {}", path, e.toString());
+                    }
+                }
+            } catch (RepositoryException e) {
+                log.error("Error while retrieving node to be versioned at {}.", path, e);
+            }
+        }
+    }
+
+    public int numModified() {
+        return numModified;
+    }
+
+    public int numErrors() {
+        return errorMap.size();
+    }
+
+    public void registerMemberships(String id, String[] members) {
+        if (memberships == null) {
+            memberships = new HashMap<String, String[]>();
+        }
+        memberships.put(id, members);
+    }
+
+    public Map<String, String[]> getMemberships() {
+        return memberships == null ? Collections.<String, String[]>emptyMap() : memberships;
+    }
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/InputSourceArtifact.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/InputSourceArtifact.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/InputSourceArtifact.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/InputSourceArtifact.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,137 @@
+/*
+ * 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.InputStream;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.fs.api.AccessType;
+import org.apache.jackrabbit.vault.fs.api.Artifact;
+import org.apache.jackrabbit.vault.fs.api.ArtifactType;
+import org.apache.jackrabbit.vault.fs.api.ImportArtifact;
+import org.apache.jackrabbit.vault.fs.api.SerializationType;
+import org.apache.jackrabbit.vault.fs.api.VaultInputSource;
+import org.apache.jackrabbit.vault.fs.impl.AbstractArtifact;
+import org.xml.sax.InputSource;
+
+/**
+ * Implements an artifact that is based on a {@link InputSource}.
+ *
+ */
+public class InputSourceArtifact extends AbstractArtifact implements ImportArtifact {
+
+    /**
+     * the input source
+     */
+    private final VaultInputSource source;
+
+    private final SerializationType serType;
+
+    /**
+     * Constructs a compatible artifact of the given base one.
+     *
+     * @param base the base artifact
+     * @param source the input source
+     */
+    public InputSourceArtifact(Artifact base, VaultInputSource source) {
+        super(base, base.getType());
+        this.source = source;
+        this.serType = base.getSerializationType();
+        setContentType(base.getContentType());
+    }
+
+    /**
+     * Constructs a new artifact for the given source. the parent artifact is
+     * only used for generating the paths and is not linked to this artifact.
+     * 
+     * @param parent the parent artifact
+     * @param repoName the repository name
+     * @param extension the platform extension
+     * @param type the type of the artifact
+     * @param source the input source of the artifact
+     * @param serType the serialization type
+     */
+    public InputSourceArtifact(Artifact parent, String repoName, String extension,
+                               ArtifactType type, VaultInputSource source,
+                               SerializationType serType) {
+        super(parent, repoName, extension, type);
+        this.source = source;
+        this.serType = serType;
+    }
+
+    /**
+     * Creates a new input source artifact based on a given one.
+     * @param isa the base source artifact
+     * @param type the new type.
+     */
+    public InputSourceArtifact(InputSourceArtifact isa, ArtifactType type) {
+        super(isa, type);
+        this.source = isa.source;
+        this.serType = isa.serType;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public SerializationType getSerializationType() {
+        return serType;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return {@link AccessType#STREAM} always.
+     */
+    public AccessType getPreferredAccess() {
+        return AccessType.STREAM;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public InputStream getInputStream()
+            throws IOException, RepositoryException {
+        return source.getByteStream();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @return the underlying source
+     */
+    public VaultInputSource getInputSource()
+            throws IOException, RepositoryException {
+        return source;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public long getLastModified() {
+        return source.getLastModified();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public long getContentLength() {
+        return source.getContentLength();
+    }
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/NodeTypeArtifactHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/NodeTypeArtifactHandler.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/NodeTypeArtifactHandler.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/NodeTypeArtifactHandler.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,78 @@
+/*
+ * 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.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.fs.api.Artifact;
+import org.apache.jackrabbit.vault.fs.api.ImportMode;
+import org.apache.jackrabbit.vault.fs.api.SerializationType;
+import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
+import org.apache.jackrabbit.vault.fs.impl.ArtifactSetImpl;
+import org.apache.jackrabbit.vault.util.PathUtil;
+
+/**
+ * Creates nt:nodeType structures from {@link SerializationType#CND} artifacts.
+ *
+ */
+public class NodeTypeArtifactHandler extends AbstractArtifactHandler {
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Handles generic artifact sets
+     */
+    protected ImportInfoImpl accept(WorkspaceFilter wspFilter, Node parent,
+                                String name, ArtifactSetImpl artifacts)
+            throws RepositoryException, IOException {
+        // need at least one primary data
+        Artifact primary = artifacts.getPrimaryData();
+        if (primary == null) {
+            return null;
+        }
+        if (artifacts.size() != 1) {
+            return null;
+        }
+        if (primary.getSerializationType() != SerializationType.CND) {
+            return null;
+        }
+        String path = PathUtil.getPath(parent, primary.getRelativePath());
+        if (wspFilter.getImportMode(path) == ImportMode.MERGE) {
+            ImportInfoImpl info = new ImportInfoImpl();
+            info.onNop(path);
+            return info;
+        }
+        // do import
+        CNDImporter importer = new CNDImporter();
+        InputStream in = primary.getInputStream();
+        try {
+            Reader r = new InputStreamReader(in, "utf-8");
+            return importer.doImport(parent, primary.getRelativePath(), r, primary.getRelativePath());
+        } finally {
+            in.close();
+        }
+    }
+
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/XmlAnalyzer.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/XmlAnalyzer.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/XmlAnalyzer.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/XmlAnalyzer.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,96 @@
+/*
+ * 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.InputStreamReader;
+import java.io.Reader;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.vault.fs.api.SerializationType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.InputSource;
+
+/**
+ * Analyzes a xml source and guesses the type. the following types are
+ * recognized:
+ * <ul>
+ * <li> {@link SerializationType#GENERIC} if the source is not a valid XML
+ * <li> {@link SerializationType#XML_GENERIC} if the XML type is not known. eg. a user-xml
+ * <li> {@link SerializationType#XML_DOCVIEW} if the XML is a docview serialization
+ * </ul>
+ * Please note, that the docview serialization is recognized if the first
+ * element contains a jcr:primaryType attribute.
+ *
+ */
+public class XmlAnalyzer {
+
+    /**
+     * the default logger
+     */
+    private static final Logger log = LoggerFactory.getLogger(XmlAnalyzer.class);
+
+    private XmlAnalyzer() {
+    }
+
+    /**
+     * Analyzes the given source.
+     *
+     * @param source the source to analyze
+     * @return the serialization type
+     * @throws IOException if an I/O error occurs
+     */
+    public static SerializationType analyze(InputSource source) throws IOException {
+        Reader r = source.getCharacterStream();
+        SerializationType type = SerializationType.UNKOWN;
+        if (r == null) {
+            if (source.getEncoding() == null) {
+                r = new InputStreamReader(source.getByteStream());
+            } else {
+                r = new InputStreamReader(source.getByteStream(), source.getEncoding());
+            }
+        }
+        try {
+            // read a couple of chars...1024 should be enough
+            char[] buffer = new char[1024];
+            int pos = 0;
+            while (pos<buffer.length) {
+                int read = r.read(buffer, pos, buffer.length - pos);
+                if (read < 0) {
+                    break;
+                }
+                pos+=read;
+            }
+            String str = new String(buffer, 0, pos);
+            // check for docview
+            if (str.contains("<jcr:root ") && str.contains("\"http://www.jcp.org/jcr/1.0\"")) {
+                type = SerializationType.XML_DOCVIEW;
+            } else if (str.contains("<?xml ")) {
+                type = SerializationType.XML_GENERIC;
+            } else {
+                type = SerializationType.GENERIC;
+            }
+        } finally {
+            IOUtils.closeQuietly(r);
+        }
+        log.debug("Analyzed {}. Type = {}", source.getSystemId(), type);
+        return type;
+    }
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/legacycnd/Lexer.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/legacycnd/Lexer.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/legacycnd/Lexer.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/legacycnd/Lexer.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,161 @@
+/*
+ * 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.legacycnd;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StreamTokenizer;
+
+/**
+ * This is copied from jackrabbit-spi-commons-1.6
+ * 
+ * Lexer
+ */
+public class Lexer {
+    public static final char SINGLE_QUOTE = '\'';
+    public static final char DOUBLE_QUOTE = '\"';
+    public static final char BEGIN_NODE_TYPE_NAME = '[';
+    public static final char END_NODE_TYPE_NAME = ']';
+    public static final char EXTENDS = '>';
+    public static final char LIST_DELIMITER = ',';
+    public static final char PROPERTY_DEFINITION = '-';
+    public static final char CHILD_NODE_DEFINITION = '+';
+    public static final char BEGIN_TYPE = '(';
+    public static final char END_TYPE = ')';
+    public static final char DEFAULT = '=';
+    public static final char CONSTRAINT = '<';
+
+    public static final String[] ORDERABLE = new String[] {"orderable", "ord", "o"};
+    public static final String[] MIXIN = new String[]{"mixin", "mix", "m"};
+
+    public static final String[] PRIMARY = new String[]{"primary", "pri", "!"};
+    public static final String[] AUTOCREATED = new String[]{"autocreated", "aut", "a"};
+    public static final String[] MANDATORY = new String[]{"mandatory", "man", "m"};
+    public static final String[] PROTECTED = new String[]{"protected", "pro", "p"};
+    public static final String[] MULTIPLE = new String[]{"multiple", "mul", "*"};
+
+    public static final String[] COPY = new String[]{"copy", "Copy", "COPY"};
+    public static final String[] VERSION = new String[]{"version", "Version", "VERSION"};
+    public static final String[] INITIALIZE = new String[]{"initialize", "Initialize", "INITIALIZE"};
+    public static final String[] COMPUTE = new String[]{"compute", "Compute", "COMPUTE"};
+    public static final String[] IGNORE = new String[]{"ignore", "Ignore", "IGNORE"};
+    public static final String[] ABORT = new String[]{"abort", "Abort", "ABORT"};
+
+    public static final String[] ATTRIBUTE = new String[]{"primary", "pri", "!",
+                                                          "autocreated", "aut", "a",
+                                                          "mandatory", "man", "m",
+                                                          "protected", "pro", "p",
+                                                          "multiple", "mul", "*",
+                                                          "copy", "Copy", "COPY",
+                                                          "version", "Version", "VERSION",
+                                                          "initialize", "Initialize", "INITIALIZE",
+                                                          "compute", "Compute", "COMPUTE",
+                                                          "ignore", "Ignore", "IGNORE",
+                                                          "abort", "Abort", "ABORT"};
+
+    public static final String[] STRING = {"string", "String", "STRING"};
+    public static final String[] BINARY = {"binary", "Binary", "BINARY"};
+    public static final String[] LONG = {"long", "Long", "LONG"};
+    public static final String[] DOUBLE = {"double", "Double", "DOUBLE"};
+    public static final String[] BOOLEAN = {"boolean", "Boolean", "BOOLEAN"};
+    public static final String[] DATE = {"date", "Date", "DATE"};
+    public static final String[] NAME = {"name", "Name", "NAME"};
+    public static final String[] PATH = {"path", "Path", "PATH"};
+    public static final String[] REFERENCE = {"reference", "Reference", "REFERENCE"};
+
+    public static final String[] UNDEFINED = new String[]{"undefined", "Undefined", "UNDEFINED", "*"};
+
+    public static final String EOF = "eof";
+
+    private final StreamTokenizer st;
+
+    private final String systemId;
+
+    /**
+     * Constructor
+     * @param r
+     */
+    public Lexer(Reader r, String systemId) {
+        this.systemId = systemId;
+        st = new StreamTokenizer(r);
+
+        st.eolIsSignificant(false);
+
+        st.lowerCaseMode(false);
+
+        st.slashSlashComments(true);
+        st.slashStarComments(true);
+
+        st.wordChars('a', 'z');
+        st.wordChars('A', 'Z');
+        st.wordChars(':', ':');
+        st.wordChars('_', '_');
+
+        st.quoteChar(SINGLE_QUOTE);
+        st.quoteChar(DOUBLE_QUOTE);
+
+        st.ordinaryChar(BEGIN_NODE_TYPE_NAME);
+        st.ordinaryChar(END_NODE_TYPE_NAME);
+        st.ordinaryChar(EXTENDS);
+        st.ordinaryChar(LIST_DELIMITER);
+        st.ordinaryChar(PROPERTY_DEFINITION);
+        st.ordinaryChar(CHILD_NODE_DEFINITION);
+        st.ordinaryChar(BEGIN_TYPE);
+        st.ordinaryChar(END_TYPE);
+        st.ordinaryChar(DEFAULT);
+        st.ordinaryChar(CONSTRAINT);
+    }
+
+    /**
+     * getNextToken
+     *
+     * @return
+     * @throws ParseException
+     */
+    public String getNextToken() throws ParseException {
+        try {
+            int tokenType = st.nextToken();
+            if (tokenType == StreamTokenizer.TT_EOF) {
+                return EOF;
+            } else if (tokenType == StreamTokenizer.TT_WORD
+                    || tokenType == SINGLE_QUOTE
+                    || tokenType == DOUBLE_QUOTE) {
+                return st.sval;
+            } else if (tokenType == StreamTokenizer.TT_NUMBER) {
+                return String.valueOf(st.nval);
+            } else {
+                return new String(new char[] {(char) tokenType});
+            }
+        } catch (IOException e) {
+            fail("IOException while attempting to read input stream", e);
+            return null;
+        }
+    }
+
+    public void fail(String message) throws ParseException {
+        throw new ParseException(message, st.lineno(), -1, systemId);
+    }
+
+    public void fail(String message, Throwable e) throws ParseException {
+        throw new ParseException(message, e, st.lineno(), -1, systemId);
+    }
+
+    public void fail(Throwable e) throws ParseException {
+        throw new ParseException(e, st.lineno(), -1, systemId);
+    }
+}

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/legacycnd/ParseException.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/legacycnd/ParseException.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/legacycnd/ParseException.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/legacycnd/ParseException.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,131 @@
+/*
+ * 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.legacycnd;
+
+/**
+ * This is copied from jackrabbit-spi-commons-1.6
+ *
+ * ParseException
+ */
+public class ParseException extends Exception {
+
+    /**
+     * the line number where the error occurred
+     */
+    private final int lineNumber;
+
+    /**
+     * the column number where the error occurred
+     */
+    private final int colNumber;
+
+    /**
+     * the systemid of the source that produced the error
+     */
+    private final String systemId;
+
+
+    /**
+     * Constructs a new instance of this class with <code>null</code> as its
+     * detail message.
+     */
+    public ParseException(int lineNumber, int colNumber, String systemId) {
+        super();
+        this.lineNumber = lineNumber;
+        this.colNumber = colNumber;
+        this.systemId = systemId;
+    }
+
+    /**
+     * Constructs a new instance of this class with the specified detail
+     * message.
+     *
+     * @param message the detail message. The detail message is saved for
+     *                later retrieval by the {@link #getMessage()} method.
+     */
+    public ParseException(String message, int lineNumber, int colNumber, String systemId) {
+        super(message);
+        this.lineNumber = lineNumber;
+        this.colNumber = colNumber;
+        this.systemId = systemId;
+    }
+
+    /**
+     * Constructs a new instance of this class with the specified detail
+     * message and root cause.
+     *
+     * @param message   the detail message. The detail message is saved for
+     *                  later retrieval by the {@link #getMessage()} method.
+     * @param rootCause root failure cause
+     */
+    public ParseException(String message, Throwable rootCause, int lineNumber, int colNumber, String systemId) {
+        super(message, rootCause);
+        this.lineNumber = lineNumber;
+        this.colNumber = colNumber;
+        this.systemId = systemId;
+    }
+
+    /**
+     * Constructs a new instance of this class with the specified root cause.
+     *
+     * @param rootCause root failure cause
+     */
+    public ParseException(Throwable rootCause, int lineNumber, int colNumber, String systemId) {
+        super(rootCause);
+        this.lineNumber = lineNumber;
+        this.colNumber = colNumber;
+        this.systemId = systemId;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getMessage() {
+        StringBuffer b = new StringBuffer(super.getMessage());
+        String delim = " (";
+        if (systemId != null && !systemId.equals("")) {
+            b.append(delim);
+            b.append(systemId);
+            delim = ", ";
+        }
+        if (lineNumber >= 0) {
+            b.append(delim);
+            b.append("line ");
+            b.append(lineNumber);
+            delim = ", ";
+        }
+        if (colNumber >= 0) {
+            b.append(delim);
+            b.append("col ");
+            b.append(colNumber);
+            delim = ", ";
+        }
+        if (delim.equals(", ")) {
+            b.append(")");
+        }
+        return b.toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return super.toString(); // + " (" + systemId + ", line " + lineNumber +", col " + colNumber +")";
+    }
+
+}

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/AbstractArchive.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/AbstractArchive.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/AbstractArchive.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/AbstractArchive.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,50 @@
+/*
+ * 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 org.apache.jackrabbit.vault.util.Constants;
+import org.apache.jackrabbit.vault.util.Text;
+
+/**
+ * <code>AbstractArchive</code>...
+ */
+abstract class AbstractArchive implements Archive {
+
+    public Entry getEntry(String path) throws IOException {
+        String[] segs = Text.explode(path, '/');
+        Entry root = getRoot();
+        for (String name: segs) {
+            root = root.getChild(name);
+            if (root == null) {
+                break;
+            }
+        }
+        return root;
+    }
+
+    public Entry getJcrRoot() throws IOException {
+        return getRoot().getChild(Constants.ROOT_DIR);
+    }
+    
+    public Archive getSubArchive(String rootPath, boolean asJcrRoot) throws IOException {
+        Entry root = getEntry(rootPath);
+        return root == null ? null : new SubArchive(this, root, asJcrRoot);
+    }
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/AbstractExporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/AbstractExporter.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/AbstractExporter.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/AbstractExporter.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,313 @@
+/*
+ * 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.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Properties;
+import java.util.Set;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeManager;
+
+import org.apache.jackrabbit.util.ISO8601;
+import org.apache.jackrabbit.vault.fs.api.AggregateManager;
+import org.apache.jackrabbit.vault.fs.api.ProgressTrackerListener;
+import org.apache.jackrabbit.vault.fs.api.SimplePathMapping;
+import org.apache.jackrabbit.vault.fs.api.VaultFile;
+import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
+import org.apache.jackrabbit.vault.fs.config.MetaInf;
+import org.apache.jackrabbit.vault.fs.spi.CNDWriter;
+import org.apache.jackrabbit.vault.fs.spi.ProgressTracker;
+import org.apache.jackrabbit.vault.fs.spi.ServiceProviderFactory;
+import org.apache.jackrabbit.vault.util.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Generic context for exporters
+ *
+ */
+public abstract class AbstractExporter {
+
+    /**
+     * default logger
+     */
+    private static final Logger log = LoggerFactory.getLogger(AbstractExporter.class);
+
+    private ProgressTracker tracker;
+
+    private boolean relativePaths;
+
+    private String rootPath = Constants.ROOT_DIR;
+
+    private Properties properties = new Properties();
+
+    private boolean noMetaInf;
+
+    protected ExportInfo exportInfo = new ExportInfo();
+
+    public boolean isVerbose() {
+        return tracker != null;
+    }
+
+    public void setVerbose(ProgressTrackerListener out) {
+        if (out == null) {
+            tracker = null;
+        } else {
+            if (tracker == null) {
+                tracker = new ProgressTracker();
+            }
+            tracker.setListener(out);
+        }
+    }
+
+    public boolean isRelativePaths() {
+        return relativePaths;
+    }
+
+    public void setProperty(String name, String value) {
+        properties.put(name, value);
+    }
+
+    public void setProperty(String name, Calendar value) {
+        properties.put(name, ISO8601.format(value));
+    }
+
+    public void setProperties(Properties properties) {
+        if (properties != null) {
+            this.properties.putAll(properties);
+        }
+    }
+
+    public String getRootPath() {
+        return rootPath;
+    }
+
+    public void setRootPath(String rootPath) {
+        this.rootPath = rootPath;
+    }
+
+    public boolean isNoMetaInf() {
+        return noMetaInf;
+    }
+
+    public void setNoMetaInf(boolean noMetaInf) {
+        this.noMetaInf = noMetaInf;
+    }
+
+    public ExportInfo getExportInfo() {
+        return exportInfo;
+    }
+
+    /**
+     * Defines if the exported files should include their entire path or just
+     * be relative to the export root. eg.: exporting /apps/components relative
+     * would not include /apps in the path.
+     *
+     * @param relativePaths relative flag
+     */
+    public void setRelativePaths(boolean relativePaths) {
+        this.relativePaths = relativePaths;
+    }
+
+    /**
+     * Exports the given vault file and writes the META-INF data.
+     * @param parent the vault file
+     * @throws RepositoryException if an error occurs
+     * @throws IOException if an I/O error occurs
+     */
+    public void export(VaultFile parent) throws RepositoryException, IOException {
+        export(parent, false);
+    }
+
+    /**
+     * Exports the given vault file and writes the META-INF data.
+     * @param parent the vault file
+     * @param noClose if <code>true</code> exporter will not be closed after export
+     * @throws RepositoryException if an error occurs
+     * @throws IOException if an I/O error occurs
+     */
+    public void export(VaultFile parent, boolean noClose)
+            throws RepositoryException, IOException {
+        exportInfo.getEntries().clear();
+        open();
+        AggregateManager mgr = parent.getFileSystem().getAggregateManager();
+        mgr.startTracking(tracker == null ? null : tracker.getListener());
+        if (!noMetaInf) {
+            createDirectory(Constants.META_INF);
+            createDirectory(Constants.META_DIR);
+            // add some 'fake' tracking
+            track("A", Constants.META_INF);
+            track("A", Constants.META_DIR);
+            track("A", Constants.META_DIR + "/" + Constants.CONFIG_XML);
+            track("A", Constants.META_DIR + "/" + Constants.FILTER_XML);
+            track("A", Constants.META_DIR + "/" + Constants.NODETYPES_CND);
+            track("A", Constants.META_DIR + "/" + Constants.PROPERTIES_XML);
+            writeFile(mgr.getConfig().getSource(), Constants.META_DIR + "/" + Constants.CONFIG_XML);
+            // get filter and translate if necessary
+            WorkspaceFilter filter = mgr.getWorkspaceFilter();
+            String mountPath = mgr.getRoot().getPath();
+            String rootPath = parent.getPath();
+            if (rootPath.equals("/")) {
+                rootPath = "";
+            }
+            if (mountPath.length() > 0 || rootPath.length() > 0) {
+                filter = filter.translate(new SimplePathMapping(mountPath, rootPath));
+            }
+            writeFile(filter.getSource(), Constants.META_DIR + "/" + Constants.FILTER_XML);
+        }
+        export(parent, "");
+        if (!noMetaInf) {
+            writeFile(getNodeTypes(mgr.getSession(), mgr.getNodeTypes()), Constants.META_DIR + "/" + Constants.NODETYPES_CND);
+            // update properties
+            setProperty(MetaInf.CREATED, Calendar.getInstance());
+            setProperty(MetaInf.CREATED_BY, mgr.getUserId());
+            setProperty(MetaInf.PACKAGE_FORMAT_VERSION, String.valueOf(MetaInf.FORMAT_VERSION_2));
+            ByteArrayOutputStream tmpOut = new ByteArrayOutputStream();
+            properties.storeToXML(tmpOut, "FileVault Package Properties", "utf-8");
+            writeFile(new ByteArrayInputStream(tmpOut.toByteArray()), Constants.META_DIR + "/" + Constants.PROPERTIES_XML);
+        }
+        if (!noClose) {
+            close();
+        }
+        mgr.stopTracking();
+    }
+
+    /**
+     * Exports the vault file to the relative path.
+     * @param parent the file
+     * @param relPath the path
+     * @throws RepositoryException if an error occurs
+     * @throws IOException if an I/O error occurs
+     */
+    public void export(VaultFile parent, String relPath)
+            throws RepositoryException, IOException {
+        for (VaultFile vaultFile : parent.getChildren()) {
+            String path = relPath + "/" + vaultFile.getName();
+            if (vaultFile.isDirectory()) {
+                createDirectory(vaultFile, path);
+                export(vaultFile, path);
+            } else {
+                writeFile(vaultFile, path);
+            }
+        }
+    }
+
+    protected void track(String action, String path) {
+        if ("E".equals(action)) {
+            log.error("{} {}", action, path);
+        } else {
+            log.debug("{} {}", action, path);
+        }
+        if (tracker != null) {
+            tracker.track(action, path);
+        }
+    }
+
+    protected void track(Exception e, String path) {
+        log.error("E {} ({})", path, e.toString());
+        if (tracker != null) {
+            tracker.track(e, path);
+        }
+    }
+    
+    protected String getPlatformFilePath(VaultFile file, String relPath) {
+        StringBuilder buf = new StringBuilder(rootPath);
+        if (isRelativePaths()) {
+            // relative paths are only needed for special exports, like the definition export of packaging
+            if (buf.length() > 0) {
+                buf.append("/");
+            }
+            buf.append(relPath);
+        } else {
+            buf.append(file.getPath());
+        }
+        return buf.toString();
+    }
+    
+    private InputStream getNodeTypes(Session s, Collection<String> nodeTypes)
+            throws IOException, RepositoryException {
+        NodeTypeManager ntMgr = s.getWorkspace().getNodeTypeManager();
+        // init with repository predefined node types
+        Set<String> written = new HashSet<String>();
+        written.addAll(ServiceProviderFactory.getProvider().getBuiltInNodeTypeNames());
+        StringWriter out = new StringWriter();
+        CNDWriter w = ServiceProviderFactory.getProvider().getCNDWriter(out, s, true);
+        for (String nt: nodeTypes) {
+            writeNodeType(ntMgr.getNodeType(nt), w, written);
+        }
+        w.close();
+        return new ByteArrayInputStream(out.getBuffer().toString().getBytes("utf-8"));
+    }
+
+    private void writeNodeType(NodeType nt, CNDWriter w, Set<String> written)
+            throws IOException, RepositoryException {
+        if (nt != null && !written.contains(nt.getName())) {
+            written.add(nt.getName());
+            w.write(nt);
+            for (NodeType s: nt.getSupertypes()) {
+                writeNodeType(s, w, written);
+            }
+            for (NodeDefinition n: nt.getChildNodeDefinitions()) {
+                writeNodeType(n.getDefaultPrimaryType(), w, written);
+                if (n.getRequiredPrimaryTypes() != null) {
+                    for (NodeType r: n.getRequiredPrimaryTypes()) {
+                        writeNodeType(r, w, written);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Opens the exporter and initializes the undelying structures.
+     * @throws IOException if an I/O error occurs
+     * @throws RepositoryException if a repository error occurs
+     */
+    public abstract void open() throws IOException, RepositoryException;
+
+    /**
+     * Closes the exporter and releases the undelying structures.
+     * @throws IOException if an I/O error occurs
+     * @throws RepositoryException if a repository error occurs
+     */
+    public abstract void close() throws IOException, RepositoryException;
+
+    public abstract void createDirectory(String relPath)
+            throws IOException;
+
+    public abstract void createDirectory(VaultFile file, String relPath)
+            throws RepositoryException, IOException;
+
+    public abstract void writeFile(InputStream in, String relPath)
+            throws IOException;
+
+    public abstract void writeFile(VaultFile file, String relPath)
+            throws RepositoryException, IOException;
+
+}
\ No newline at end of file

Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/AccessControlHandling.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/AccessControlHandling.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/AccessControlHandling.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/AccessControlHandling.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,59 @@
+/*
+ * 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;
+
+/**
+ * <code>AccessControlHandling</code> defines the behavior when importing
+ * access control nodes.
+ */
+public enum AccessControlHandling {
+
+    /**
+     * Ignores the packaged access control and leaves the target unchanged.
+     */
+    IGNORE,
+
+    /**
+     * Applies the access control provided with the package to the target. this
+     * also removes existing access control.
+     */
+    OVERWRITE,
+
+    /**
+     * Tries to merge access control provided with the package with the one on
+     * the target.
+     *
+     * This is currently not fully supported and behaves like {@link #OVERWRITE}
+     * for existing ACLs. ACLs not in the package are retained.
+     */
+    MERGE,
+
+    /**
+     * Tries to merge access control in the content with the one provided by the package.
+     *
+     * This is currently not fully supported and behaves like {@link #IGNORE}
+     * for existing ACLs. ACLs not in the package are retained.
+     */
+    MERGE_PRESERVE,
+
+    /**
+     * Clears all access control on the target system.
+     */
+    CLEAR,
+
+}