/* * Copyright (C) 2009 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.services.jcr.impl.core.nodetype; import org.exoplatform.services.jcr.core.nodetype.ExtendedNodeTypeManager; import org.exoplatform.services.jcr.core.nodetype.NodeDefinitionData; import org.exoplatform.services.jcr.core.nodetype.NodeTypeData; import org.exoplatform.services.jcr.core.nodetype.NodeTypeDataManager; import org.exoplatform.services.jcr.core.nodetype.PropertyDefinitionData; import org.exoplatform.services.jcr.core.nodetype.PropertyDefinitionDatas; import org.exoplatform.services.jcr.dataflow.ItemDataConsumer; import org.exoplatform.services.jcr.datamodel.InternalQName; import org.exoplatform.services.jcr.impl.Constants; import org.exoplatform.services.jcr.impl.core.LocationFactory; import org.exoplatform.services.jcr.impl.core.value.BaseValue; import org.exoplatform.services.jcr.impl.core.value.ValueConstraintsMatcher; import org.exoplatform.services.jcr.impl.util.JCRDateFormat; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.CharsetEncoder; import java.nio.charset.MalformedInputException; import java.nio.charset.UnmappableCharacterException; import java.util.Set; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; import javax.jcr.Value; import javax.jcr.ValueFactory; import javax.jcr.nodetype.NoSuchNodeTypeException; import javax.jcr.nodetype.NodeDefinition; import javax.jcr.nodetype.NodeType; import javax.jcr.nodetype.PropertyDefinition; /** * Created by The eXo Platform SAS. <br> * Date: 02.12.2008 * * @author <a href="mailto:peter.nedonosko@exoplatform.com.ua">Peter * Nedonosko</a> * @version $Id: NodeTypeImpl.java 111 2008-11-11 11:11:11Z pnedonosko $ */ public class NodeTypeImpl extends NodeTypeDefinitionImpl implements NodeType { /** * Logger. */ protected static final Log LOG = ExoLogger.getLogger("exo.jcr.component.core.NodeTypeImpl"); /** * NodeTypeImpl contructor. * * @param nodeTypeData NodeTypeData * @param nodeTypeDataManager NodeTypeDataManager * @param nodeTypeManager ExtendedNodeTypeManager * @param locationFactory LocationFactory * @param valueFactory ValueFactory * @param dataManager ItemDataConsumer */ public NodeTypeImpl(NodeTypeData nodeTypeData, NodeTypeDataManager nodeTypeDataManager, ExtendedNodeTypeManager nodeTypeManager, LocationFactory locationFactory, ValueFactory valueFactory, ItemDataConsumer dataManager) { super(nodeTypeData, nodeTypeDataManager, nodeTypeManager, locationFactory, valueFactory, dataManager); } /** * {@inheritDoc} */ public boolean canAddChildNode(String childNodeName) { try { InternalQName cname = locationFactory.parseJCRName(childNodeName).getInternalName(); NodeDefinitionData childNodeDef = nodeTypeDataManager.getChildNodeDefinition(cname, nodeTypeData.getName()); return !(childNodeDef == null || childNodeDef.isProtected() || childNodeDef.getDefaultPrimaryType() == null); } catch (RepositoryException e) { LOG.error("canAddChildNode " + e, e); return false; } } /** * {@inheritDoc} */ public boolean canAddChildNode(String childNodeName, String nodeTypeName) { try { InternalQName cname = locationFactory.parseJCRName(childNodeName).getInternalName(); InternalQName ntname = locationFactory.parseJCRName(nodeTypeName).getInternalName(); NodeDefinitionData childNodeDef = nodeTypeDataManager.getChildNodeDefinition(cname, ntname, nodeTypeData.getName()); return !(childNodeDef == null || childNodeDef.isProtected()) && isChildNodePrimaryTypeAllowed(cname, ntname); } catch (RepositoryException e) { if (LOG.isDebugEnabled()) LOG.debug("canAddChildNode " + e, e); return false; } } /** * {@inheritDoc} */ public boolean canRemoveItem(String itemName) { try { InternalQName iname = locationFactory.parseJCRName(itemName).getInternalName(); PropertyDefinitionDatas pdefs = nodeTypeDataManager.getPropertyDefinitions(iname, nodeTypeData.getName()); if (pdefs != null) { PropertyDefinitionData pd = pdefs.getAnyDefinition(); if (pd != null) return !(pd.isMandatory() || pd.isProtected()); } NodeDefinitionData cndef = nodeTypeDataManager.getChildNodeDefinition(iname, nodeTypeData.getName()); if (cndef != null) return !(cndef.isMandatory() || cndef.isProtected()); return false; } catch (RepositoryException e) { if (LOG.isDebugEnabled()) LOG.debug("canRemoveItem " + e, e); return false; } } /** * {@inheritDoc} */ public boolean canSetProperty(String propertyName, Value value) { try { InternalQName pname = locationFactory.parseJCRName(propertyName).getInternalName(); PropertyDefinitionDatas pdefs = nodeTypeDataManager.getPropertyDefinitions(pname, nodeTypeData.getName()); if (pdefs != null) { PropertyDefinitionData pd = pdefs.getDefinition(false); if (pd != null) { if (pd.isProtected()) { // can set (edit) return false; } else if (value != null) { // can set (add or edit) return canSetPropertyForType(pd.getRequiredType(), value, pd.getValueConstraints()); } else { // can remove return !pd.isMandatory(); } } } return false; } catch (RepositoryException e) { LOG.error("canSetProperty value " + e, e); return false; } } /** * {@inheritDoc} */ public boolean canSetProperty(String propertyName, Value[] values) { try { InternalQName pname = locationFactory.parseJCRName(propertyName).getInternalName(); PropertyDefinitionDatas pdefs = nodeTypeDataManager.getPropertyDefinitions(pname, nodeTypeData.getName()); if (pdefs != null) { PropertyDefinitionData pd = pdefs.getDefinition(true); if (pd != null) { if (pd.isProtected()) { // can set (edit) return false; } else if (values != null) { // can set (add or edit) int res = 0; for (Value value : values) { if (canSetPropertyForType(pd.getRequiredType(), value, pd.getValueConstraints())) { res++; } } return res == values.length; } else { // can remove return !pd.isMandatory(); } } } return false; } catch (RepositoryException e) { LOG.error("canSetProperty value " + e, e); return false; } } /** * {@inheritDoc} */ public NodeDefinition[] getChildNodeDefinitions() { NodeDefinitionData[] nodeDefs = nodeTypeDataManager.getAllChildNodeDefinitions(new InternalQName[]{nodeTypeData.getName()}); NodeDefinition[] ndefs = new NodeDefinition[nodeDefs.length]; for (int i = 0; i < nodeDefs.length; i++) { NodeDefinitionData cnd = nodeDefs[i]; ndefs[i] = new NodeDefinitionImpl(cnd, nodeTypeDataManager, nodeTypeManager, locationFactory, valueFactory, dataManager); } return ndefs; } /** * {@inheritDoc} */ public NodeDefinition[] getDeclaredChildNodeDefinitions() { NodeDefinitionData[] cndefs = nodeTypeData.getDeclaredChildNodeDefinitions(); NodeDefinition[] ndefs = new NodeDefinition[cndefs.length]; for (int i = 0; i < cndefs.length; i++) { NodeDefinitionData cnd = cndefs[i]; ndefs[i] = new NodeDefinitionImpl(cnd, nodeTypeDataManager, nodeTypeManager, locationFactory, valueFactory, dataManager); } return ndefs; } /** * {@inheritDoc} */ public PropertyDefinition[] getDeclaredPropertyDefinitions() { PropertyDefinitionData[] pdefs = nodeTypeData.getDeclaredPropertyDefinitions(); return getPropertyDefinition(pdefs); } public NodeType[] getDeclaredSupertypes() { InternalQName[] snames = nodeTypeData.getDeclaredSupertypeNames(); NodeType[] supers = new NodeType[snames.length]; for (int i = 0; i < snames.length; i++) { NodeTypeData superNodeTypeData = nodeTypeDataManager.getNodeType(snames[i]); supers[i] = new NodeTypeImpl(superNodeTypeData, nodeTypeDataManager, nodeTypeManager, locationFactory, valueFactory, dataManager); } return supers; } /** * {@inheritDoc} */ public String getName() { try { return locationFactory.createJCRName(nodeTypeData.getName()).getAsString(); } catch (RepositoryException e) { throw new RuntimeException("Wrong name in nodeTypeData " + e, e); } } /** * {@inheritDoc} */ public String getPrimaryItemName() { try { if (nodeTypeData.getPrimaryItemName() != null) return locationFactory.createJCRName(nodeTypeData.getPrimaryItemName()).getAsString(); else return null; } catch (RepositoryException e) { throw new RuntimeException("Wrong primary item name in nodeTypeData " + e, e); } } /** * {@inheritDoc} */ public PropertyDefinition[] getPropertyDefinitions() { PropertyDefinitionData[] propertyDefs = nodeTypeDataManager.getAllPropertyDefinitions(nodeTypeData.getName()); return getPropertyDefinition(propertyDefs); } /** * {@inheritDoc} */ public InternalQName getQName() { return nodeTypeData.getName(); } /** * {@inheritDoc} */ public NodeType[] getSupertypes() { Set<InternalQName> supers = nodeTypeDataManager.getSupertypes(nodeTypeData.getName()); NodeType[] superTypes = new NodeType[supers.size()]; int i = 0; for (InternalQName nodeTypeName : supers) { try { superTypes[i++] = nodeTypeManager.findNodeType(nodeTypeName); } catch (NoSuchNodeTypeException e) { LOG.error(e.getLocalizedMessage(), e); } catch (RepositoryException e) { LOG.error(e.getLocalizedMessage(), e); } } return superTypes; } /** * {@inheritDoc} */ public boolean hasOrderableChildNodes() { return nodeTypeData.hasOrderableChildNodes(); } public boolean isChildNodePrimaryTypeAllowed(InternalQName nodeName, InternalQName typeName) { try { return nodeTypeDataManager.isChildNodePrimaryTypeAllowed(nodeName, typeName, nodeTypeData.getName(), new InternalQName[0]); } catch (RepositoryException e) { return false; } } /** * {@inheritDoc} */ public boolean isMixin() { return nodeTypeData.isMixin(); } public boolean isNodeType(InternalQName nodeTypeQName) { return nodeTypeDataManager.isNodeType(nodeTypeQName, nodeTypeData.getName(), new InternalQName[0]); } /** * {@inheritDoc} */ public boolean isNodeType(String nodeTypeName) { try { return nodeTypeDataManager.isNodeType(locationFactory.parseJCRName(nodeTypeName).getInternalName(), nodeTypeData.getName()); } catch (RepositoryException e) { throw new RuntimeException("Wrong nodetype name " + e, e); } } /** * Returns true if satisfy type and constrains. Otherwise returns false. * * @param requiredType - type. * @param value - value. * @param constraints - constraints. * @return a boolean. */ private boolean canSetPropertyForType(int requiredType, Value value, String[] constraints) { if (requiredType == value.getType()) { return checkValueConstraints(requiredType, constraints, value); } else if (requiredType == PropertyType.BINARY && (value.getType() == PropertyType.STRING || value.getType() == PropertyType.DATE || value.getType() == PropertyType.LONG || value.getType() == PropertyType.DOUBLE || value.getType() == PropertyType.NAME || value.getType() == PropertyType.PATH || value .getType() == PropertyType.BOOLEAN)) { return checkValueConstraints(requiredType, constraints, value); } else if (requiredType == PropertyType.BOOLEAN) { if (value.getType() == PropertyType.STRING) { return checkValueConstraints(requiredType, constraints, value); } else if (value.getType() == PropertyType.BINARY) { try { return isCharsetString(value.getString(), Constants.DEFAULT_ENCODING) && checkValueConstraints(requiredType, constraints, value); } catch (Exception e) { // Hm, this is not string and not UTF-8 too return false; } } else { return false; } } else if (requiredType == PropertyType.DATE) { String likeDataString = null; try { if (value.getType() == PropertyType.STRING) { likeDataString = value.getString(); } else if (value.getType() == PropertyType.BINARY) { likeDataString = getCharsetString(value.getString(), Constants.DEFAULT_ENCODING); } else if (value.getType() == PropertyType.DOUBLE || value.getType() == PropertyType.LONG) { return checkValueConstraints(requiredType, constraints, value); } else { return false; } // try parse... JCRDateFormat.parse(likeDataString); // validate return checkValueConstraints(requiredType, constraints, value); } catch (Exception e) { // Hm, this is not date format string return false; } } else if (requiredType == PropertyType.DOUBLE) { String likeDoubleString = null; try { if (value.getType() == PropertyType.STRING) { likeDoubleString = value.getString(); } else if (value.getType() == PropertyType.BINARY) { likeDoubleString = getCharsetString(value.getString(), Constants.DEFAULT_ENCODING); } else if (value.getType() == PropertyType.DATE) { return true; } else if (value.getType() == PropertyType.LONG) { return checkValueConstraints(requiredType, constraints, value); } else { return false; } Double doubleValue = new Double(likeDoubleString); return doubleValue != null && checkValueConstraints(requiredType, constraints, value); } catch (Exception e) { // Hm, this is not double formated string return false; } } else if (requiredType == PropertyType.LONG) { String likeLongString = null; try { if (value.getType() == PropertyType.STRING) { likeLongString = value.getString(); } else if (value.getType() == PropertyType.BINARY) { likeLongString = getCharsetString(value.getString(), Constants.DEFAULT_ENCODING); } else if (value.getType() == PropertyType.DATE) { return true; } else if (value.getType() == PropertyType.DOUBLE) { return true; } else { return false; } Long longValue = new Long(likeLongString); return longValue != null && checkValueConstraints(requiredType, constraints, value); } catch (Exception e) { // Hm, this is not long formated string return false; } } else if (requiredType == PropertyType.NAME) { String likeNameString = null; try { if (value.getType() == PropertyType.STRING) { likeNameString = value.getString(); } else if (value.getType() == PropertyType.BINARY) { likeNameString = getCharsetString(value.getString(), Constants.DEFAULT_ENCODING); } else if (value.getType() == PropertyType.PATH) { String pathString = value.getString(); String[] pathParts = pathString.split("\\/"); if (pathString.startsWith("/") && (pathParts.length > 1 || pathString.indexOf("[") > 0)) { // Path is not relative - absolute // FALSE if it is more than one element long // or has an index return false; } else if (!pathString.equals("/") && pathParts.length == 1 && pathString.indexOf("[") < 0) { // Path is relative // TRUE if it is one element long // and has no index return checkValueConstraints(requiredType, constraints, value); } else if (pathString.startsWith("/") && pathString.lastIndexOf("/") < 1 && pathString.indexOf("[") < 0) { return checkValueConstraints(requiredType, constraints, value); } else { return false; } } else { return false; } try { Value nameValue = valueFactory.createValue(likeNameString, requiredType); return nameValue != null && checkValueConstraints(requiredType, constraints, value); } catch (Exception e) { return false; } } catch (Exception e) { return false; } } else if (requiredType == PropertyType.PATH) { String likeNameString = null; try { if (value.getType() == PropertyType.STRING) { likeNameString = value.getString(); } else if (value.getType() == PropertyType.BINARY) { likeNameString = getCharsetString(value.getString(), Constants.DEFAULT_ENCODING); } else if (value.getType() == PropertyType.NAME) { return checkValueConstraints(requiredType, constraints, value); } else { return false; } try { Value nameValue = valueFactory.createValue(likeNameString, requiredType); return nameValue != null && checkValueConstraints(requiredType, constraints, value); } catch (Exception e) { return false; } } catch (Exception e) { return false; } } else if (requiredType == PropertyType.STRING) { String likeStringString = null; try { if (value.getType() == PropertyType.BINARY) { likeStringString = getCharsetString(value.getString(), Constants.DEFAULT_ENCODING); } else if (value.getType() == PropertyType.DATE || value.getType() == PropertyType.LONG || value.getType() == PropertyType.BOOLEAN || value.getType() == PropertyType.NAME || value.getType() == PropertyType.PATH || value.getType() == PropertyType.DOUBLE) { likeStringString = value.getString(); } else { return false; } return likeStringString != null && checkValueConstraints(requiredType, constraints, value); } catch (Exception e) { return false; } } else if (requiredType == PropertyType.UNDEFINED) { return checkValueConstraints(requiredType, constraints, value); } else { return false; } } /** * Check value constrains. * * @param requiredType int * @param constraints - string constrains. * @param value - value to check. * @return result of check. */ private boolean checkValueConstraints(int requiredType, String[] constraints, Value value) { ValueConstraintsMatcher constrMatcher = new ValueConstraintsMatcher(constraints, locationFactory, dataManager, nodeTypeDataManager); try { return constrMatcher.match(((BaseValue)value).getInternalData(), requiredType); } catch (RepositoryException e1) { return false; } } private String getCharsetString(String source, String charSetName) { try { CharBuffer cb = CharBuffer.wrap(source.toCharArray()); Charset cs = Charset.forName(charSetName); CharsetEncoder cse = cs.newEncoder(); ByteBuffer encoded = cse.encode(cb); return new String(encoded.array()).trim(); // Trim is very important!!! } catch (IllegalStateException e) { return null; } catch (MalformedInputException e) { return null; } catch (UnmappableCharacterException e) { return null; } catch (CharacterCodingException e) { return null; } } /** * @param pdefs * @return */ private PropertyDefinition[] getPropertyDefinition(PropertyDefinitionData[] pdefs) { PropertyDefinition[] propertyDefinitions = new PropertyDefinition[pdefs.length]; for (int i = 0; i < pdefs.length; i++) { propertyDefinitions[i] = new PropertyDefinitionImpl(pdefs[i], nodeTypeDataManager, nodeTypeManager, locationFactory, valueFactory, dataManager); } return propertyDefinitions; } private boolean isCharsetString(String source, String charSetName) { try { String s = getCharsetString(source, charSetName); return s != null; } catch (Exception e) { return false; } } }