/*
* 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.core.nodetype.xml;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.List;
import javax.jcr.NamespaceException;
import javax.jcr.NamespaceRegistry;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.query.qom.QueryObjectModelConstants;
import javax.jcr.version.OnParentVersionAction;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.jackrabbit.commons.query.qom.Operator;
import org.apache.jackrabbit.core.util.DOMBuilder;
import org.apache.jackrabbit.core.value.InternalValueFactory;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.QNodeDefinition;
import org.apache.jackrabbit.spi.QNodeTypeDefinition;
import org.apache.jackrabbit.spi.QPropertyDefinition;
import org.apache.jackrabbit.spi.QValue;
import org.apache.jackrabbit.spi.QValueConstraint;
import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver;
import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
import org.apache.jackrabbit.spi.commons.nodetype.constraint.ValueConstraint;
import org.apache.jackrabbit.spi.commons.value.ValueFactoryQImpl;
/**
* Node type definition writer. This class is used to write the
* persistent node type definition files used by Jackrabbit.
*/
public final class NodeTypeWriter {
/**
* Writes a node type definition file. The file contents are written
* to the given output stream and will contain the given node type
* definitions. The given namespace registry is used for namespace
* mappings.
*
* @param xml XML output stream
* @param registry namespace registry
* @param types node types
* @throws IOException if the node type definitions cannot
* be written
* @throws RepositoryException on repository errors
*/
public static void write(
OutputStream xml, QNodeTypeDefinition[] types, NamespaceRegistry registry)
throws IOException, RepositoryException {
try {
NodeTypeWriter writer = new NodeTypeWriter(registry);
for (QNodeTypeDefinition type : types) {
writer.addNodeTypeDef(type);
}
writer.write(xml);
} catch (ParserConfigurationException e) {
IOException e2 = new IOException(e.getMessage());
e2.initCause(e);
throw e2;
} catch (NamespaceException e) {
throw new RepositoryException(
"Invalid namespace reference in a node type definition", e);
}
}
/** The node type document builder. */
private final DOMBuilder builder;
/** The namespace resolver. */
private final NamePathResolver resolver;
private final ValueFactoryQImpl factory;
/**
* Creates a node type definition file writer. The given namespace
* registry is used for the XML namespace bindings.
*
* @param registry namespace registry
* @throws ParserConfigurationException if the node type definition
* document cannot be created
* @throws RepositoryException if the namespace mappings cannot
* be retrieved from the registry
*/
private NodeTypeWriter(NamespaceRegistry registry)
throws ParserConfigurationException, RepositoryException {
builder = new DOMBuilder(Constants.NODETYPES_ELEMENT);
String[] prefixes = registry.getPrefixes();
for (String prefix : prefixes) {
if (!"".equals(prefix)) {
String uri = registry.getURI(prefix);
builder.setAttribute("xmlns:" + prefix, uri);
}
}
NamespaceResolver nsResolver = new AdditionalNamespaceResolver(registry);
resolver = new DefaultNamePathResolver(nsResolver);
factory = new ValueFactoryQImpl(InternalValueFactory.getInstance(), resolver);
}
/**
* Builds a node type definition element under the current element.
*
* @param def node type definition
* @throws RepositoryException if the default property values
* cannot be serialized
* @throws NamespaceException if the node type definition contains
* invalid namespace references
*/
private void addNodeTypeDef(QNodeTypeDefinition def)
throws NamespaceException, RepositoryException {
builder.startElement(Constants.NODETYPE_ELEMENT);
// simple attributes
builder.setAttribute(
Constants.NAME_ATTRIBUTE, resolver.getJCRName(def.getName()));
builder.setAttribute(
Constants.ISMIXIN_ATTRIBUTE, def.isMixin());
builder.setAttribute(
Constants.ISQUERYABLE_ATTRIBUTE, def.isQueryable());
builder.setAttribute(
Constants.ISABSTRACT_ATTRIBUTE, def.isAbstract());
builder.setAttribute(
Constants.HASORDERABLECHILDNODES_ATTRIBUTE,
def.hasOrderableChildNodes());
// primary item name
Name item = def.getPrimaryItemName();
if (item != null) {
builder.setAttribute(
Constants.PRIMARYITEMNAME_ATTRIBUTE,
resolver.getJCRName(item));
} else {
builder.setAttribute(Constants.PRIMARYITEMNAME_ATTRIBUTE, "");
}
// supertype declarations
Name[] supertypes = def.getSupertypes();
if (supertypes.length > 0) {
builder.startElement(Constants.SUPERTYPES_ELEMENT);
for (Name supertype : supertypes) {
builder.addContentElement(
Constants.SUPERTYPE_ELEMENT,
resolver.getJCRName(supertype));
}
builder.endElement();
}
// property definitions
QPropertyDefinition[] properties = def.getPropertyDefs();
for (QPropertyDefinition property : properties) {
addPropDef(property);
}
// child node definitions
QNodeDefinition[] nodes = def.getChildNodeDefs();
for (QNodeDefinition node : nodes) {
addChildNodeDef(node);
}
builder.endElement();
}
/**
* Builds a property definition element under the current element.
*
* @param def property definition
* @throws RepositoryException if the default values cannot
* be serialized
* @throws NamespaceException if the property definition contains
* invalid namespace references
*/
private void addPropDef(QPropertyDefinition def)
throws NamespaceException, RepositoryException {
builder.startElement(Constants.PROPERTYDEFINITION_ELEMENT);
// simple attributes
builder.setAttribute(
Constants.NAME_ATTRIBUTE, resolver.getJCRName(def.getName()));
builder.setAttribute(
Constants.AUTOCREATED_ATTRIBUTE, def.isAutoCreated());
builder.setAttribute(
Constants.MANDATORY_ATTRIBUTE, def.isMandatory());
builder.setAttribute(
Constants.PROTECTED_ATTRIBUTE, def.isProtected());
builder.setAttribute(
Constants.ONPARENTVERSION_ATTRIBUTE,
OnParentVersionAction.nameFromValue(def.getOnParentVersion()));
builder.setAttribute(
Constants.MULTIPLE_ATTRIBUTE, def.isMultiple());
builder.setAttribute(
Constants.ISFULLTEXTSEARCHABLE_ATTRIBUTE, def.isFullTextSearchable());
builder.setAttribute(
Constants.ISQUERYORDERABLE_ATTRIBUTE, def.isQueryOrderable());
// TODO do properly...
String[] qops = def.getAvailableQueryOperators();
if (qops != null && qops.length > 0) {
List<String> ops = Arrays.asList(qops);
List<String> defaultOps = Arrays.asList(Operator.getAllQueryOperators());
if (!ops.containsAll(defaultOps)) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < qops.length; i++) {
if (i > 0) {
sb.append(' ');
}
if (qops[i].equals(QueryObjectModelConstants.JCR_OPERATOR_EQUAL_TO)) {
sb.append(Constants.EQ_ENTITY);
} else if (qops[i].equals(QueryObjectModelConstants.JCR_OPERATOR_NOT_EQUAL_TO)) {
sb.append(Constants.NE_ENTITY);
} else if (qops[i].equals(QueryObjectModelConstants.JCR_OPERATOR_GREATER_THAN)) {
sb.append(Constants.GT_ENTITY);
} else if (qops[i].equals(QueryObjectModelConstants.JCR_OPERATOR_GREATER_THAN_OR_EQUAL_TO)) {
sb.append(Constants.GE_ENTITY);
} else if (qops[i].equals(QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN)) {
sb.append(Constants.LT_ENTITY);
} else if (qops[i].equals(QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN_OR_EQUAL_TO)) {
sb.append(Constants.LE_ENTITY);
} else if (qops[i].equals(QueryObjectModelConstants.JCR_OPERATOR_LIKE)) {
sb.append(Constants.LIKE_ENTITY);
}
}
builder.setAttribute(
Constants.AVAILABLEQUERYOPERATORS_ATTRIBUTE, sb.toString());
}
}
builder.setAttribute(
Constants.REQUIREDTYPE_ATTRIBUTE,
PropertyType.nameFromValue(def.getRequiredType()));
// value constraints
QValueConstraint[] constraints = def.getValueConstraints();
if (constraints != null && constraints.length > 0) {
builder.startElement(Constants.VALUECONSTRAINTS_ELEMENT);
for (QValueConstraint constraint : constraints) {
ValueConstraint vc = ValueConstraint.create(
def.getRequiredType(), constraint.getString());
builder.addContentElement(
Constants.VALUECONSTRAINT_ELEMENT,
vc.getDefinition(resolver));
}
builder.endElement();
}
// default values
QValue[] defaults = def.getDefaultValues();
if (defaults != null && defaults.length > 0) {
builder.startElement(Constants.DEFAULTVALUES_ELEMENT);
for (QValue v : defaults) {
builder.addContentElement(
Constants.DEFAULTVALUE_ELEMENT,
factory.createValue(v).getString());
}
builder.endElement();
}
builder.endElement();
}
/**
* Builds a child node definition element under the current element.
*
* @param def child node definition
* @throws NamespaceException if the child node definition contains
* invalid namespace references
*/
private void addChildNodeDef(QNodeDefinition def)
throws NamespaceException {
builder.startElement(Constants.CHILDNODEDEFINITION_ELEMENT);
// simple attributes
builder.setAttribute(
Constants.NAME_ATTRIBUTE, resolver.getJCRName(def.getName()));
builder.setAttribute(
Constants.AUTOCREATED_ATTRIBUTE, def.isAutoCreated());
builder.setAttribute(
Constants.MANDATORY_ATTRIBUTE, def.isMandatory());
builder.setAttribute(
Constants.PROTECTED_ATTRIBUTE, def.isProtected());
builder.setAttribute(
Constants.ONPARENTVERSION_ATTRIBUTE,
OnParentVersionAction.nameFromValue(def.getOnParentVersion()));
builder.setAttribute(
Constants.SAMENAMESIBLINGS_ATTRIBUTE, def.allowsSameNameSiblings());
// default primary type
Name type = def.getDefaultPrimaryType();
if (type != null) {
builder.setAttribute(
Constants.DEFAULTPRIMARYTYPE_ATTRIBUTE,
resolver.getJCRName(type));
} else {
builder.setAttribute(Constants.DEFAULTPRIMARYTYPE_ATTRIBUTE, "");
}
// required primary types
Name[] requiredTypes = def.getRequiredPrimaryTypes();
builder.startElement(Constants.REQUIREDPRIMARYTYPES_ELEMENT);
for (Name requiredType : requiredTypes) {
builder.addContentElement(
Constants.REQUIREDPRIMARYTYPE_ELEMENT,
resolver.getJCRName(requiredType));
}
builder.endElement();
builder.endElement();
}
/**
* Writes the node type definition document to the given output stream.
*
* @param xml XML output stream
* @throws IOException if the node type document could not be written
*/
private void write(OutputStream xml) throws IOException {
builder.write(xml);
}
}