Property constraint dictionary support

Constraint implementation support
Regular expression constraint


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2547 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2006-03-14 22:57:50 +00:00
parent f3658f2a58
commit 783de4f80a
24 changed files with 1066 additions and 54 deletions

View File

@@ -71,6 +71,7 @@
<value>alfresco.messages.template-service</value> <value>alfresco.messages.template-service</value>
<value>alfresco.messages.lock-service</value> <value>alfresco.messages.lock-service</value>
<value>alfresco.messages.patch-service</value> <value>alfresco.messages.patch-service</value>
<value>alfresco.messages.dictionary-messages</value>
</list> </list>
</property> </property>
</bean> </bean>

View File

@@ -0,0 +1,22 @@
# Dictionary-related messages
d_dictionary.model.err.no_model=Model ''{0}'' does not exist
d_dictionary.model.err.type_not_found=Failed to create anonymous type as specified type {0} not found
d_dictionary.model.err.aspect_not_found=Failed to create anonymous type as specified aspect {0} not found
d_dictionary.model.err.cyclic_ref=Constraint ''{0}'' is part of a cyclic reference of constraints
d_dictionary.model.err.type_and_ref=Constraint ''{0}'' cannot have a ''type'' and be a ''reference'' attribute
d_dictionary.model.err.type_or_ref=Constraint ''{0}'' cannot have a ''type'' and be a ''reference'' attribute
d_dictionary.model.err.ref_not_found=Constraint reference ''{0}'' not found on constraint ''{1}''
d_dictionary.model.err.anon_needs_property=Anonymous constraints can only be declared within the context of a property
d_dictionary.model.err.invalid_type=Constraint type ''{0}'' on constraint ''{1}'' is not a well-known type or a valid Constraint implementation
d_dictionary.model.err.construct_failure=Failed to construct an instance of type ''{0}'' for constraint ''{1}''
d_dictionary.model.err.property_not_set=Property ''{0}'' has not been set on constraint ''{1}''
d_dictionary.model.err.evaluate_exception=Exception during evaluation of constraint ''{0}'': {1}
d_dictionary.property.err.property_type_not_specified=Property type of property ''{0}'' must be specified
d_dictionary.property.err.property_type_not_found=Property type ''{0}'' of property ''{1}'' is not found
d_dictionary.property.err.single_valued_content=Content properties must be single-valued
d_dictionary.property.err.duplicate_constraint_on_property=Found duplicate constraint definition ''{0}'' within property ''{1}''
d_dictionary.constraint.regex.no_match=Value ''{0}'' does not match regular expression: {1}

View File

@@ -26,6 +26,7 @@ import java.util.TreeMap;
import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.ClassDefinition; import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryException; import org.alfresco.service.cmr.dictionary.DictionaryException;
import org.alfresco.service.cmr.dictionary.ModelDefinition; import org.alfresco.service.cmr.dictionary.ModelDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.PropertyDefinition;
@@ -66,7 +67,7 @@ import org.apache.commons.logging.LogFactory;
private Map<QName, AspectDefinition> aspects = new HashMap<QName, AspectDefinition>(); private Map<QName, AspectDefinition> aspects = new HashMap<QName, AspectDefinition>();
private Map<QName, PropertyDefinition> properties = new HashMap<QName, PropertyDefinition>(); private Map<QName, PropertyDefinition> properties = new HashMap<QName, PropertyDefinition>();
private Map<QName, AssociationDefinition> associations = new HashMap<QName, AssociationDefinition>(); private Map<QName, AssociationDefinition> associations = new HashMap<QName, AssociationDefinition>();
private Map<QName, ConstraintDefinition> constraints = new HashMap<QName, ConstraintDefinition>();
/** /**
* Construct * Construct
@@ -82,11 +83,11 @@ import org.apache.commons.logging.LogFactory;
// Phase 1: Construct model definitions from model entries // Phase 1: Construct model definitions from model entries
// resolving qualified names // resolving qualified names
this.model = model; this.model = model;
constructDefinitions(model, dictionaryDAO, namespaceDAO); constructDefinitions(model, namespaceDAO);
// Phase 2: Resolve dependencies between model definitions // Phase 2: Resolve dependencies between model definitions
ModelQuery query = new DelegateModelQuery(this, dictionaryDAO); ModelQuery query = new DelegateModelQuery(this, dictionaryDAO);
resolveDependencies(query); resolveDependencies(query, namespaceDAO);
// Phase 3: Resolve inheritance of values within class hierachy // Phase 3: Resolve inheritance of values within class hierachy
resolveInheritance(query); resolveInheritance(query);
@@ -111,10 +112,9 @@ import org.apache.commons.logging.LogFactory;
* Construct compiled definitions * Construct compiled definitions
* *
* @param model model definition * @param model model definition
* @param dictionaryDAO dictionary DAO
* @param namespaceDAO namespace DAO * @param namespaceDAO namespace DAO
*/ */
private void constructDefinitions(M2Model model, DictionaryDAO dictionaryDAO, NamespaceDAO namespaceDAO) private void constructDefinitions(M2Model model, NamespaceDAO namespaceDAO)
{ {
NamespacePrefixResolver localPrefixes = createLocalPrefixResolver(model, namespaceDAO); NamespacePrefixResolver localPrefixes = createLocalPrefixResolver(model, namespaceDAO);
@@ -155,6 +155,18 @@ import org.apache.commons.logging.LogFactory;
classes.put(def.getName(), def); classes.put(def.getName(), def);
aspects.put(def.getName(), def); aspects.put(def.getName(), def);
} }
// Construct Constraint Definitions
for (M2Constraint constraint : model.getConstraints())
{
M2ConstraintDefinition def = new M2ConstraintDefinition(modelDefinition, null, constraint, localPrefixes);
QName qname = def.getName();
if (constraints.containsKey(qname))
{
throw new DictionaryException("Found duplicate constraint definition " + constraint.getName() + " (an aspect)");
}
constraints.put(qname, def);
}
} }
@@ -196,15 +208,21 @@ import org.apache.commons.logging.LogFactory;
* *
* @param query support for querying other items in model * @param query support for querying other items in model
*/ */
private void resolveDependencies(ModelQuery query) private void resolveDependencies(ModelQuery query, NamespaceDAO namespaceDAO)
{ {
NamespacePrefixResolver prefixResolver = createLocalPrefixResolver(model, namespaceDAO);
for (DataTypeDefinition def : dataTypes.values()) for (DataTypeDefinition def : dataTypes.values())
{ {
((M2DataTypeDefinition)def).resolveDependencies(query); ((M2DataTypeDefinition)def).resolveDependencies(query);
} }
for (ClassDefinition def : classes.values()) for (ClassDefinition def : classes.values())
{ {
((M2ClassDefinition)def).resolveDependencies(query); ((M2ClassDefinition)def).resolveDependencies(query, prefixResolver, constraints);
}
for (ConstraintDefinition def : constraints.values())
{
((M2ConstraintDefinition)def).resolveDependencies(query);
} }
} }
@@ -363,4 +381,11 @@ import org.apache.commons.logging.LogFactory;
return associations.get(name); return associations.get(name);
} }
/* (non-Javadoc)
* @see org.alfresco.repo.dictionary.impl.ModelQuery#getConstraint(QName)
*/
public ConstraintDefinition getConstraint(QName name)
{
return constraints.get(name);
}
} }

