/* * 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.nodetype; import java.util.ArrayList; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.Value; import javax.jcr.nodetype.ConstraintViolationException; import javax.jcr.nodetype.NoSuchNodeTypeException; import javax.jcr.nodetype.NodeDefinition; import javax.jcr.nodetype.NodeType; import javax.jcr.nodetype.NodeTypeDefinition; import javax.jcr.nodetype.PropertyDefinition; import org.apache.jackrabbit.jcr2spi.ManagerProvider; 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.commons.conversion.NameException; import org.apache.jackrabbit.spi.commons.conversion.NamePathResolver; import org.apache.jackrabbit.spi.commons.nodetype.AbstractNodeType; import org.apache.jackrabbit.spi.commons.nodetype.constraint.ValueConstraint; import org.apache.jackrabbit.spi.commons.value.ValueFormat; import org.apache.jackrabbit.value.ValueHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * <code>NodeTypeImpl</code> ... */ public class NodeTypeImpl extends AbstractNodeType implements NodeTypeDefinition { private static Logger log = LoggerFactory.getLogger(NodeTypeImpl.class); private final EffectiveNodeType ent; private final NodeTypeManagerImpl ntMgr; private final ManagerProvider mgrProvider; /** * Package private constructor * <p> * Creates a valid node type instance. * We assume that the node type definition is valid and all referenced * node types (supertypes, required node types etc.) do exist and are valid. * * @param ent the effective (i.e. merged and resolved) node type representation * @param ntd the definition of this node type * @param ntMgr the node type manager associated with this node type * @param mgrProvider the manager provider */ NodeTypeImpl(EffectiveNodeType ent, QNodeTypeDefinition ntd, NodeTypeManagerImpl ntMgr, ManagerProvider mgrProvider) { super(ntd, ntMgr, mgrProvider.getNamePathResolver()); this.ent = ent; this.ntMgr = ntMgr; this.mgrProvider = mgrProvider; } private NamePathResolver resolver() { return mgrProvider.getNamePathResolver(); } private ItemDefinitionProvider definitionProvider() { return mgrProvider.getItemDefinitionProvider(); } /** * Returns the applicable property definition for a property with the * specified name and type. * * @param propertyName * @param type * @param multiValued * @return * @throws RepositoryException if no applicable property definition * could be found */ private QPropertyDefinition getApplicablePropDef(Name propertyName, int type, boolean multiValued) throws RepositoryException { return definitionProvider().getQPropertyDefinition(ntd.getName(), propertyName, type, multiValued); } /** * {@inheritDoc} */ @Override public boolean isNodeType(Name nodeTypeName) { return ent.includesNodeType(nodeTypeName); } /** * Tests if the value constraints defined in the property definition * <code>def</code> are satisfied by the the specified <code>values</code>. * <p> * Note that the <i>protected</i> flag is not checked. Also note that no * type conversions are attempted if the type of the given values does not * match the required type as specified in the given definition. * * @param def The definition of the property * @param values An array of <code>QValue</code> objects. * @throws ConstraintViolationException If a constraint is violated. * @throws RepositoryException If another error occurs. */ private static void checkSetPropertyValueConstraints(QPropertyDefinition def, QValue[] values) throws ConstraintViolationException, RepositoryException { ValueConstraint.checkValueConstraints(def, values); } //-------------------------------------------------< NodeTypeDefinition >--- /** * @see javax.jcr.nodetype.NodeTypeDefinition#hasOrderableChildNodes() */ public boolean hasOrderableChildNodes() { return ntd.hasOrderableChildNodes(); } //-----------------------------------------------------------< NodeType >--- /** * @see javax.jcr.nodetype.NodeType#getSupertypes() */ public NodeType[] getSupertypes() { Name[] ntNames = ent.getInheritedNodeTypes(); NodeType[] supertypes = new NodeType[ntNames.length]; for (int i = 0; i < ntNames.length; i++) { try { supertypes[i] = ntMgr.getNodeType(ntNames[i]); } catch (NoSuchNodeTypeException e) { // should never get here log.error("undefined supertype", e); return new NodeType[0]; } } return supertypes; } /** * @see javax.jcr.nodetype.NodeType#getChildNodeDefinitions() */ public NodeDefinition[] getChildNodeDefinitions() { QNodeDefinition[] cnda = ent.getAllQNodeDefinitions(); NodeDefinition[] nodeDefs = new NodeDefinition[cnda.length]; for (int i = 0; i < cnda.length; i++) { nodeDefs[i] = ntMgr.getNodeDefinition(cnda[i]); } return nodeDefs; } /** * @see javax.jcr.nodetype.NodeType#getPropertyDefinitions() */ public PropertyDefinition[] getPropertyDefinitions() { QPropertyDefinition[] pda = ent.getAllQPropertyDefinitions(); PropertyDefinition[] propDefs = new PropertyDefinition[pda.length]; for (int i = 0; i < pda.length; i++) { propDefs[i] = ntMgr.getPropertyDefinition(pda[i]); } return propDefs; } /** * @see javax.jcr.nodetype.NodeType#canSetProperty(String, Value) */ public boolean canSetProperty(String propertyName, Value value) { if (value == null) { // setting a property to null is equivalent of removing it return canRemoveItem(propertyName); } try { Name name = resolver().getQName(propertyName); QPropertyDefinition def; try { // try to get definition that matches the given value type def = getApplicablePropDef(name, value.getType(), false); } catch (ConstraintViolationException cve) { // fallback: ignore type def = getApplicablePropDef(name, PropertyType.UNDEFINED, false); } if (def.isProtected()) { return false; } if (def.isMultiple()) { return false; } Value v; if (def.getRequiredType() != PropertyType.UNDEFINED && def.getRequiredType() != value.getType()) { // type conversion required v = ValueHelper.convert(value, def.getRequiredType(), mgrProvider.getJcrValueFactory()); } else { // no type conversion required v = value; } // create QValue from Value QValue qValue = ValueFormat.getQValue(v, resolver(), mgrProvider.getQValueFactory()); checkSetPropertyValueConstraints(def, new QValue[]{qValue}); return true; } catch (NameException re) { // fall through } catch (RepositoryException e) { // fall through } return false; } /** * @see javax.jcr.nodetype.NodeType#canSetProperty(String, Value[]) */ public boolean canSetProperty(String propertyName, Value[] values) { if (values == null) { // setting a property to null is equivalent of removing it return canRemoveItem(propertyName); } try { Name name = resolver().getQName(propertyName); // determine type of values int type = PropertyType.UNDEFINED; for (int i = 0; i < values.length; i++) { if (values[i] == null) { // skip null values as those would be purged continue; } if (type == PropertyType.UNDEFINED) { type = values[i].getType(); } else if (type != values[i].getType()) { // inhomogeneous types return false; } } QPropertyDefinition def; try { // try to get definition that matches the given value type def = getApplicablePropDef(name, type, true); } catch (ConstraintViolationException cve) { // fallback: ignore type def = getApplicablePropDef(name, PropertyType.UNDEFINED, true); } if (def.isProtected()) { return false; } if (!def.isMultiple()) { return false; } // determine target type int targetType; if (def.getRequiredType() != PropertyType.UNDEFINED && def.getRequiredType() != type) { // type conversion required targetType = def.getRequiredType(); } else { // no type conversion required targetType = type; } ArrayList<QValue> list = new ArrayList<QValue>(); // convert values and compact array (purge null entries) for (int i = 0; i < values.length; i++) { if (values[i] != null) { // create QValue from Value and perform // type conversion as necessary Value v = ValueHelper.convert(values[i], targetType, mgrProvider.getJcrValueFactory()); QValue qValue = ValueFormat.getQValue(v, resolver(), mgrProvider.getQValueFactory()); list.add(qValue); } } QValue[] internalValues = list.toArray(new QValue[list.size()]); checkSetPropertyValueConstraints(def, internalValues); return true; } catch (NameException be) { // implementation specific exception, fall through } catch (RepositoryException re) { // fall through } return false; } /** * @see javax.jcr.nodetype.NodeType#canAddChildNode(String) */ public boolean canAddChildNode(String childNodeName) { try { ent.checkAddNodeConstraints(resolver().getQName(childNodeName), definitionProvider()); return true; } catch (NameException be) { // implementation specific exception, fall through } catch (RepositoryException re) { // fall through } return false; } /** * @see javax.jcr.nodetype.NodeType#canAddChildNode(String, String) */ public boolean canAddChildNode(String childNodeName, String nodeTypeName) { try { Name ntName = resolver().getQName(nodeTypeName); QNodeTypeDefinition def = ntMgr.getNodeTypeDefinition(ntName); ent.checkAddNodeConstraints(resolver().getQName(childNodeName), def, definitionProvider()); return true; } catch (NameException be) { // implementation specific exception, fall through } catch (RepositoryException re) { // fall through } return false; } /** * @see javax.jcr.nodetype.NodeType#canRemoveItem(String) */ public boolean canRemoveItem(String itemName) { try { ent.checkRemoveItemConstraints(resolver().getQName(itemName)); return true; } catch (NameException be) { // implementation specific exception, fall through } catch (RepositoryException re) { // fall through } return false; } /** * @see javax.jcr.nodetype.NodeType#canRemoveNode(String) */ public boolean canRemoveNode(String nodeName) { Name name; try { name = resolver().getQName(nodeName); } catch (RepositoryException e) { // should never get here log.warn("Unable to determine if there are any remove constraints for a node with name " + nodeName); return false; } return !ent.hasRemoveNodeConstraint(name); } /** * @see javax.jcr.nodetype.NodeType#canRemoveProperty(String) */ public boolean canRemoveProperty(String propertyName) { Name name; try { name = resolver().getQName(propertyName); } catch (RepositoryException e) { // should never get here log.warn("Unable to determine if there are any remove constraints for a property with name " + propertyName); return false; } return !ent.hasRemovePropertyConstraint(name); } }