You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ba...@apache.org on 2013/03/07 19:18:19 UTC

svn commit: r1453985 [2/2] - in /jackrabbit/oak/trunk/oak-jcr: ./ src/main/java/org/apache/jackrabbit/oak/jcr/ src/main/java/org/apache/jackrabbit/oak/jcr/xml/

Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/SysViewImportHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/SysViewImportHandler.java?rev=1453985&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/SysViewImportHandler.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/SysViewImportHandler.java Thu Mar  7 18:18:19 2013
@@ -0,0 +1,362 @@
+/*
+ * 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.oak.jcr.xml;
+
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Stack;
+import javax.jcr.InvalidSerializedDataException;
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.ValueFactory;
+
+import org.apache.jackrabbit.commons.NamespaceHelper;
+import org.apache.jackrabbit.oak.plugins.name.NamespaceConstants;
+import org.apache.jackrabbit.oak.spi.xml.NodeInfo;
+import org.apache.jackrabbit.oak.spi.xml.PropInfo;
+import org.apache.jackrabbit.oak.spi.xml.TextValue;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * <code>SysViewImportHandler</code>  ...
+ */
+class SysViewImportHandler extends TargetImportHandler {
+
+    /**
+     * stack of ImportState instances; an instance is pushed onto the stack
+     * in the startElement method every time a sv:node element is encountered;
+     * the same instance is popped from the stack in the endElement method
+     * when the corresponding sv:node element is encountered.
+     */
+    private final Stack<ImportState> stack = new Stack<ImportState>();
+
+    /**
+     * fields used temporarily while processing sv:property and sv:value elements
+     */
+    private NameInfo currentPropName;
+    private int currentPropType = PropertyType.UNDEFINED;
+    private PropInfo.MultipleStatus currentPropMultipleStatus = PropInfo.MultipleStatus.UNKNOWN;
+    // list of appendable value objects
+    private ArrayList<BufferedStringValue> currentPropValues =
+            new ArrayList<BufferedStringValue>();
+    private BufferedStringValue currentPropValue;
+
+    /**
+     * Constructs a new <code>SysViewImportHandler</code>.
+     *
+     * @param importer     the underlying importer
+     * @param valueFactory the value factory
+     */
+    SysViewImportHandler(Importer importer, ValueFactory valueFactory, NamespaceHelper helper) {
+        super(importer, valueFactory, helper);
+    }
+
+    private void processNode(ImportState state, boolean start, boolean end)
+            throws SAXException {
+        if (!start && !end) {
+            return;
+        }
+        String[] mixinNames = null;
+        if (state.mixinNames != null) {
+            mixinNames = state.mixinNames.toArray(
+                    new String[state.mixinNames.size()]);
+        }
+        String id = state.uuid;
+        NodeInfo node =
+                new NodeInfo(state.nodeName, state.nodeTypeName, mixinNames, id);
+        // call Importer
+        try {
+            if (start) {
+                importer.startNode(node, state.props);
+                // dispose temporary property values
+                for (PropInfo pi : state.props) {
+                    pi.dispose();
+                }
+
+            }
+            if (end) {
+                importer.endNode(node);
+            }
+        } catch (RepositoryException re) {
+            throw new SAXException(re);
+        }
+    }
+
+    //-------------------------------------------------------< ContentHandler >
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void startElement(String namespaceURI, String localName,
+                             String qName, Attributes atts)
+            throws SAXException {
+        // check element name
+        if (namespaceURI.equals(NamespaceConstants.NAMESPACE_SV) && "node".equals(localName)) {
+            // sv:node element
+
+            // node name (value of sv:name attribute)
+            String svName = getAttribute(atts, NamespaceConstants.NAMESPACE_SV, "name");
+            if (svName == null) {
+                throw new SAXException(new InvalidSerializedDataException(
+                        "missing mandatory sv:name attribute of element sv:node"));
+            }
+
+            if (!stack.isEmpty()) {
+                // process current node first
+                ImportState current = stack.peek();
+                // need to start current node
+                if (!current.started) {
+                    processNode(current, true, false);
+                    current.started = true;
+                }
+            }
+
+            // push new ImportState instance onto the stack
+            ImportState state = new ImportState();
+            try {
+                state.nodeName = new NameInfo(svName).getRepoQualifiedName();
+            } catch (RepositoryException e) {
+                throw new SAXException(new InvalidSerializedDataException("illegal node name: " + svName, e));
+            }
+            stack.push(state);
+        } else if (namespaceURI.equals(NamespaceConstants.NAMESPACE_SV) && "property".equals(localName)) {
+            // sv:property element
+
+            // reset temp fields
+            currentPropValues.clear();
+
+            // property name (value of sv:name attribute)
+            String svName = getAttribute(atts, NamespaceConstants.NAMESPACE_SV, "name");
+            if (svName == null) {
+                throw new SAXException(new InvalidSerializedDataException(
+                        "missing mandatory sv:name attribute of element sv:property"));
+            }
+            try {
+                currentPropName = new NameInfo(svName);
+            } catch (RepositoryException e) {
+                throw new SAXException(new InvalidSerializedDataException("illegal property name: " + svName, e));
+            }
+            // property type (sv:type attribute)
+            String type = getAttribute(atts, NamespaceConstants.NAMESPACE_SV, "type");
+            if (type == null) {
+                throw new SAXException(new InvalidSerializedDataException(
+                        "missing mandatory sv:type attribute of element sv:property"));
+            }
+            try {
+                currentPropType = PropertyType.valueFromName(type);
+            } catch (IllegalArgumentException e) {
+                throw new SAXException(new InvalidSerializedDataException(
+                        "Unknown property type: " + type, e));
+            }
+            // 'multi-value' hint (sv:multiple attribute)
+            String multiple = getAttribute(atts, NamespaceConstants.NAMESPACE_SV, "multiple");
+            if (multiple != null) {
+                currentPropMultipleStatus = PropInfo.MultipleStatus.MULTIPLE;
+            } else {
+                currentPropMultipleStatus = PropInfo.MultipleStatus.UNKNOWN;
+            }
+        } else if (namespaceURI.equals(NamespaceConstants.NAMESPACE_SV) && "value".equals(localName)) {
+            // sv:value element
+            currentPropValue = new BufferedStringValue(valueFactory, currentNamePathMapper());
+            String xsiType = atts.getValue("xsi:type");
+            currentPropValue.setBase64("xs:base64Binary".equals(xsiType));
+        } else {
+            throw new SAXException(new InvalidSerializedDataException(
+                    "Unexpected element in system view xml document: {" + namespaceURI + "}" + localName));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void characters(char[] ch, int start, int length)
+            throws SAXException {
+        if (currentPropValue != null) {
+            // property value (character data of sv:value element)
+            try {
+                currentPropValue.append(ch, start, length);
+            } catch (IOException ioe) {
+                throw new SAXException("error while processing property value",
+                        ioe);
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void ignorableWhitespace(char[] ch, int start, int length)
+            throws SAXException {
+        if (currentPropValue != null) {
+            // property value
+
+            // data reported by the ignorableWhitespace event within
+            // sv:value tags is considered part of the value
+            try {
+                currentPropValue.append(ch, start, length);
+            } catch (IOException ioe) {
+                throw new SAXException("error while processing property value",
+                        ioe);
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void endElement(String namespaceURI, String localName, String qName)
+            throws SAXException {
+        // check element name
+        ImportState state = stack.peek();
+        if (namespaceURI.equals(NamespaceConstants.NAMESPACE_SV) && "node".equals(localName)) {
+            // sv:node element
+            if (!state.started) {
+                // need to start & end current node
+                processNode(state, true, true);
+                state.started = true;
+            } else {
+                // need to end current node
+                processNode(state, false, true);
+            }
+            // pop current state from stack
+            stack.pop();
+        } else if (namespaceURI.equals(NamespaceConstants.NAMESPACE_SV) && "property".equals(localName)) {
+            // sv:property element
+
+            // check if all system properties (jcr:primaryType, jcr:uuid etc.)
+            // have been collected and create node as necessary primaryType
+            if (currentPropName != null
+                    && currentPropName.getNamespaceUri().equals(NamespaceRegistry.NAMESPACE_JCR)
+                    && currentPropName.getLocalName().equals("primaryType")) {
+                BufferedStringValue val = currentPropValues.get(0);
+                String s = null;
+                try {
+                    s = val.retrieve();
+                    state.nodeTypeName = new NameInfo(s).getRepoQualifiedName();
+                } catch (IOException e) {
+                    throw new SAXException(new InvalidSerializedDataException("illegal node type name: " + s, e));
+                } catch (RepositoryException e) {
+                    throw new SAXException(new InvalidSerializedDataException("illegal node type name: " + s, e));
+                }
+            } else if (currentPropName != null
+                    && currentPropName.getNamespaceUri().equals(NamespaceRegistry.NAMESPACE_JCR)
+                    && currentPropName.getLocalName().equals("mixinTypes")) {
+                if (state.mixinNames == null) {
+                    state.mixinNames = new ArrayList<String>(currentPropValues.size());
+                }
+                for (BufferedStringValue val : currentPropValues) {
+                    String s = null;
+                    try {
+                        s = val.retrieve();
+                        state.mixinNames.add(new NameInfo(s).getRepoQualifiedName());
+                    } catch (IOException ioe) {
+                        throw new SAXException("error while retrieving value", ioe);
+                    } catch (RepositoryException e) {
+                        throw new SAXException(new InvalidSerializedDataException("illegal mixin type name: " + s, e));
+                    }
+                }
+            } else if (currentPropName != null
+                    && currentPropName.getNamespaceUri().equals(NamespaceRegistry.NAMESPACE_JCR)
+                    && currentPropName.getLocalName().equals("uuid")) {
+                BufferedStringValue val = currentPropValues.get(0);
+                try {
+                    state.uuid = val.retrieve();
+                } catch (IOException ioe) {
+                    throw new SAXException("error while retrieving value", ioe);
+                }
+            } else {
+                if (currentPropMultipleStatus == PropInfo.MultipleStatus.UNKNOWN
+                        && currentPropValues.size() != 1) {
+                    currentPropMultipleStatus = PropInfo.MultipleStatus.MULTIPLE;
+                }
+                PropInfo prop = new PropInfo(
+                        currentPropName == null ? null : currentPropName.getRepoQualifiedName(),
+                        currentPropType,
+                        currentPropValues.toArray(new TextValue[currentPropValues.size()]),
+                        currentPropMultipleStatus);
+                state.props.add(prop);
+            }
+            // reset temp fields
+            currentPropValues.clear();
+        } else if (namespaceURI.equals(NamespaceConstants.NAMESPACE_SV) && "value".equals(localName)) {
+            // sv:value element
+            currentPropValues.add(currentPropValue);
+            // reset temp fields
+            currentPropValue = null;
+        } else {
+            throw new SAXException(new InvalidSerializedDataException("invalid element in system view xml document: " + localName));
+        }
+    }
+
+    //--------------------------------------------------------< inner classes >
+
+    /**
+     * The state of parsing the XML stream.
+     */
+    static class ImportState {
+        /**
+         * name of current node
+         */
+        String nodeName;
+        /**
+         * primary type of current node
+         */
+        String nodeTypeName;
+        /**
+         * list of mixin types of current node
+         */
+        List<String> mixinNames;
+        /**
+         * uuid of current node
+         */
+        String uuid;
+
+        /**
+         * list of PropInfo instances representing properties of current node
+         */
+        List<PropInfo> props = new ArrayList<PropInfo>();
+
+        /**
+         * flag indicating whether startNode() has been called for current node
+         */
+        boolean started;
+    }
+
+    //-------------------------------------------------------------< private >
+
+    /**
+     * Returns the value of the named XML attribute.
+     *
+     * @param attributes set of XML attributes
+     * @param namespaceUri attribute namespace
+     * @param localName attribute local name
+     * @return attribute value,
+     *         or <code>null</code> if the named attribute is not found
+     */
+    private static String getAttribute(Attributes attributes, String namespaceUri, String localName) {
+        return attributes.getValue(namespaceUri, localName);
+    }
+
+}

Added: jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/TargetImportHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/TargetImportHandler.java?rev=1453985&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/TargetImportHandler.java (added)
+++ jackrabbit/oak/trunk/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/xml/TargetImportHandler.java Thu Mar  7 18:18:19 2013
@@ -0,0 +1,213 @@
+/*
+ * 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.oak.jcr.xml;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.jcr.RepositoryException;
+import javax.jcr.ValueFactory;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ListMultimap;
+import org.apache.jackrabbit.commons.NamespaceHelper;
+import org.apache.jackrabbit.oak.namepath.LocalNameMapper;
+import org.apache.jackrabbit.oak.namepath.NamePathMapper;
+import org.apache.jackrabbit.oak.namepath.NamePathMapperImpl;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * <code>TargetImportHandler</code> serves as the base class for the concrete
+ * classes <code>{@link DocViewImportHandler}</code> and
+ * <code>{@link SysViewImportHandler}</code>.
+ */
+public abstract class TargetImportHandler extends DefaultHandler {
+
+    protected final Importer importer;
+    protected final ValueFactory valueFactory;
+    protected final NamespaceHelper helper;
+    private final ListMultimap<String, String> documentContext = ArrayListMultimap.create();
+    private Map<String, String> documentPrefixMap = Collections.emptyMap();
+
+    protected TargetImportHandler(Importer importer, ValueFactory valueFactory, NamespaceHelper helper) {
+        this.importer = importer;
+        this.valueFactory = valueFactory;
+        this.helper = helper;
+    }
+
+    //--------------------------------------------------------< ImportHandler >
+
+    @Override
+    public void startPrefixMapping(String prefix, String uri) throws SAXException {
+        documentContext.put(prefix, uri);
+        documentPrefixMap = createCurrentPrefixMap();
+    }
+
+    @Override
+    public void endPrefixMapping(String prefix) throws SAXException {
+        List<String> uris = documentContext.get(prefix);
+        if (!uris.isEmpty()) {
+            uris.remove(uris.size() - 1);
+        }
+        documentPrefixMap = createCurrentPrefixMap();
+    }
+
+    /**
+     * Initializes the underlying {@link Importer} instance. This method
+     * is called by the XML parser when the XML document starts.
+     *
+     * @throws SAXException if the importer can not be initialized
+     * @see DefaultHandler#startDocument()
+     */
+    @Override
+    public void startDocument() throws SAXException {
+        try {
+            importer.start();
+        } catch (RepositoryException re) {
+            throw new SAXException(re);
+        }
+    }
+
+    /**
+     * Closes the underlying {@link Importer} instance. This method
+     * is called by the XML parser when the XML document ends.
+     *
+     * @throws SAXException if the importer can not be closed
+     * @see DefaultHandler#endDocument()
+     */
+    @Override
+    public void endDocument() throws SAXException {
+        try {
+            importer.end();
+        } catch (RepositoryException re) {
+            throw new SAXException(re);
+        }
+    }
+
+
+    //--------------------------------------------------------
+
+    public NamePathMapper currentNamePathMapper() {
+        return new NamePathMapperImpl(new LocalNameMapper(documentPrefixMap) {
+            @Override
+            protected Map<String, String> getNamespaceMap() {
+                try {
+                    return helper.getNamespaces();
+                } catch (RepositoryException e) {
+                    return Collections.emptyMap();
+                }
+            }
+        });
+    }
+
+    private Map<String, String> createCurrentPrefixMap() {
+        Map<String, String> result = new HashMap<String, String>();
+        Set<Map.Entry<String, Collection<String>>> entries = documentContext.asMap().entrySet();
+        for (Map.Entry<String, Collection<String>> entry : entries) {
+            String key = entry.getKey();
+            List<String> value = (List<String>) entry.getValue();
+            if (value != null && !value.isEmpty()) {
+                result.put(key, value.get(value.size() - 1));
+            }
+        }
+        return result;
+    }
+
+
+    protected class NameInfo {
+
+        private final String localName;
+        private final String docPrefix;
+        private String namespaceUri;
+        private String repoPrefix;
+
+        NameInfo(String docQualifiedName) throws RepositoryException {
+            int idx = docQualifiedName.indexOf(":");
+            if (idx == -1) {
+                docPrefix = null;
+                localName = docQualifiedName;
+            } else {
+                String[] splits = docQualifiedName.split(":", 2);
+                docPrefix = splits[0];
+                localName = splits[1];
+            }
+            init();
+        }
+
+        NameInfo(String docPrefix, String localName) throws RepositoryException {
+            this.localName = localName;
+            this.docPrefix = docPrefix;
+            init();
+        }
+
+        private void init() throws RepositoryException {
+            if (docPrefix == null) {
+                namespaceUri = "";
+                repoPrefix = null;
+            } else {
+                List<String> uris = documentContext.get(docPrefix);
+                if (uris.isEmpty()) {
+                    namespaceUri = helper.getURI(docPrefix);
+                    repoPrefix = docPrefix;
+                } else {
+                    namespaceUri = uris.get(uris.size() - 1);
+                    repoPrefix = helper.getPrefix(namespaceUri);
+                }
+            }
+        }
+
+        String getLocalName() {
+            return localName;
+        }
+
+        String getNamespaceUri() {
+            return namespaceUri;
+        }
+
+        String getDocPrefix() {
+            return docPrefix;
+        }
+
+        String getRepoPrefix() {
+            return repoPrefix;
+        }
+
+        String getDocQualifiedName() {
+            if (docPrefix == null || docPrefix.length() == 0) {
+                return localName;
+            } else {
+                return docPrefix + ":" + localName;
+            }
+        }
+
+        String getRepoQualifiedName() {
+            if (repoPrefix == null || repoPrefix.length() == 0) {
+                return localName;
+            } else {
+                return repoPrefix + ":" + localName;
+            }
+        }
+
+        String getExpandedName() {
+            return "{" + namespaceUri + "}" + localName;
+        }
+    }
+}