View File

@@ -19,6 +19,7 @@ package org.alfresco.repo.dictionary;
import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.ClassDefinition; import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition;
@@ -148,4 +149,16 @@ import org.alfresco.service.namespace.QName;
return def; return def;
} }
/* (non-Javadoc)
* @see ModelQuery#getConstraint(QName)
*/
public ConstraintDefinition getConstraint(QName name)
{
ConstraintDefinition def = query.getConstraint(name);
if (def == null)
{
def = delegate.getConstraint(name);
}
return def;
}
} }

View File

@@ -24,6 +24,7 @@ import java.util.Map;
import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.ClassDefinition; import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryException; import org.alfresco.service.cmr.dictionary.DictionaryException;
import org.alfresco.service.cmr.dictionary.ModelDefinition; import org.alfresco.service.cmr.dictionary.ModelDefinition;
@@ -144,7 +145,7 @@ public class DictionaryDAOImpl implements DictionaryDAO
if (model == null) if (model == null)
{ {
// TODO: Load model from persistent store // TODO: Load model from persistent store
throw new DictionaryException("Model " + modelName + " does not exist"); throw new DictionaryException("d_dictionary.model.err.no_model", modelName);
} }
return model; return model;
} }
@@ -226,6 +227,11 @@ public class DictionaryDAOImpl implements DictionaryDAO
return (model == null) ? null : model.getProperty(propertyName); return (model == null) ? null : model.getProperty(propertyName);
} }
public ConstraintDefinition getConstraint(QName constraintQName)
{
CompiledModel model = getCompiledModelForNamespace(constraintQName.getNamespaceURI());
return (model == null) ? null : model.getConstraint(constraintQName);
}
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.repo.dictionary.impl.ModelQuery#getAssociation(org.alfresco.repo.ref.QName) * @see org.alfresco.repo.dictionary.impl.ModelQuery#getAssociation(org.alfresco.repo.ref.QName)
@@ -288,7 +294,7 @@ public class DictionaryDAOImpl implements DictionaryDAO
TypeDefinition typeDef = getType(type); TypeDefinition typeDef = getType(type);
if (typeDef == null) if (typeDef == null)
{ {
throw new DictionaryException("Failed to create anonymous type as specified type " + type + " not found"); throw new DictionaryException("d_dictionary.model.err.type_not_found", type);
} }
Collection<AspectDefinition> aspectDefs = new ArrayList<AspectDefinition>(); Collection<AspectDefinition> aspectDefs = new ArrayList<AspectDefinition>();
if (aspects != null) if (aspects != null)
@@ -298,7 +304,7 @@ public class DictionaryDAOImpl implements DictionaryDAO
AspectDefinition aspectDef = getAspect(aspect); AspectDefinition aspectDef = getAspect(aspect);
if (typeDef == null) if (typeDef == null)
{ {
throw new DictionaryException("Failed to create anonymous type as specified aspect " + aspect + " not found"); throw new DictionaryException("d_dictionary.model.err.aspect_not_found", aspect);
} }
aspectDefs.add(aspectDef); aspectDefs.add(aspectDef);
} }

View File

