diff --git a/config/alfresco/messages/dictionary-messages.properties b/config/alfresco/messages/dictionary-messages.properties
index 2de0873860..f232cb6b60 100644
--- a/config/alfresco/messages/dictionary-messages.properties
+++ b/config/alfresco/messages/dictionary-messages.properties
@@ -10,6 +10,7 @@ d_dictionary.constraint.err.type_or_ref=Constraint ''{0}'' cannot have a ''type'
d_dictionary.constraint.err.ref_not_found=Constraint reference ''{0}'' not found on constraint ''{1}''
d_dictionary.constraint.err.anon_needs_property=Anonymous constraints can only be declared within the context of a property
d_dictionary.constraint.err.invalid_type=Constraint type ''{0}'' on constraint ''{1}'' is not a well-known type or a valid Constraint implementation
+d_dictionary.constraint.err.property_simple_and_list="Constraint ''{0}'' has both a simple and list value for property ''{1}''
d_dictionary.constraint.err.construct_failure=Failed to construct an instance of type ''{0}'' for constraint ''{1}''
d_dictionary.constraint.err.property_mismatch=Property mismatch setting property ''{0}'' on constraint ''{1}''
d_dictionary.constraint.err.property_not_set=Property ''{0}'' has not been set on constraint ''{1}''
@@ -34,3 +35,7 @@ d_dictionary.constraint.string_length.invalid_min_length=Invalid ''minLength'' p
d_dictionary.constraint.string_length.invalid_max_length=Invalid ''maxLength'' property: {0}
d_dictionary.constraint.string_length.non_string=Property value could not be converted to a String: {0}
d_dictionary.constraint.string_length.invalid_length=String length of ''{0}'' is not in range [{1}; {2}]
+
+d_dictionary.constraint.list_of_values.no_values=The list of allowed values is empty
+d_dictionary.constraint.list_of_values.non_string=Property value could not be converted to a String: {0}
+d_dictionary.constraint.list_of_values.invalid_value=The value is not an allowed value: {0}
diff --git a/config/alfresco/model/contentModel.xml b/config/alfresco/model/contentModel.xml
index 9b8d61686c..21e3a7b5bf 100644
--- a/config/alfresco/model/contentModel.xml
+++ b/config/alfresco/model/contentModel.xml
@@ -16,8 +16,8 @@
- \<\?\/\:\|\¬\£\%\&\+\;]+]]>
- true
+ \<\?\/\:\|\¬\£\%\&\+\;]+]]>
+ true
diff --git a/source/java/org/alfresco/repo/dictionary/DictionaryDAOTest.java b/source/java/org/alfresco/repo/dictionary/DictionaryDAOTest.java
index 913e4dc11a..3fc1f8de32 100644
--- a/source/java/org/alfresco/repo/dictionary/DictionaryDAOTest.java
+++ b/source/java/org/alfresco/repo/dictionary/DictionaryDAOTest.java
@@ -126,7 +126,7 @@ public class DictionaryDAOTest extends TestCase
// 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());
+ assertNotNull("Property without constraints returned null list", propNoConstraintsDef.getConstraints());
// get the constraints defined for the property
QName prop1QName = QName.createQName(TEST_URL, "prop1");
diff --git a/source/java/org/alfresco/repo/dictionary/M2ConstraintDefinition.java b/source/java/org/alfresco/repo/dictionary/M2ConstraintDefinition.java
index b87e845bea..a6a3cde0c4 100644
--- a/source/java/org/alfresco/repo/dictionary/M2ConstraintDefinition.java
+++ b/source/java/org/alfresco/repo/dictionary/M2ConstraintDefinition.java
@@ -18,6 +18,7 @@ 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.StringLengthConstraint;
@@ -29,7 +30,8 @@ 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.TypeMismatchException;
+import org.springframework.beans.InvalidPropertyException;
+import org.springframework.beans.PropertyAccessException;
/**
* Compiled Property Constraint
@@ -44,6 +46,7 @@ import org.springframework.beans.TypeMismatchException;
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";
@@ -178,11 +181,28 @@ import org.springframework.beans.TypeMismatchException;
List constraintNamedValues = m2Constraint.getParameters();
for (M2NamedValue namedValue : constraintNamedValues)
{
+ 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(namedValue.getName(), namedValue.getValue());
+ beanWrapper.setPropertyValue(namedValue.getName(), value);
}
- catch (TypeMismatchException e)
+ catch (PropertyAccessException e)
+ {
+ throw new DictionaryException(ERR_PROPERTY_MISMATCH, e, namedValue.getName(), shortName);
+ }
+ catch (InvalidPropertyException e)
{
throw new DictionaryException(ERR_PROPERTY_MISMATCH, e, namedValue.getName(), shortName);
}
@@ -244,6 +264,14 @@ import org.springframework.beans.TypeMismatchException;
{
return new StringLengthConstraint();
}
+ },
+ LIST
+ {
+ @Override
+ protected Constraint newInstance()
+ {
+ return new ListOfValuesConstraint();
+ }
};
/**
diff --git a/source/java/org/alfresco/repo/dictionary/M2NamedValue.java b/source/java/org/alfresco/repo/dictionary/M2NamedValue.java
index 6c2e7918ab..a50d9b08ce 100644
--- a/source/java/org/alfresco/repo/dictionary/M2NamedValue.java
+++ b/source/java/org/alfresco/repo/dictionary/M2NamedValue.java
@@ -16,6 +16,8 @@
*/
package org.alfresco.repo.dictionary;
+import java.util.List;
+
/**
* Definition of a named value that can be used for property injection.
*
@@ -24,8 +26,8 @@ package org.alfresco.repo.dictionary;
public class M2NamedValue
{
private String name;
- private String value;
-
+ private String simpleValue;
+ private List listValue;
/*package*/ M2NamedValue()
{
@@ -35,7 +37,7 @@ public class M2NamedValue
@Override
public String toString()
{
- return (name + "=" + value);
+ return (name + "=" + (simpleValue == null ? listValue : simpleValue));
}
public String getName()
@@ -46,8 +48,16 @@ public class M2NamedValue
/**
* @return Returns the raw, unconverted value
*/
- public String getValue()
+ public String getSimpleValue()
{
- return value;
+ return simpleValue;
+ }
+
+ /**
+ * @return Returns the list of raw, unconverted values
+ */
+ public List getListValue()
+ {
+ return listValue;
}
}
diff --git a/source/java/org/alfresco/repo/dictionary/constraint/AbstractConstraint.java b/source/java/org/alfresco/repo/dictionary/constraint/AbstractConstraint.java
index 39dc1077c2..f4c343a4ed 100644
--- a/source/java/org/alfresco/repo/dictionary/constraint/AbstractConstraint.java
+++ b/source/java/org/alfresco/repo/dictionary/constraint/AbstractConstraint.java
@@ -33,6 +33,22 @@ public abstract class AbstractConstraint implements Constraint
public static final String ERR_PROP_NOT_SET = "d_dictionary.constraint.err.property_not_set";
public static final String ERR_EVALUATE_EXCEPTION = "d_dictionary.constraint.err.evaluate_exception";
+ /**
+ * Check that the given value is not null.
+ *
+ * @param name the name of the property
+ * @param value the value to check for null
+ *
+ * @throws DictionaryException if the the property is null
+ */
+ protected void checkPropertyNotNull(String name, Object value)
+ {
+ if (value == null)
+ {
+ throw new DictionaryException(AbstractConstraint.ERR_PROP_NOT_SET, value);
+ }
+ }
+
/**
* @see #evaluateSingleValue(Object)
* @see #evaluateCollection(Collection)
diff --git a/source/java/org/alfresco/repo/dictionary/constraint/ConstraintsTest.java b/source/java/org/alfresco/repo/dictionary/constraint/ConstraintsTest.java
index d46d60f1e4..7905dd077c 100644
--- a/source/java/org/alfresco/repo/dictionary/constraint/ConstraintsTest.java
+++ b/source/java/org/alfresco/repo/dictionary/constraint/ConstraintsTest.java
@@ -18,6 +18,7 @@ package org.alfresco.repo.dictionary.constraint;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import junit.framework.TestCase;
@@ -148,6 +149,32 @@ public class ConstraintsTest extends TestCase
evaluate(constraint, Arrays.asList("abc", "abcdefg"), true);
}
+ public void testListOfValuesConstraint() throws Exception
+ {
+ ListOfValuesConstraint constraint = new ListOfValuesConstraint();
+ try
+ {
+ constraint.setAllowedValues(Collections.emptyList());
+ }
+ catch (DictionaryException e)
+ {
+ // expected
+ checkI18NofExceptionMessage(e);
+ }
+ List allowedValues = Arrays.asList(new String[] {"abc", "def", "ghi"});
+ constraint.setAllowedValues(allowedValues);
+
+ evaluate(constraint, "def", false);
+ evaluate(constraint, "DEF", true);
+ evaluate(constraint, Arrays.asList("abc", "def"), false);
+ evaluate(constraint, Arrays.asList("abc", "DEF"), true);
+
+ // now make it case-insensitive
+ constraint.setCaseSensitive(false);
+ evaluate(constraint, "DEF", false);
+ evaluate(constraint, Arrays.asList("abc", "DEF"), false);
+ }
+
public void testNumericRangeConstraint() throws Exception
{
NumericRangeConstraint constraint = new NumericRangeConstraint();
diff --git a/source/java/org/alfresco/repo/dictionary/constraint/ListOfValuesConstraint.java b/source/java/org/alfresco/repo/dictionary/constraint/ListOfValuesConstraint.java
new file mode 100644
index 0000000000..7ebdddf6f5
--- /dev/null
+++ b/source/java/org/alfresco/repo/dictionary/constraint/ListOfValuesConstraint.java
@@ -0,0 +1,152 @@
+/*
+ * 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.Collections;
+import java.util.List;
+
+import org.alfresco.service.cmr.dictionary.ConstraintException;
+import org.alfresco.service.cmr.dictionary.DictionaryException;
+import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
+import org.alfresco.service.cmr.repository.datatype.TypeConversionException;
+
+/**
+ * Constraint implementation that ensures the value is one of a constrained
+ * list of values. By default, this constraint is case-sensitive.
+ *
+ * @see #setAllowedValues(List)
+ * @see #setCaseSensitive(boolean)
+ *
+ * @author Derek Hulley
+ */
+public class ListOfValuesConstraint extends AbstractConstraint
+{
+ private static final String ERR_NO_VALUES = "d_dictionary.constraint.list_of_values.no_values";
+ private static final String ERR_NON_STRING = "d_dictionary.constraint.string_length.non_string";
+ private static final String ERR_INVALID_VALUE = "d_dictionary.constraint.list_of_values.invalid_value";
+
+ private List allowedValues;
+ private List allowedValuesUpper;
+ private boolean caseSensitive;
+
+ public ListOfValuesConstraint()
+ {
+ caseSensitive = true;
+ }
+
+ @Override
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(80);
+ sb.append("ListOfValuesConstraint")
+ .append("[ allowedValues=").append(allowedValues)
+ .append(", caseSensitive=").append(caseSensitive)
+ .append("]");
+ return sb.toString();
+ }
+
+ /**
+ * Get the allowed values. Note that these are String instances, but may
+ * represent non-String values. It is up to the caller to distinguish.
+ *
+ * @return Returns the values allowed
+ */
+ public List getAllowedValues()
+ {
+ return allowedValues;
+ }
+
+ /**
+ * Set the values that are allowed by the constraint.
+ *
+ * @param values a list of allowed values
+ */
+ @SuppressWarnings("unchecked")
+ public void setAllowedValues(List allowedValues)
+ {
+ if (allowedValues == null)
+ {
+ throw new DictionaryException(ERR_NO_VALUES);
+ }
+ int valueCount = allowedValues.size();
+ if (valueCount == 0)
+ {
+ throw new DictionaryException(ERR_NO_VALUES);
+ }
+ this.allowedValues = Collections.unmodifiableList(allowedValues);
+ // make the upper case versions
+ this.allowedValuesUpper = new ArrayList(valueCount);
+ for (String allowedValue : this.allowedValues)
+ {
+ allowedValuesUpper.add(allowedValue.toUpperCase());
+ }
+ }
+
+ /**
+ * @return Returns true if this constraint is case-sensitive (default)
+ */
+ public boolean isCaseSensitive()
+ {
+ return caseSensitive;
+ }
+
+ /**
+ * Set the handling of case checking.
+ *
+ * @param caseSensitive true if the constraint is case-sensitive (default),
+ * or false for case-insensitive.
+ */
+ public void setCaseSensitive(boolean caseSensitive)
+ {
+ this.caseSensitive = caseSensitive;
+ }
+
+ public void initialize()
+ {
+ checkPropertyNotNull("allowedValues", allowedValues);
+ }
+
+ protected void evaluateSingleValue(Object value)
+ {
+ // convert the value to a String
+ String valueStr = null;
+ try
+ {
+ valueStr = DefaultTypeConverter.INSTANCE.convert(String.class, value);
+ }
+ catch (TypeConversionException e)
+ {
+ throw new ConstraintException(ERR_NON_STRING, value);
+ }
+ // check that the value is in the set of allowed values
+ if (caseSensitive)
+ {
+ if (!allowedValues.contains(valueStr))
+ {
+ throw new ConstraintException(ERR_INVALID_VALUE, value);
+ }
+ }
+ else
+ {
+ if (!allowedValuesUpper.contains(valueStr.toUpperCase()))
+ {
+ throw new ConstraintException(ERR_INVALID_VALUE, value);
+ }
+ }
+ }
+}
diff --git a/source/java/org/alfresco/repo/dictionary/constraint/RegexConstraint.java b/source/java/org/alfresco/repo/dictionary/constraint/RegexConstraint.java
index b71dc06d1c..035a5ba049 100644
--- a/source/java/org/alfresco/repo/dictionary/constraint/RegexConstraint.java
+++ b/source/java/org/alfresco/repo/dictionary/constraint/RegexConstraint.java
@@ -20,7 +20,6 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.alfresco.service.cmr.dictionary.ConstraintException;
-import org.alfresco.service.cmr.dictionary.DictionaryException;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
/**
@@ -94,13 +93,11 @@ public class RegexConstraint extends AbstractConstraint
{
this.requiresMatch = requiresMatch;
}
-
+
public void initialize()
{
- if (expression == null)
- {
- throw new DictionaryException(AbstractConstraint.ERR_PROP_NOT_SET, "expression");
- }
+ checkPropertyNotNull("expression", expression);
+
this.patternMatcher = Pattern.compile(expression);
}
diff --git a/source/java/org/alfresco/repo/dictionary/dictionarydaotest_model.xml b/source/java/org/alfresco/repo/dictionary/dictionarydaotest_model.xml
index 35a3d4f39c..1a1113310f 100644
--- a/source/java/org/alfresco/repo/dictionary/dictionarydaotest_model.xml
+++ b/source/java/org/alfresco/repo/dictionary/dictionarydaotest_model.xml
@@ -24,16 +24,25 @@
- [A-Z]*
- false
+ [A-Z]*
+ false
- 0
- 256
+ 0
+ 256
- 0
- 256
+ 0
+ 256
+
+
+
+
+ ABC
+ DEF
+
+
+ true
diff --git a/source/java/org/alfresco/repo/dictionary/m2binding.xml b/source/java/org/alfresco/repo/dictionary/m2binding.xml
index a14f27f488..90459e7b59 100644
--- a/source/java/org/alfresco/repo/dictionary/m2binding.xml
+++ b/source/java/org/alfresco/repo/dictionary/m2binding.xml
@@ -119,7 +119,16 @@
-
+
+
+
+
+
+
+
+
+
+