001: /*
002: * Copyright 2005-2007 The Kuali Foundation.
003: *
004: * Licensed under the Educational Community License, Version 1.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.opensource.org/licenses/ecl1.php
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.kuali.core.datadictionary;
018:
019: import org.apache.commons.lang.StringUtils;
020: import org.apache.commons.logging.Log;
021: import org.apache.commons.logging.LogFactory;
022: import org.kuali.core.datadictionary.control.ControlDefinition;
023: import org.kuali.core.datadictionary.exception.AttributeValidationException;
024: import org.kuali.core.datadictionary.exception.ClassValidationException;
025: import org.kuali.core.datadictionary.exception.CompletionException;
026: import org.kuali.core.datadictionary.exception.ReferenceValidationException;
027: import org.kuali.core.datadictionary.mask.Mask;
028: import org.kuali.core.datadictionary.validation.ValidationPattern;
029: import org.kuali.core.web.format.Formatter;
030:
031: /**
032: * A single attribute definition in the DataDictionary, which contains information relating to the display, validation, and general
033: * maintenance of a specific attribute of an entry.
034: *
035: *
036: */
037: public class AttributeReferenceDefinition extends AttributeDefinition {
038: // logger
039: private static Log LOG = LogFactory
040: .getLog(AttributeReferenceDefinition.class);
041:
042: private String sourceClassName;
043: private String sourceAttributeName;
044: private AttributeDefinition delegate;
045:
046: /**
047: * Constructs an AttributeReferenceDefinition
048: */
049: public AttributeReferenceDefinition() {
050: LOG.debug("creating new AttributeReferenceDefinition");
051: }
052:
053: public void setSourceClassName(String sourceClassName) {
054: if (StringUtils.isBlank(sourceClassName)) {
055: throw new IllegalArgumentException(
056: "invalid (blank) sourceClassName");
057: }
058:
059: this .sourceClassName = sourceClassName;
060: }
061:
062: public String getSourceClassName() {
063: return this .sourceClassName;
064: }
065:
066: public void setSourceAttributeName(String sourceAttributeName) {
067: if (StringUtils.isBlank(sourceAttributeName)) {
068: throw new IllegalArgumentException(
069: "invalid (blank) sourceAttributeName");
070: }
071:
072: this .sourceAttributeName = sourceAttributeName;
073: }
074:
075: public String getSourceAttributeName() {
076: return this .sourceAttributeName;
077: }
078:
079: /**
080: * @return AttributeDefinition acting as delegate for this AttributeReferenceDefinition
081: */
082: AttributeDefinition getDelegate() {
083: if (delegate == null) {
084: throw new IllegalStateException(
085: "unable to retrieve null delegate");
086: }
087:
088: return delegate;
089: }
090:
091: /**
092: * Sets the given AttributeDefinition as the delegate for this instance
093: *
094: * @param delegate
095: */
096: void setDelegate(AttributeDefinition delegate) {
097: if (delegate == null) {
098: throw new IllegalArgumentException(
099: "invalid (null) delegate");
100: }
101:
102: this .delegate = delegate;
103: }
104:
105: /**
106: * If forceUppercase wasn't set on this instance, use the value from its delegate.
107: *
108: * @see org.kuali.core.datadictionary.AttributeDefinition#getForceUppercase()
109: */
110: public Boolean getForceUppercase() {
111: Boolean value = super .getForceUppercase();
112: if (value == null) {
113: value = getDelegate().getForceUppercase();
114: }
115:
116: return value;
117: }
118:
119: /**
120: * If name wasn't set on this instance, use the value from its delegate.
121: *
122: * @see org.kuali.core.datadictionary.AttributeDefinition#getName()
123: */
124: public String getName() {
125: String name = super .getName();
126: if (name == null) {
127: name = getDelegate().getName();
128: }
129:
130: return name;
131: }
132:
133: /**
134: * If label wasn't set on this instance, use the value from its delegate.
135: *
136: * @see org.kuali.core.datadictionary.AttributeDefinition#getLabel()
137: */
138: public String getLabel() {
139: String label = super .getLabel();
140:
141: if (label == null) {
142: label = getDelegate().getLabel();
143: }
144:
145: return label;
146: }
147:
148: /**
149: * If shortlabel wasn't set on this instance, use the value from its delegate.
150: *
151: * @see org.kuali.core.datadictionary.AttributeDefinition#getShortLabel()
152: */
153: public String getShortLabel() {
154: String shortLabel = super .getDirectShortLabel();
155: if (shortLabel == null) {
156: shortLabel = getDelegate().getShortLabel();
157: }
158:
159: return shortLabel;
160: }
161:
162: /**
163: * If maxLength wasn't set on this instance, use the value from its delegate.
164: *
165: * @see org.kuali.core.datadictionary.AttributeDefinition#getMaxLength()
166: */
167: public Integer getMaxLength() {
168: Integer maxLength = super .getMaxLength();
169: if (maxLength == null) {
170: maxLength = getDelegate().getMaxLength();
171: }
172:
173: return maxLength;
174: }
175:
176: /**
177: * @return true if a validationPattern is available, directly or indirectly
178: *
179: * @see org.kuali.core.datadictionary.AttributeDefinition#hasValidationPattern()
180: */
181: public boolean hasValidationPattern() {
182: return (getValidationPattern() != null);
183: }
184:
185: /**
186: * If validationPattern wasn't set on this instance, use the value from its delegate.
187: *
188: * @see org.kuali.core.datadictionary.AttributeDefinition#getValidationPattern()
189: */
190: public ValidationPattern getValidationPattern() {
191: ValidationPattern validationPattern = super
192: .getValidationPattern();
193: if (validationPattern == null) {
194: validationPattern = getDelegate().getValidationPattern();
195: }
196:
197: return validationPattern;
198: }
199:
200: /**
201: * If required wasn't set on this instance, use the value from its delegate.
202: *
203: * @see org.kuali.core.datadictionary.AttributeDefinition#isRequired()
204: */
205: public Boolean isRequired() {
206: Boolean required = super .isRequired();
207: if (required == null) {
208: required = getDelegate().isRequired();
209: }
210:
211: return required;
212: }
213:
214: /**
215: * If control wasn't set on this instance, use the value from its delegate.
216: *
217: * @see org.kuali.core.datadictionary.AttributeDefinition#getControl()
218: */
219: public ControlDefinition getControl() {
220: ControlDefinition control = super .getControl();
221: if (control == null) {
222: control = getDelegate().getControl();
223: }
224:
225: return control;
226: }
227:
228: /**
229: * If summary wasn't set on this instance, use the value from its delegate.
230: *
231: * @see org.kuali.core.datadictionary.AttributeDefinition#getSummary()
232: */
233: public String getSummary() {
234: String summary = super .getSummary();
235: if (summary == null) {
236: summary = getDelegate().getSummary();
237: }
238:
239: return summary;
240: }
241:
242: /**
243: * If description wasn't set on this instance, use the value from its delegate.
244: *
245: * @see org.kuali.core.datadictionary.AttributeDefinition#getDescription()
246: */
247: public String getDescription() {
248: String description = super .getDescription();
249: if (description == null) {
250: description = getDelegate().getDescription();
251: }
252:
253: return description;
254: }
255:
256: /**
257: * @return true if a formatterClass is available, directly or indirectly
258: *
259: * @see org.kuali.core.datadictionary.AttributeDefinition#hasFormatterClass()
260: */
261: public boolean hasFormatterClass() {
262: return (getFormatterClass() != null);
263: }
264:
265: /**
266: * If a formatterClass wasn't set for this instance, use the value from its delegate.
267: *
268: * @see org.kuali.core.datadictionary.AttributeDefinition#getFormatterClass()
269: */
270: public Class getFormatterClass() {
271: Class formatterClass = super .getFormatterClass();
272: if (formatterClass == null) {
273: formatterClass = getDelegate().getFormatterClass();
274: }
275:
276: return formatterClass;
277: }
278:
279: /**
280: * @see org.kuali.core.datadictionary.AttributeDefinition#getDisplayMask()
281: */
282: @Override
283: public Mask getDisplayMask() {
284: Mask displayMask = super .getDisplayMask();
285: if (displayMask == null) {
286: displayMask = getDelegate().getDisplayMask();
287: }
288: return displayMask;
289: }
290:
291: /**
292: * @see org.kuali.core.datadictionary.AttributeDefinition#getDisplayWorkgroup()
293: */
294: @Override
295: public String getDisplayWorkgroup() {
296: String displayWorkgroup = super .getDisplayWorkgroup();
297: if (StringUtils.isBlank(displayWorkgroup)) {
298: displayWorkgroup = getDelegate().getDisplayWorkgroup();
299: }
300: return displayWorkgroup;
301: }
302:
303: /**
304: * @see org.kuali.core.datadictionary.AttributeDefinition#hasDisplayMask()
305: */
306: @Override
307: public boolean hasDisplayMask() {
308: return (super .getDisplayMask() != null || getDelegate()
309: .getDisplayMask() != null);
310: }
311:
312: /**
313: * @see org.kuali.core.datadictionary.AttributeDefinition#getDisplayLabelAttribute()
314: */
315: @Override
316: public String getDisplayLabelAttribute() {
317: String displayLabelAttribute = super .getDisplayLabelAttribute();
318: if (StringUtils.isBlank(displayLabelAttribute)) {
319: displayLabelAttribute = getDelegate()
320: .getDisplayLabelAttribute();
321: }
322: return displayLabelAttribute;
323: }
324:
325: /**
326: * Validate the fields associated with locating the delegate. Other validation must be deferred until the delegate class has
327: * been assigned.
328: *
329: * @see org.kuali.core.datadictionary.DataDictionaryEntry#completeValidation()
330: */
331: public void completeValidation(Class rootObjectClass,
332: Class otherObjectClass,
333: ValidationCompletionUtils validationCompletionUtils) {
334: if (StringUtils.isBlank(sourceClassName)) {
335: throw new IllegalArgumentException(
336: "invalid (blank) sourceClassName for attribute '"
337: + rootObjectClass.getName() + "."
338: + getName() + "'");
339: }
340: if (StringUtils.isBlank(sourceAttributeName)) {
341: throw new IllegalArgumentException(
342: "invalid (blank) sourceAttributeName for attribute '"
343: + rootObjectClass.getName() + "."
344: + getName() + "'");
345: }
346:
347: // defer the rest of the validation until assignDelegate gets called, since you (may) need to DataDictionary
348: // to be completely constructed before you can verify the existence of sourceClass and sourceAttribute
349: }
350:
351: /**
352: * Use instance parameters to locate and assign an existing AttributeDefinition as delegate
353: *
354: * @param dataDictionary
355: * @throws CompletionException if unable to find class and attribute matching sourceClassName and sourceAttributeName
356: */
357: public void assignDelegate(
358: DataDictionaryEntryBase parentObjectEntry,
359: DataDictionary dataDictionary) {
360: String parentClassName = StringUtils.substringAfter(
361: parentObjectEntry.getEntryClass().getName(),
362: parentObjectEntry.getEntryClass().getPackage()
363: .getName()
364: + ".");
365: String msgPrefix = "error validating " + parentClassName + "."
366: + this .getName() + ": ";
367: String msgSuffix = " (" + getParseLocation() + ")";
368:
369: BusinessObjectEntry delegateEntry = dataDictionary
370: .getBusinessObjectEntry(getSourceClassName());
371: if (delegateEntry == null) {
372: throw new CompletionException(
373: msgPrefix
374: + "no BusinessObjectEntry exists for sourceClassName '"
375: + getSourceClassName() + "'" + msgSuffix);
376: }
377: AttributeDefinition delegateDefinition = delegateEntry
378: .getAttributeDefinition(getSourceAttributeName());
379: if (delegateDefinition == null) {
380: throw new CompletionException(
381: msgPrefix
382: + "no AttributeDefnintion exists for sourceAttributeName '"
383: + getSourceClassName() + "."
384: + getSourceAttributeName() + "'"
385: + msgSuffix);
386: // This error could be caused by AttributeReferenceDummy.xml.
387: }
388:
389: if (delegateDefinition instanceof AttributeReferenceDefinition) {
390: ((AttributeReferenceDefinition) delegateDefinition)
391: .assignDelegate(parentObjectEntry, dataDictionary);
392: }
393:
394: setDelegate(delegateDefinition);
395: }
396:
397: /**
398: * Complete deferred validation
399: *
400: * @param rootBusinessObjectClass
401: */
402: public void completeDeferredValidation(
403: Class rootBusinessObjectClass,
404: ValidationCompletionUtils validationCompletionUtils) {
405: // the above throws a CompletionException if delegate is null
406: // (ain't side-effects grand)
407: getDelegate();
408:
409: // name can't be delegated, so validate it directly
410: // (where "directly" means "by getting it from the superclass")
411: String name = super .getName();
412:
413: if (!validationCompletionUtils.isPropertyOf(
414: rootBusinessObjectClass, name)) {
415: throw new AttributeValidationException("property '" + name
416: + "' is not a property of class '"
417: + rootBusinessObjectClass.getName() + "' ("
418: + getParseLocation() + ")");
419: }
420:
421: boolean isDelegate = false;
422:
423: Class formatterClass = null;
424: if (super .hasFormatterClass()) {
425: isDelegate = false;
426: formatterClass = super .getFormatterClass();
427: } else if (hasFormatterClass()) {
428: isDelegate = true;
429: formatterClass = getFormatterClass();
430: }
431: if (formatterClass != null) {
432: if (!Formatter.class.isAssignableFrom(formatterClass)) {
433: String source = isDelegate ? "delegated" : "overridden";
434: throw new ClassValidationException(
435: source
436: + " formatterClass '"
437: + formatterClass.getName()
438: + "' for attribute '"
439: + name
440: + "' is not a subclass of pojo.format.Formatter ("
441: + getParseLocation() + ")");
442: }
443: }
444:
445: ControlDefinition controlDefinition = null;
446: if (super .getControl() != null) {
447: isDelegate = false;
448: controlDefinition = super .getControl();
449: } else if (getControl() != null) {
450: isDelegate = true;
451: controlDefinition = getControl();
452: }
453: if (controlDefinition != null) {
454: try {
455: controlDefinition.completeValidation(
456: rootBusinessObjectClass, null,
457: validationCompletionUtils);
458: } catch (DataDictionaryException e) {
459: String source = isDelegate ? "delegated" : "overridden";
460: throw buildReferenceValidationException(
461: "error validating " + source + " control", e);
462: }
463: }
464: }
465:
466: private ReferenceValidationException buildReferenceValidationException(
467: String newMessage, DataDictionaryException e) {
468: String caughtMessage = StringUtils.substringBeforeLast(e
469: .getMessage(), "(");
470: String referenceMessage = caughtMessage + "("
471: + getParseLocation() + ")";
472:
473: return new ReferenceValidationException(newMessage + ": "
474: + referenceMessage, e);
475: }
476:
477: /**
478: * @see java.lang.Object#toString()
479: */
480: public String toString() {
481: String name = super .getName();
482:
483: // workaround for the mysterious, still-unreproducible-on-my-machine, null delegate exception on Tomcat startup
484: if ((name == null) && (delegate != null)) {
485: name = getDelegate().getName();
486: }
487: return "AttributeReferenceDefinition for attribute " + name;
488: }
489: }
|