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;
+ }
+ }
+}