This project has retired. For details please refer to its Attic page.
TypeValidator xref

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.chemistry.opencmis.server.support;
20  
21  import java.math.BigDecimal;
22  import java.math.BigInteger;
23  import java.util.ArrayList;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  
28  import org.apache.chemistry.opencmis.commons.PropertyIds;
29  import org.apache.chemistry.opencmis.commons.data.Acl;
30  import org.apache.chemistry.opencmis.commons.data.Properties;
31  import org.apache.chemistry.opencmis.commons.data.PropertyData;
32  import org.apache.chemistry.opencmis.commons.data.PropertyDecimal;
33  import org.apache.chemistry.opencmis.commons.data.PropertyInteger;
34  import org.apache.chemistry.opencmis.commons.definitions.Choice;
35  import org.apache.chemistry.opencmis.commons.definitions.DocumentTypeDefinition;
36  import org.apache.chemistry.opencmis.commons.definitions.PropertyDecimalDefinition;
37  import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
38  import org.apache.chemistry.opencmis.commons.definitions.PropertyIntegerDefinition;
39  import org.apache.chemistry.opencmis.commons.definitions.PropertyStringDefinition;
40  import org.apache.chemistry.opencmis.commons.definitions.RelationshipTypeDefinition;
41  import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
42  import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
43  import org.apache.chemistry.opencmis.commons.enums.Cardinality;
44  import org.apache.chemistry.opencmis.commons.enums.VersioningState;
45  import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
46  import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
47  
48  /**
49   * @author Jens
50   */
51  public class TypeValidator {
52  
53      public static void validateRequiredSystemProperties(Properties properties) {
54          if (properties == null || properties.getProperties() == null) {
55              throw new CmisInvalidArgumentException("Cannot create object, no properties are given");
56          }
57  
58          if (!properties.getProperties().containsKey(PropertyIds.OBJECT_TYPE_ID)) {
59              throw new CmisInvalidArgumentException("Cannot create object, type id is missing");
60          }
61  
62      }
63  
64      private static boolean isMandatorySystemProperty(String propertyId) {
65          // TODO Auto-generated method stub
66          return propertyId.equals(PropertyIds.OBJECT_TYPE_ID);
67      }
68  
69      @SuppressWarnings("unchecked")
70      static <T> PropertyValidator<T> createPropertyValidator(PropertyDefinition<?> propDef) {
71          PropertyValidator<T> result = null;
72          if (propDef instanceof PropertyIntegerDefinition) {
73              result = (PropertyValidator<T>) new PropertyValidatorInteger();
74          } else if (propDef instanceof PropertyDecimalDefinition) {
75              result = (PropertyValidator<T>) new PropertyValidatorDecimal();
76          } else if (propDef instanceof PropertyStringDefinition) {
77              result = (PropertyValidator<T>) new PropertyValidatorString();
78          } else {
79              result = new PropertyValidator<T>();
80          }
81          return result;
82      }
83  
84      /*
85       * property validations: not readonly, all required are given, all are known
86       * in type cardinality: no multi values for single value, def min max check
87       * for Integer and Decimal, choices and in list Strings, max length set
88       * default value for omitted properties
89       */
90      static class PropertyValidator<T> {
91  
92          public void validate(PropertyDefinition<T> propDef, PropertyData<T> prop) {
93  
94              // check general constraints for all property types
95              if (propDef.getCardinality() == Cardinality.SINGLE && prop.getValues().size() > 1) {
96                  throw new CmisConstraintException("The property with id " + propDef.getId()
97                          + " is single valued, but multiple values are passed " + prop.getValues());
98              }
99  
100             if (propDef.getChoices() != null && propDef.getChoices().size() > 0) {
101                 validateChoices(propDef, prop);
102             }
103         }
104 
105         private void validateChoices(PropertyDefinition<T> propDef, PropertyData<T> prop) {
106             boolean isAllowedValue = true;
107             boolean hasMultiValueChoiceLists = false;
108             for (Choice<?> allowedValue : propDef.getChoices()) {
109                 if (allowedValue.getValue() != null && allowedValue.getValue().size() > 1) {
110                     hasMultiValueChoiceLists = true;
111                 }
112             }
113 
114             // check if value is in list
115             if (hasMultiValueChoiceLists) {
116                 // do a complex check if this combination of actual values is
117                 // allowed
118                 // check if value is in list
119                 isAllowedValue = false;
120                 List<?> actualValues = prop.getValues();
121                 for (Choice<?> allowedValue : propDef.getChoices()) {
122                     if (allowedValue.getValue().size() == actualValues.size()) {
123                         boolean listValuesAreEqual = true;
124                         Iterator<?> it = allowedValue.getValue().iterator();
125                         for (Object actualValue : actualValues) {
126                             if (!actualValue.equals(it.next())) {
127                                 listValuesAreEqual = false;
128                                 break;
129                             }
130                         }
131                         if (listValuesAreEqual) {
132                             isAllowedValue = true;
133                         }
134                     }
135 
136                     if (isAllowedValue) {
137                         break;
138                     }
139                 }
140 
141             } else {
142                 List<T> allowedValues = getAllowedValues(propDef.getChoices());
143                 // do a simpler check if all values are choice elements
144 
145                 for (Object actualValue : prop.getValues()) {
146                     if (!allowedValues.contains(actualValue)) {
147                         isAllowedValue = false;
148                         break;
149                     }
150                 }
151             }
152 
153             if (!isAllowedValue) {
154                 throw new CmisConstraintException("The property with id " + propDef.getId()
155                         + " has a fixed set of values. Value(s) " + prop.getValues() + " are not listed.");
156             }
157         }
158 
159         /**
160          * Calculate the list of allowed values for this property definition by
161          * recursively collecting all choice values from property definition
162          *
163          * @param propDef
164          *            property definition
165          * @return list of possible values in complete hierarchy
166          */
167         private List<T> getAllowedValues(List<Choice<T>> choices) {
168             List<T> allowedValues = new ArrayList<T>(choices.size());
169             for (Choice<T> choice : choices) {
170                 if (choice.getValue() != null) {
171                     allowedValues.add(choice.getValue().get(0));
172                 }
173                 if (choice.getChoice() != null) {
174                     List<Choice<T>> x = choice.getChoice();
175                     allowedValues.addAll(getAllowedValues(x));
176                 }
177             }
178             return allowedValues;
179         }
180     }
181 
182     static class PropertyValidatorInteger extends PropertyValidator<BigInteger> {
183 
184         @Override
185         public void validate(PropertyDefinition<BigInteger> propDef, PropertyData<BigInteger> property) {
186 
187             super.validate(propDef, property);
188 
189             BigInteger propVal = property.getFirstValue();
190             BigInteger minVal = ((PropertyIntegerDefinition) propDef).getMinValue();
191             BigInteger maxVal = ((PropertyIntegerDefinition) propDef).getMaxValue();
192 
193             // check min and max
194             if (minVal != null && propVal != null && propVal.compareTo(minVal) == -1) {
195                 throw new CmisConstraintException("For property with id " + propDef.getId() + " the value " + propVal
196                         + " is less than the minimum value " + minVal);
197             }
198             if (maxVal != null && propVal != null && propVal.compareTo(maxVal) == 1) {
199                 throw new CmisConstraintException("For property with id " + propDef.getId() + " the value " + propVal
200                         + " is bigger than the maximum value " + maxVal);
201             }
202         }
203     }
204 
205     static class PropertyValidatorDecimal extends PropertyValidator<BigDecimal> {
206 
207         @Override
208         public void validate(PropertyDefinition<BigDecimal> propDef, PropertyData<BigDecimal> property) {
209 
210             super.validate(propDef, property);
211 
212             BigDecimal propVal = property.getFirstValue();
213             BigDecimal minVal = ((PropertyDecimalDefinition) propDef).getMinValue();
214             BigDecimal maxVal = ((PropertyDecimalDefinition) propDef).getMaxValue();
215 
216             // check min and max
217             if (minVal != null && propVal != null && propVal.compareTo(minVal) == -1) {
218                 throw new CmisConstraintException("For property with id " + propDef.getId() + " the value " + propVal
219                         + " is less than the minimum value " + minVal);
220             }
221             if (maxVal != null && propVal != null && propVal.compareTo(maxVal) == 1) {
222                 throw new CmisConstraintException("For property with id " + propDef.getId() + " the value " + propVal
223                         + " is bigger than the maximum value " + maxVal);
224             }
225         }
226     }
227 
228     static class PropertyValidatorString extends PropertyValidator<String> {
229 
230         @Override
231         public void validate(PropertyDefinition<String> propDef, PropertyData<String> property) {
232 
233             super.validate(propDef, property);
234 
235             long maxLen = ((PropertyStringDefinition) propDef).getMaxLength() == null ? -1
236                     : ((PropertyStringDefinition) propDef).getMaxLength().longValue();
237             long len = property.getFirstValue() == null ? -1
238                     : property.getFirstValue().length();
239 
240             // check max length
241             if (maxLen >= 0 && len >= 0 && maxLen < len) {
242                 throw new CmisConstraintException("For property with id " + propDef.getId() + " the length of " + len
243                         + "is bigger than the maximum allowed length  " + maxLen);
244             }
245         }
246     }
247 
248     @SuppressWarnings("unchecked")
249     public static <T> void validateProperties(TypeDefinition typeDef, Properties properties, boolean checkMandatory) {
250 
251         List<String> propDefsRequired = getMandatoryPropDefs(typeDef.getPropertyDefinitions());
252 
253         if(properties != null) {
254 			for (PropertyData<?> prop : properties.getProperties().values()) {
255 				String propertyId = prop.getId();
256 				BaseTypeId baseTypeId = typeDef.getBaseTypeId();
257 
258 				// check that all mandatory attributes are present
259 				if (checkMandatory && propDefsRequired.contains(propertyId)) {
260 					propDefsRequired.remove(propertyId);
261 				}
262 
263 				if (isSystemProperty(baseTypeId, propertyId)) {
264 					continue; // ignore system properties for validation
265 				}
266 
267 				// Check if all properties are known in the type
268 				if (!typeContainsProperty(typeDef, propertyId)) {
269 					throw new CmisConstraintException("Unknown property "
270 							+ propertyId + " in type " + typeDef.getId());
271 				}
272 
273 				// check all type specific constraints:
274 				PropertyDefinition<T> propDef = getPropertyDefinition(typeDef,
275 						propertyId);
276 				PropertyValidator<T> validator = createPropertyValidator(propDef);
277 				validator.validate(propDef, (PropertyData<T>) prop);
278 			}
279         }
280 
281         if (checkMandatory && !propDefsRequired.isEmpty()) {
282             throw new CmisConstraintException("The following mandatory properties are missing: " + propDefsRequired);
283         }
284     }
285 
286     public static void validateVersionStateForCreate(DocumentTypeDefinition typeDef, VersioningState verState) {
287         if (null == verState) {
288             return;
289         }
290         if (typeDef.isVersionable() && verState.equals(VersioningState.NONE) || !typeDef.isVersionable()
291                 && !verState.equals(VersioningState.NONE)) {
292             throw new CmisConstraintException("The versioning state flag is imcompatible to the type definition.");
293         }
294 
295     }
296 
297     public static void validateAllowedChildObjectTypes(TypeDefinition childTypeDef, List<String> allowedChildTypes) {
298 
299      	validateAllowedTypes(childTypeDef, allowedChildTypes, "in this folder");
300     }
301     
302     public static void validateAllowedRelationshipTypes (RelationshipTypeDefinition relationshipTypeDef, TypeDefinition sourceTypeDef,
303  		   TypeDefinition targetTypeDef)
304     {
305     	List<String> allowedSourceTypes = relationshipTypeDef.getAllowedSourceTypeIds();	
306     	validateAllowedTypes(sourceTypeDef, allowedSourceTypes, " as source type in this relationship");
307       	List<String> allowedTargetTypes = relationshipTypeDef.getAllowedTargetTypeIds();
308         validateAllowedTypes(targetTypeDef, allowedTargetTypes, " as target type in this relationship");	
309     }
310     
311     protected static void validateAllowedTypes(TypeDefinition typeDef, List<String> allowedTypes, String description)
312     {
313     	 if (null == allowedTypes || allowedTypes.size() == 0)
314              return; // all types are allowed
315 
316          for (String allowedType : allowedTypes) {
317              if (allowedType.equals(typeDef.getId()))
318                  return;
319          }
320          throw new CmisConstraintException("The requested type " + typeDef.getId() + " is not allowed " + description);
321      }
322     	 
323     public static void  validateAcl(TypeDefinition typeDef, Acl addACEs, Acl removeACEs)
324     {
325     	if (!typeDef.isControllableAcl() && (addACEs != null || removeACEs != null))
326     	{
327     		throw new CmisConstraintException("acl set for type: " + typeDef.getDisplayName() + " that is not controllableACL");
328     	}
329     }
330 
331     private static List<String> getMandatoryPropDefs(Map<String, PropertyDefinition<?>> propDefs) {
332         List<String> res = new ArrayList<String>();
333         if (null != propDefs) {
334             for (PropertyDefinition<?> propDef : propDefs.values()) {
335                 if (propDef.isRequired() && !isMandatorySystemProperty(propDef.getId())) {
336                     res.add(propDef.getId());
337                 }
338             }
339         }
340         return res;
341     }
342 
343     public static boolean typeContainsProperty(TypeDefinition typeDef, String propertyId) {
344 
345         Map<String, PropertyDefinition<?>> propDefs = typeDef.getPropertyDefinitions();
346         if (null == propDefs) {
347             return false;
348         }
349 
350         PropertyDefinition<?> propDef = propDefs.get(propertyId);
351 
352         if (null == propDef) {
353             return false; // unknown property id in this type
354         } else {
355             return true;
356         }
357     }
358 
359     public static boolean typeContainsPropertyWithQueryName(TypeDefinition typeDef, String propertyQueryName) {
360 
361         Map<String, PropertyDefinition<?>> propDefs = typeDef.getPropertyDefinitions();
362         if (null == propDefs) {
363             return false;
364         }
365 
366         for (PropertyDefinition<?> propDef : propDefs.values()) {
367             if (propDef.getQueryName().toLowerCase().equals(propertyQueryName.toLowerCase())) {
368                 return true;
369             }
370         }
371 
372         return false; // unknown property query name in this type
373     }
374 
375     @SuppressWarnings("unchecked")
376     private static <T> PropertyDefinition<T> getPropertyDefinition(TypeDefinition typeDef, String propertyId) {
377 
378         Map<String, PropertyDefinition<?>> propDefs = typeDef.getPropertyDefinitions();
379         if (null == propDefs) {
380             return null;
381         }
382 
383         PropertyDefinition<?> propDef = propDefs.get(propertyId);
384 
385         if (null == propDef) {
386             return null; // not found
387         } else {
388             return (PropertyDefinition<T>) propDef;
389         }
390     }
391 
392     private static boolean isSystemProperty(BaseTypeId baseTypeId, String propertyId) {
393 
394         if (propertyId.equals(PropertyIds.NAME)) {
395             return true;
396         } else if (propertyId.equals(PropertyIds.OBJECT_ID)) {
397             return true;
398         } else if (propertyId.equals(PropertyIds.OBJECT_TYPE_ID)) {
399             return true;
400         } else if (propertyId.equals(PropertyIds.BASE_TYPE_ID)) {
401             return true;
402         } else if (propertyId.equals(PropertyIds.CREATED_BY)) {
403             return true;
404         } else if (propertyId.equals(PropertyIds.CREATION_DATE)) {
405             return true;
406         } else if (propertyId.equals(PropertyIds.LAST_MODIFIED_BY)) {
407             return true;
408         } else if (propertyId.equals(PropertyIds.LAST_MODIFICATION_DATE)) {
409             return true;
410         } else if (propertyId.equals(PropertyIds.CHANGE_TOKEN)) {
411             return true;
412         }
413 
414         if (baseTypeId.equals(BaseTypeId.CMIS_DOCUMENT)) {
415             if (propertyId.equals(PropertyIds.IS_IMMUTABLE)) {
416                 return true;
417             } else if (propertyId.equals(PropertyIds.IS_LATEST_VERSION)) {
418                 return true;
419             } else if (propertyId.equals(PropertyIds.IS_MAJOR_VERSION)) {
420                 return true;
421             } else if (propertyId.equals(PropertyIds.VERSION_SERIES_ID)) {
422                 return true;
423             } else if (propertyId.equals(PropertyIds.IS_LATEST_MAJOR_VERSION)) {
424                 return true;
425             } else if (propertyId.equals(PropertyIds.VERSION_LABEL)) {
426                 return true;
427             } else if (propertyId.equals(PropertyIds.VERSION_SERIES_ID)) {
428                 return true;
429             } else if (propertyId.equals(PropertyIds.IS_VERSION_SERIES_CHECKED_OUT)) {
430                 return true;
431             } else if (propertyId.equals(PropertyIds.VERSION_SERIES_CHECKED_OUT_BY)) {
432                 return true;
433             } else if (propertyId.equals(PropertyIds.VERSION_SERIES_CHECKED_OUT_ID)) {
434                 return true;
435             } else if (propertyId.equals(PropertyIds.CHECKIN_COMMENT)) {
436                 return true;
437             } else if (propertyId.equals(PropertyIds.CONTENT_STREAM_LENGTH)) {
438                 return true;
439             } else if (propertyId.equals(PropertyIds.CONTENT_STREAM_MIME_TYPE)) {
440                 return true;
441             } else if (propertyId.equals(PropertyIds.CONTENT_STREAM_FILE_NAME)) {
442                 return true;
443             } else if (propertyId.equals(PropertyIds.CONTENT_STREAM_ID)) {
444                 return true;
445             } else {
446                 return false;
447             }
448         } else if (baseTypeId.equals(BaseTypeId.CMIS_FOLDER)) {
449             if (propertyId.equals(PropertyIds.PARENT_ID)) {
450                 return true;
451             } else if (propertyId.equals(PropertyIds.ALLOWED_CHILD_OBJECT_TYPE_IDS)) {
452                 return true;
453             } else if (propertyId.equals(PropertyIds.PATH)) {
454                 return true;
455             } else {
456                 return false;
457             }
458         } else if (baseTypeId.equals(BaseTypeId.CMIS_POLICY)) {
459             if (propertyId.equals(PropertyIds.SOURCE_ID)) {
460                 return true;
461             } else if (propertyId.equals(PropertyIds.TARGET_ID)) {
462                 return true;
463             } else {
464                 return false;
465             }
466         } else { // relationship
467             if (propertyId.equals(PropertyIds.POLICY_TEXT)) {
468                 return true;
469             } else {
470                 return false;
471             }
472         }
473     }
474 }