@@ -21,7 +21,11 @@ import java.util.List;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.repo.dictionary.constraint.RegexConstraint;
import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.Constraint;
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.InvalidTypeException; import org.alfresco.service.cmr.dictionary.InvalidTypeException;
@@ -33,7 +37,9 @@ import org.alfresco.service.namespace.QName;
public class DictionaryDAOTest extends TestCase public class DictionaryDAOTest extends TestCase
{ {
public static final String TEST_RESOURCE_MESSAGES = "alfresco/messages/dictionary-messages";
private static final String TEST_URL = "http://www.alfresco.org/test/dictionarydaotest/1.0";
private static final String TEST_MODEL = "org/alfresco/repo/dictionary/dictionarydaotest_model.xml"; private static final String TEST_MODEL = "org/alfresco/repo/dictionary/dictionarydaotest_model.xml";
private static final String TEST_BUNDLE = "org/alfresco/repo/dictionary/dictionarydaotest_model"; private static final String TEST_BUNDLE = "org/alfresco/repo/dictionary/dictionarydaotest_model";
private DictionaryService service; private DictionaryService service;
@@ -42,6 +48,9 @@ public class DictionaryDAOTest extends TestCase
@Override @Override
public void setUp() public void setUp()
{ {
// register resource bundles for messages
I18NUtil.registerResourceBundle(TEST_RESOURCE_MESSAGES);
// Instantiate Dictionary Service // Instantiate Dictionary Service
NamespaceDAO namespaceDAO = new NamespaceDAOImpl(); NamespaceDAO namespaceDAO = new NamespaceDAOImpl();
DictionaryDAOImpl dictionaryDAO = new DictionaryDAOImpl(namespaceDAO); DictionaryDAOImpl dictionaryDAO = new DictionaryDAOImpl(namespaceDAO);
@@ -90,34 +99,59 @@ public class DictionaryDAOTest extends TestCase
public void testLabels() public void testLabels()
{ {
QName model = QName.createQName("http://www.alfresco.org/test/dictionarydaotest/1.0", "dictionarydaotest"); QName model = QName.createQName(TEST_URL, "dictionarydaotest");
ModelDefinition modelDef = service.getModel(model); ModelDefinition modelDef = service.getModel(model);
assertEquals("Model Description", modelDef.getDescription()); assertEquals("Model Description", modelDef.getDescription());
QName type = QName.createQName("http://www.alfresco.org/test/dictionarydaotest/1.0", "base"); QName type = QName.createQName(TEST_URL, "base");
TypeDefinition typeDef = service.getType(type); TypeDefinition typeDef = service.getType(type);
assertEquals("Base Title", typeDef.getTitle()); assertEquals("Base Title", typeDef.getTitle());
assertEquals("Base Description", typeDef.getDescription()); assertEquals("Base Description", typeDef.getDescription());
QName prop = QName.createQName("http://www.alfresco.org/test/dictionarydaotest/1.0", "prop1"); QName prop = QName.createQName(TEST_URL, "prop1");
PropertyDefinition propDef = service.getProperty(prop); PropertyDefinition propDef = service.getProperty(prop);
assertEquals("Prop1 Title", propDef.getTitle()); assertEquals("Prop1 Title", propDef.getTitle());
assertEquals("Prop1 Description", propDef.getDescription()); assertEquals("Prop1 Description", propDef.getDescription());
QName assoc = QName.createQName("http://www.alfresco.org/test/dictionarydaotest/1.0", "assoc1"); QName assoc = QName.createQName(TEST_URL, "assoc1");
AssociationDefinition assocDef = service.getAssociation(assoc); AssociationDefinition assocDef = service.getAssociation(assoc);
assertEquals("Assoc1 Title", assocDef.getTitle()); assertEquals("Assoc1 Title", assocDef.getTitle());
assertEquals("Assoc1 Description", assocDef.getDescription()); assertEquals("Assoc1 Description", assocDef.getDescription());
QName datatype = QName.createQName("http://www.alfresco.org/test/dictionarydaotest/1.0", "datatype"); QName datatype = QName.createQName(TEST_URL, "datatype");
DataTypeDefinition datatypeDef = service.getDataType(datatype); DataTypeDefinition datatypeDef = service.getDataType(datatype);
assertEquals("Datatype Analyser", datatypeDef.getAnalyserClassName()); assertEquals("Datatype Analyser", datatypeDef.getAnalyserClassName());
} }
public void testConstraints()
{
// get the constraints for a property without constraints
QName propNoConstraintsQName = QName.createQName(TEST_URL, "fileprop");
PropertyDefinition propNoConstraintsDef = service.getProperty(propNoConstraintsQName);
assertNotNull("Property without constraints returned empty list", propNoConstraintsDef.getConstraints());
// get the constraints defined for the property
QName prop1QName = QName.createQName(TEST_URL, "prop1");
PropertyDefinition propDef = service.getProperty(prop1QName);
List<ConstraintDefinition> constraints = propDef.getConstraints();
assertNotNull("Null constraints list", constraints);
assertEquals("Incorrect number of constraints", 1, constraints.size());
// check the individual constraints
ConstraintDefinition constraintDef = constraints.get(0);
assertTrue("Constraint anonymous name incorrect", constraintDef.getName().getLocalName().startsWith("prop1_anon"));
// check that the constraint implementation is valid (it used a reference)
Constraint constraint = constraintDef.getConstraint();
assertNotNull("Reference constraint has no implementation", constraint);
// make sure it is the correct type of constraint
assertTrue("Expected type REGEX constraint", constraint instanceof RegexConstraint);
}
public void testSubClassOf() public void testSubClassOf()
{ {
QName invalid = QName.createQName("http://www.alfresco.org/test/dictionarydaotest/1.0", "invalid"); QName invalid = QName.createQName(TEST_URL, "invalid");
QName base = QName.createQName("http://www.alfresco.org/test/dictionarydaotest/1.0", "base"); QName base = QName.createQName(TEST_URL, "base");
QName file = QName.createQName("http://www.alfresco.org/test/dictionarydaotest/1.0", "file"); QName file = QName.createQName(TEST_URL, "file");
QName folder = QName.createQName("http://www.alfresco.org/test/dictionarydaotest/1.0", "folder"); QName folder = QName.createQName(TEST_URL, "folder");
QName referenceable = QName.createQName("http://www.alfresco.org/test/dictionarydaotest/1.0", "referenceable"); QName referenceable = QName.createQName(TEST_URL, "referenceable");
// Test invalid args // Test invalid args
try try

View File

@@ -27,6 +27,7 @@ import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition; import org.alfresco.service.cmr.dictionary.ChildAssociationDefinition;
import org.alfresco.service.cmr.dictionary.ClassDefinition; import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryException; import org.alfresco.service.cmr.dictionary.DictionaryException;
import org.alfresco.service.cmr.dictionary.ModelDefinition; import org.alfresco.service.cmr.dictionary.ModelDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.PropertyDefinition;
@@ -166,7 +167,10 @@ import org.alfresco.service.namespace.QName;
} }
/*package*/ void resolveDependencies(ModelQuery query) /*package*/ void resolveDependencies(
ModelQuery query,
NamespacePrefixResolver prefixResolver,
Map<QName, ConstraintDefinition> modelConstraints)
{ {
if (parentName != null) if (parentName != null)
{ {
@@ -179,7 +183,7 @@ import org.alfresco.service.namespace.QName;
for (PropertyDefinition def : properties.values()) for (PropertyDefinition def : properties.values())
{ {
((M2PropertyDefinition)def).resolveDependencies(query); ((M2PropertyDefinition)def).resolveDependencies(query, prefixResolver, modelConstraints);
} }
for (AssociationDefinition def : associations.values()) for (AssociationDefinition def : associations.values())
{ {

View File

@@ -0,0 +1,74 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.repo.dictionary;
import java.util.ArrayList;
import java.util.List;
/**
* Abstract Property Constraint.
*
* @author Derek Hulley
*/
public class M2Constraint
{
private String name;
private String ref;
private String type;
private String description;
private List<M2NamedValue> parameters = new ArrayList<M2NamedValue>(2);
/*package*/ M2Constraint()
{
}
@Override
public String toString()
{
return this.name;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getRef()
{
return ref;
}
public String getType()
{
return type;
}
public String getDescription()
{
return description;
}
public List<M2NamedValue> getParameters()
{
return parameters;
}
}

View File

@@ -0,0 +1,227 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.repo.dictionary;
import java.util.List;
import org.alfresco.repo.dictionary.constraint.RegexConstraint;
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;
/**
* Compiled Property Constraint
*
* @author Derek Hulley
*/
/*package*/ class M2ConstraintDefinition implements ConstraintDefinition
{
public static final String ERR_CYCLIC_REF = "d_dictionary.model.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_CONSTRUCT_FAILURE = "d_dictionary.constraint.err.construct_failure";
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(localName);
}
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();
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();
}
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
{
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<M2NamedValue> constraintNamedValues = m2Constraint.getParameters();
for (M2NamedValue namedValue : constraintNamedValues)
{
beanWrapper.setPropertyValue(namedValue.getName(), namedValue.getValue());
}
// now initialize
constraint.initialize();
}
}
/**
* @see #getName()
*/
@Override
public String toString()
{
return getName().toString();
}
public ModelDefinition getModel()
{
return model;
}
public QName getName()
{
return name;
}
public Constraint getConstraint()
{
return constraint;
}
/**
* Well-known constraint types
*/
public static enum ConstraintType
{
REGEX
{
@Override
protected Constraint newInstance()
{
return new RegexConstraint();
}
};
/**
* @return Returns the constraint implementation
*/
protected abstract Constraint newInstance();
}
}

View File

@@ -50,7 +50,7 @@ public class M2Model
private List<M2DataType> dataTypes = new ArrayList<M2DataType>(); private List<M2DataType> dataTypes = new ArrayList<M2DataType>();
private List<M2Type> types = new ArrayList<M2Type>(); private List<M2Type> types = new ArrayList<M2Type>();
private List<M2Aspect> aspects = new ArrayList<M2Aspect>(); private List<M2Aspect> aspects = new ArrayList<M2Aspect>();
private List<M2Constraint> constraints = new ArrayList<M2Constraint>();
private M2Model() private M2Model()
{ {
@@ -379,8 +379,13 @@ public class M2Model
return null; return null;
} }
public List<M2Constraint> getConstraints()
{
return Collections.unmodifiableList(constraints);
}
// Do not delete: referenced by m2binding.xml // Do not delete: referenced by m2binding.xml
@SuppressWarnings("unused")
private static List createList() private static List createList()
{ {
return new ArrayList(); return new ArrayList();

View File

@@ -0,0 +1,53 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.repo.dictionary;
/**
* Definition of a named value that can be used for property injection.
*
* @author Derek Hulley
*/
public class M2NamedValue
{
private String name;
private String value;
/*package*/ M2NamedValue()
{
}
@Override
public String toString()
{
return (name + "=" + value);
}
public String getName()
{
return name;
}
/**
* @return Returns the raw, unconverted value
*/
public String getValue()
{
return value;
}
}

View File

@@ -16,6 +16,9 @@
*/ */
package org.alfresco.repo.dictionary; package org.alfresco.repo.dictionary;
import java.util.Collections;
import java.util.List;
/** /**
* Property Definition * Property Definition
@@ -37,7 +40,7 @@ public class M2Property
private boolean isIndexedAtomically = true; private boolean isIndexedAtomically = true;
private boolean isStoredInIndex = false; private boolean isStoredInIndex = false;
private boolean isTokenisedInIndex = true; private boolean isTokenisedInIndex = true;
private List<M2Constraint> constraints;
/*package*/ M2Property() /*package*/ M2Property()
{ {
@@ -193,4 +196,16 @@ public class M2Property
this.isTokenisedInIndex = isTokenisedInIndex; this.isTokenisedInIndex = isTokenisedInIndex;
} }
public List<M2Constraint> getConstraints()
{
if (constraints == null)
{
return Collections.emptyList();
}
else
{
return constraints;
}
}
} }

View File

@@ -16,11 +16,18 @@
*/ */
package org.alfresco.repo.dictionary; package org.alfresco.repo.dictionary;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.alfresco.service.cmr.dictionary.ClassDefinition; import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryException; import org.alfresco.service.cmr.dictionary.DictionaryException;
import org.alfresco.service.cmr.dictionary.ModelDefinition; import org.alfresco.service.cmr.dictionary.ModelDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.namespace.NamespacePrefixResolver; import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
@@ -33,49 +40,85 @@ import org.alfresco.service.namespace.QName;
/*package*/ class M2PropertyDefinition implements PropertyDefinition /*package*/ class M2PropertyDefinition implements PropertyDefinition
{ {
private ClassDefinition classDef; private ClassDefinition classDef;
private M2Property property; private M2Property m2Property;
private QName name; private QName name;
private QName propertyTypeName; private QName propertyTypeName;
private DataTypeDefinition dataType; private DataTypeDefinition dataType;
private List<ConstraintDefinition> constraints = new ArrayList<ConstraintDefinition>(5);
private Map<QName, ConstraintDefinition> constraintsByQName = new HashMap<QName, ConstraintDefinition>(7);
/*package*/ M2PropertyDefinition(
/*package*/ M2PropertyDefinition(ClassDefinition classDef, M2Property m2Property, NamespacePrefixResolver resolver) ClassDefinition classDef,
M2Property m2Property,
NamespacePrefixResolver prefixResolver)
{ {
this.classDef = classDef; this.classDef = classDef;
this.property = m2Property; this.m2Property = m2Property;
// Resolve Names // Resolve Names
this.name = QName.createQName(property.getName(), resolver); this.name = QName.createQName(m2Property.getName(), prefixResolver);
this.propertyTypeName = QName.createQName(property.getType(), resolver); this.propertyTypeName = QName.createQName(m2Property.getType(), prefixResolver);
} }
/*package*/ M2PropertyDefinition(ClassDefinition classDef, PropertyDefinition propertyDef, M2PropertyOverride override) /*package*/ M2PropertyDefinition(
ClassDefinition classDef,
PropertyDefinition propertyDef,
M2PropertyOverride override)
{ {
this.classDef = classDef; this.classDef = classDef;
this.property = createOverriddenProperty(propertyDef, override); this.m2Property = createOverriddenProperty(propertyDef, override);
this.name = propertyDef.getName(); this.name = propertyDef.getName();
this.dataType = propertyDef.getDataType(); this.dataType = propertyDef.getDataType();
this.propertyTypeName = this.dataType.getName(); this.propertyTypeName = this.dataType.getName();
} }
/*package*/ void resolveDependencies(ModelQuery query) /*package*/ void resolveDependencies(
ModelQuery query,
NamespacePrefixResolver prefixResolver,
Map<QName, ConstraintDefinition> modelConstraints)
{ {
if (propertyTypeName == null) if (propertyTypeName == null)
{ {
throw new DictionaryException("Property type of property " + name.toPrefixString() + " must be specified"); throw new DictionaryException(
"d_dictionary.property.err.property_type_not_specified",
name.toPrefixString());
} }
dataType = query.getDataType(propertyTypeName); dataType = query.getDataType(propertyTypeName);
if (dataType == null) if (dataType == null)
{ {
throw new DictionaryException("Property type " + propertyTypeName.toPrefixString() + " of property " + name.toPrefixString() + " is not found"); throw new DictionaryException(
"d_dictionary.property.err.property_type_not_found",
propertyTypeName.toPrefixString(), name.toPrefixString());
} }
// ensure content properties are not multi-valued // ensure content properties are not multi-valued
if (propertyTypeName.equals(DataTypeDefinition.CONTENT) && isMultiValued()) if (propertyTypeName.equals(DataTypeDefinition.CONTENT) && isMultiValued())
{ {
throw new DictionaryException("Content properties must be single-valued"); throw new DictionaryException("d_dictionary.property.err.single_valued_content");
}
// Construct constraints
for (M2Constraint constraint : m2Property.getConstraints())
{
ConstraintDefinition def = new M2ConstraintDefinition(this, constraint, prefixResolver);
QName qname = def.getName();
if (constraintsByQName.containsKey(qname))
{
throw new DictionaryException(
"d_dictionary.property.err.duplicate_constraint_on_property",
def.getName().toPrefixString(), name.toPrefixString());
}
else if (modelConstraints.containsKey(qname))
{
throw new DictionaryException(
"d_dictionary.model.err.duplicate_constraint_on_model",
def.getName().toPrefixString());
}
constraintsByQName.put(qname, def);
constraints.add(def);
modelConstraints.put(qname, def);
} }
} }
@@ -153,7 +196,7 @@ import org.alfresco.service.namespace.QName;
String value = M2Label.getLabel(classDef.getModel(), "property", name, "title"); String value = M2Label.getLabel(classDef.getModel(), "property", name, "title");
if (value == null) if (value == null)
{ {
value = property.getTitle(); value = m2Property.getTitle();
} }
return value; return value;
} }
@@ -167,7 +210,7 @@ import org.alfresco.service.namespace.QName;
String value = M2Label.getLabel(classDef.getModel(), "property", name, "description"); String value = M2Label.getLabel(classDef.getModel(), "property", name, "description");
if (value == null) if (value == null)
{ {
value = property.getDescription(); value = m2Property.getDescription();
} }
return value; return value;
} }
@@ -178,7 +221,7 @@ import org.alfresco.service.namespace.QName;
*/ */
public String getDefaultValue() public String getDefaultValue()
{ {
return property.getDefaultValue(); return m2Property.getDefaultValue();
} }
@@ -205,7 +248,7 @@ import org.alfresco.service.namespace.QName;
*/ */
public boolean isMultiValued() public boolean isMultiValued()
{ {
return property.isMultiValued(); return m2Property.isMultiValued();
} }
@@ -214,7 +257,7 @@ import org.alfresco.service.namespace.QName;
*/ */
public boolean isMandatory() public boolean isMandatory()
{ {
return property.isMandatory(); return m2Property.isMandatory();
} }
@@ -223,7 +266,7 @@ import org.alfresco.service.namespace.QName;
*/ */
public boolean isProtected() public boolean isProtected()
{ {
return property.isProtected(); return m2Property.isProtected();
} }
@@ -232,7 +275,7 @@ import org.alfresco.service.namespace.QName;
*/ */
public boolean isIndexed() public boolean isIndexed()
{ {
return property.isIndexed(); return m2Property.isIndexed();
} }
@@ -241,7 +284,7 @@ import org.alfresco.service.namespace.QName;
*/ */
public boolean isStoredInIndex() public boolean isStoredInIndex()
{ {
return property.isStoredInIndex(); return m2Property.isStoredInIndex();
} }
@@ -250,7 +293,7 @@ import org.alfresco.service.namespace.QName;
*/ */
public boolean isIndexedAtomically() public boolean isIndexedAtomically()
{ {
return property.isIndexedAtomically(); return m2Property.isIndexedAtomically();
} }
@@ -259,7 +302,11 @@ import org.alfresco.service.namespace.QName;
*/ */
public boolean isTokenisedInIndex() public boolean isTokenisedInIndex()
{ {
return property.isTokenisedInIndex(); return m2Property.isTokenisedInIndex();
} }
public List<ConstraintDefinition> getConstraints()
{
return Collections.unmodifiableList(constraints);
}
} }

View File

@@ -20,6 +20,7 @@ import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.ClassDefinition; import org.alfresco.service.cmr.dictionary.ClassDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.dictionary.ConstraintDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
@@ -81,6 +82,14 @@ import org.alfresco.service.namespace.QName;
*/ */
public PropertyDefinition getProperty(QName name); public PropertyDefinition getProperty(QName name);
/**
* Gets the specified property constraint
*
* @param name the qualified name of the property constraint
* @return
*/
public ConstraintDefinition getConstraint(QName name);
/** /**
* Gets the specified association * Gets the specified association
* *

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.repo.dictionary.constraint;
import java.util.Collection;
import org.alfresco.service.cmr.dictionary.Constraint;
import org.alfresco.service.cmr.dictionary.DictionaryException;
/**
* Base services for constraints.
*
* @author Derek Hulley
*/
public abstract class AbstractConstraint implements Constraint
{
public static final String ERR_PROP_NOT_SET = "d_dictionary.model.err.property_not_set";
public static final String ERR_EVALUATE_EXCEPTION = "d_dictionary.model.err.evaluate_exception";
/**
* @see #evaluateSingleValue(Object)
* @see #evaluateCollection(Collection)
*/
@SuppressWarnings("unchecked")
public final void evaluate(Object value)
{
try
{
// check for collection
if (value instanceof Collection)
{
Collection collection = (Collection) value;
evaluateCollection(collection);
}
else
{
evaluateSingleValue(value);
}
}
catch (DictionaryException e)
{
// this can go
throw e;
}
catch (Throwable e)
{
throw new DictionaryException(AbstractConstraint.ERR_EVALUATE_EXCEPTION, this, e.getMessage());
}
}
/**
* Only override if there is some specific evaluation that needs to be performed on the
* collection as a whole.
*
* @param collection the collection of values to evaluate
*
* @see #evaluateSingleValue(Object)
*/
protected void evaluateCollection(Collection<Object> collection)
{
for (Object value : collection)
{
evaluateSingleValue(value);
}
}
/**
* Support for evaluation of properties. The value passed in will never be a
* collection.
*
* @throws DictionaryException throw this when the evaluation fails
*/
protected abstract void evaluateSingleValue(Object value);
}

View File

@@ -0,0 +1,142 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.repo.dictionary.constraint;
import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase;
import org.alfresco.i18n.I18NUtil;
import org.alfresco.repo.dictionary.DictionaryDAOTest;
import org.alfresco.service.cmr.dictionary.DictionaryException;
/**
* @see org.alfresco.service.cmr.dictionary.Constraint
* @see org.alfresco.repo.dictionary.constraint.AbstractConstraint
* @see org.alfresco.repo.dictionary.constraint.RegexConstraint
*
* @author Derek Hulley
*/
public class ConstraintsTest extends TestCase
{
@Override
protected void setUp() throws Exception
{
// register resource bundles for messages
I18NUtil.registerResourceBundle(DictionaryDAOTest.TEST_RESOURCE_MESSAGES);
}
/**
* ensure that the default handling of checks on collections will work
*/
public void testCollections() throws Exception
{
DummyConstraint constraint = new DummyConstraint();
constraint.initialize();
List<Object> dummyObjects = new ArrayList<Object>(3);
dummyObjects.add("ABC"); // correct
dummyObjects.add("DEF"); // correct
dummyObjects.add(this); // NO
try
{
constraint.evaluate(dummyObjects);
fail("Failed to detected constraint violation in collection");
}
catch (DictionaryException e)
{
// expected
}
// check that the two strings were properly dealt with
assertEquals("String values not checked", 2, constraint.tested.size());
}
public void testRegexConstraint() throws Exception
{
RegexConstraint constraint = new RegexConstraint();
constraint.setExpression("[A-Z]*");
constraint.initialize();
// do some successful stuff
constraint.evaluate("ABC");
constraint.evaluate("DEF");
// now some failures
try
{
constraint.evaluate("abc");
fail("Regular expression evaluation should have failed: abc");
}
catch (DictionaryException e)
{
String msg = e.getMessage();
assertFalse("I18N of constraint message failed", msg.startsWith("d_dictionary.constraint"));
}
// now a case of passing in an object that could be a string
constraint.evaluate(DummyEnum.ABC);
constraint.evaluate(DummyEnum.DEF);
try
{
constraint.evaluate(DummyEnum.abc);
fail("Regular expression evaluation should have failed for enum: " + DummyEnum.abc);
}
catch (DictionaryException e)
{
}
}
private enum DummyEnum
{
ABC,
DEF,
abc;
}
private class DummyConstraint extends AbstractConstraint
{
private List<Object> tested;
public void initialize()
{
tested = new ArrayList<Object>(4);
}
/**
* Fails on everything but String values, which pass.
* Null values cause runtime exceptions and all other failures are by
* DictionaryException.
*/
@Override
protected void evaluateSingleValue(Object value)
{
if (value == null)
{
throw new NullPointerException("Null value in dummy test");
}
else if (value instanceof String)
{
tested.add(value);
}
else
{
throw new DictionaryException("Non-String value");
}
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.repo.dictionary.constraint;
import org.alfresco.service.cmr.dictionary.DictionaryException;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
/**
* Constraint implementation that performs regular expression comparisons.
* Where possible, the {@link org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter type converter}
* will be used to first convert the value to a <code>String</code>, so the evaluation
* will be against the value's <code>String</code> equivalent.
*
* @see java.lang.String#matches(java.lang.String)
*
* @author Derek Hulley
*/
public class RegexConstraint extends AbstractConstraint
{
public static final String CONSTRAINT_REGEX_NO_MATCH = "d_dictionary.constraint.regex.no_match";
private String expression;
/**
* Set the regular expression used to evaluate string values
* @param expression similar to the {@link String#matches(java.lang.String) argument
*/
public void setExpression(String expression)
{
this.expression = expression;
}
public void initialize()
{
if (expression == null)
{
throw new DictionaryException(AbstractConstraint.ERR_PROP_NOT_SET, "expression");
}
}
public void evaluateSingleValue(Object value)
{
// convert the value to a String
String valueStr = DefaultTypeConverter.INSTANCE.convert(String.class, value);
boolean matches = valueStr.matches(expression);
if (!matches)
{
throw new DictionaryException(RegexConstraint.CONSTRAINT_REGEX_NO_MATCH, value, expression);
}
}
}

View File

@@ -22,6 +22,12 @@
</data-types> </data-types>
<constraints>
<constraint name="test:regex1" type="REGEX">
<parameter name="expression">[A-Z]*</parameter>
</constraint>
</constraints>
<types> <types>
<type name="test:base"> <type name="test:base">
@@ -34,6 +40,9 @@
<type>d:text</type> <type>d:text</type>
<protected>true</protected> <protected>true</protected>
<default></default> <default></default>
<constraints>
<constraint ref="test:regex1"/>
</constraints>
</property> </property>
</properties> </properties>

View File

@@ -41,6 +41,10 @@
</collection> </collection>
</structure> </structure>
<structure name="constraints" usage="optional">
<collection field="constraints" item-type="org.alfresco.repo.dictionary.M2Constraint" factory="org.alfresco.repo.dictionary.M2Model.createList"/>
</structure>
<structure name="types" usage="optional"> <structure name="types" usage="optional">
<collection field="types" item-type="org.alfresco.repo.dictionary.M2Type" factory="org.alfresco.repo.dictionary.M2Model.createList"/> <collection field="types" item-type="org.alfresco.repo.dictionary.M2Type" factory="org.alfresco.repo.dictionary.M2Model.createList"/>
</structure> </structure>
@@ -102,6 +106,24 @@
<value name="stored" field="isStoredInIndex" usage="optional"/> <value name="stored" field="isStoredInIndex" usage="optional"/>
<value name="tokenised" field="isTokenisedInIndex" usage="optional"/> <value name="tokenised" field="isTokenisedInIndex" usage="optional"/>
</structure> </structure>
<structure name="constraints" usage="optional">
<collection field="constraints" item-type="org.alfresco.repo.dictionary.M2Constraint" factory="org.alfresco.repo.dictionary.M2Model.createList"/>
</structure>
</mapping>
<mapping abstract="true" class="org.alfresco.repo.dictionary.M2NamedValue">
<value style="attribute" name="name" field="name" />
<value style="text" field="value"/>
</mapping>
<mapping name="constraint" class="org.alfresco.repo.dictionary.M2Constraint">
<value style="attribute" name="name" field="name" usage="optional"/>
<value style="attribute" name="type" field="type" usage="optional"/>
<value style="attribute" name="ref" field="ref" usage="optional"/>
<value name="description" field="description" usage="optional"/>
<collection field="parameters" factory="org.alfresco.repo.dictionary.M2Model.createList" usage="optional">
<structure name="parameter" type="org.alfresco.repo.dictionary.M2NamedValue" />
</collection>
</mapping> </mapping>
<mapping class="org.alfresco.repo.dictionary.M2ClassAssociation" abstract="true"> <mapping class="org.alfresco.repo.dictionary.M2ClassAssociation" abstract="true">

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.service.cmr.dictionary;
/**
* The interface for classes that implement constraints on property values.
* <p>
* Implementations of the actual constraint code should must not synchronize
* or in any other way block threads. Concurrent access of the evaluation
* method is expected, but will always occur after initialization has completed.
* <p>
* Attention to performance is <u>crucial</u> for all implementations as
* instances of this class are heavily used.
* <p>
* The constraint implementations can provide standard setter methods that will
* be populated by bean setter injection. Once all the available properties have
* been set, the contraint will be initialized.
*
* @author Derek Hulley
*/
public interface Constraint
{
/**
* Initializes the constraint with appropriate values, which will depend
* on the implementation itself. This method can be implemented as a
* once-off, i.e. reinitialization does not have to be supported.
*
* @param parameters constraint parameters
*/
public void initialize();
/**
* Evaluates a property value according to the implementation and initialization
* parameters provided.
*
* @param value the property value to check
*
* @throws ConstraintException if the value doesn't pass all constraints
*/
public void evaluate(Object value);
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.service.cmr.dictionary;
import org.alfresco.service.namespace.QName;
/**
* Property constraint definition
*
* @author Derek Hulley
*/
public interface ConstraintDefinition
{
/**
* @return defining model
*/
public ModelDefinition getModel();
/**
* @return Returns the qualified name of the constraint
*/
public QName getName();
/**
* @return Returns the constraint implementation
*/
public Constraint getConstraint();
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (C) 2005 Alfresco, Inc.
*
* Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a
* copy of the License at
*
* http://www.alfresco.org/legal/license.txt
*
* 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.alfresco.service.cmr.dictionary;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* Thrown when property value fails to meet a property constraint.
*
* @author Derek Hulley
*/
public class ConstraintException extends AlfrescoRuntimeException
{
private static final long serialVersionUID = -3925105163386197586L;
public ConstraintException(String msgId, Object ... args)
{
super(msgId, args);
}
}

View File

@@ -16,13 +16,14 @@
*/ */
package org.alfresco.service.cmr.dictionary; package org.alfresco.service.cmr.dictionary;
import org.alfresco.error.AlfrescoRuntimeException;
/** /**
* Base Exception of Data Dictionary Exceptions. * Base Exception of Data Dictionary Exceptions.
* *
* @author David Caruana * @author David Caruana
*/ */
public class DictionaryException extends RuntimeException public class DictionaryException extends AlfrescoRuntimeException
{ {
private static final long serialVersionUID = 3257008761007847733L; private static final long serialVersionUID = 3257008761007847733L;
@@ -36,4 +37,13 @@ public class DictionaryException extends RuntimeException
super(msg, cause); super(msg, cause);
} }
public DictionaryException(String msgId, Object ... args)
{
super(msgId, args);
}
public DictionaryException(String msgId, Throwable cause, Object ... args)
{
super(msgId, args, cause);
}
} }

View File

@@ -16,6 +16,8 @@
*/ */
package org.alfresco.service.cmr.dictionary; package org.alfresco.service.cmr.dictionary;
import java.util.List;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
/** /**
@@ -98,4 +100,10 @@ public interface PropertyDefinition
*/ */
public boolean isIndexedAtomically(); public boolean isIndexedAtomically();
/**
* Get all constraints that apply to the property value
*
* @return Returns a list of property constraint definitions
*/
public List<ConstraintDefinition> getConstraints();
} }