View Javadoc

1   /*
2    * Copyright 2005-2007 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.kuali.rice.kns.datadictionary;
18  
19  import org.apache.commons.lang.StringUtils;
20  import org.kuali.rice.kns.datadictionary.exception.AttributeValidationException;
21  import org.kuali.rice.kns.datadictionary.exception.ClassValidationException;
22  import org.kuali.rice.kns.datadictionary.mask.Mask;
23  import org.kuali.rice.kns.lookup.valueFinder.ValueFinder;
24  import org.kuali.rice.kns.service.BusinessObjectMetaDataService;
25  import org.kuali.rice.kns.service.KNSServiceLocator;
26  
27  /**
28   * Contains field-related information for DataDictionary entries.  Used by lookups and inquiries.
29   *
30   * Note: the setters do copious amounts of validation, to facilitate generating errors during the parsing process.
31   */
32  public class FieldDefinition extends DataDictionaryDefinitionBase implements FieldDefinitionI {
33      private static final long serialVersionUID = -3426603523049661524L;
34      
35  	protected String attributeName;
36      protected boolean required = false;
37      protected boolean forceInquiry = false;
38      protected boolean noInquiry = false;
39      protected boolean noDirectInquiry = false;
40      protected boolean forceLookup = false;
41      protected boolean noLookup = false;
42      protected boolean useShortLabel = false;
43      protected String defaultValue;
44      protected Class<? extends ValueFinder> defaultValueFinderClass;
45      protected String quickfinderParameterString;
46      protected Class<? extends ValueFinder> quickfinderParameterStringBuilderClass;
47  
48      protected Integer maxLength = null;
49  
50      protected String displayEditMode;
51      protected Mask displayMask;
52  
53  	protected boolean hidden 	= false;
54  	protected boolean readOnly 	= false;
55  
56  	protected boolean treatWildcardsAndOperatorsAsLiteral = false;
57  	
58      protected String alternateDisplayAttributeName;
59      protected String additionalDisplayAttributeName;
60  	
61  	protected boolean triggerOnChange;
62  	protected boolean total = false;
63  	
64      public FieldDefinition() {
65      }
66  
67  
68      /**
69       * @return attributeName
70       */
71      public String getAttributeName() {
72          return attributeName;
73      }
74  
75      /**
76       * Sets attributeName to the given value.
77       *
78       * @param attributeName
79       * @throws IllegalArgumentException if the given attributeName is blank
80       */
81      public void setAttributeName(String attributeName) {
82          if (StringUtils.isBlank(attributeName)) {
83              throw new IllegalArgumentException("invalid (blank) attributeName");
84          }
85          this.attributeName = attributeName;
86      }
87  
88  
89      /**
90       * @return true if this attribute is required
91       */
92      public boolean isRequired() {
93          return required;
94      }
95  
96  
97      /**
98                      required = true means that the user must enter something
99                          into the search criterion lookup field
100      */
101     public void setRequired(boolean required) {
102         this.required = required;
103     }
104 
105 
106     /**
107      * @return Returns the forceInquiry.
108      */
109     public boolean isForceInquiry() {
110         return forceInquiry;
111     }
112 
113 
114     /**
115      * forceInquiry = true means that the displayed field value will
116                     always be made inquirable (this attribute is not used within the code).
117      */
118     public void setForceInquiry(boolean forceInquiry) {
119         this.forceInquiry = forceInquiry;
120     }
121 
122     /**
123      * @return Returns the forceLookup.
124      */
125     public boolean isForceLookup() {
126         return forceLookup;
127     }
128 
129     /**
130      * forceLookup = this attribute is not used
131      */
132     public void setForceLookup(boolean forceLookup) {
133         this.forceLookup = forceLookup;
134     }
135 
136     /**
137      * @return Returns the noInquiry.
138      */
139     public boolean isNoInquiry() {
140         return noInquiry;
141     }
142 
143     /**
144      * @return Returns a boolean value indicating whether or not to provide
145      *          a direct inquiry for the lookup field
146      */
147     public boolean isNoDirectInquiry()
148     {
149         return noDirectInquiry;
150     }
151 
152     /**
153      * noInquiry = true means that the displayed field will never be made inquirable.
154      */
155     public void setNoInquiry(boolean noInquiry) {
156         this.noInquiry = noInquiry;
157     }
158 
159     /**
160      * @param noInquiry If true, the displayed field will not have a direct
161 	 *     inquiry facility
162      */
163     public void setNoDirectInquiry(boolean noDirectInquiry) {
164         this.noDirectInquiry = noDirectInquiry;
165     }
166 
167     /**
168      * @return Returns the noLookup.
169      */
170     public boolean isNoLookup() {
171         return noLookup;
172     }
173 
174     /**
175      * noLookup = true means that field should not include magnifying glass (i.e. quickfinder)
176      */
177     public void setNoLookup(boolean noLookup) {
178         this.noLookup = noLookup;
179     }
180 
181 
182     /**
183 	 * @return the useShortLabel
184 	 */
185 	public boolean isUseShortLabel() {
186 		return this.useShortLabel;
187 	}
188 
189 
190 	/**
191 	 * @param useShortLabel the useShortLabel to set
192 	 */
193 	public void setUseShortLabel(boolean useShortLabel) {
194 		this.useShortLabel = useShortLabel;
195 	}
196 
197 
198 	/**
199      * @return Returns the defaultValue.
200      */
201     public String getDefaultValue() {
202         return defaultValue;
203     }
204 
205 
206     /**
207            The defaultValue element will pre-load the specified value
208            into the field.
209      */
210     public void setDefaultValue(String defaultValue) {
211         this.defaultValue = defaultValue;
212     }
213 
214     /**
215 	 * the quickfinderParameterString is a comma separated list of parameter/value pairs, of the format
216 	 * "param1=value1,param2=value2", where the parameters correspond to attributes of the target class
217 	 * for the quickfinder, and the values to literals that those attributes will default to when the
218 	 * quickfinder is used.
219 	 * @return the quickfinderParameterString
220 	 */
221 	public String getQuickfinderParameterString() {
222 		return this.quickfinderParameterString;
223 	}
224 
225 	/**
226 	 * @param quickfinderParameterString the quickfinderParameterString to set.  See {@link #getQuickfinderParameterString()}
227 	 */
228 	public void setQuickfinderParameterString(String quickfinderParameterString) {
229 		this.quickfinderParameterString = quickfinderParameterString;
230 	}
231 
232 
233     /**
234      * the quickfinderParameterStringBuilderClass specifies the java class that will be used
235      * to determine the default value(s) for field(s) on the target lookup when the quickfinder
236      * is used. The classname specified in this field must implement
237      * {@link org.kuali.rice.kns.lookup.valueFinder.ValueFinder}.  See {@link #getQuickfinderParameterString()}
238      * for the result string format.
239 	 * @return the quickfinderParameterStringBuilderClass
240 	 */
241 	public Class<? extends ValueFinder> getQuickfinderParameterStringBuilderClass() {
242 		return this.quickfinderParameterStringBuilderClass;
243 	}
244 
245     /**
246      * See {@link #getQuickfinderParameterStringBuilderClass()}
247 	 * @param quickfinderParameterStringBuilderClass the quickfinderParameterStringBuilderClass to set
248      */
249 	public void setQuickfinderParameterStringBuilderClass(
250 			Class<? extends ValueFinder> quickfinderParameterStringBuilderClass) {
251         if (quickfinderParameterStringBuilderClass == null) {
252             throw new IllegalArgumentException("invalid (null) quickfinderParameterStringBuilderClass");
253         }
254 		this.quickfinderParameterStringBuilderClass = quickfinderParameterStringBuilderClass;
255     }
256 
257     /**
258      * Directly validate simple fields.
259      *
260      * @see org.kuali.rice.kns.datadictionary.DataDictionaryDefinition#completeValidation(java.lang.Class, java.lang.Object)
261      */
262     public void completeValidation(Class rootBusinessObjectClass, Class otherBusinessObjectClass) {
263     	BusinessObjectMetaDataService boMetadataService = KNSServiceLocator.getBusinessObjectMetaDataService();
264 
265         if (!DataDictionary.isPropertyOf(rootBusinessObjectClass, getAttributeName())) {
266             throw new AttributeValidationException("unable to find attribute '" + attributeName + "' in rootBusinessObjectClass '" + rootBusinessObjectClass.getName() + "' (" + "" + ")");
267         }
268 
269         if (StringUtils.isNotBlank(getAlternateDisplayAttributeName())) {
270             if (!DataDictionary.isPropertyOf(rootBusinessObjectClass, getAlternateDisplayAttributeName())) {
271                 throw new AttributeValidationException("unable to find attribute named '" + getName() + "' in rootBusinessObjectClass '" + rootBusinessObjectClass.getName() + "' (" + "" + ")");
272             }
273         }
274         
275         if (StringUtils.isNotBlank(getAdditionalDisplayAttributeName())) {
276             if (!DataDictionary.isPropertyOf(rootBusinessObjectClass, getAdditionalDisplayAttributeName())) {
277                 throw new AttributeValidationException("unable to find attribute named '" + getName() + "' in rootBusinessObjectClass '" + rootBusinessObjectClass.getName() + "' (" + "" + ")");
278             }
279         }
280 
281         if (defaultValueFinderClass != null && defaultValue != null) {
282             throw new AttributeValidationException("Both defaultValue and defaultValueFinderClass can not be specified on attribute " + getAttributeName() + " in rootBusinessObjectClass " + rootBusinessObjectClass.getName());
283         }
284 
285         validateQuickfinderParameters(rootBusinessObjectClass, boMetadataService);
286 
287         if (forceInquiry == true && noInquiry == true) {
288             throw new AttributeValidationException("Both forceInquiry and noInquiry can not be set to true on attribute " + getAttributeName() + " in rootBusinessObjectClass " + rootBusinessObjectClass.getName());
289         }
290         if (forceLookup == true && noLookup == true) {
291             throw new AttributeValidationException("Both forceLookup and noLookup can not be set to true on attribute " + getAttributeName() + " in rootBusinessObjectClass " + rootBusinessObjectClass.getName());
292         }
293     }
294 
295 
296     /**
297 	 * This method does validation on the quickfinderParameterString and quickfinderParameterStringBuilderClass members
298 	 *
299 	 * @param rootBusinessObjectClass
300 	 * @param boMetadataService
301 	 */
302 	private void validateQuickfinderParameters(Class rootBusinessObjectClass,
303 			BusinessObjectMetaDataService boMetadataService) {
304 		if (quickfinderParameterStringBuilderClass != null && quickfinderParameterString != null) {
305             throw new AttributeValidationException("Both quickfinderParameterString and quickfinderParameterStringBuilderClass can not be specified on attribute " + getAttributeName() + " in rootBusinessObjectClass " + rootBusinessObjectClass.getName());
306         }
307 
308         // String used for building exception messages
309         String quickfinderParameterStringSource = "quickfinderParameterString";
310 
311         if (quickfinderParameterStringBuilderClass != null) {
312         	try {
313 		        quickfinderParameterStringSource = "quickfinderParameterStringBuilderClass " + quickfinderParameterStringBuilderClass.getCanonicalName();
314 				quickfinderParameterString = quickfinderParameterStringBuilderClass.newInstance().getValue();
315 			} catch (InstantiationException e) {
316 				throw new ClassValidationException("unable to create new instance of "+  quickfinderParameterStringSource +" while validating rootBusinessObjectClass '"+ rootBusinessObjectClass.getName() +"'", e);
317 			} catch (IllegalAccessException e) {
318 				throw new ClassValidationException("unable to create new instance of "+  quickfinderParameterStringSource +" while validating rootBusinessObjectClass '"+ rootBusinessObjectClass.getName() +"'", e);
319 			}
320         }
321 
322         if (!StringUtils.isEmpty(quickfinderParameterString)) {
323         	// quickfinderParameterString will look something like "campusTypeCode=P,active=Y"
324         	for (String quickfinderParam : quickfinderParameterString.split(",")) { // this is guaranteed to return at least one
325         		if (quickfinderParam.contains("=")) {
326         			String propertyName = quickfinderParam.split("=")[0];
327         			RelationshipDefinition relationship = boMetadataService.getBusinessObjectRelationshipDefinition(rootBusinessObjectClass, attributeName);
328         			Class targetClass = relationship.getTargetClass();
329 
330         			// This is insufficient to ensure the property is valid for a lookup default, but it's better than nothing.
331             		if (!DataDictionary.isPropertyOf(targetClass, propertyName)) {
332             			throw new ClassValidationException("malformed parameter string  '"+ quickfinderParameterString +"' from "+ quickfinderParameterStringSource +
333             					", '"+ propertyName +"' is not a property of "+ targetClass +"' for rootBusinessObjectClass '"+ rootBusinessObjectClass.getName() +"'");
334             		}
335 
336         		} else {
337         			throw new ClassValidationException("malformed parameter string '"+ quickfinderParameterString +"' from "+ quickfinderParameterStringSource +
338         					" for rootBusinessObjectClass '"+ rootBusinessObjectClass.getName() +"'");
339         		}
340         	}
341         }
342 	}
343 
344 
345     /**
346      * @see java.lang.Object#toString()
347      */
348     public String toString() {
349         return "FieldDefinition for attribute " + getAttributeName();
350     }
351 
352 
353     public String getName() {
354         return attributeName;
355     }
356 
357 
358     public String getDisplayEditMode() {
359         return displayEditMode;
360     }
361 
362 
363     /*
364                         The document authorizer classes have a method getEditMode, which is a map of edit mode to
365                         value mappings.  Depending on the context, the value of the mapping may be relevant, and the logic determining
366                         whether the value is relevant is often implemented in the JSP/tag layer.
367 
368                         Fields on a document (particularily maintenance documents) may be associated with
369                         an edit mode.  If the edit mode is mapped to a relevant value, then the all fields associated with the edit mode
370                         will be rendered unhidden.
371 
372                         The displayEditMode element is used to specify the edit mode that will be associated with the field.
373                         If the document authorizer returns a map with this edit mode mapped to a proper value, then the field will be unhidden to the user.
374      */
375     public void setDisplayEditMode(String displayEditMode) {
376         this.displayEditMode = displayEditMode;
377     }
378 
379 
380     public Mask getDisplayMask() {
381         return displayMask;
382     }
383 
384     /**
385      * The displayMask element specifies the type of masking to
386                     be used to hide the value from un-authorized users.
387                     There are three types of masking.
388      */
389     public void setDisplayMask(Mask displayMask) {
390         this.displayMask = displayMask;
391     }
392 
393 
394 
395     public boolean isReadOnlyAfterAdd() {
396         return false;
397     }
398 
399 
400     /**
401      * Gets the maxLength attribute.
402      * @return Returns the maxLength.
403      */
404     public Integer getMaxLength() {
405         return maxLength;
406     }
407 
408 
409     /**
410      * maxLength = the maximum allowable length of the field in the lookup result fields.  In other contexts,
411                     like inquiries, this field has no effect.
412      */
413     public void setMaxLength(Integer maxLength) {
414         this.maxLength = maxLength;
415     }
416 
417     /**
418      * @return custom defaultValue class
419      */
420     public Class<? extends ValueFinder> getDefaultValueFinderClass() {
421         return this.defaultValueFinderClass;
422     }
423 
424     /**
425                       The defaultValueFinderClass specifies the java class that will be
426                       used to determine the default value of a field.  The classname
427                       specified in this field must implement org.kuali.rice.kns.lookup.valueFinder.ValueFinder
428      */
429     public void setDefaultValueFinderClass(Class<? extends ValueFinder> defaultValueFinderClass) {
430         if (defaultValueFinderClass == null) {
431             throw new IllegalArgumentException("invalid (null) defaultValueFinderClass");
432         }
433         this.defaultValueFinderClass = defaultValueFinderClass;
434     }
435     
436 	/**
437 	 * @return the hidden
438 	 */
439 	public boolean isHidden() {
440 		return this.hidden;
441 	}
442 
443 	/**
444 	 * @param hidden
445      *  If the ControlDefinition.isHidden == true then a corresponding LookupDefinition would
446      *  automatically be removed from the search criteria.  In some cases you might want the
447      *  hidden field to be used as a search criteria.  For example, in PersonImpl.xml a client
448      *  might want to have the campus code hidden and preset to Bloomington.  So when the search
449      *  is run, only people from the bloomington campus are returned.
450      *
451      *   So, if you want to have a hidden search criteria, set this variable to true. Defaults to
452      *   false.
453      */
454 	public void setHidden(boolean hidden) {
455 		this.hidden = hidden;
456 	}
457 	
458 	/**
459 	 * @return the readOnly
460 	 */
461 	public boolean isReadOnly() {
462 		return this.readOnly;
463 	}
464 	
465 	/**
466 	 * @param readOnly the readOnly to set
467 	 */
468 	public void setReadOnly(boolean readOnly) {
469 		this.readOnly = readOnly;
470 	}
471 	
472 	public boolean isTriggerOnChange() {
473 		return this.triggerOnChange;
474 	}
475 
476 	public void setTriggerOnChange(boolean triggerOnChange) {
477 		this.triggerOnChange = triggerOnChange;
478 	}
479 
480 	/**
481 	 * @return the treatWildcardsAndOperatorsAsLiteralOnLookups
482 	 */
483 	public boolean isTreatWildcardsAndOperatorsAsLiteral() {
484 		return this.treatWildcardsAndOperatorsAsLiteral;
485 	}
486 
487 
488 	/**
489 	 * @param treatWildcardsAndOperatorsAsLiteralOnLookups the treatWildcardsAndOperatorsAsLiteralOnLookups to set
490 	 */
491 	public void setTreatWildcardsAndOperatorsAsLiteral(
492 			boolean treatWildcardsAndOperatorsAsLiteralOnLookups) {
493 		this.treatWildcardsAndOperatorsAsLiteral = treatWildcardsAndOperatorsAsLiteralOnLookups;
494 	}
495 
496 
497 	public String getAlternateDisplayAttributeName() {
498 		return this.alternateDisplayAttributeName;
499 	}
500 
501 
502 	public void setAlternateDisplayAttributeName(String alternateDisplayAttributeName) {
503 		this.alternateDisplayAttributeName = alternateDisplayAttributeName;
504 	}
505 
506 
507 	public String getAdditionalDisplayAttributeName() {
508 		return this.additionalDisplayAttributeName;
509 	}
510 
511 
512 	public void setAdditionalDisplayAttributeName(String additionalDisplayAttributeName) {
513 		this.additionalDisplayAttributeName = additionalDisplayAttributeName;
514 	}
515 
516 
517 	public boolean isTotal() {
518 		return this.total;
519 	}
520 
521 
522 	public void setTotal(boolean total) {
523 		this.total = total;
524 	}
525 	
526 }