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