/* * Copyright (C) 2005-2009 Alfresco Software Limited. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program 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 General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * As a special exception to the terms and conditions of version 2.0 of * the GPL, you may redistribute this Program in connection with Free/Libre * and Open Source Software ("FLOSS") applications as described in Alfresco's * FLOSS exception. You should have recieved a copy of the text describing * the FLOSS exception, and it is also available here: * http://www.alfresco.com/legal/licensing" */ package org.alfresco.repo.dictionary; import java.util.List; import org.alfresco.repo.dictionary.constraint.ListOfValuesConstraint; import org.alfresco.repo.dictionary.constraint.NumericRangeConstraint; import org.alfresco.repo.dictionary.constraint.RegexConstraint; import org.alfresco.repo.dictionary.constraint.RegisteredConstraint; import org.alfresco.repo.dictionary.constraint.StringLengthConstraint; import org.alfresco.service.cmr.dictionary.Constraint; import org.alfresco.service.cmr.dictionary.ConstraintDefinition; import org.alfresco.service.cmr.dictionary.DictionaryException; import org.alfresco.service.cmr.dictionary.ModelDefinition; import org.alfresco.service.namespace.NamespacePrefixResolver; import org.alfresco.service.namespace.QName; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.beans.InvalidPropertyException; import org.springframework.beans.PropertyAccessException; /** * Compiled Property Constraint * * @author Derek Hulley */ /* package */class M2ConstraintDefinition implements ConstraintDefinition { private static final String PROP_SHORT_NAME = "shortName"; private static final String PROP_TITLE = "title"; private static final String PROP_DESCRIPTION = "description"; public static final String ERR_CYCLIC_REF = "d_dictionary.constraint.err.cyclic_ref"; public static final String ERR_TYPE_AND_REF = "d_dictionary.constraint.err.type_and_ref"; public static final String ERR_TYPE_OR_REF = "d_dictionary.constraint.err.type_or_ref"; public static final String ERR_REF_NOT_FOUND = "d_dictionary.constraint.err.ref_not_found"; public static final String ERR_ANON_NEEDS_PROPERTY = "d_dictionary.constraint.err.anon_needs_property"; public static final String ERR_INVALID_TYPE = "d_dictionary.constraint.err.invalid_type"; public static final String ERR_SIMPLE_AND_LIST = "d_dictionary.constraint.err.property_simple_and_list"; public static final String ERR_CONSTRUCT_FAILURE = "d_dictionary.constraint.err.construct_failure"; public static final String ERR_PROPERTY_MISMATCH = "d_dictionary.constraint.err.property_mismatch"; public static final String ERR_RESERVED_PROPERTY = "d_dictionary.constraint.err.reserved_property"; private static int anonPropCount = 0; private ModelDefinition model; private NamespacePrefixResolver prefixResolver; private M2Constraint m2Constraint; private QName name; private Constraint constraint; private boolean resolving; /* package */M2ConstraintDefinition(M2PropertyDefinition m2PropertyDef, M2Constraint m2Constraint, NamespacePrefixResolver prefixResolver) { this(m2PropertyDef.getModel(), m2PropertyDef, m2Constraint, prefixResolver); } /* package */M2ConstraintDefinition(ModelDefinition modelDefinition, M2PropertyDefinition m2PropertyDef, M2Constraint m2Constraint, NamespacePrefixResolver prefixResolver) { this.model = modelDefinition; this.m2Constraint = m2Constraint; this.prefixResolver = prefixResolver; String constraintName = m2Constraint.getName(); if (constraintName == null) { // the constraint is anonymous, so it has to be defined within the context of a property if (m2PropertyDef == null) { throw new DictionaryException(ERR_ANON_NEEDS_PROPERTY); } // pick the name up from the property and some anonymous value String localName = m2PropertyDef.getName().getLocalName() + "_anon_" + (++anonPropCount); this.name = QName.createQName(m2PropertyDef.getName().getNamespaceURI(), localName); m2Constraint.setName(this.name.getPrefixedQName(prefixResolver).toPrefixString()); } else { this.name = QName.createQName(m2Constraint.getName(), prefixResolver); } } /* package */synchronized void resolveDependencies(ModelQuery query) { if (resolving) { throw new DictionaryException(ERR_CYCLIC_REF, name.toPrefixString()); } // prevent circular references try { resolving = true; resolveInternal(query); } finally { resolving = false; } } private synchronized void resolveInternal(ModelQuery query) { if (constraint != null) { // already been resolved return; } String shortName = name.toPrefixString(); String ref = m2Constraint.getRef(); String type = m2Constraint.getType(); String title = m2Constraint.getTitle(); String description = m2Constraint.getDescription(); if (ref != null && type != null) { throw new DictionaryException(ERR_TYPE_AND_REF, shortName); } else if (ref == null && type == null) { throw new DictionaryException(ERR_TYPE_OR_REF, shortName); } else if (ref != null) { // resolve the reference name QName qnameRef = QName.createQName(ref, prefixResolver); // ensure that the reference exists in the model M2ConstraintDefinition constraintDef = (M2ConstraintDefinition) query.getConstraint(qnameRef); if (constraintDef == null) { throw new DictionaryException(ERR_REF_NOT_FOUND, ref, shortName); } // make sure that the constraint definition has itself been resolved constraintDef.resolveDependencies(query); // just use the constraint provided by the referenced definition this.constraint = constraintDef.getConstraint(); if (m2Constraint.getTitle() == null) { m2Constraint.setTitle(constraintDef.getTitle()); } if (m2Constraint.getDescription() == null) { m2Constraint.setDescription(constraintDef.getDescription()); } } else { // we have to build the constraint from the type try { ConstraintType constraintType = ConstraintType.valueOf(type); constraint = constraintType.newInstance(); } catch (IllegalArgumentException e) { // try to establish it as a class try { @SuppressWarnings("unchecked") Class clazz = Class.forName(type); constraint = (Constraint) clazz.newInstance(); } catch (ClassNotFoundException ee) { throw new DictionaryException(ERR_INVALID_TYPE, type, shortName); } catch (ClassCastException ee) { throw new DictionaryException(ERR_INVALID_TYPE, type, shortName); } catch (Exception ee) { throw new DictionaryException(ERR_CONSTRUCT_FAILURE, type, shortName); } } // property setters BeanWrapper beanWrapper = new BeanWrapperImpl(constraint); List constraintNamedValues = m2Constraint.getParameters(); if (constraintNamedValues != null) { for (M2NamedValue namedValue : constraintNamedValues) { String namedValueName = namedValue.getName(); // Check for reserved properties if (namedValueName.equals(PROP_SHORT_NAME)) { throw new DictionaryException(ERR_RESERVED_PROPERTY, PROP_SHORT_NAME, namedValueName); } Object value = null; if (namedValue.getSimpleValue() != null && namedValue.getListValue() != null) { throw new DictionaryException(ERR_SIMPLE_AND_LIST, shortName, namedValue.getName()); } else if (namedValue.getSimpleValue() != null) { value = namedValue.getSimpleValue(); } else if (namedValue.getListValue() != null) { value = namedValue.getListValue(); } try { beanWrapper.setPropertyValue(namedValueName, value); } catch (PropertyAccessException e) { throw new DictionaryException(ERR_PROPERTY_MISMATCH, e, namedValueName, shortName); } catch (InvalidPropertyException e) { throw new DictionaryException(ERR_PROPERTY_MISMATCH, e, namedValueName, shortName); } } // Pass in the short name as a special property, if it is available if (beanWrapper.isWritableProperty(PROP_SHORT_NAME)) { try { beanWrapper.setPropertyValue(PROP_SHORT_NAME, shortName); } catch (PropertyAccessException e) { throw new DictionaryException(ERR_PROPERTY_MISMATCH, e, shortName, shortName); } catch (InvalidPropertyException e) { throw new DictionaryException(ERR_PROPERTY_MISMATCH, e, shortName, shortName); } } if ((title != null) && (beanWrapper.isWritableProperty(PROP_TITLE))) { beanWrapper.setPropertyValue(PROP_TITLE, title); } if ((title != null) && (beanWrapper.isWritableProperty(PROP_DESCRIPTION))) { beanWrapper.setPropertyValue(PROP_DESCRIPTION, description); } } // now initialize constraint.initialize(); } } /** * @see #getName() */ @Override public String toString() { return getName().toString(); } public ModelDefinition getModel() { return model; } public QName getName() { return name; } public String getTitle() { String value = M2Label.getLabel(model, "constraint", name, "title"); if (value == null) { value = m2Constraint.getTitle(); } return value; } public String getDescription() { String value = M2Label.getLabel(model, "constraint", name, "description"); if (value == null) { value = m2Constraint.getDescription(); } return value; } public Constraint getConstraint() { return constraint; } /** * Well-known constraint types */ public static enum ConstraintType { REGISTERED { @Override protected Constraint newInstance() { return new RegisteredConstraint(); } }, REGEX { @Override protected Constraint newInstance() { return new RegexConstraint(); } }, MINMAX { @Override protected Constraint newInstance() { return new NumericRangeConstraint(); } }, LENGTH { @Override protected Constraint newInstance() { return new StringLengthConstraint(); } }, LIST { @Override protected Constraint newInstance() { return new ListOfValuesConstraint(); } }; /** * @return Returns the constraint implementation */ protected abstract Constraint newInstance(); } }