org.modeshape.jcr.JcrSystemViewExporter Maven / Gradle / Ivy
/*
* 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.InputStream;
import java.util.Arrays;
import java.util.List;
import javax.jcr.Binary;
import javax.jcr.NamespaceException;
import javax.jcr.NamespaceRegistry;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.Property;
import javax.jcr.PropertyIterator;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
import org.modeshape.common.annotation.NotThreadSafe;
import org.modeshape.common.util.Base64;
import org.modeshape.common.xml.XmlCharacters;
import org.modeshape.jcr.value.Name;
import org.modeshape.jcr.value.ValueFactory;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
/**
* Implementation of {@link AbstractJcrExporter} that implements the system view mapping described in section 7.2 of the JCR 2.0
* specification.
*
* @see JcrSession#exportSystemView(String, ContentHandler, boolean, boolean)
* @see JcrSession#exportSystemView(String, java.io.OutputStream, boolean, boolean)
*/
@NotThreadSafe
class JcrSystemViewExporter extends AbstractJcrExporter {
/**
* Buffer size for reading Base64-encoded binary streams for export.
*/
private static final int BASE_64_BUFFER_SIZE = 1024;
/**
* The list of the special JCR properties that must be exported first for each node. These properties must be exported in list
* order if they are present on the node as per section 6.4.1 rule 11.
*/
private static final List SPECIAL_PROPERTY_NAMES = Arrays.asList(JcrLexicon.PRIMARY_TYPE,
JcrLexicon.MIXIN_TYPES,
JcrLexicon.UUID);
JcrSystemViewExporter( JcrSession session ) {
super(session, Arrays.asList("xml"));
}
/**
* 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
*/
@Override
public void exportNode( Node node,
ContentHandler contentHandler,
boolean skipBinary,
boolean noRecurse ) throws RepositoryException, SAXException {
exportNode(node, contentHandler, skipBinary, noRecurse, node.getDepth() == 0);
}
/**
* 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.
* @param isRoot true if the supplied node is the root node (supplied as an efficiency)
* @throws SAXException if an exception occurs during generation of the XML document
* @throws RepositoryException if an exception occurs accessing the content repository
*/
protected void exportNode( Node node,
ContentHandler contentHandler,
boolean skipBinary,
boolean noRecurse,
boolean isRoot ) throws RepositoryException, SAXException {
// start the sv:node element for this JCR node
AttributesImpl atts = new AttributesImpl();
String nodeName = node.getName();
if (isRoot && node.getDepth() == 0) {
// This is the root node ...
nodeName = "jcr:root";
}
atts.addAttribute(JcrSvLexicon.NAME.getNamespaceUri(),
JcrSvLexicon.NAME.getLocalName(),
getPrefixedName(JcrSvLexicon.NAME),
org.modeshape.jcr.api.PropertyType.nameFromValue(PropertyType.STRING),
nodeName);
startElement(contentHandler, JcrSvLexicon.NODE, atts);
JcrSharedNode sharedNode = asSharedNode(node);
if (sharedNode != null) {
// This is a shared node, and per Section 14.7 of the JCR 2.0 specification, they have to be written out
// in a special way ...
// jcr:primaryType = nt:share ...
emitProperty(JcrLexicon.PRIMARY_TYPE, PropertyType.NAME, JcrNtLexicon.SHARE, contentHandler, skipBinary);
// jcr:uuid = UUID of shared node ...
emitProperty(JcrLexicon.UUID, PropertyType.STRING, sharedNode.getIdentifier(), contentHandler, skipBinary);
} else {
exporting(node);
// Output any special properties first (see Javadoc for SPECIAL_PROPERTY_NAMES for more context)
for (Name specialPropertyName : SPECIAL_PROPERTY_NAMES) {
Property specialProperty = ((AbstractJcrNode)node).getProperty(specialPropertyName);
if (specialProperty != null) {
emitProperty(specialProperty, contentHandler, skipBinary);
}
}
PropertyIterator properties = node.getProperties();
while (properties.hasNext()) {
exportProperty(properties.nextProperty(), contentHandler, skipBinary);
}
if (!noRecurse) {
// the node iterator should check permissions and return only those nodes on which there is READ permission
NodeIterator nodes = node.getNodes();
while (nodes.hasNext()) {
Node child = nodes.nextNode();
//MODE-2171 Ignore any ACL nodes
if (!child.isNodeType(ModeShapeLexicon.ACCESS_LIST_NODE_TYPE_STRING)) {
exportNode(child, contentHandler, skipBinary, noRecurse, false);
}
}
}
}
endElement(contentHandler, JcrSvLexicon.NODE);
}
/**
* @param property
* @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
* @throws SAXException if an exception occurs during generation of the XML document
* @throws RepositoryException if an exception occurs accessing the content repository
*/
private void exportProperty( Property property,
ContentHandler contentHandler,
boolean skipBinary ) throws RepositoryException, SAXException {
assert property instanceof AbstractJcrProperty : "Illegal attempt to use " + getClass().getName()
+ " on non-ModeShape property";
AbstractJcrProperty prop = (AbstractJcrProperty)property;
Name propertyName = prop.name();
if (SPECIAL_PROPERTY_NAMES.contains(propertyName)) {
return;
}
emitProperty(property, contentHandler, skipBinary);
}
/**
* Fires the appropriate SAX events on the content handler to build the XML elements for the property.
*
* @param property the property to 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
* @throws SAXException if an exception occurs during generation of the XML document
* @throws RepositoryException if an exception occurs accessing the content repository
*/
private void emitProperty( Property property,
ContentHandler contentHandler,
boolean skipBinary ) throws RepositoryException, SAXException {
assert property instanceof AbstractJcrProperty : "Illegal attempt to use " + getClass().getName()
+ " on non-ModeShape property";
AbstractJcrProperty prop = (AbstractJcrProperty)property;
// first set the property sv:name attribute
AttributesImpl propAtts = new AttributesImpl();
propAtts.addAttribute(JcrSvLexicon.NAME.getNamespaceUri(),
JcrSvLexicon.NAME.getLocalName(),
getPrefixedName(JcrSvLexicon.NAME),
PropertyType.nameFromValue(PropertyType.STRING),
prop.getName());
// and it's sv:type attribute
propAtts.addAttribute(JcrSvLexicon.TYPE.getNamespaceUri(),
JcrSvLexicon.TYPE.getLocalName(),
getPrefixedName(JcrSvLexicon.TYPE),
PropertyType.nameFromValue(PropertyType.STRING),
org.modeshape.jcr.api.PropertyType.nameFromValue(prop.getType()));
// and it's sv:multiple attribute
if (prop.isMultiple()) {
propAtts.addAttribute(JcrSvLexicon.TYPE.getNamespaceUri(),
JcrSvLexicon.TYPE.getLocalName(),
getPrefixedName(JcrSvLexicon.MULTIPLE),
PropertyType.nameFromValue(PropertyType.BOOLEAN),
Boolean.TRUE.toString());
}
// output the sv:property element
startElement(contentHandler, JcrSvLexicon.PROPERTY, propAtts);
// then output a sv:value element for each of its values
if (prop instanceof JcrMultiValueProperty) {
Value[] values = prop.getValues();
for (Value value : values) {
emitValue(value, contentHandler, property.getType(), skipBinary);
}
} else {
emitValue(property.getValue(), contentHandler, property.getType(), skipBinary);
}
// end the sv:property element
endElement(contentHandler, JcrSvLexicon.PROPERTY);
}
/**
* Fires the appropriate SAX events on the content handler to build the XML elements for the value.
*
* @param value the value to be exported
* @param contentHandler the SAX content handler for which SAX events will be invoked as the XML document is created.
* @param propertyType the {@link PropertyType} for the given value
* @param skipBinary if true
, indicates that binary properties should not be exported
* @throws SAXException if an exception occurs during generation of the XML document
* @throws RepositoryException if an exception occurs accessing the content repository
*/
private void emitValue( Value value,
ContentHandler contentHandler,
int propertyType,
boolean skipBinary ) throws RepositoryException, SAXException {
if (PropertyType.BINARY == propertyType) {
startElement(contentHandler, JcrSvLexicon.VALUE, null);
// Per section 6.5 of the 1.0.1 spec, we need to emit one empty-value tag for each value if the property is
// multi-valued and skipBinary is true
if (!skipBinary) {
byte[] bytes = new byte[BASE_64_BUFFER_SIZE];
int len;
Binary binary = value.getBinary();
try {
InputStream stream = new Base64.InputStream(binary.getStream(), Base64.ENCODE);
try {
while (-1 != (len = stream.read(bytes))) {
contentHandler.characters(new String(bytes, 0, len).toCharArray(), 0, len);
}
} finally {
stream.close();
}
} catch (IOException ioe) {
throw new RepositoryException(ioe);
} finally {
binary.dispose();
}
}
endElement(contentHandler, JcrSvLexicon.VALUE);
} else {
emitValue(value.getString(), contentHandler);
}
}
private void emitValue( String value,
ContentHandler contentHandler ) throws RepositoryException, SAXException {
// Per Section 7.2 Rule 11a of the JCR 2.0 spec, need to check invalid XML characters
char[] chars = value.toCharArray();
boolean allCharsAreValidXml = true;
for (int i = 0; i < chars.length; i++) {
if (!XmlCharacters.isValid(chars[i])) {
allCharsAreValidXml = false;
break;
}
}
if (allCharsAreValidXml) {
startElement(contentHandler, JcrSvLexicon.VALUE, null);
contentHandler.characters(chars, 0, chars.length);
endElement(contentHandler, JcrSvLexicon.VALUE);
} else {
AttributesImpl valueAtts = new AttributesImpl();
NamespaceRegistry namespaceRegistry = session.getWorkspace().getNamespaceRegistry();
String xsiPrefix = null;
try {
xsiPrefix = namespaceRegistry.getPrefix("“http://www.w3.org/2001/XMLSchema-instance");
} catch (NamespaceException e) {
xsiPrefix = "xsi";
}
String xsdPrefix = null;
try {
xsdPrefix = namespaceRegistry.getPrefix("http://www.w3.org/2001/XMLSchema");
} catch (RepositoryException e) {
xsdPrefix = "xsd";
}
valueAtts.addAttribute(xsiPrefix, "type", xsiPrefix + ":type", "STRING", xsdPrefix + ":base64Binary");
startElement(contentHandler, JcrSvLexicon.VALUE, valueAtts);
try {
chars = Base64.encodeBytes(value.getBytes("UTF-8")).toCharArray();
} catch (IOException ioe) {
throw new RepositoryException(ioe);
}
contentHandler.characters(chars, 0, chars.length);
endElement(contentHandler, JcrSvLexicon.VALUE);
}
}
/**
* Fires the appropriate SAX events on the content handler to build the XML elements for the property.
*
* @param propertyName the name of the property to be exported
* @param propertyType the type of the property to be exported
* @param value the value of the single-valued property to 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
* @throws SAXException if an exception occurs during generation of the XML document
* @throws RepositoryException if an exception occurs accessing the content repository
*/
private void emitProperty( Name propertyName,
int propertyType,
Object value,
ContentHandler contentHandler,
boolean skipBinary ) throws RepositoryException, SAXException {
ValueFactory strings = session.stringFactory();
// first set the property sv:name attribute
AttributesImpl propAtts = new AttributesImpl();
propAtts.addAttribute(JcrSvLexicon.NAME.getNamespaceUri(),
JcrSvLexicon.NAME.getLocalName(),
getPrefixedName(JcrSvLexicon.NAME),
PropertyType.nameFromValue(PropertyType.STRING),
strings.create(propertyName));
// and it's sv:type attribute
propAtts.addAttribute(JcrSvLexicon.TYPE.getNamespaceUri(),
JcrSvLexicon.TYPE.getLocalName(),
getPrefixedName(JcrSvLexicon.TYPE),
PropertyType.nameFromValue(PropertyType.STRING),
org.modeshape.jcr.api.PropertyType.nameFromValue(propertyType));
// output the sv:property element
startElement(contentHandler, JcrSvLexicon.PROPERTY, propAtts);
// then output a sv:value element for each of its values
emitValue(strings.create(value), contentHandler);
// end the sv:property element
endElement(contentHandler, JcrSvLexicon.PROPERTY);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy