You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by an...@apache.org on 2006/07/12 15:33:27 UTC

svn commit: r421270 [18/23] - in /jackrabbit/trunk/contrib/spi: ./ commons/ commons/src/ commons/src/main/ commons/src/main/java/ commons/src/main/java/org/ commons/src/main/java/org/apache/ commons/src/main/java/org/apache/jackrabbit/ commons/src/main...

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/DocViewSAXEventGenerator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/DocViewSAXEventGenerator.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/ImportHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/ImportHandler.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/ImportHandler.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/ImportHandler.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,367 @@
+/*
+ * 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.jcr2spi.xml;
+
+import org.apache.jackrabbit.name.NamespaceResolver;
+import org.apache.jackrabbit.name.AbstractNamespaceResolver;
+import org.apache.jackrabbit.name.QName;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.helpers.NamespaceSupport;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
+
+import javax.jcr.NamespaceException;
+import javax.jcr.RepositoryException;
+import javax.jcr.NamespaceRegistry;
+import org.apache.jackrabbit.spi.IdFactory;
+
+/**
+ * An <code>ImportHandler</code> instance can be used to import serialized
+ * data in System View XML or Document View XML. Processing of the XML is
+ * handled by specialized <code>ContentHandler</code>s
+ * (i.e. <code>SysViewImportHandler</code> and <code>DocViewImportHandler</code>).
+ * <p/>
+ * The actual task of importing though is delegated to the implementation of
+ * the <code>{@link Importer}</code> interface.
+ * <p/>
+ * <b>Important Note:</b>
+ * <p/>
+ * These SAX Event Handlers expect that Namespace URI's and local names are
+ * reported in the <code>start/endElement</code> events and that
+ * <code>start/endPrefixMapping</code> events are reported
+ * (i.e. default SAX2 Namespace processing).
+ */
+public class ImportHandler extends DefaultHandler {
+
+    private static Logger log = LoggerFactory.getLogger(ImportHandler.class);
+
+    private final Importer importer;
+    private final NamespaceRegistry nsReg;
+    private final NamespaceResolver nsResolver;
+    private final IdFactory idFactory;
+
+    private Locator locator;
+    private ContentHandler targetHandler;
+    private boolean systemViewXML;
+    private boolean initialized;
+
+    private final NamespaceContext nsContext;
+
+    /**
+     * this flag is used to determine whether a namespace context needs to be
+     * started in the startElement event or if the namespace context has already
+     * been started in a preceeding startPrefixMapping event;
+     * the flag is set per element in the first startPrefixMapping event and is
+     * cleared again in the following startElement event;
+     */
+    protected boolean nsContextStarted;
+
+    // DIFF JACKRABBIT: pass NamespaceRegistry instead of impl.
+    public ImportHandler(Importer importer, NamespaceResolver nsResolver,
+                         NamespaceRegistry nsReg, IdFactory idFactory) {
+        this.importer = importer;
+        this.nsResolver = nsResolver;
+        this.nsReg = nsReg;
+        this.idFactory = idFactory;
+
+        nsContext = new NamespaceContext();
+    }
+
+    //---------------------------------------------------------< ErrorHandler >
+    /**
+     * {@inheritDoc}
+     */
+    public void warning(SAXParseException e) throws SAXException {
+        // log exception and carry on...
+        log.warn("warning encountered at line: " + e.getLineNumber()
+                + ", column: " + e.getColumnNumber()
+                + " while parsing XML stream", e);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void error(SAXParseException e) throws SAXException {
+        // log exception and carry on...
+        log.error("error encountered at line: " + e.getLineNumber()
+                + ", column: " + e.getColumnNumber()
+                + " while parsing XML stream: " + e.toString());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void fatalError(SAXParseException e) throws SAXException {
+        // log and re-throw exception
+        log.error("fatal error encountered at line: " + e.getLineNumber()
+                + ", column: " + e.getColumnNumber()
+                + " while parsing XML stream: " + e.toString());
+        throw e;
+    }
+
+    //-------------------------------------------------------< ContentHandler >
+    /**
+     * {@inheritDoc}
+     */
+    public void startDocument() throws SAXException {
+        systemViewXML = false;
+        initialized = false;
+        targetHandler = null;
+
+        /**
+         * start initial context containing existing mappings reflected
+         * by nsResolver
+         */
+        nsContext.reset();
+        nsContext.pushContext();
+        try {
+            String[] uris = nsReg.getURIs();
+            for (int i = 0; i < uris.length; i++) {
+                nsContext.declarePrefix(nsResolver.getPrefix(uris[i]), uris[i]);
+            }
+        } catch (RepositoryException re) {
+            throw new SAXException(re);
+        }
+
+        // initialize flag
+        nsContextStarted = false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void endDocument() throws SAXException {
+        // delegate to target handler
+        targetHandler.endDocument();
+        // cleanup
+        nsContext.reset();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void startPrefixMapping(String prefix, String uri)
+            throws SAXException {
+        // check if new context needs to be started
+        if (!nsContextStarted) {
+            // entering new namespace context
+            nsContext.pushContext();
+            nsContextStarted = true;
+        }
+
+        try {
+            // this will trigger NamespaceException if namespace is unknown
+            nsContext.getPrefix(uri);
+        } catch (NamespaceException nse) {
+            // namespace is not yet registered ...
+            try {
+                String newPrefix;
+                if ("".equals(prefix)) {
+                    /**
+                     * the xml document specifies a default namespace
+                     * (i.e. an empty prefix); we need to create a random
+                     * prefix as the empty prefix is reserved according
+                     * to the JCR spec.
+                     */
+                    newPrefix = getUniquePrefix(uri);
+                } else {
+                    newPrefix = prefix;
+                }
+                // register new namespace
+                nsReg.registerNamespace(newPrefix, uri);
+            } catch (RepositoryException re) {
+                throw new SAXException(re);
+            }
+        }
+        // map namespace in this context to given prefix
+        nsContext.declarePrefix(prefix, uri);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void endPrefixMapping(String prefix) throws SAXException {
+        /**
+         * nothing to do here as namespace context has already been popped
+         * in endElement event
+         */
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void startElement(String namespaceURI, String localName, String qName,
+                             Attributes atts) throws SAXException {
+        // check if new context needs to be started
+        if (!nsContextStarted) {
+            // there hasn't been a preceeding startPrefixMapping event
+            // so enter new namespace context
+            nsContext.pushContext();
+        } else {
+            // reset flag
+            nsContextStarted = false;
+        }
+
+        if (!initialized) {
+            // the namespace of the first element determines the type of XML
+            // (system view/document view)
+            systemViewXML = QName.NS_SV_URI.equals(namespaceURI);
+
+            if (systemViewXML) {
+                targetHandler = new SysViewImportHandler(importer, nsContext, idFactory);
+            } else {
+                targetHandler = new DocViewImportHandler(importer, nsContext, idFactory);
+            }
+            targetHandler.startDocument();
+            initialized = true;
+        }
+
+        // delegate to target handler
+        targetHandler.startElement(namespaceURI, localName, qName, atts);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void characters(char[] ch, int start, int length) throws SAXException {
+        // delegate to target handler
+        targetHandler.characters(ch, start, length);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void endElement(String namespaceURI, String localName, String qName)
+            throws SAXException {
+        // leaving element, pop namespace context
+        nsContext.popContext();
+
+        // delegate to target handler
+        targetHandler.endElement(namespaceURI, localName, qName);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setDocumentLocator(Locator locator) {
+        this.locator = locator;
+    }
+
+    //--------------------------------------------------------< inner classes >
+    /**
+     * <code>NamespaceContext</code> supports scoped namespace declarations.
+     */
+    class NamespaceContext extends AbstractNamespaceResolver {
+
+        private final NamespaceSupport nsContext;
+
+        /**
+         * NamespaceSupport doesn't accept "" as default uri;
+         * internally we're using " " instead
+         */
+        private static final String DUMMY_DEFAULT_URI = " ";
+
+        NamespaceContext() {
+            nsContext = new NamespaceSupport();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        void popContext() {
+            nsContext.popContext();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        void pushContext() {
+            nsContext.pushContext();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        void reset() {
+            nsContext.reset();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        boolean declarePrefix(String prefix, String uri) {
+            if (QName.NS_DEFAULT_URI.equals(uri)) {
+                uri = DUMMY_DEFAULT_URI;
+            }
+            return nsContext.declarePrefix(prefix, uri);
+        }
+
+        //------------------------------------------------< NamespaceResolver >
+        /**
+         * {@inheritDoc}
+         */
+        public String getURI(String prefix) throws NamespaceException {
+            String uri = nsContext.getURI(prefix);
+            if (uri == null) {
+                throw new NamespaceException("unknown prefix");
+            } else if (DUMMY_DEFAULT_URI.equals(uri)) {
+                return QName.NS_DEFAULT_URI;
+            } else {
+                return uri;
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public String getPrefix(String uri) throws NamespaceException {
+            if (QName.NS_DEFAULT_URI.equals(uri)) {
+                uri = DUMMY_DEFAULT_URI;
+            }
+            String prefix = nsContext.getPrefix(uri);
+            if (prefix == null) {
+                /**
+                 * NamespaceSupport#getPrefix will never return the empty
+                 * (default) prefix; we have to do a reverse-lookup to check
+                 * whether it's the current default namespace
+                 */
+                if (uri.equals(nsContext.getURI(QName.NS_EMPTY_PREFIX))) {
+                    return QName.NS_EMPTY_PREFIX;
+                }
+                throw new NamespaceException("unknown uri");
+            }
+            return prefix;
+        }
+    }
+
+    /**
+     * Returns a prefix that is unique among the already registered prefixes.
+     *
+     * @param uriHint namespace uri that serves as hint for the prefix generation
+     * @return a unique prefix
+     */
+    // DIFF JACKRABBIT: method moved from NamespaceRegistryImpl (only used here)
+    public String getUniquePrefix(String uriHint) throws RepositoryException {
+        // @todo smarter unique prefix generation
+        return "_pre" + (nsReg.getPrefixes().length + 1);
+    }
+}

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/ImportHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/ImportHandler.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/Importer.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/Importer.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/Importer.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/Importer.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,177 @@
+/*
+ * 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.jcr2spi.xml;
+
+import org.apache.jackrabbit.name.NamespaceResolver;
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.spi.NodeId;
+
+import javax.jcr.RepositoryException;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.List;
+
+/**
+ * The <code>Importer</code> interface ...
+ */
+public interface Importer {
+
+    /**
+     * @throws RepositoryException
+     */
+    void start() throws RepositoryException;
+
+    /**
+     * @param nodeInfo
+     * @param propInfos list of <code>PropInfo</code> instances
+     * @param nsContext prefix mappings of current context
+     * @throws RepositoryException
+     */
+    void startNode(NodeInfo nodeInfo, List propInfos, NamespaceResolver nsContext)
+            throws RepositoryException;
+
+    /**
+     * @param nodeInfo
+     * @throws RepositoryException
+     */
+    void endNode(NodeInfo nodeInfo) throws RepositoryException;
+
+    /**
+     * @throws RepositoryException
+     */
+    void end() throws RepositoryException;
+
+    //--------------------------------------------------------< inner classes >
+    static class NodeInfo {
+        private QName name;
+        private QName nodeTypeName;
+        private QName[] mixinNames;
+        private NodeId id;
+
+        public NodeInfo() {
+        }
+
+        public NodeInfo(QName name, QName nodeTypeName, QName[] mixinNames, NodeId id) {
+            this.name = name;
+            this.nodeTypeName = nodeTypeName;
+            this.mixinNames = mixinNames;
+            this.id = id;
+        }
+
+        public void setName(QName name) {
+            this.name = name;
+        }
+
+        public QName getName() {
+            return name;
+        }
+
+        public void setNodeTypeName(QName nodeTypeName) {
+            this.nodeTypeName = nodeTypeName;
+        }
+
+        public QName getNodeTypeName() {
+            return nodeTypeName;
+        }
+
+        public void setMixinNames(QName[] mixinNames) {
+            this.mixinNames = mixinNames;
+        }
+
+        public QName[] getMixinNames() {
+            return mixinNames;
+        }
+
+        public void setId(NodeId id) {
+            this.id = id;
+        }
+
+        public NodeId getId() {
+            return id;
+        }
+    }
+
+    static class PropInfo {
+        private QName name;
+        private int type;
+        private TextValue[] values;
+
+        public PropInfo() {
+        }
+
+        public PropInfo(QName name, int type, TextValue[] values) {
+            this.name = name;
+            this.type = type;
+            this.values = values;
+        }
+
+        public void setName(QName name) {
+            this.name = name;
+        }
+
+        public QName getName() {
+            return name;
+        }
+
+        public void setType(int type) {
+            this.type = type;
+        }
+
+        public int getType() {
+            return type;
+        }
+
+        public void setValues(TextValue[] values) {
+            this.values = values;
+        }
+
+        public TextValue[] getValues() {
+            return values;
+        }
+    }
+
+    /**
+     * <code>TextValue</code> represents a serialized property value read
+     * from a System or Document View XML document.
+     */
+    interface TextValue {
+        /**
+         * Returns the length of the serialized value.
+         *
+         * @return the length of the serialized value
+         * @throws IOException if an I/O error occurs
+         */
+        long length() throws IOException;
+
+        /**
+         * Retrieves the serialized value.
+         *
+         * @return the serialized value
+         * @throws IOException if an I/O error occurs
+         */
+        String retrieve() throws IOException;
+
+        /**
+         * Returns a <code>Reader</code> for reading the serialized value.
+         *
+         * @return a <code>Reader</code> for reading the serialized value.
+         * @throws IOException if an I/O error occurs
+         */
+        Reader reader() throws IOException;
+    }
+}

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/Importer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/Importer.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/ImporterImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/ImporterImpl.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/ImporterImpl.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/ImporterImpl.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,616 @@
+/*
+ * 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.jcr2spi.xml;
+
+import org.apache.jackrabbit.jcr2spi.state.NodeState;
+import org.apache.jackrabbit.jcr2spi.state.PropertyState;
+import org.apache.jackrabbit.jcr2spi.state.ItemStateValidator;
+import org.apache.jackrabbit.jcr2spi.state.ItemState;
+import org.apache.jackrabbit.jcr2spi.state.SessionItemStateManager;
+import org.apache.jackrabbit.jcr2spi.SessionImpl;
+import org.apache.jackrabbit.jcr2spi.HierarchyManager;
+import org.apache.jackrabbit.jcr2spi.SessionListener;
+import org.apache.jackrabbit.jcr2spi.util.ReferenceChangeTracker;
+import org.apache.jackrabbit.jcr2spi.nodetype.EffectiveNodeType;
+import org.apache.jackrabbit.jcr2spi.operation.AddNode;
+import org.apache.jackrabbit.jcr2spi.operation.Remove;
+import org.apache.jackrabbit.jcr2spi.operation.AddProperty;
+import org.apache.jackrabbit.jcr2spi.operation.SetPropertyValue;
+import org.apache.jackrabbit.jcr2spi.operation.SetMixin;
+import org.apache.jackrabbit.jcr2spi.operation.Operation;
+import org.apache.jackrabbit.name.NamespaceResolver;
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.util.Base64;
+import org.apache.jackrabbit.util.TransientFileFactory;
+import org.apache.jackrabbit.spi.IdFactory;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.ImportUUIDBehavior;
+import javax.jcr.ItemExistsException;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.PropertyType;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Session;
+import javax.jcr.Value;
+import javax.jcr.lock.LockException;
+import javax.jcr.version.VersionException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import org.apache.jackrabbit.spi.NodeId;
+import org.apache.jackrabbit.spi.PropertyId;
+import org.apache.jackrabbit.name.Path;
+import org.apache.jackrabbit.name.MalformedPathException;
+import org.apache.jackrabbit.spi.QPropertyDefinition;
+import org.apache.jackrabbit.spi.QNodeDefinition;
+import org.apache.jackrabbit.value.QValue;
+import org.apache.jackrabbit.value.ValueHelper;
+import org.apache.jackrabbit.value.ValueFormat;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.Reader;
+import java.io.IOException;
+import java.util.Stack;
+import java.util.List;
+import java.util.Iterator;
+
+/**
+ * <code>ImporterImpl</code>...
+ */
+public class ImporterImpl implements Importer, SessionListener {
+
+    private static Logger log = LoggerFactory.getLogger(ImporterImpl.class);
+
+    private final NodeState importTarget;
+    private final SessionImpl session;
+    private final HierarchyManager hierMgr;
+
+    private final SessionItemStateManager stateMgr;
+    private final ItemStateValidator validator;
+    final IdFactory idFactory;
+
+    private final Stack parents;
+
+    private final int uuidBehavior;
+    private final boolean isWspImport;
+
+    private boolean importerClosed;
+    private boolean sessionClosed;
+
+    /**
+     * helper object that keeps track of remapped uuid's and imported reference
+     * properties that might need correcting depending on the uuid mappings
+     */
+    private final ReferenceChangeTracker refTracker;
+
+
+    /**
+     * Creates a new <code>WorkspaceImporter</code> instance.
+     *
+     * @param parentPath qualified path of target node where to add the imported
+     * subtree
+     * @param session
+     * @param validator
+     * @param uuidBehavior Flag that governs how incoming UUIDs are handled.
+     * @throws PathNotFoundException If no node exists at <code>parentPath</code>
+     * or if the current session is not granted read access.
+     * @throws ConstraintViolationException If the node at <code>parentPath</code>
+     * is protected.
+     * @throws VersionException If the node at <code>parentPath</code> is not
+     * checked-out.
+     * @throws LockException If a lock prevents the addition of the subtree.
+     * @throws RepositoryException If another error occurs.
+     */
+    public ImporterImpl(Path parentPath, SessionImpl session, HierarchyManager hierManager,
+                     SessionItemStateManager stateManager, ItemStateValidator validator,
+                     IdFactory idFactory, int uuidBehavior, boolean isWspImport)
+        throws PathNotFoundException, ConstraintViolationException,
+        VersionException, LockException, RepositoryException {
+
+        this.validator = validator;
+        this.session = session;
+        this.hierMgr = hierManager;
+        this.stateMgr = stateManager;
+        this.idFactory = idFactory;
+
+        // perform preliminary checks
+        importTarget = validator.getNodeState(parentPath);
+        // DIFF JR: remove check for overall writability on target-node.
+
+        this.uuidBehavior = uuidBehavior;
+        this.isWspImport = isWspImport;
+
+        refTracker = new ReferenceChangeTracker();
+        parents = new Stack();
+        parents.push(importTarget);
+    }
+
+    //-----------------------------------------------------------< Importer >---
+    /**
+     * {@inheritDoc}
+     */
+    public void start() throws RepositoryException {
+        // explicitely set status of importer and start listening on session
+        setClosed(false);
+    }
+
+   /**
+     * {@inheritDoc}
+     */
+    public void startNode(NodeInfo nodeInfo, List propInfos, NamespaceResolver nsContext)
+            throws RepositoryException {
+       if (isClosed()) {
+           // workspace-importer only: ignore if import has been aborted before.
+           return;
+       }
+       boolean succeeded = false;
+       try {
+           checkSession();
+           NodeState parent = (NodeState) parents.peek();
+           if (parent == null) {
+               // parent node was skipped, skip this child node also
+               parents.push(null); // push null onto stack for skipped node
+               log.debug("Skipping node '" + nodeInfo.getName() + "'.");
+               return;
+           }
+
+           NodeState nodeState = null;
+           if (parent.hasChildNodeEntry(nodeInfo.getName())) {
+               // a node with that name already exists...
+               NodeState.ChildNodeEntry entry = parent.getChildNodeEntry(nodeInfo.getName(), 1);
+               NodeState existing = validator.getNodeState(entry.getId());
+               QNodeDefinition def = existing.getDefinition();
+               if (!def.allowsSameNameSiblings()) {
+                   // existing doesn't allow same-name siblings, check for conflicts
+                   EffectiveNodeType entExisting = validator.getEffectiveNodeType(existing);
+                   if (def.isProtected() && entExisting.includesNodeType(nodeInfo.getNodeTypeName())) {
+                       // skip protected node
+                       parents.push(null); // push null onto stack for skipped node
+                       log.debug("skipping protected node " + hierMgr.safeGetJCRPath(existing.getId()));
+                       return;
+                   }
+                   if (def.isAutoCreated() && entExisting.includesNodeType(nodeInfo.getNodeTypeName())) {
+                       // this node has already been auto-created, no need to create it
+                       nodeState = existing;
+                   } else {
+                       throw new ItemExistsException(hierMgr.safeGetJCRPath(existing.getId()));
+                   }
+               }
+           }
+
+           if (nodeState == null) {
+               // node does not exist -> create new one
+               if (nodeInfo.getId() == null) {
+                   // no potential uuid conflict, add new node from given info
+                   nodeState = importNode(nodeInfo, parent);
+               } else {
+                   // potential uuid conflict
+                   try {
+                       NodeState conflicting = validator.getNodeState(nodeInfo.getId());
+                       nodeState = resolveUUIDConflict(parent, conflicting, nodeInfo);
+                   } catch (ItemNotFoundException infe) {
+                       // no conflict: create new with given uuid
+                       nodeState = importNode(nodeInfo, parent);
+                   }
+               }
+           }
+
+           // node state may be 'null' if applicable def is protected
+           if (nodeState != null) {
+               // process properties
+               Iterator iter = propInfos.iterator();
+               while (iter.hasNext()) {
+                   PropInfo pi = (PropInfo) iter.next();
+                   importProperty(pi, nodeState, nsContext);
+               }
+           }
+
+           // push current nodeState onto stack of parents
+           parents.push(nodeState);
+           succeeded = true;
+       } finally {
+           // workspace-importer only: abort the import by closing the importer
+           // in case of failure.
+           if (!succeeded && isWspImport) {
+               setClosed(true);
+           }
+       }
+   }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void endNode(NodeInfo nodeInfo) throws RepositoryException {
+        if(isClosed()) {
+            // workspace-importer only: ignore if import has been aborted before.
+            return;
+        }
+        parents.pop();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void end() throws RepositoryException {
+        if(isClosed()) {
+            // workspace-importer only: ignore if import has been aborted before.
+            return;
+        }
+
+        try {
+            checkSession();
+            // adjust references refering to remapped uuids
+            stateMgr.adjustReferences(refTracker);
+        } finally {
+            // close this importer since we are done.
+            setClosed(true);
+        }
+    }
+    //----------------------------------------------------< SessionListener >---
+    /**
+     *
+     * @param session
+     * @see SessionListener#loggingOut(Session)
+     */
+    public void loggingOut(Session session) {
+        // the session will be be valid any more, thus any further calls on
+        // the importer must fail
+        sessionClosed = true;
+    }
+
+    /**
+     *
+     * @param session
+     * @see SessionListener#loggedOut(Session)
+     */
+    public void loggedOut(Session session) {
+        // ignore
+    }
+
+    //--------------------------------------------< Importer/Session Status >---
+    private void setClosed(boolean isClosed) {
+        importerClosed = isClosed;
+        if (isClosed) {
+            session.removeListener(this);
+        } else {
+            session.addListener(this);
+        }
+    }
+
+    private boolean isClosed() {
+        return importerClosed;
+    }
+
+    private void checkSession() throws RepositoryException {
+        if (sessionClosed) {
+            throw new RepositoryException("This session has been closed.");
+        }
+    }
+
+    //----------------------------------------------------< Private methods >---
+    /**
+     * @param parent
+     * @param conflicting
+     * @param nodeInfo
+     * @return
+     * @throws RepositoryException
+     */
+    NodeState resolveUUIDConflict(NodeState parent, NodeState conflicting,
+                                  NodeInfo nodeInfo) throws RepositoryException {
+        NodeState nodeState;
+        switch (uuidBehavior) {
+            case ImportUUIDBehavior.IMPORT_UUID_CREATE_NEW:
+                NodeId original = nodeInfo.getId();
+                // reset id on nodeInfo to force creation with new uuid:
+                nodeInfo.setId(null);
+                nodeState = importNode(nodeInfo, parent);
+                if (nodeState != null) {
+                    // remember uuid mapping
+                    EffectiveNodeType ent = validator.getEffectiveNodeType(nodeState);
+                    if (ent.includesNodeType(QName.MIX_REFERENCEABLE)) {
+                        refTracker.mappedNodeIds(original, nodeState.getNodeId());
+                    }
+                }
+                break;
+
+            case ImportUUIDBehavior.IMPORT_UUID_COLLISION_THROW:
+                String msg = "a node with uuid " + nodeInfo.getId() + " already exists!";
+                log.debug(msg);
+                throw new ItemExistsException(msg);
+
+            case ImportUUIDBehavior.IMPORT_UUID_COLLISION_REMOVE_EXISTING:
+                // make sure conflicting node is not importTarget or an ancestor thereof
+                Path p0 = hierMgr.getQPath(importTarget.getId());
+                Path p1 = hierMgr.getQPath(conflicting.getId());
+                try {
+                    if (p1.equals(p0) || p1.isAncestorOf(p0)) {
+                        msg = "cannot remove ancestor node";
+                        log.debug(msg);
+                        throw new ConstraintViolationException(msg);
+                    }
+                } catch (MalformedPathException e) {
+                    // should never get here...
+                    msg = "internal error: failed to determine degree of relationship";
+                    log.error(msg, e);
+                    throw new RepositoryException(msg, e);
+                }
+                // do remove conflicting (recursive) including validation check
+                Operation op = Remove.create(conflicting);
+                stateMgr.execute(op);
+                // create new with given uuid:
+                nodeState = importNode(nodeInfo, parent);
+                break;
+
+            case ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING:
+                NodeId parentId = conflicting.getParentId();
+                if (parentId == null) {
+                    msg = "Root node cannot be replaced";
+                    log.debug(msg);
+                    throw new RepositoryException(msg);
+                }
+                // 'replace' current parent with parent of conflicting
+                try {
+                    parent = validator.getNodeState(parentId);
+                } catch (ItemNotFoundException infe) {
+                    // should never get here...
+                    msg = "Internal error: failed to retrieve parent state";
+                    log.error(msg, infe);
+                    throw new RepositoryException(msg, infe);
+                }
+                // do remove conflicting (recursive), including validation checks
+                op = Remove.create(conflicting);
+                stateMgr.execute(op);
+                // create new with given uuid at same location as conflicting
+                nodeState = importNode(nodeInfo, parent);
+                break;
+
+            default:
+                msg = "Unknown uuidBehavior: " + uuidBehavior;
+                log.debug(msg);
+                throw new RepositoryException(msg);
+        }
+        return nodeState;
+    }
+
+    /**
+     *
+     * @param nodeInfo
+     * @param parent
+     * @return
+     * @throws ConstraintViolationException
+     * @throws ItemNotFoundException
+     * @throws RepositoryException
+     */
+    private NodeState importNode(NodeInfo nodeInfo, NodeState parent) throws ConstraintViolationException, ItemNotFoundException, RepositoryException {
+        QName nodeName = nodeInfo.getName();
+        if (parent.hasPropertyName(nodeName)) {
+            /**
+             * a property with the same name already exists; if this property
+             * has been imported as well (e.g. through document view import
+             * where an element can have the same name as one of the attributes
+             * of its parent element) we have to rename the conflicting property;
+             *
+             * see http://issues.apache.org/jira/browse/JCR-61
+             */
+            PropertyState conflicting = validator.getPropertyState(parent.getNodeId(), nodeName);
+            if (conflicting.getStatus() == ItemState.STATUS_NEW) {
+                // assume this property has been imported as well;
+                // rename conflicting property
+                // @todo use better reversible escaping scheme to create unique name
+                QName newName = new QName(nodeName.getNamespaceURI(), nodeName.getLocalName() + "_");
+                if (parent.hasPropertyName(newName)) {
+                    newName = new QName(newName.getNamespaceURI(), newName.getLocalName() + "_");
+                }
+                // since name changes, need to find new applicable definition
+                QPropertyDefinition propDef;
+                if (conflicting.getValues().length == 1) {
+                    // could be single- or multi-valued (n == 1)
+                    try {
+                        // try single-valued
+                        propDef = validator.getApplicablePropertyDefinition(newName, conflicting.getType(), false, parent);
+                    } catch (ConstraintViolationException cve) {
+                        // try multi-valued
+                        propDef = validator.getApplicablePropertyDefinition(newName, conflicting.getType(), true, parent);
+                    }
+                } else {
+                    // can only be multi-valued (n == 0 || n > 1)
+                    propDef = validator.getApplicablePropertyDefinition(newName, conflicting.getType(), true, parent);
+                }
+
+                PropertyId newPId = idFactory.createPropertyId(parent.getNodeId(), newName);
+                Operation ap = AddProperty.create(newPId, conflicting.getType(), propDef, conflicting.getValues());
+                stateMgr.execute(ap);
+                Operation rm = Remove.create(conflicting);
+                stateMgr.execute(rm);
+            }
+        }
+
+        // do create new nodeState
+        QNodeDefinition def = validator.getApplicableNodeDefinition(nodeInfo.getName(), nodeInfo.getNodeTypeName(), parent);
+        if (def.isProtected()) {
+            log.debug("Skipping protected nodeState (" + nodeInfo.getName() + ")");
+            return null;
+        } else {
+            Operation an = AddNode.create(parent, nodeInfo.getName(), nodeInfo.getNodeTypeName(), nodeInfo.getId());
+            stateMgr.execute(an);
+            NodeId nId = AddNode.getLastCreated(parent, nodeInfo.getName());
+            NodeState nodeState = validator.getNodeState(nId);
+            
+            // and set mixin types
+            PropertyId mixinPId = idFactory.createPropertyId(nId, QName.JCR_MIXINTYPES);
+            Operation sm = SetMixin.create(mixinPId, nodeInfo.getMixinNames());
+            stateMgr.execute(sm);
+            return nodeState;
+        }
+    }
+
+    /**
+     *
+     * @param pi
+     * @param nodeState
+     * @param nsResolver
+     * @throws RepositoryException
+     * @throws ConstraintViolationException
+     */
+    private void importProperty(PropInfo pi, NodeState nodeState, NamespaceResolver nsResolver) throws RepositoryException, ConstraintViolationException {
+        QName propName = pi.getName();
+        TextValue[] tva = pi.getValues();
+        int infoType = pi.getType();
+
+        PropertyState prop = null;
+        QPropertyDefinition def;
+
+        if (nodeState.hasPropertyName(propName)) {
+            // a property with that name already exists...
+            PropertyState existing = validator.getPropertyState(nodeState.getNodeId(), propName);
+            def = existing.getDefinition();
+            if (def.isProtected()) {
+                // skip protected property
+                log.debug("skipping protected property " + hierMgr.safeGetJCRPath(existing.getPropertyId()));
+                return;
+            }
+            if (def.isAutoCreated()
+                && (existing.getType() == infoType || infoType == PropertyType.UNDEFINED)
+                && def.isMultiple() == existing.isMultiValued()) {
+                // this property has already been auto-created, no need to create it
+                prop = existing;
+            } else {
+                throw new ItemExistsException(hierMgr.safeGetJCRPath(existing.getPropertyId()));
+            }
+        } else {
+            // there's no property with that name, find applicable definition
+            if (tva.length == 1) {
+                // could be single- or multi-valued (n == 1)
+                def = validator.getApplicablePropertyDefinition(propName, infoType, nodeState);
+            } else {
+                // can only be multi-valued (n == 0 || n > 1)
+                def = validator.getApplicablePropertyDefinition(propName, infoType, true, nodeState);
+            }
+            if (def.isProtected()) {
+                // skip protected property
+                log.debug("skipping protected property " + propName);
+                return;
+            }
+        }
+
+        // retrieve the target property type needed for creation of QValue(s)
+        // including an eventual conversion. the targetType is then needed for
+        // setting/updating the type of the property-state.
+        int targetType = def.getRequiredType();
+        if (targetType == PropertyType.UNDEFINED) {
+            if (infoType == PropertyType.UNDEFINED) {
+                targetType = PropertyType.STRING;
+            } else {
+                targetType = infoType;
+            }
+        }
+
+        QValue[] values = getPropertyValues(pi, targetType, def.isMultiple(), nsResolver);
+        if (prop == null) {
+            // create new property
+            PropertyId newPId = idFactory.createPropertyId(nodeState.getNodeId(), propName);
+            Operation ap = AddProperty.create(newPId, targetType, def, values);
+            stateMgr.execute(ap);
+            prop = validator.getPropertyState(nodeState.getNodeId(), propName);
+        } else {
+            // modify value of existing property
+            Operation sp = SetPropertyValue.create(prop, values, targetType);
+            stateMgr.execute(sp);
+        }
+
+        // store reference for later resolution
+        if (prop != null && prop.getType() == PropertyType.REFERENCE) {
+            refTracker.processedReference(prop);
+        }
+    }
+
+    /**
+     *
+     * @param propertyInfo
+     * @param targetType
+     * @param isMultiple
+     * @param nsResolver
+     * @return
+     * @throws RepositoryException
+     */
+    private QValue[] getPropertyValues(PropInfo propertyInfo, int targetType, boolean isMultiple, NamespaceResolver nsResolver) throws RepositoryException {
+        TextValue[] tva = propertyInfo.getValues();
+        // check multi-valued characteristic
+        if ((tva.length == 0 || tva.length > 1) && !isMultiple) {
+            throw new ConstraintViolationException(propertyInfo.getName() + " is not multi-valued.");
+        }
+        // convert serialized values to QValue objects
+        QValue[] iva = new QValue[tva.length];
+        for (int i = 0; i < tva.length; i++) {
+            iva[i] = buildQValue(tva[i], targetType, nsResolver);
+        }
+        return iva;
+    }
+
+    /**
+     *
+     * @param tv
+     * @param targetType
+     * @param nsResolver
+     * @return
+     * @throws RepositoryException
+     */
+    private QValue buildQValue(TextValue tv, int targetType, NamespaceResolver nsResolver) throws RepositoryException {
+        QValue iv;
+        try {
+            switch (targetType) {
+                case PropertyType.BINARY:
+                    // base64 encoded BINARY type
+                    if (tv.length() < 0x10000) {
+                        // < 65kb: deserialize BINARY type in memory
+                        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                        Base64.decode(tv.retrieve(), baos);
+                        // no need to close ByteArrayOutputStream
+                        //baos.close();
+                        iv = QValue.create(baos.toByteArray());
+                    } else {
+                        // >= 65kb: deserialize BINARY type
+                        // using Reader and temporay file
+                        TransientFileFactory fileFactory = TransientFileFactory.getInstance();
+                        File tmpFile = fileFactory.createTransientFile("bin", null, null);
+                        FileOutputStream out = new FileOutputStream(tmpFile);
+                        Reader reader = tv.reader();
+                        try {
+                            Base64.decode(reader, out);
+                        } finally {
+                            reader.close();
+                            out.close();
+                        }
+                        iv = QValue.create(tmpFile);
+                    }
+                    break;
+                default:
+                    // build iv using namespace context of xml document
+                    Value v = ValueHelper.convert(tv.retrieve(), targetType, session.getValueFactory());
+                    iv = ValueFormat.getQValue(v, nsResolver);
+                    break;
+            }
+            return iv;
+        } catch (IOException e) {
+            String msg = "failed to retrieve serialized value";
+            log.debug(msg, e);
+            throw new RepositoryException(msg, e);
+        }
+    }
+}
\ No newline at end of file

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/ImporterImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/ImporterImpl.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SysViewImportHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SysViewImportHandler.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SysViewImportHandler.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SysViewImportHandler.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,328 @@
+/*
+ * 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.jcr2spi.xml;
+
+import org.apache.jackrabbit.name.IllegalNameException;
+import org.apache.jackrabbit.name.NamespaceResolver;
+import org.apache.jackrabbit.name.UnknownPrefixException;
+import org.apache.jackrabbit.name.QName;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import javax.jcr.InvalidSerializedDataException;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import org.apache.jackrabbit.spi.IdFactory;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Stack;
+
+/**
+ * <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 stack = new Stack();
+
+    /**
+     * fields used temporarily while processing sv:property and sv:value elements
+     */
+    private QName currentPropName;
+    private int currentPropType = PropertyType.UNDEFINED;
+    // list of AppendableValue objects
+    private ArrayList currentPropValues = new ArrayList();
+    private AppendableValue currentPropValue;
+
+    /**
+     * Constructs a new <code>SysViewImportHandler</code>.
+     *
+     * @param importer
+     * @param nsContext
+     */
+    SysViewImportHandler(Importer importer, NamespaceResolver nsContext, IdFactory idFactory) {
+        super(importer, nsContext, idFactory);
+    }
+
+    private void processNode(ImportState state, boolean start, boolean end)
+            throws SAXException {
+        if (!start && !end) {
+            return;
+        }
+        Importer.NodeInfo nodeInfo = new Importer.NodeInfo();
+        nodeInfo.setName(state.nodeName);
+        nodeInfo.setNodeTypeName(state.nodeTypeName);
+        if (state.mixinNames != null) {
+            QName[] mixins = (QName[]) state.mixinNames.toArray(new QName[state.mixinNames.size()]);
+            nodeInfo.setMixinNames(mixins);
+        }
+        if (state.uuid != null) {
+            nodeInfo.setId(idFactory.createNodeId(state.uuid));
+        }
+        // call Importer
+        try {
+            if (start) {
+                importer.startNode(nodeInfo, state.props, nsContext);
+                // dispose temporary property values
+                for (Iterator iter = state.props.iterator(); iter.hasNext();) {
+                    Importer.PropInfo pi = (Importer.PropInfo) iter.next();
+                    disposePropertyValues(pi);
+                }
+            }
+            if (end) {
+                importer.endNode(nodeInfo);
+            }
+        } catch (RepositoryException re) {
+            throw new SAXException(re);
+        }
+    }
+
+    //-------------------------------------------------------< ContentHandler >
+    /**
+     * {@inheritDoc}
+     */
+    public void startElement(String namespaceURI, String localName,
+                             String qName, Attributes atts)
+            throws SAXException {
+        // check namespace
+        if (!QName.NS_SV_URI.equals(namespaceURI)) {
+            throw new SAXException(new InvalidSerializedDataException("invalid namespace for element in system view xml document: "
+                    + namespaceURI));
+        }
+        // check element name
+        if (SysViewSAXEventGenerator.NODE_ELEMENT.equals(localName)) {
+            // sv:node element
+
+            // node name (value of sv:name attribute)
+            String name = atts.getValue(SysViewSAXEventGenerator.PREFIXED_NAME_ATTRIBUTE);
+            if (name == null) {
+                throw new SAXException(new InvalidSerializedDataException(
+                        "missing mandatory sv:name attribute of element sv:node"));
+            }
+
+            if (!stack.isEmpty()) {
+                // process current node first
+                ImportState current = (ImportState) 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 = nsContext.getQName(name);
+            } catch (IllegalNameException ine) {
+                throw new SAXException(new InvalidSerializedDataException("illegal node name: " + name, ine));
+            } catch (UnknownPrefixException upe) {
+                throw new SAXException(new InvalidSerializedDataException("illegal node name: " + name, upe));
+            }
+            stack.push(state);
+        } else if (SysViewSAXEventGenerator.PROPERTY_ELEMENT.equals(localName)) {
+            // sv:property element
+
+            // reset temp fields
+            currentPropValues.clear();
+
+            // property name (value of sv:name attribute)
+            String name = atts.getValue(SysViewSAXEventGenerator.PREFIXED_NAME_ATTRIBUTE);
+            if (name == null) {
+                throw new SAXException(new InvalidSerializedDataException(
+                        "missing mandatory sv:name attribute of element sv:property"));
+            }
+            try {
+                currentPropName = nsContext.getQName(name);
+            } catch (IllegalNameException ine) {
+                throw new SAXException(new InvalidSerializedDataException("illegal property name: " + name, ine));
+            } catch (UnknownPrefixException upe) {
+                throw new SAXException(new InvalidSerializedDataException("illegal property name: " + name, upe));
+            }
+            // property type (sv:type attribute)
+            String type = atts.getValue(SysViewSAXEventGenerator.PREFIXED_TYPE_ATTRIBUTE);
+            if (type == null) {
+                throw new SAXException(new InvalidSerializedDataException(
+                        "missing mandatory sv:type attribute of element sv:property"));
+            }
+            currentPropType = PropertyType.valueFromName(type);
+        } else if (SysViewSAXEventGenerator.VALUE_ELEMENT.equals(localName)) {
+            // sv:value element
+
+            // reset temp fields
+            currentPropValue = new BufferedStringValue();
+        } else {
+            throw new SAXException(new InvalidSerializedDataException("unexpected element found in system view xml document: "
+                    + localName));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    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}
+     */
+    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}
+     */
+    public void endElement(String namespaceURI, String localName, String qName)
+            throws SAXException {
+        // check element name
+        ImportState state = (ImportState) stack.peek();
+        if (SysViewSAXEventGenerator.NODE_ELEMENT.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 (SysViewSAXEventGenerator.PROPERTY_ELEMENT.equals(localName)) {
+            // sv:property element
+
+            // check if all system properties (jcr:primaryType, jcr:uuid etc.)
+            // have been collected and create node as necessary
+            if (currentPropName.equals(QName.JCR_PRIMARYTYPE)) {
+                AppendableValue val = (AppendableValue) currentPropValues.get(0);
+                String s = null;
+                try {
+                    s = val.retrieve();
+                    state.nodeTypeName = nsContext.getQName(s);
+                } catch (IOException ioe) {
+                    throw new SAXException("error while retrieving value", ioe);
+                } catch (IllegalNameException ine) {
+                    throw new SAXException(new InvalidSerializedDataException("illegal node type name: " + s, ine));
+                } catch (UnknownPrefixException upe) {
+                    throw new SAXException(new InvalidSerializedDataException("illegal node type name: " + s, upe));
+                }
+            } else if (currentPropName.equals(QName.JCR_MIXINTYPES)) {
+                if (state.mixinNames == null) {
+                    state.mixinNames = new ArrayList(currentPropValues.size());
+                }
+                for (int i = 0; i < currentPropValues.size(); i++) {
+                    AppendableValue val =
+                            (AppendableValue) currentPropValues.get(i);
+                    String s = null;
+                    try {
+                        s = val.retrieve();
+                        QName mixin = nsContext.getQName(s);
+                        state.mixinNames.add(mixin);
+                    } catch (IOException ioe) {
+                        throw new SAXException("error while retrieving value", ioe);
+                    } catch (IllegalNameException ine) {
+                        throw new SAXException(new InvalidSerializedDataException("illegal mixin type name: " + s, ine));
+                    } catch (UnknownPrefixException upe) {
+                        throw new SAXException(new InvalidSerializedDataException("illegal mixin type name: " + s, upe));
+                    }
+                }
+            } else if (currentPropName.equals(QName.JCR_UUID)) {
+                AppendableValue val = (AppendableValue) currentPropValues.get(0);
+                try {
+                    state.uuid = val.retrieve();
+                } catch (IOException ioe) {
+                    throw new SAXException("error while retrieving value", ioe);
+                }
+            } else {
+                Importer.PropInfo prop = new Importer.PropInfo();
+                prop.setName(currentPropName);
+                prop.setType(currentPropType);
+                prop.setValues((Importer.TextValue[])
+                        currentPropValues.toArray(new Importer.TextValue[currentPropValues.size()]));
+                state.props.add(prop);
+            }
+            // reset temp fields
+            currentPropValues.clear();
+        } else if (SysViewSAXEventGenerator.VALUE_ELEMENT.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 >
+    class ImportState {
+        /**
+         * name of current node
+         */
+        QName nodeName;
+        /**
+         * primary type of current node
+         */
+        QName nodeTypeName;
+        /**
+         * list of mixin types of current node
+         */
+        ArrayList mixinNames;
+        /**
+         * uuid of current node
+         */
+        String uuid;
+
+        /**
+         * list of PropInfo instances representing properties of current node
+         */
+        ArrayList props = new ArrayList();
+
+        /**
+         * flag indicating whether startNode() has been called for current node
+         */
+        boolean started = false;
+    }
+}

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SysViewImportHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SysViewImportHandler.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SysViewSAXEventGenerator.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SysViewSAXEventGenerator.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SysViewSAXEventGenerator.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SysViewSAXEventGenerator.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,226 @@
+/*
+ * 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.jcr2spi.xml;
+
+import org.apache.jackrabbit.value.ValueHelper;
+import org.apache.jackrabbit.name.QName;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * A <code>SysViewSAXEventGenerator</code> instance can be used to generate SAX events
+ * representing the serialized form of an item in System View XML.
+ */
+public class SysViewSAXEventGenerator extends AbstractSAXEventGenerator {
+
+    /**
+     * The XML elements and attributes used in serialization
+     */
+    public static final String NODE_ELEMENT = "node";
+    public static final String PREFIXED_NODE_ELEMENT =
+        QName.NS_SV_PREFIX + ":" + NODE_ELEMENT;
+
+    public static final String PROPERTY_ELEMENT = "property";
+    public static final String PREFIXED_PROPERTY_ELEMENT =
+        QName.NS_SV_PREFIX + ":" + PROPERTY_ELEMENT;;
+
+    public static final String VALUE_ELEMENT = "value";
+    public static final String PREFIXED_VALUE_ELEMENT =
+        QName.NS_SV_PREFIX + ":" + VALUE_ELEMENT;;
+
+    public static final String NAME_ATTRIBUTE = "name";
+    public static final String PREFIXED_NAME_ATTRIBUTE =
+        QName.NS_SV_PREFIX + ":" + NAME_ATTRIBUTE;
+
+    public static final String TYPE_ATTRIBUTE = "type";
+    public static final String PREFIXED_TYPE_ATTRIBUTE =
+        QName.NS_SV_PREFIX + ":" + TYPE_ATTRIBUTE;
+
+    public static final String CDATA_TYPE = "CDATA";
+    public static final String ENUMERATION_TYPE = "ENUMERATION";
+
+    /**
+     * Constructor
+     *
+     * @param node           the node state which should be serialized
+     * @param noRecurse      if true, only <code>node</code> and its properties will
+     *                       be serialized; otherwise the entire hierarchy starting with
+     *                       <code>node</code> will be serialized.
+     * @param skipBinary     flag governing whether binary properties are to be serialized.
+     * @param contentHandler the content handler to feed the SAX events to
+     * @throws RepositoryException if an error occurs
+     */
+    public SysViewSAXEventGenerator(Node node, boolean noRecurse,
+                                    boolean skipBinary,
+                                    ContentHandler contentHandler)
+            throws RepositoryException {
+        super(node, noRecurse, skipBinary, contentHandler);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void entering(Node node, int level)
+            throws RepositoryException, SAXException {
+        AttributesImpl attrs = new AttributesImpl();
+        // name attribute
+        String nodeName;
+        if (node.getDepth() == 0) {
+            // root node needs a name
+            nodeName = jcrRoot;
+        } else {
+            // encode node name to make sure it's a valid xml name
+            nodeName = node.getName();
+        }
+
+        attrs.addAttribute(QName.NS_SV_URI, NAME_ATTRIBUTE, PREFIXED_NAME_ATTRIBUTE,
+                CDATA_TYPE, nodeName);
+        // start node element
+        contentHandler.startElement(QName.NS_SV_URI, NODE_ELEMENT,
+                PREFIXED_NODE_ELEMENT, attrs);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void enteringProperties(Node node, int level)
+            throws RepositoryException, SAXException {
+        // nop
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void leavingProperties(Node node, int level)
+            throws RepositoryException, SAXException {
+        // nop
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void leaving(Node node, int level)
+            throws RepositoryException, SAXException {
+        // end node element
+        contentHandler.endElement(QName.NS_SV_URI, NODE_ELEMENT, PREFIXED_NODE_ELEMENT);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void entering(Property prop, int level)
+            throws RepositoryException, SAXException {
+        String propName = prop.getName();
+        AttributesImpl attrs = new AttributesImpl();
+        // name attribute
+        attrs.addAttribute(QName.NS_SV_URI, NAME_ATTRIBUTE, PREFIXED_NAME_ATTRIBUTE,
+                CDATA_TYPE, propName);
+        // type attribute
+        int type = prop.getType();
+        String typeName;
+        try {
+            typeName = PropertyType.nameFromValue(type);
+        } catch (IllegalArgumentException iae) {
+            // should never be getting here
+            throw new RepositoryException("unexpected property-type ordinal: "
+                    + type, iae);
+        }
+        attrs.addAttribute(QName.NS_SV_URI, TYPE_ATTRIBUTE, PREFIXED_TYPE_ATTRIBUTE,
+                ENUMERATION_TYPE, typeName);
+
+        // start property element
+        contentHandler.startElement(QName.NS_SV_URI, PROPERTY_ELEMENT,
+                PREFIXED_PROPERTY_ELEMENT, attrs);
+
+        // values
+        if (prop.getType() == PropertyType.BINARY && skipBinary) {
+            // empty value element
+            contentHandler.startElement(QName.NS_SV_URI, VALUE_ELEMENT,
+                    PREFIXED_VALUE_ELEMENT, new AttributesImpl());
+            contentHandler.endElement(QName.NS_SV_URI, VALUE_ELEMENT,
+                    PREFIXED_VALUE_ELEMENT);
+        } else {
+            boolean multiValued = prop.getDefinition().isMultiple();
+            Value[] vals;
+            if (multiValued) {
+                vals = prop.getValues();
+            } else {
+                vals = new Value[]{prop.getValue()};
+            }
+            for (int i = 0; i < vals.length; i++) {
+                Value val = vals[i];
+
+                // start value element
+                contentHandler.startElement(QName.NS_SV_URI, VALUE_ELEMENT,
+                        PREFIXED_VALUE_ELEMENT, new AttributesImpl());
+
+                // characters
+                Writer writer = new Writer() {
+                    public void close() /*throws IOException*/ {
+                    }
+
+                    public void flush() /*throws IOException*/ {
+                    }
+
+                    public void write(char[] cbuf, int off, int len) throws IOException {
+                        try {
+                            contentHandler.characters(cbuf, off, len);
+                        } catch (SAXException se) {
+                            throw new IOException(se.toString());
+                        }
+                    }
+                };
+                try {
+                    ValueHelper.serialize(val, false, writer);
+                    // no need to close our Writer implementation
+                    //writer.close();
+                } catch (IOException ioe) {
+                    // check if the exception wraps a SAXException
+                    // (see Writer.write(char[], int, int) above)
+                    Throwable t = ioe.getCause();
+                    if (t != null && t instanceof SAXException) {
+                        throw (SAXException) t;
+                    } else {
+                        throw new SAXException(ioe);
+                    }
+                }
+
+                // end value element
+                contentHandler.endElement(QName.NS_SV_URI, VALUE_ELEMENT,
+                        PREFIXED_VALUE_ELEMENT);
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void leaving(Property prop, int level)
+            throws RepositoryException, SAXException {
+        contentHandler.endElement(QName.NS_SV_URI, PROPERTY_ELEMENT,
+                PREFIXED_PROPERTY_ELEMENT);
+    }
+}

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SysViewSAXEventGenerator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/SysViewSAXEventGenerator.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url

Added: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/TargetImportHandler.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/TargetImportHandler.java?rev=421270&view=auto
==============================================================================
--- jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/TargetImportHandler.java (added)
+++ jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/TargetImportHandler.java Wed Jul 12 06:33:19 2006
@@ -0,0 +1,371 @@
+/*
+ * 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.jcr2spi.xml;
+
+import org.apache.jackrabbit.name.NamespaceResolver;
+import org.apache.jackrabbit.util.TransientFileFactory;
+import org.apache.jackrabbit.spi.IdFactory;
+import org.xml.sax.helpers.DefaultHandler;
+import org.xml.sax.SAXException;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Logger;
+
+import javax.jcr.RepositoryException;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+
+/**
+ * <code>TargetImportHandler</code> serves as the base class for the concrete
+ * classes <code>{@link DocViewImportHandler}</code> and
+ * <code>{@link SysViewImportHandler}</code>.
+ */
+abstract class TargetImportHandler extends DefaultHandler {
+
+    private static Logger log = LoggerFactory.getLogger(TargetImportHandler.class);
+
+    protected final Importer importer;
+    protected final NamespaceResolver nsContext;
+    protected final IdFactory idFactory;
+
+    protected TargetImportHandler(Importer importer,
+                                  NamespaceResolver nsContext,
+                                  IdFactory idFactory) {
+        this.importer = importer;
+        this.nsContext = nsContext;
+        this.idFactory = idFactory;
+    }
+
+    /**
+     * Disposes all instances of <code>AppendableValue</code> contained in the
+     * given property info's value array.
+     *
+     * @param prop property info
+     */
+    protected void disposePropertyValues(Importer.PropInfo prop) {
+        Importer.TextValue[] vals = prop.getValues();
+        for (int i = 0; i < vals.length; i++) {
+            if (vals[i] instanceof AppendableValue) {
+                try {
+                    ((AppendableValue) vals[i]).dispose();
+                } catch (IOException ioe) {
+                    log.warn("error while disposing temporary value", ioe);
+                    // fall through...
+                }
+            }
+        }
+    }
+
+    //-------------------------------------------------------< ContentHandler >
+
+    /**
+     * 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()
+     */
+    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()
+     */
+    public void endDocument() throws SAXException {
+        try {
+            importer.end();
+        } catch (RepositoryException re) {
+            throw new SAXException(re);
+        }
+    }
+
+    //--------------------------------------------------------< inner classes >
+    /**
+     * <code>AppendableValue</code> represents a serialized value that is
+     * appendable.
+     * <p/>
+     * <b>Important:</b> Note that in order to free resources
+     * <code>{@link #dispose()}</code> should be called as soon as an
+     * <code>AppendableValue</code> object is not used anymore.
+     */
+    public interface AppendableValue extends Importer.TextValue {
+        /**
+         * Append a portion of an array of characters.
+         *
+         * @param chars  the characters to be appended
+         * @param start  the index of the first character to append
+         * @param length the number of characters to append
+         * @throws IOException if an I/O error occurs
+         */
+        void append(char[] chars, int start, int length)
+                throws IOException;
+
+        /**
+         * Close this value. Once a value has been closed,
+         * further append() invocations will cause an IOException to be thrown.
+         *
+         * @throws IOException if an I/O error occurs
+         */
+        void close() throws IOException;
+
+        /**
+         * Dispose this value, i.e. free all bound resources. Once a value has
+         * been disposed, further method invocations will cause an IOException
+         * to be thrown.
+         *
+         * @throws IOException if an I/O error occurs
+         */
+        void dispose() throws IOException;
+    }
+
+    /**
+     * <code>StringValue</code> represents an immutable serialized value.
+     */
+    protected class StringValue implements Importer.TextValue {
+
+        private final String value;
+
+        /**
+         * Constructs a new <code>StringValue</code> representing the given
+         * value.
+         *
+         * @param value
+         */
+        protected StringValue(String value) {
+            this.value = value;
+        }
+
+        //--------------------------------------------------------< TextValue >
+        /**
+         * {@inheritDoc}
+         */
+        public long length() {
+            return value.length();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public String retrieve() {
+            return value;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Reader reader() {
+            return new StringReader(value);
+        }
+    }
+
+    /**
+     * <code>BufferedStringValue</code> represents an appendable
+     * serialized value that is either buffered in-memory or backed
+     * by a temporary file if its size exceeds a certain limit.
+     * <p/>
+     * <b>Important:</b> Note that in order to free resources
+     * <code>{@link #dispose()}</code> should be called as soon as
+     * <code>BufferedStringValue</code> instance is not used anymore.
+     */
+    protected class BufferedStringValue implements AppendableValue {
+
+        /**
+         * max size for buffering data in memory
+         */
+        private static final int MAX_BUFFER_SIZE = 0x10000;
+        /**
+         * size of increment if capacity buffer needs to be enlarged
+         */
+        private static final int BUFFER_INCREMENT = 0x2000;
+        /**
+         * in-memory buffer
+         */
+        private char[] buffer;
+        /**
+         * current position within buffer (size of actual data in buffer)
+         */
+        private int bufferPos;
+
+        /**
+         * backing temporary file created when size of data exceeds
+         * MAX_BUFFER_SIZE
+         */
+        private File tmpFile;
+        /**
+         * writer used to write to tmpFile; writer & tmpFile are always
+         * instantiated together, i.e. they are either both null or both not null.
+         */
+        private Writer writer;
+
+        /**
+         * Constructs a new empty <code>BufferedStringValue</code>.
+         */
+        protected BufferedStringValue() {
+            buffer = new char[0x2000];
+            bufferPos = 0;
+            tmpFile = null;
+            writer = null;
+        }
+
+        //--------------------------------------------------------< TextValue >
+        /**
+         * {@inheritDoc}
+         */
+        public long length() throws IOException {
+            if (buffer != null) {
+                return bufferPos;
+            } else if (tmpFile != null) {
+                // flush writer first
+                writer.flush();
+                return tmpFile.length();
+            } else {
+                throw new IOException("this instance has already been disposed");
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public String retrieve() throws IOException {
+            if (buffer != null) {
+                return new String(buffer, 0, bufferPos);
+            } else if (tmpFile != null) {
+                // flush writer first
+                writer.flush();
+                if (tmpFile.length() > Integer.MAX_VALUE) {
+                    throw new IOException("size of value is too big, use reader()");
+                }
+                StringBuffer sb = new StringBuffer((int) tmpFile.length());
+                char[] chunk = new char[0x2000];
+                int read;
+                Reader reader = new FileReader(tmpFile);
+                try {
+                    while ((read = reader.read(chunk)) > -1) {
+                        sb.append(chunk, 0, read);
+                    }
+                } finally {
+                    reader.close();
+                }
+                return sb.toString();
+            } else {
+                throw new IOException("this instance has already been disposed");
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Reader reader() throws IOException {
+            if (buffer != null) {
+                return new StringReader(new String(buffer, 0, bufferPos));
+            } else if (tmpFile != null) {
+                // flush writer first
+                writer.flush();
+                return new FileReader(tmpFile);
+            } else {
+                throw new IOException("this instance has already been disposed");
+            }
+        }
+
+        //--------------------------------------------------< AppendableValue >
+        /**
+         * {@inheritDoc}
+         */
+        public void append(char[] chars, int start, int length)
+                throws IOException {
+            if (buffer != null) {
+                if (bufferPos + length > MAX_BUFFER_SIZE) {
+                    // threshold for keeping data in memory exceeded;
+                    // create temp file and spool buffer contents
+                    TransientFileFactory fileFactory = TransientFileFactory.getInstance();
+                    tmpFile = fileFactory.createTransientFile("txt", null, null);
+                    final FileOutputStream fout = new FileOutputStream(tmpFile);
+                    writer = new OutputStreamWriter(fout) {
+                        public void flush() throws IOException {
+                            // flush this writer
+                            super.flush();
+                            // force synchronization with underlying file
+                            fout.getFD().sync();
+                        }
+                    };
+                    writer.write(buffer, 0, bufferPos);
+                    writer.write(chars, start, length);
+                    // reset fields
+                    buffer = null;
+                    bufferPos = 0;
+                } else {
+                    if (bufferPos + length > buffer.length) {
+                        // reallocate new buffer and spool old buffer contents
+                        char[] newBuffer = new char[buffer.length + BUFFER_INCREMENT];
+                        System.arraycopy(buffer, 0, newBuffer, 0, bufferPos);
+                        buffer = newBuffer;
+                    }
+                    System.arraycopy(chars, start, buffer, bufferPos, length);
+                    bufferPos += length;
+                }
+            } else if (tmpFile != null) {
+                writer.write(chars, start, length);
+            } else {
+                throw new IOException("this instance has already been disposed");
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void close() throws IOException {
+            if (buffer != null) {
+                // nop
+            } else if (tmpFile != null) {
+                writer.close();
+            } else {
+                throw new IOException("this instance has already been disposed");
+            }
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public void dispose() throws IOException {
+            if (buffer != null) {
+                buffer = null;
+                bufferPos = 0;
+            } else if (tmpFile != null) {
+                writer.close();
+                tmpFile.delete();
+                tmpFile = null;
+                writer = null;
+            } else {
+                throw new IOException("this instance has already been disposed");
+            }
+        }
+    }
+}

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/TargetImportHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/contrib/spi/jcr2spi/src/main/java/org/apache/jackrabbit/jcr2spi/xml/TargetImportHandler.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision url