This project has retired. For details please refer to its Attic page.
PropertyCreationHelper 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.inmemory.types;
20  
21  import java.math.BigDecimal;
22  import java.math.BigInteger;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.GregorianCalendar;
26  import java.util.HashMap;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Map.Entry;
30  
31  import org.apache.chemistry.opencmis.commons.PropertyIds;
32  import org.apache.chemistry.opencmis.commons.data.Acl;
33  import org.apache.chemistry.opencmis.commons.data.AllowableActions;
34  import org.apache.chemistry.opencmis.commons.data.ExtensionsData;
35  import org.apache.chemistry.opencmis.commons.data.ObjectData;
36  import org.apache.chemistry.opencmis.commons.data.Properties;
37  import org.apache.chemistry.opencmis.commons.data.PropertyData;
38  import org.apache.chemistry.opencmis.commons.data.PropertyInteger;
39  import org.apache.chemistry.opencmis.commons.definitions.Choice;
40  import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
41  import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
42  import org.apache.chemistry.opencmis.commons.enums.Cardinality;
43  import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
44  import org.apache.chemistry.opencmis.commons.enums.PropertyType;
45  import org.apache.chemistry.opencmis.commons.enums.Updatability;
46  import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
47  import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
48  import org.apache.chemistry.opencmis.commons.impl.dataobjects.AbstractPropertyData;
49  import org.apache.chemistry.opencmis.commons.impl.dataobjects.AbstractPropertyDefinition;
50  import org.apache.chemistry.opencmis.commons.impl.dataobjects.BindingsObjectFactoryImpl;
51  import org.apache.chemistry.opencmis.commons.impl.dataobjects.ChoiceImpl;
52  import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectDataImpl;
53  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
54  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyBooleanDefinitionImpl;
55  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyBooleanImpl;
56  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDateTimeDefinitionImpl;
57  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDateTimeImpl;
58  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDecimalDefinitionImpl;
59  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDecimalImpl;
60  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyHtmlDefinitionImpl;
61  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyHtmlImpl;
62  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIdDefinitionImpl;
63  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIdImpl;
64  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIntegerDefinitionImpl;
65  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIntegerImpl;
66  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringDefinitionImpl;
67  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringImpl;
68  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyUriDefinitionImpl;
69  import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyUriImpl;
70  import org.apache.chemistry.opencmis.commons.spi.BindingsObjectFactory;
71  import org.apache.chemistry.opencmis.inmemory.DataObjectCreator;
72  import org.apache.chemistry.opencmis.inmemory.FilterParser;
73  import org.apache.chemistry.opencmis.inmemory.NameValidator;
74  import org.apache.chemistry.opencmis.inmemory.storedobj.api.DocumentVersion;
75  import org.apache.chemistry.opencmis.inmemory.storedobj.api.StoredObject;
76  import org.apache.commons.logging.Log;
77  import org.apache.commons.logging.LogFactory;
78  
79  /**
80   * @author Jens
81   * 
82   */
83  public class PropertyCreationHelper {
84  
85      private static final Log log = LogFactory.getLog(PropertyCreationHelper.class);
86  
87      private PropertyCreationHelper() {
88      }
89  
90      public static PropertyBooleanDefinitionImpl createBooleanDefinition(String id, String displayName, Updatability upd) {
91          PropertyBooleanDefinitionImpl prop = new PropertyBooleanDefinitionImpl();
92          createStandardDefinition(prop, id, PropertyType.BOOLEAN, displayName, Cardinality.SINGLE, upd);
93          return prop;
94      }
95  
96      public static PropertyBooleanDefinitionImpl createBooleanMultiDefinition(String id, String displayName,
97              Updatability upd) {
98          PropertyBooleanDefinitionImpl prop = new PropertyBooleanDefinitionImpl();
99          createStandardDefinition(prop, id, PropertyType.BOOLEAN, displayName, Cardinality.MULTI, upd);
100         return prop;
101     }
102 
103     public static PropertyDateTimeDefinitionImpl createDateTimeDefinition(String id, String displayName,
104             Updatability upd) {
105         PropertyDateTimeDefinitionImpl prop = new PropertyDateTimeDefinitionImpl();
106         createStandardDefinition(prop, id, PropertyType.DATETIME, displayName, Cardinality.SINGLE, upd);
107         return prop;
108     }
109 
110     public static PropertyDateTimeDefinitionImpl createDateTimeMultiDefinition(String id, String displayName,
111             Updatability upd) {
112         PropertyDateTimeDefinitionImpl prop = new PropertyDateTimeDefinitionImpl();
113         createStandardDefinition(prop, id, PropertyType.DATETIME, displayName, Cardinality.MULTI, upd);
114         return prop;
115     }
116 
117     public static PropertyDecimalDefinitionImpl createDecimalDefinition(String id, String displayName, Updatability upd) {
118         PropertyDecimalDefinitionImpl prop = new PropertyDecimalDefinitionImpl();
119         createStandardDefinition(prop, id, PropertyType.DECIMAL, displayName, Cardinality.SINGLE, upd);
120         return prop;
121     }
122 
123     public static PropertyDecimalDefinitionImpl createDecimalMultiDefinition(String id, String displayName,
124             Updatability upd) {
125         PropertyDecimalDefinitionImpl prop = new PropertyDecimalDefinitionImpl();
126         createStandardDefinition(prop, id, PropertyType.DECIMAL, displayName, Cardinality.MULTI, upd);
127         return prop;
128     }
129 
130     public static PropertyHtmlDefinitionImpl createHtmlDefinition(String id, String displayName, Updatability upd) {
131         PropertyHtmlDefinitionImpl prop = new PropertyHtmlDefinitionImpl();
132         createStandardDefinition(prop, id, PropertyType.HTML, displayName, Cardinality.SINGLE, upd);
133         return prop;
134     }
135 
136     public static PropertyHtmlDefinitionImpl createHtmlMultiDefinition(String id, String displayName, Updatability upd) {
137         PropertyHtmlDefinitionImpl prop = new PropertyHtmlDefinitionImpl();
138         createStandardDefinition(prop, id, PropertyType.HTML, displayName, Cardinality.MULTI, upd);
139         return prop;
140     }
141 
142     public static PropertyIdDefinitionImpl createIdDefinition(String id, String displayName, Updatability upd) {
143         PropertyIdDefinitionImpl prop = new PropertyIdDefinitionImpl();
144         createStandardDefinition(prop, id, PropertyType.ID, displayName, Cardinality.SINGLE, upd);
145         return prop;
146     }
147 
148     public static PropertyIdDefinitionImpl createIdMultiDefinition(String id, String displayName, Updatability upd) {
149         PropertyIdDefinitionImpl prop = new PropertyIdDefinitionImpl();
150         createStandardDefinition(prop, id, PropertyType.ID, displayName, Cardinality.MULTI, upd);
151         return prop;
152     }
153 
154     public static PropertyIntegerDefinitionImpl createIntegerDefinition(String id, String displayName, Updatability upd) {
155         PropertyIntegerDefinitionImpl prop = new PropertyIntegerDefinitionImpl();
156         createStandardDefinition(prop, id, PropertyType.INTEGER, displayName, Cardinality.SINGLE, upd);
157         return prop;
158     }
159 
160     public static PropertyIntegerDefinitionImpl createIntegerMultiDefinition(String id, String displayName,
161             Updatability upd) {
162         PropertyIntegerDefinitionImpl prop = new PropertyIntegerDefinitionImpl();
163         createStandardDefinition(prop, id, PropertyType.INTEGER, displayName, Cardinality.MULTI, upd);
164         return prop;
165     }
166 
167     public static PropertyStringDefinitionImpl createStringDefinition(String id, String displayName, Updatability upd) {
168         PropertyStringDefinitionImpl prop = new PropertyStringDefinitionImpl();
169         createStandardDefinition(prop, id, PropertyType.STRING, displayName, Cardinality.SINGLE, upd);
170         return prop;
171     }
172 
173     public static PropertyStringDefinitionImpl createStringMultiDefinition(String id, String displayName,
174             Updatability upd) {
175         PropertyStringDefinitionImpl prop = new PropertyStringDefinitionImpl();
176         createStandardDefinition(prop, id, PropertyType.STRING, displayName, Cardinality.MULTI, upd);
177         return prop;
178     }
179 
180     public static PropertyUriDefinitionImpl createUriDefinition(String id, String displayName, Updatability upd) {
181         PropertyUriDefinitionImpl prop = new PropertyUriDefinitionImpl();
182         createStandardDefinition(prop, id, PropertyType.URI, displayName, Cardinality.SINGLE, upd);
183         return prop;
184     }
185 
186     public static PropertyUriDefinitionImpl createUriMultiDefinition(String id, String displayName, Updatability upd) {
187         PropertyUriDefinitionImpl prop = new PropertyUriDefinitionImpl();
188         createStandardDefinition(prop, id, PropertyType.URI, displayName, Cardinality.MULTI, upd);
189         return prop;
190     }
191 
192     public static <T> void addElemToPicklist(AbstractPropertyDefinition<T> prop, T value) {
193         List<Choice<T>> choiceList = prop.getChoices();
194         if (choiceList == null) {
195             choiceList = new ArrayList<Choice<T>>();
196             prop.setChoices(choiceList);
197         }
198 
199         ChoiceImpl<T> elem = new ChoiceImpl<T>();
200         elem.setValue(Collections.singletonList(value));
201         choiceList.add(elem);
202     }
203 
204     public static <T> void setDefaultValue(AbstractPropertyDefinition<T> prop, T defVal) {
205         prop.setDefaultValue(Collections.singletonList(defVal));
206     }
207 
208     public static Properties getPropertiesFromObject(StoredObject so, TypeDefinition td, List<String> requestedIds,
209             boolean fillOptionalPropertyData) {
210         // build properties collection
211 
212         BindingsObjectFactory objectFactory = new BindingsObjectFactoryImpl();
213         Map<String, PropertyData<?>> properties = new HashMap<String, PropertyData<?>>();
214         so.fillProperties(properties, objectFactory, requestedIds);
215 
216         String typeId = so.getTypeId();
217         if (FilterParser.isContainedInFilter(PropertyIds.BASE_TYPE_ID, requestedIds)) {
218             if (td == null) {
219                 log.warn("getPropertiesFromObject(), cannot get type definition, a type with id " + typeId
220                         + " is unknown");
221             } else {
222                 String baseTypeId = td.getBaseTypeId().value();
223                 properties.put(PropertyIds.BASE_TYPE_ID,
224                         objectFactory.createPropertyIdData(PropertyIds.BASE_TYPE_ID, baseTypeId));
225             }
226         }
227 
228         // fill not-set properties from type definition (as spec requires)
229         Map<String, PropertyDefinition<?>> propDefs = td.getPropertyDefinitions();
230         for (PropertyDefinition<?> propDef : propDefs.values()) {
231             if (!properties.containsKey(propDef.getId()) && FilterParser.isContainedInFilter(propDef.getId(), requestedIds))
232                 properties.put(propDef.getId(), getEmptyValue(propDef));
233         }
234 
235         List<PropertyData<?>> propertiesList = new ArrayList<PropertyData<?>>(properties.values());
236 
237         if (fillOptionalPropertyData) { // add query name, local name, display
238                                         // name
239             fillOptionalPropertyData(td, propertiesList);
240         }
241 
242         Properties props = objectFactory.createPropertiesData(propertiesList);
243         return props;
244     }
245 
246     public static Properties getPropertiesFromObject(StoredObject so, TypeDefinition td,
247             Map<String, String> requestedIds, Map<String, String> requestedFuncs) {
248         // build properties collection
249 
250         List<String> idList = new ArrayList<String>(requestedIds.values());
251         BindingsObjectFactory objectFactory = new BindingsObjectFactoryImpl();
252         Map<String, PropertyData<?>> properties = new HashMap<String, PropertyData<?>>();
253         so.fillProperties(properties, objectFactory, idList);
254 
255         String typeId = so.getTypeId();
256         if (FilterParser.isContainedInFilter(PropertyIds.BASE_TYPE_ID, idList)) {
257             if (td == null) {
258                 log.warn("getPropertiesFromObject(), cannot get type definition, a type with id " + typeId
259                         + " is unknown");
260             } else {
261                 String baseTypeId = td.getBaseTypeId().value();
262                 properties.put(PropertyIds.BASE_TYPE_ID,
263                         objectFactory.createPropertyIdData(PropertyIds.BASE_TYPE_ID, baseTypeId));
264             }
265         }
266 
267         Map<String, PropertyData<?>> mappedProperties = new HashMap<String, PropertyData<?>>();
268         if (requestedIds.containsValue("*")) {
269             for (Map.Entry<String, PropertyData<?>> prop : properties.entrySet()) {
270                 // map property id to property query name
271                 String queryName = td.getPropertyDefinitions().get(prop.getKey()).getQueryName();
272                 String localName = td.getPropertyDefinitions().get(prop.getKey()).getLocalName();
273                 String displayName = td.getPropertyDefinitions().get(prop.getKey()).getDisplayName();
274                 AbstractPropertyData<?> ad = clonePropertyData(prop.getValue()); 
275                 
276                 ad.setQueryName(queryName);
277                 ad.setLocalName(localName);
278                 ad.setDisplayName(displayName);
279                 mappedProperties.put(queryName, ad);
280             }
281         } else {
282             // replace all ids with query names or alias:
283             for (Entry<String, String> propAlias : requestedIds.entrySet()) {
284                 String queryNameOrAlias = propAlias.getKey();
285                 PropertyData<?> prop = properties.get(propAlias.getValue());
286                 String localName = td.getPropertyDefinitions().get(prop.getId()).getLocalName();
287                 String displayName = td.getPropertyDefinitions().get(prop.getId()).getDisplayName();
288                 AbstractPropertyData<?> ad = clonePropertyData(prop); 
289                 
290                 ad.setQueryName(queryNameOrAlias);
291                 ad.setLocalName(localName);
292                 ad.setDisplayName(displayName);
293                 mappedProperties.put(queryNameOrAlias, ad);
294             }
295         }
296         // add functions:
297         BindingsObjectFactory objFactory = new BindingsObjectFactoryImpl();
298         for (Entry<String, String> funcEntry : requestedFuncs.entrySet()) {
299             PropertyInteger pi = objFactory.createPropertyIntegerData(funcEntry.getValue(), BigInteger.valueOf(100));
300             // fixed dummy value
301             mappedProperties.put(funcEntry.getKey(), pi);
302         }
303 
304         Properties props = new PropertiesImpl(mappedProperties.values());
305         return props;
306     }
307 
308     public static ObjectData getObjectData(TypeDefinition typeDef, StoredObject so, String filter, String user,
309             Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter,
310             Boolean includePolicyIds, Boolean includeACL, ExtensionsData extension) {
311 
312         ObjectDataImpl od = new ObjectDataImpl();
313 
314         if (so == null) {
315             throw new CmisObjectNotFoundException("Illegal object id: null");
316         }
317 
318         // build properties collection
319         List<String> requestedIds = FilterParser.getRequestedIdsFromFilter(filter);
320         Properties props = getPropertiesFromObject(so, typeDef, requestedIds, true);
321 
322         // fill output object
323         if (null != includeAllowableActions && includeAllowableActions) {
324             AllowableActions allowableActions = so.getAllowableActions(user);
325             od.setAllowableActions(allowableActions);
326         }
327 
328         if (null != includeACL && includeACL) {
329             Acl acl = so instanceof DocumentVersion ? ((DocumentVersion) so).getParentDocument().getAcl() : so.getAcl();
330             od.setAcl(acl);
331         }
332         od.setIsExactAcl(true);
333 
334         if (null != includePolicyIds && includePolicyIds) {
335             od.setPolicyIds(DataObjectCreator.fillPolicyIds(so));
336         }
337 
338         if (null != includeRelationships && includeRelationships != IncludeRelationships.NONE) {
339             od.setRelationships(DataObjectCreator.fillRelationships(includeRelationships, so, user));
340         }
341 
342         if (renditionFilter != null && renditionFilter.length() > 0) {
343             od.setRenditions(DataObjectCreator.fillRenditions(so));
344         }
345 
346         od.setProperties(props);
347 
348         // Note: do not set change event info for this call
349         return od;
350     }
351 
352     public static ObjectData getObjectDataQueryResult(TypeDefinition typeDef, StoredObject so, String user,
353             Map<String, String> requestedProperties, Map<String, String> requestedFuncs,
354             Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter) {
355 
356         ObjectDataImpl od = new ObjectDataImpl();
357 
358         // build properties collection
359         Properties props = getPropertiesFromObject(so, typeDef, requestedProperties, requestedFuncs);
360 
361         // fill output object
362         if (null != includeAllowableActions && includeAllowableActions) {
363             // AllowableActions allowableActions =
364             // DataObjectCreator.fillAllowableActions(so, user);
365             AllowableActions allowableActions = so.getAllowableActions(user);
366             od.setAllowableActions(allowableActions);
367         }
368 
369         od.setAcl(so.getAcl());
370         od.setIsExactAcl(true);
371 
372         if (null != includeRelationships && includeRelationships != IncludeRelationships.NONE) {
373             od.setRelationships(DataObjectCreator.fillRelationships(includeRelationships, so, user));
374         }
375 
376         if (renditionFilter != null && renditionFilter.length() > 0) {
377             od.setRenditions(DataObjectCreator.fillRenditions(so));
378         }
379 
380         od.setProperties(props);
381 
382         return od;
383     }
384 
385     // internal helpers
386     private static void createStandardDefinition(AbstractPropertyDefinition<?> prop, String id, PropertyType propType,
387             String displayName, Cardinality card, Updatability upd) {
388 
389         if (!NameValidator.isValidId(id)) {
390             if (!NameValidator.isValidId(id)) {
391                 throw new CmisInvalidArgumentException(NameValidator.ERROR_ILLEGAL_NAME);
392             }
393         }
394 
395         prop.setId(id);
396         if (displayName == null) {
397             prop.setDisplayName("Sample " + prop.getId() + " boolean property");
398         } else {
399             prop.setDisplayName(displayName);
400         }
401         prop.setDescription("This is a " + prop.getDisplayName() + " property.");
402         prop.setLocalName(id);
403         prop.setLocalNamespace("local");
404         prop.setQueryName(id);
405         prop.setIsInherited(false);
406         prop.setCardinality(card);
407         prop.setIsOpenChoice(false);
408         prop.setIsQueryable(true);
409         prop.setIsRequired(false);
410         prop.setIsOrderable(true);
411         prop.setPropertyType(propType);
412         prop.setUpdatability(upd);
413     }
414 
415     private static void fillOptionalPropertyData(TypeDefinition td, List<PropertyData<?>> properties) {
416         for (PropertyData<?> pd : properties) {
417             fillOptionalPropertyData(td, (AbstractPropertyData<?>) pd);
418         }
419     }
420 
421     private static void fillOptionalPropertyData(TypeDefinition td, AbstractPropertyData<?> property) {
422         PropertyDefinition<?> pd = td.getPropertyDefinitions().get(property.getId());
423         if (null != pd) {
424             String displayName = pd.getDisplayName();
425             String queryName = pd.getQueryName();
426             String localName = pd.getLocalName();
427             property.setDisplayName(displayName);
428             property.setLocalName(localName);
429             property.setQueryName(queryName);
430         }
431     }
432 
433     private static PropertyData<?> getEmptyValue(PropertyDefinition<?> propDef) {
434         if (propDef.getPropertyType().equals(PropertyType.BOOLEAN))
435             return new PropertyBooleanImpl(propDef.getId(), (Boolean) null);
436         else if (propDef.getPropertyType().equals(PropertyType.DATETIME))
437             return new PropertyDateTimeImpl(propDef.getId(), (GregorianCalendar) null);
438         else if (propDef.getPropertyType().equals(PropertyType.DECIMAL))
439             return new PropertyDecimalImpl(propDef.getId(), (BigDecimal) null);
440         else if (propDef.getPropertyType().equals(PropertyType.HTML))
441             return new PropertyHtmlImpl(propDef.getId(), (String) null);
442         else if (propDef.getPropertyType().equals(PropertyType.ID))
443             return new PropertyIdImpl(propDef.getId(), (String) null);
444         else if (propDef.getPropertyType().equals(PropertyType.INTEGER))
445             return new PropertyIntegerImpl(propDef.getId(), (BigInteger) null);
446         else if (propDef.getPropertyType().equals(PropertyType.STRING))
447             return new PropertyStringImpl(propDef.getId(), (String) null);
448         else if (propDef.getPropertyType().equals(PropertyType.URI))
449             return new PropertyUriImpl(propDef.getId(), (String) null);
450         else
451             return null;
452     }
453     
454     private static AbstractPropertyData<?> clonePropertyData(PropertyData<?> prop) {
455         AbstractPropertyData<?> ad = null;
456         
457         if (prop instanceof PropertyBooleanImpl) {
458             PropertyBooleanImpl clone = new PropertyBooleanImpl();
459             clone.setValues(((PropertyBooleanImpl)prop).getValues());
460             ad = clone;
461         } else if (prop instanceof PropertyDateTimeImpl) {
462             PropertyDateTimeImpl clone = new PropertyDateTimeImpl();
463             clone.setValues(((PropertyDateTimeImpl)prop).getValues());
464             ad = clone;
465         } else if (prop instanceof PropertyDecimalImpl) {
466             PropertyDecimalImpl clone = new PropertyDecimalImpl();
467             clone.setValues(((PropertyDecimalImpl)prop).getValues());
468             ad = clone;
469         } else if (prop instanceof PropertyHtmlImpl) {
470             PropertyHtmlImpl clone = new PropertyHtmlImpl();
471             clone.setValues(((PropertyHtmlImpl)prop).getValues());
472             ad = clone;
473         } else if (prop instanceof PropertyIdImpl) {
474             PropertyIdImpl clone = new PropertyIdImpl();
475             clone.setValues(((PropertyIdImpl)prop).getValues());
476             ad = clone;
477         } else if (prop instanceof PropertyIntegerImpl) {
478             PropertyIntegerImpl clone = new PropertyIntegerImpl();
479             clone.setValues(((PropertyIntegerImpl)prop).getValues());
480             ad = clone;
481         } else if (prop instanceof PropertyStringImpl) {
482             PropertyStringImpl clone = new PropertyStringImpl();
483             clone.setValues(((PropertyStringImpl)prop).getValues());
484             ad = clone;
485         } else if (prop instanceof PropertyUriImpl) {
486             PropertyUriImpl clone = new PropertyUriImpl();
487             clone.setValues(((PropertyUriImpl)prop).getValues());
488             ad = clone;
489         } else {
490             throw new RuntimeException("Unknown property type: " + prop.getClass());
491         }
492         
493         ad.setDisplayName(prop.getDisplayName());
494         ad.setId(prop.getId());
495         ad.setLocalName(prop.getLocalName());
496         ad.setQueryName(prop.getQueryName());
497         ad.setExtensions(prop.getExtensions());
498 
499         return ad;        
500     }
501 }