/*
* 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 org.apache.jackrabbit.core.nodetype.InvalidNodeTypeDefException;
import org.apache.jackrabbit.core.util.DOMWalker;
import org.apache.jackrabbit.core.value.InternalValue;
import org.apache.jackrabbit.core.value.InternalValueFactory;
import org.apache.jackrabbit.spi.commons.conversion.NameException;
import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver;
import org.apache.jackrabbit.spi.commons.conversion.DefaultNamePathResolver;
import org.apache.jackrabbit.spi.commons.namespace.NamespaceResolver;
import org.apache.jackrabbit.spi.commons.value.ValueFactoryQImpl;
import org.apache.jackrabbit.spi.commons.value.ValueFormat;
import org.apache.jackrabbit.spi.commons.nodetype.constraint.ValueConstraint;
import org.apache.jackrabbit.spi.commons.nodetype.InvalidConstraintException;
import org.apache.jackrabbit.spi.commons.nodetype.QNodeDefinitionBuilder;
import org.apache.jackrabbit.spi.commons.nodetype.QPropertyDefinitionBuilder;
import org.apache.jackrabbit.spi.commons.nodetype.QNodeTypeDefinitionBuilder;
import org.apache.jackrabbit.spi.commons.name.NameConstants;
import org.apache.jackrabbit.spi.Name;
import org.apache.jackrabbit.spi.QValueFactory;
import org.apache.jackrabbit.spi.QValueConstraint;
import org.apache.jackrabbit.spi.QPropertyDefinition;
import org.apache.jackrabbit.spi.QNodeDefinition;
import org.apache.jackrabbit.spi.QNodeTypeDefinition;
import org.apache.jackrabbit.value.ValueHelper;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.NamespaceException;
import javax.jcr.ValueFactory;
import javax.jcr.Value;
import javax.jcr.query.qom.QueryObjectModelConstants;
import javax.jcr.version.OnParentVersionAction;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.List;
import java.util.ArrayList;
/**
* Node type definition reader. This class is used to read the
* persistent node type definition files used by Jackrabbit.
*/
public class NodeTypeReader {
/**
* Reads a node type definition file. The file contents are read from
* the given input stream and the parsed node type definitions are
* returned.
*
* @param xml XML input stream
* @return node type definitions
* @throws IOException if the node type definitions
* cannot be read
* @throws InvalidNodeTypeDefException if the node type definition
* format is invalid
*/
public static QNodeTypeDefinition[] read(InputStream xml)
throws IOException, InvalidNodeTypeDefException {
try {
NodeTypeReader reader = new NodeTypeReader(xml);
return reader.getNodeTypeDefs();
} catch (NameException e) {
throw new InvalidNodeTypeDefException(
"Invalid namespace reference in a node type definition", e);
} catch (NamespaceException e) {
throw new InvalidNodeTypeDefException(
"Invalid namespace reference in a node type definition", e);
}
}
/** The node type document walker. */
private final DOMWalker walker;
/** The namespaces associated with the node type XML document. */
private final Properties namespaces;
/** The name, path resolver. */
private final NamePathResolver resolver;
private final ValueFactory valueFactory;
private final QValueFactory qValueFactory = InternalValueFactory.getInstance();
/**
* Creates a node type definition file reader.
*
* @param xml node type definition file
* @throws IOException if the node type definition file cannot be read
*/
public NodeTypeReader(InputStream xml) throws IOException {
walker = new DOMWalker(xml);
namespaces = walker.getNamespaces();
NamespaceResolver nsResolver = new AdditionalNamespaceResolver(namespaces);
resolver = new DefaultNamePathResolver(nsResolver);
valueFactory = new ValueFactoryQImpl(qValueFactory, resolver);
}
/**
* Returns the namespaces declared in the node type definition
* file.
* @return the namespaces
*/
public Properties getNamespaces() {
return namespaces;
}
/**
* Returns all node type definitions specified by node type elements
* under the current element.
*
* @return node type definitions
* @throws InvalidNodeTypeDefException if a definition is invalid
* @throws NameException if a definition contains an
* illegal name
* @throws NamespaceException if a namespace is not defined
*/
public QNodeTypeDefinition[] getNodeTypeDefs()
throws InvalidNodeTypeDefException, NameException, NamespaceException {
List<QNodeTypeDefinition> defs = new ArrayList<QNodeTypeDefinition>();
while (walker.iterateElements(Constants.NODETYPE_ELEMENT)) {
defs.add(getNodeTypeDef());
}
return defs.toArray(new QNodeTypeDefinition[defs.size()]);
}
/**
* Returns the node type definition specified by the current element.
*
* @return node type definition
* @throws InvalidNodeTypeDefException if the definition is invalid
* @throws NameException if the definition contains an
* illegal name
* @throws NamespaceException if a namespace is not defined
*/
private QNodeTypeDefinition getNodeTypeDef()
throws InvalidNodeTypeDefException, NameException, NamespaceException {
QNodeTypeDefinitionBuilder type = new QNodeTypeDefinitionBuilder();
type.setName(resolver.getQName(
walker.getAttribute(Constants.NAME_ATTRIBUTE)));
type.setMixin(Boolean.valueOf(
walker.getAttribute(Constants.ISMIXIN_ATTRIBUTE)));
type.setOrderableChildNodes(Boolean.valueOf(
walker.getAttribute(Constants.HASORDERABLECHILDNODES_ATTRIBUTE)));
type.setAbstract(Boolean.valueOf(
walker.getAttribute(Constants.ISABSTRACT_ATTRIBUTE)));
if (walker.getAttribute(Constants.ISQUERYABLE_ATTRIBUTE) != null) {
type.setQueryable(Boolean.valueOf(
walker.getAttribute(Constants.ISQUERYABLE_ATTRIBUTE)));
}
String primaryItemName =
walker.getAttribute(Constants.PRIMARYITEMNAME_ATTRIBUTE);
if (primaryItemName != null && primaryItemName.length() > 0) {
type.setPrimaryItemName(
resolver.getQName(primaryItemName));
}
// supertype declarations
if (walker.enterElement(Constants.SUPERTYPES_ELEMENT)) {
List<Name> supertypes = new ArrayList<Name>();
while (walker.iterateElements(Constants.SUPERTYPE_ELEMENT)) {
supertypes.add(
resolver.getQName(walker.getContent()));
}
type.setSupertypes(supertypes.toArray(new Name[supertypes.size()]));
walker.leaveElement();
}
// property definitions
List<QPropertyDefinition> properties = new ArrayList<QPropertyDefinition>();
while (walker.iterateElements(Constants.PROPERTYDEFINITION_ELEMENT)) {
QPropertyDefinitionBuilder def = getPropDef();
def.setDeclaringNodeType(type.getName());
properties.add(def.build());
}
type.setPropertyDefs(properties.toArray(new QPropertyDefinition[properties.size()]));
// child node definitions
List<QNodeDefinition> nodes = new ArrayList<QNodeDefinition>();
while (walker.iterateElements(Constants.CHILDNODEDEFINITION_ELEMENT)) {
QNodeDefinitionBuilder def = getChildNodeDef();
def.setDeclaringNodeType(type.getName());
nodes.add(def.build());
}
type.setChildNodeDefs(nodes.toArray(new QNodeDefinition[nodes.size()]));
return type.build();
}
/**
* Returns the property definition specified by the current element.
*
* @return property definition
* @throws InvalidNodeTypeDefException if the definition is invalid
* @throws NameException if the definition contains an
* illegal name
* @throws NamespaceException if a namespace is not defined
*/
private QPropertyDefinitionBuilder getPropDef()
throws InvalidNodeTypeDefException, NameException, NamespaceException {
QPropertyDefinitionBuilder def = new QPropertyDefinitionBuilder();
String name = walker.getAttribute(Constants.NAME_ATTRIBUTE);
if (name.equals("*")) {
def.setName(NameConstants.ANY_NAME);
} else {
def.setName(resolver.getQName(name));
}
// simple attributes
def.setAutoCreated(Boolean.valueOf(
walker.getAttribute(Constants.AUTOCREATED_ATTRIBUTE)));
def.setMandatory(Boolean.valueOf(
walker.getAttribute(Constants.MANDATORY_ATTRIBUTE)));
def.setProtected(Boolean.valueOf(
walker.getAttribute(Constants.PROTECTED_ATTRIBUTE)));
def.setOnParentVersion(OnParentVersionAction.valueFromName(
walker.getAttribute(Constants.ONPARENTVERSION_ATTRIBUTE)));
def.setMultiple(Boolean.valueOf(
walker.getAttribute(Constants.MULTIPLE_ATTRIBUTE)));
def.setFullTextSearchable(Boolean.valueOf(
walker.getAttribute(Constants.ISFULLTEXTSEARCHABLE_ATTRIBUTE)));
def.setQueryOrderable(Boolean.valueOf(
walker.getAttribute(Constants.ISQUERYORDERABLE_ATTRIBUTE)));
String s = walker.getAttribute(Constants.AVAILABLEQUERYOPERATORS_ATTRIBUTE);
if (s != null && s.length() > 0) {
String[] ops = s.split(" ");
List<String> queryOps = new ArrayList<String>();
for (String op1 : ops) {
String op = op1.trim();
if (op.equals(Constants.EQ_ENTITY)) {
queryOps.add(QueryObjectModelConstants.JCR_OPERATOR_EQUAL_TO);
} else if (op.equals(Constants.NE_ENTITY)) {
queryOps.add(QueryObjectModelConstants.JCR_OPERATOR_NOT_EQUAL_TO);
} else if (op.equals(Constants.LT_ENTITY)) {
queryOps.add(QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN);
} else if (op.equals(Constants.LE_ENTITY)) {
queryOps.add(QueryObjectModelConstants.JCR_OPERATOR_LESS_THAN_OR_EQUAL_TO);
} else if (op.equals(Constants.GT_ENTITY)) {
queryOps.add(QueryObjectModelConstants.JCR_OPERATOR_GREATER_THAN);
} else if (op.equals(Constants.GE_ENTITY)) {
queryOps.add(QueryObjectModelConstants.JCR_OPERATOR_GREATER_THAN_OR_EQUAL_TO);
} else if (op.equals(Constants.LIKE_ENTITY)) {
queryOps.add(QueryObjectModelConstants.JCR_OPERATOR_LIKE);
} else {
throw new InvalidNodeTypeDefException("'" + op + "' is not a valid query operator");
}
}
def.setAvailableQueryOperators(queryOps.toArray(new String[queryOps.size()]));
}
def.setRequiredType(PropertyType.valueFromName(
walker.getAttribute(Constants.REQUIREDTYPE_ATTRIBUTE)));
// value constraints
if (walker.enterElement(Constants.VALUECONSTRAINTS_ELEMENT)) {
List<QValueConstraint> constraints = new ArrayList<QValueConstraint>();
int type = def.getRequiredType();
while (walker.iterateElements(Constants.VALUECONSTRAINT_ELEMENT)) {
String constraint = walker.getContent();
try {
constraints.add(ValueConstraint.create(
type, constraint.trim(), resolver));
} catch (InvalidConstraintException e) {
throw new InvalidNodeTypeDefException(
"Invalid value constraint " + constraint, e);
}
}
def.setValueConstraints(constraints.toArray(
new QValueConstraint[constraints.size()]));
walker.leaveElement();
}
// default values
if (walker.enterElement(Constants.DEFAULTVALUES_ELEMENT)) {
List<InternalValue> values = new ArrayList<InternalValue>();
int type = def.getRequiredType();
if (type == PropertyType.UNDEFINED) {
type = PropertyType.STRING;
}
while (walker.iterateElements(Constants.DEFAULTVALUE_ELEMENT)) {
String value = walker.getContent();
try {
Value v = ValueHelper.convert(value, type, valueFactory);
values.add((InternalValue) ValueFormat.getQValue(v, resolver, qValueFactory));
} catch (RepositoryException e) {
throw new InvalidNodeTypeDefException(
"Unable to create default value: " + value, e);
}
}
def.setDefaultValues(values.toArray(new InternalValue[values.size()]));
walker.leaveElement();
}
return def;
}
/**
* Returns the child node definition specified by the current element.
*
* @return child node definition
* @throws NameException if the definition contains an illegal name
* @throws NamespaceException if a namespace is not defined
*/
private QNodeDefinitionBuilder getChildNodeDef() throws NameException, NamespaceException {
QNodeDefinitionBuilder def = new QNodeDefinitionBuilder();
String name = walker.getAttribute(Constants.NAME_ATTRIBUTE);
if (name.equals("*")) {
def.setName(NameConstants.ANY_NAME);
} else {
def.setName(resolver.getQName(name));
}
// simple attributes
def.setAutoCreated(Boolean.valueOf(
walker.getAttribute(Constants.AUTOCREATED_ATTRIBUTE)));
def.setMandatory(Boolean.valueOf(
walker.getAttribute(Constants.MANDATORY_ATTRIBUTE)));
def.setProtected(Boolean.valueOf(
walker.getAttribute(Constants.PROTECTED_ATTRIBUTE)));
def.setOnParentVersion(OnParentVersionAction.valueFromName(
walker.getAttribute(Constants.ONPARENTVERSION_ATTRIBUTE)));
def.setAllowsSameNameSiblings(Boolean.valueOf(
walker.getAttribute(Constants.SAMENAMESIBLINGS_ATTRIBUTE)));
// default primary type
String type =
walker.getAttribute(Constants.DEFAULTPRIMARYTYPE_ATTRIBUTE);
if (type != null && type.length() > 0) {
def.setDefaultPrimaryType(resolver.getQName(type));
}
// required primary types
if (walker.enterElement(Constants.REQUIREDPRIMARYTYPES_ELEMENT)) {
List<Name> types = new ArrayList<Name>();
while (walker.iterateElements(Constants.REQUIREDPRIMARYTYPE_ELEMENT)) {
types.add(resolver.getQName(walker.getContent()));
}
def.setRequiredPrimaryTypes(types.toArray(new Name[types.size()]));
walker.leaveElement();
} else {
/* Default to nt:base?
throw new InvalidNodeTypeDefException(
"Required primary type(s) not defined for child node "
+ def.getName() + " of node type "
+ def.getDeclaringNodeType());
*/
}
return def;
}
}