All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.modeshape.jcr.AbstractJcrExporter Maven / Gradle / Ivy

There is a newer version: 5.4.1.Final
Show newest version
/*
 * ModeShape (http://www.modeshape.org)
 *
 * Licensed 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.modeshape.jcr;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.jcr.ItemVisitor;
import javax.jcr.NamespaceRegistry;
import javax.jcr.Node;
import javax.jcr.RepositoryException;
import org.modeshape.common.annotation.NotThreadSafe;
import org.modeshape.common.text.TextEncoder;
import org.modeshape.common.text.XmlNameEncoder;
import org.modeshape.common.xml.StreamingContentHandler;
import org.modeshape.jcr.cache.NodeKey;
import org.modeshape.jcr.value.Name;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;

/**
 * Superclass of ModeShape JCR exporters, provides basic support for traversing through the nodes recursively (if needed),
 * exception wrapping (since {@link ItemVisitor} does not allow checked exceptions to be thrown from its visit* methods, and the
 * ability to wrap an {@link OutputStream} with a {@link ContentHandler}.
 * 

* Each exporter is only intended to be used once (by calling exportView) and discarded. This class is NOT * thread-safe. * * @see JcrSystemViewExporter * @see JcrDocumentViewExporter */ @NotThreadSafe abstract class AbstractJcrExporter { /** * Encoder to properly escape XML names. * * @see XmlNameEncoder */ private static final TextEncoder NAME_ENCODER = new XmlNameEncoder(); /** * The list of XML namespaces that are predefined and should not be exported by the content handler. */ private static final List UNEXPORTABLE_NAMESPACES = Arrays.asList("", "xml", "xmlns"); /** * The session in which this exporter was created. */ protected final JcrSession session; /** * The list of XML namespace prefixes that should never be exported. */ private final Collection restrictedPrefixes; /** * Cache from {@link Name}s to their rewritten version based on session uri mappings. */ private final Map prefixedNames; /** * The set of node keys for the shareable nodes that were already exported. */ private final Set shareableNodes = new HashSet(); /** * Creates the exporter * * @param session the session in which the exporter is created * @param restrictedPrefixes the list of XML namespace prefixes that should not be exported */ AbstractJcrExporter( JcrSession session, Collection restrictedPrefixes ) { this.session = session; this.restrictedPrefixes = restrictedPrefixes; this.prefixedNames = new HashMap(); } /** * Returns the "prefixed" or rewritten version of baseName based on the URI mappings in the current * session. For example:

If the namespace "http://www.example.com/JCR/example/1.0" is mapped to the prefix * "foo" in the current session (or as a persistent mapping that has not been re-mapped in the current session), * this method will return the string "foo:bar" when passed a {@link Name} with uri * "http://www.example.com/JCR/example/1.0" and local name "bar".

This method does manage and utilize * a {@link Name} to {@link String} cache at the instance scope. * * @param baseName the name to be re-mapped into its prefixed version * @return the prefixed version of baseName based on the current session URI mappings (which include all * persistent URI mappings by default). * @see #prefixedNames * @see javax.jcr.Session#setNamespacePrefix(String, String) * @see javax.jcr.Session#getNamespacePrefix(String) */ protected String getPrefixedName( Name baseName ) { String prefixedName = prefixedNames.get(baseName); if (prefixedName == null) { prefixedName = baseName.getString(session.namespaces()); prefixedNames.put(baseName, prefixedName); } return prefixedName; } protected void startDocument( ContentHandler handler ) throws SAXException { shareableNodes.clear(); handler.startDocument(); } protected void endDocument( ContentHandler handler ) throws SAXException { handler.endDocument(); } /** * Exports node (or the subtree rooted at node) into an XML document by invoking SAX events on * contentHandler. * * @param exportRootNode the node which should be exported. If noRecursion was set to false in the * constructor, the entire subtree rooted at node will be exported. * @param contentHandler the SAX content handler for which SAX events will be invoked as the XML document is created. * @param skipBinary if true, indicates that binary properties should not be exported * @param noRecurse iftrue, indicates that only the given node should be exported, otherwise a recursice export * and not any of its child nodes. * @throws SAXException if an exception occurs during generation of the XML document * @throws RepositoryException if an exception occurs accessing the content repository */ public void exportView( Node exportRootNode, ContentHandler contentHandler, boolean skipBinary, boolean noRecurse ) throws RepositoryException, SAXException { assert exportRootNode != null; assert contentHandler != null; session.checkLive(); // Export the namespace mappings used in this session NamespaceRegistry registry = session.getWorkspace().getNamespaceRegistry(); startDocument(contentHandler); String[] namespacePrefixes = registry.getPrefixes(); for (int i = 0; i < namespacePrefixes.length; i++) { String prefix = namespacePrefixes[i]; if (!restrictedPrefixes.contains(prefix)) { contentHandler.startPrefixMapping(prefix, registry.getURI(prefix)); } } exportNode(exportRootNode, contentHandler, skipBinary, noRecurse); for (int i = 0; i < namespacePrefixes.length; i++) { if (!restrictedPrefixes.contains(namespacePrefixes[i])) { contentHandler.endPrefixMapping(namespacePrefixes[i]); } } endDocument(contentHandler); } /** * Exports node (or the subtree rooted at node) into an XML document that is written to * os. * * @param node the node which should be exported. If noRecursion was set to false in the * constructor, the entire subtree rooted at node will be exported. * @param os the {@link OutputStream} to which the XML document will be written * @param skipBinary if true, indicates that binary properties should not be exported * @param noRecurse iftrue, indicates that only the given node should be exported, otherwise a recursive export * and not any of its child nodes. * @throws RepositoryException if an exception occurs accessing the content repository, generating the XML document, or * writing it to the output stream os. * @throws IOException if there is a problem writing to the supplied stream */ public void exportView( Node node, OutputStream os, boolean skipBinary, boolean noRecurse ) throws IOException, RepositoryException { try { exportView(node, new StreamingContentHandler(os, UNEXPORTABLE_NAMESPACES), skipBinary, noRecurse); os.flush(); } catch (SAXException se) { throw new RepositoryException(se); } } /** * Exports node (or the subtree rooted at node) into an XML document by invoking SAX events on * contentHandler. * * @param node the node which should be exported. If noRecursion was set to false in the * constructor, the entire subtree rooted at node will be exported. * @param contentHandler the SAX content handler for which SAX events will be invoked as the XML document is created. * @param skipBinary if true, indicates that binary properties should not be exported * @param noRecurse iftrue, indicates that only the given node should be exported, otherwise a recursive export * and not any of its child nodes. * @throws SAXException if an exception occurs during generation of the XML document * @throws RepositoryException if an exception occurs accessing the content repository */ public abstract void exportNode( Node node, ContentHandler contentHandler, boolean skipBinary, boolean noRecurse ) throws RepositoryException, SAXException; /** * Convenience method to invoke the {@link ContentHandler#startElement(String, String, String, Attributes)} method on the * given content handler. The name will be encoded to properly escape invalid XML characters. * * @param contentHandler the content handler on which the startElement method should be invoked. * @param name the un-encoded, un-prefixed name of the element to start * @param atts the attributes that should be created for the given element * @throws SAXException if there is an error starting the element */ protected void startElement( ContentHandler contentHandler, Name name, Attributes atts ) throws SAXException { contentHandler.startElement(name.getNamespaceUri(), NAME_ENCODER.encode(name.getLocalName()), NAME_ENCODER.encode(getPrefixedName(name)), atts); } /** * Convenience method to invoke the {@link ContentHandler#endElement(String, String, String)} method on the given content * handler. The name will be encoded to properly escape invalid XML characters. * * @param contentHandler the content handler on which the endElement method should be invoked. * @param name the un-encoded, un-prefixed name of the element to end * @throws SAXException if there is an error ending the element */ protected void endElement( ContentHandler contentHandler, Name name ) throws SAXException { contentHandler.endElement(name.getNamespaceUri(), NAME_ENCODER.encode(name.getLocalName()), NAME_ENCODER.encode(getPrefixedName(name))); } protected void exporting( Node node ) throws RepositoryException { if (node instanceof AbstractJcrNode) { AbstractJcrNode jcrNode = (AbstractJcrNode)node; if (jcrNode.isShareable()) { shareableNodes.add(jcrNode.key()); } } } protected JcrSharedNode asSharedNode( Node node ) { if (node instanceof JcrSharedNode) { JcrSharedNode sharedNode = (JcrSharedNode)node; NodeKey shareableKey = sharedNode.key(); // See if the node is already exported ... if (!shareableNodes.add(shareableKey)) { // Already saw it, so return the shared node ... return sharedNode; } } // Otherwise, it's not shareable or it is but hasn't yet been seen, so return null so that it's treated like a regular // node return null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy