001: package com.xoetrope.data.pojo;
002:
003: import java.lang.reflect.InvocationTargetException;
004: import java.lang.reflect.Method;
005: import java.util.Collection;
006: import java.util.Observable;
007: import java.util.Vector;
008: import net.xoetrope.xui.data.XModel;
009:
010: /**
011: * Model supporting properties with arguments. The first child of this node
012: * is a model node representing finder arguments, if there are any.
013: * <p> Copyright (c) Xoetrope Ltd., 2001-2007, This software is licensed under
014: * the GNU Public License (GPL), please see license.txt for more details. If
015: * you make commercial use of this software you must purchase a commercial
016: * license from Xoetrope.</p>
017: */
018: public class XPojoModelEx extends XPojoModelVis {
019: public static final int GETTER_ATTRIBUTE = 0;
020: public static final int SETTER_ATTRIBUTE = 1;
021: public static final int TRANSIENT_ATTRIBUTE = 2;
022: public static final int NUM_FIXED_ATTRIBUTE = 3;
023:
024: protected boolean hasParameters;
025: protected XPojoMethodArgs pojoMethodArgs;
026: protected String propertyName;
027: protected boolean invocationOk;
028: protected boolean isTransient;
029:
030: /**
031: * Creates a new instance of <code>XPojoModelEx</code>
032: * @param ds the data source object
033: * @param p parent POJO
034: * @param fm finder method
035: * @param propName the name of the pojo property to be wrapped
036: * @param pm the parent model node
037: */
038: protected XPojoModelEx(XModel pn, Method gtr, Method str,
039: String propName, XPojoDataSourceEx ds) {
040: super (pn, gtr, str, ds);
041: isTransient = false;
042: propertyName = propName;
043: Class[] pt = pojoGetter.getParameterTypes();
044: hasParameters = ((pt != null) && (pt.length > 0));
045: invocationOk = true;
046: String caption = (propertyName + ": " + pojoGetter
047: .getReturnType().getSimpleName());
048: setCaption(caption);
049: pojoMethodArgs = new XPojoMethodArgs(this , pojoGetter,
050: dataSource);
051: obtainPropertyValue();
052: // set caption
053: caption = (pojo != null ? propertyName + ": "
054: + XPojoDataSourceEx.getDispValue(pojo) : propertyName
055: + ": " + pojoGetter.getReturnType().getSimpleName());
056: setCaption(caption);
057: }
058:
059: /**
060: * Creates a new instance of <code>XPojoModelEx</code>
061: * @param pn parent model node
062: * @param clazz the class of the underlying POJO
063: * @param propName the name of the POJO property to be wrapped
064: * @param ds the data source object that's been used to load this model node
065: */
066: protected XPojoModelEx(XModel pn, Class clazz, String propName,
067: XPojoDataSourceEx ds) {
068: super (pn, clazz, ds);
069: isTransient = true;
070: propertyName = propName;
071: adapter = (clazz != null ? dataSource.getAdapter(clazz) : null);
072: hasParameters = false;
073: setCaption(propertyName + ": " + pojoClass.getSimpleName());
074: }
075:
076: /**
077: * Creates a new instance of <code>XPojoModelEx</code>
078: * @param ds data source object
079: * @param p POJO to be wrapped by this object
080: * @param pn parent model node
081: */
082: protected XPojoModelEx(XModel pn, Object p, XPojoDataSourceEx ds) {
083: super (pn, p, ds);
084: isTransient = false;
085: propertyName = null;
086: adapter = (pojo != null ? dataSource
087: .getAdapter(pojo.getClass()) : null);
088: hasParameters = false;
089: idAttrib = "";
090: valueAttrib = "";
091: invocationOk = true;
092: }
093:
094: public Class getPojoClass() {
095: Class pojoClass = super .getPojoClass();
096: return (isTransient && (pojoClass == null) ? String.class
097: : pojoClass);
098: }
099:
100: /**
101: * Indicates whether the "transient" property of
102: * this mode is set.
103: */
104: public boolean isTransient() {
105: return isTransient;
106: }
107:
108: /**
109: * Sets the "transient" property of this model node.
110: * @param state new state of the "transient" property
111: */
112: public void setTransient(boolean state) {
113: isTransient = state;
114: }
115:
116: /**
117: * Obtains the underlying property value
118: */
119: protected void obtainPropertyValue() {
120: if (pojoGetter == null)
121: return;
122: if ((pojo == null) && parametersSet()) {
123: try {
124: Object[] parameterValues = getParameterValues();
125: if (invocationOk) {
126:
127: Object parentPojo = null;
128: XModel parent = getParent();
129: if (parent instanceof XPojoModelEx)
130: parentPojo = ((XPojoModelEx) parent).getPojo();
131:
132: if (parentPojo != null) {
133: setPojo(pojoGetter.invoke(parentPojo,
134: parameterValues));
135: adapter = (pojo != null ? dataSource
136: .getAdapter(pojo.getClass()) : null);
137: } else {
138: adapter = dataSource.getAdapter(pojoGetter
139: .getReturnType());
140: }
141:
142: }
143: } catch (RuntimeException ex) {
144: invocationOk = false;
145: if (pojoMethodArgs != null)
146: pojoMethodArgs.removeAllArgumentValues();
147: String message = "cannot obtain property \""
148: + propertyName + "\": ";
149: String msg = (ex.getCause() != null ? ex.getCause()
150: .getMessage() : ex.getMessage());
151: if ((msg == null) || "".equals(msg))
152: msg = ex.getMessage();
153: message += msg;
154: invocationErrorNotifier.notifyObservers(message);
155:
156: } catch (InvocationTargetException ex) {
157: invocationOk = false;
158: if (pojoMethodArgs != null)
159: pojoMethodArgs.removeAllArgumentValues();
160: String message = "cannot obtain property \""
161: + propertyName + "\": ";
162: String msg = ex.getCause().getMessage();
163: if ((msg == null) || "".equals(msg))
164: msg = ex.getMessage();
165: message += msg;
166: invocationErrorNotifier.notifyObservers(message);
167: } catch (Exception ex) {
168: invocationOk = false;
169: if (pojoMethodArgs != null)
170: pojoMethodArgs.removeAllArgumentValues();
171: String message = "cannot obtain property \""
172: + propertyName + "\": ";
173: message += ex.getMessage();
174: invocationErrorNotifier.notifyObservers(message);
175: }
176: }
177: }
178:
179: protected void notifyObservers(Exception ex) {
180: invocationOk = false;
181: String message = "cannot obtain property: \"" + propertyName
182: + "\": ";
183: String msg = ex.getCause().getMessage();
184: if ((msg == null) || "".equals(msg))
185: msg = ex.getMessage();
186: message += msg;
187: invocationErrorNotifier.notifyObservers(message);
188: }
189:
190: /**
191: * Gets the XModel object located at the <code>i</code>
192: * position.
193: * @param i position of the node
194: * @return XModel object
195: */
196: public XModel get(int i) {
197: XPojoModelVis model = null;
198: obtainPropertyValue();
199:
200: int argInd = (hasParameters ? 0 : -1);
201: if (i == argInd) {
202: // Method parameters
203: model = pojoMethodArgs;
204: } else if ((pojo == null) && (pojoClass == null)) {
205: // The last child
206: model = null;
207: } else if ((pojo instanceof Collection) && (i == argInd + 1)) {
208: // Iterator
209: model = new XPojoIteratorEx(dataSource, (Collection) pojo,
210: this );
211: } else if (pojo instanceof Collection) {
212: // Collection element
213: i -= (argInd + 2);
214: model = pojoCollection[i];
215: String caption = model.getCaption();
216: model.setCaption("" + i + ": " + caption);
217: } else {
218: // Scalar
219: XPojoAdapterEx pojoAdapter = (XPojoAdapterEx) adapter;
220: i -= (argInd + 1);
221:
222: XPojoProperties properties = adapter.getProperties();
223: String propertyName = properties.getPropertyName(i);
224: Object propertyValue = properties.getProperty(i);
225: Method propertySetter = properties.getPropertySetter(i);
226:
227: if (propertyValue instanceof Method)
228: model = dataSource.adaptPojo((Method) propertyValue,
229: propertySetter, propertyName, this );
230: else if (propertyValue instanceof Class)
231: model = dataSource.adaptPojo((Class) propertyValue,
232: propertyName, this );
233: }
234:
235: return model;
236: }
237:
238: /**
239: * Gets the value located at the specified path
240: * @param element the path
241: * @return the value of the XPojoModel
242: */
243: public Object get(String element) {
244: if ((element == null) || (element.length() == 0))
245: return null;
246: if (element.charAt(0) == '/')
247: element = element.substring(1);
248:
249: XModel model = null;
250: int pos = element.indexOf('/');
251: String subPath = (pos > -1 ? element.substring(0, pos)
252: : element);
253: obtainPropertyValue();
254: if ("arguments".equals(subPath)) {
255: model = pojoMethodArgs;
256: } else if ("iterator".equals(subPath)) {
257: model = new XPojoIteratorEx(dataSource, (Collection) pojo,
258: this );
259: } else if ((pojo != null) && (pojo instanceof Collection)) {
260: // collection
261: try {
262: int idx = Integer.parseInt(subPath);
263: Object childObject = ((Collection) pojo).toArray()[idx]; //@todo performance issue (.toArray())
264: if (childObject != null) {
265: model = ((XPojoDataSourceEx) dataSource).adaptPojo(
266: childObject, this );
267: String caption = ((XPojoModelVis) model)
268: .getCaption();
269: ((XPojoModelVis) model).setCaption("" + idx + ": "
270: + caption);
271: }
272: } catch (NumberFormatException ex) {
273: ex.printStackTrace();
274: }
275: } else if (adapter != null) {
276: model = adapter.getProperty(subPath, this );
277: }
278:
279: if ((pos >= 0) && (model != null))
280: model = (XModel) model.get(element.substring(pos + 1));
281: return model;
282: }
283:
284: /**
285: * Gets the number of children of this model node
286: * @return the number of children
287: */
288: public int getNumChildren() {
289: int numChildren = 0;
290: if (pojoGetter != null)
291: obtainPropertyValue();
292:
293: // one extra child as the parameter node
294: if (hasParameters)
295: numChildren++;
296:
297: if (pojo instanceof Collection) {
298: // one extra child as the iterator
299: numChildren += (1 + ((Collection) pojo).size());
300: } else if ((adapter != null) && !pojoClass.isPrimitive()) {
301: numChildren += adapter.getNumChildren();
302: }
303:
304: return numChildren;
305: }
306:
307: public boolean hasParameters() {
308: return hasParameters;
309: }
310:
311: /**
312: * Indicates whether all finder arguments values were set.
313: * @return true if arguments values were set or the finder
314: * method is argumentless, false otherwise.
315: */
316: public boolean parametersSet() {
317: if (!hasParameters)
318: return true;
319: if (pojoMethodArgs == null)
320: return false;
321:
322: int numArgs = pojoMethodArgs.getNumChildren();
323: boolean parametersOk = true;
324: for (int i = 0; parametersOk && (i < numArgs); i++) {
325: XPojoMethodArg finderArg = (XPojoMethodArg) pojoMethodArgs
326: .get(i);
327: parametersOk = finderArg.hasValue();
328: }
329: return parametersOk;
330: }
331:
332: /**
333: * Retrieves the method parameter values
334: * @return Table containing values of the parameters
335: */
336: protected Object[] getParameterValues() {
337: // argumentless finder
338: if (pojoMethodArgs == null)
339: return (new Object[0]);
340:
341: int numArgs = pojoMethodArgs.getNumChildren();
342: Object[] values = new Object[numArgs];
343:
344: for (int i = 0; i < numArgs; i++) {
345: XPojoMethodArg methodArg = (XPojoMethodArg) pojoMethodArgs
346: .get(i);
347: values[i] = methodArg.getValue();
348: }
349: return values;
350: }
351:
352: public String getPropertyName() {
353: return propertyName;
354: }
355:
356: /**
357: * Gets the method parameter types.
358: * @return Table containing types of the parameters
359: */
360: public Class[] getParameterTypes() {
361: return (pojoMethodArgs != null ? pojoMethodArgs
362: .getParameterTypes() : new Class[0]);
363: }
364:
365: /**
366: * Gets the list of this collection elemement's properties.
367: * @return Vector containing binding attributes that this model
368: * node can provide
369: */
370: public Vector getBindingAttributes() {
371: Vector bindingAttributes = new Vector();
372: // get the underlying POJO
373: obtainPropertyValue();
374:
375: // if the POJO is a Collection get the adapter for its elements
376: Class elementType = null;
377: if ((pojo instanceof Collection)
378: && (((Collection) pojo).size() > 0)) {
379: elementType = ((Collection) pojo).iterator().next()
380: .getClass();
381: } else if (parentModel instanceof XPojoModelEx) {
382: //@todo check whether the underlying POJO is a collection
383: elementType = ((XPojoModelEx) parentModel).getAdapter()
384: .getProperties().getCollectionType(propertyName);
385: }
386:
387: // get the adapter of the collection element
388: XPojoAdapterEx adapter = dataSource.getAdapter(elementType);
389: // retrieve the binding attributes
390: if (adapter != null) {
391: XPojoProperties properties = adapter.getProperties();
392: int numProperties = properties.getNumProperties();
393: for (int i = 0; i < numProperties; i++)
394: bindingAttributes.add(properties.getPropertyName(i));
395: }
396:
397: return bindingAttributes;
398: }
399:
400: /**
401: * Indicates whether this model node is wrapping collection's
402: * element
403: * @return true if this node wraps collection's element,
404: * false otherwise
405: */
406: public boolean isCollectionElement() {
407: XModel parent = getParent();
408: if (!(parent instanceof XPojoModelEx))
409: return false;
410: return ((XPojoModelEx) parent).isCollection();
411: }
412:
413: /**
414: * Indicates whether this model node wraps a
415: * collection.
416: */
417: public boolean isCollection() {
418: return (pojo instanceof Collection);
419: }
420:
421: /**
422: * Gets pojo wrapped by this model node
423: * @return pojo
424: */
425: public Object getPojo() {
426: return pojo;
427: }
428:
429: /**
430: * Gets the runtime binding path of this model node
431: * @return the runtime binding path
432: */
433: public String getBindingPath() {
434: String path = "";
435: if (isCollectionElement()) {
436: XPojoModelEx parent = (XPojoModelEx) getParent();
437: path = parent.getBindingPath();
438: path += ("[" + getId() + "]");
439: } else {
440: XModel parentNode = getParent();
441: if ((parentNode != null)
442: && (parentNode instanceof XPojoModelVis))
443: path = (((XPojoModelVis) parentNode).getBindingPath() + "/");
444: path += getId();
445:
446: if (hasParameters && parametersSet()) {
447: path = null;//XPojoHelper.getPropertyName( path );
448: path += "(";
449: Object[] values = getParameterValues();
450: Class[] types = getParameterTypes();
451: for (int i = 0; i < values.length; i++) {
452: String type = types[i].getSimpleName();
453: String value = (values[i] != null ? values[i]
454: .toString() : "null");
455: path += (type + ":" + value + ",");
456: }
457: path = path.substring(0, path.length() - 1);
458: path += ")";
459: } else if (hasParameters) {
460: path = null;
461: }
462: }
463: return path;
464: }
465:
466: public int getNumAttributes() {
467: return (super .getNumAttributes() + NUM_FIXED_ATTRIBUTE);
468: }
469:
470: public String getAttribName(int i) {
471: int na = super .getNumAttributes();
472: if (i == (na + GETTER_ATTRIBUTE))
473: return "getter";
474: else if (i == (na + SETTER_ATTRIBUTE))
475: return "setter";
476: else if (i == (na + TRANSIENT_ATTRIBUTE))
477: return "transient";
478: else
479: return super .getAttribName(i);
480: }
481:
482: public Object getAttribValue(int i) {
483: Object av = null;
484: int na = super .getNumAttributes();
485: if (i == (na + GETTER_ATTRIBUTE)) {
486: if (pojoGetter != null)
487: av = pojoGetter.getName()
488: + XPojoAdapterEx.getSignature(pojoGetter);
489: else
490: av = "none";
491: } else if (i == (na + SETTER_ATTRIBUTE)) {
492: if (pojoSetter != null)
493: av = pojoSetter.getName()
494: + XPojoAdapterEx.getSignature(pojoSetter);
495: else
496: av = "none";
497: } else if (i == (na + TRANSIENT_ATTRIBUTE)) {
498: av = (isTransient() ? "true" : "false");
499: } else {
500: av = super .getAttribValue(i);
501: }
502: return av;
503: }
504:
505: public void setAttribValue(int i, Object value) {
506: int na = super .getNumAttributes();
507: if (i == na + GETTER_ATTRIBUTE) {
508: if (parentModel instanceof XPojoModelVis) {
509: XPojoAdapterEx adapter = ((XPojoModelVis) parentModel)
510: .getAdapter();
511: Method method = adapter.getMethod((String) value);
512: adapter.getProperties().setPropertyGetter(propertyName,
513: method);
514: pojoGetter = method;
515: ((XPojoModelVis) parentModel).setDirty(true);
516: }
517: } else if (i == na + SETTER_ATTRIBUTE) {
518: if (parentModel instanceof XPojoModelVis) {
519: XPojoAdapterEx adapter = ((XPojoModelVis) parentModel)
520: .getAdapter();
521: if ("none".equals(value)) {
522: adapter.getProperties().setPropertySetter(
523: propertyName, null);
524: pojoSetter = null;
525: } else {
526: Method method = adapter.getMethod((String) value);
527: adapter.getProperties().setPropertySetter(
528: propertyName, method);
529: pojoSetter = method;
530: ((XPojoModelVis) parentModel).setDirty(true);
531: }
532: }
533: }
534: }
535:
536: /**
537: * Returns the value of the attribute converted to a String
538: * at the specified index
539: * @param i the index of the attribute
540: * @return String value of the attribute
541: */
542: public String getAttribValueAsString(int i) {
543: return (String) getAttribValue(i);
544: }
545:
546: /**
547: * Gets the id of this node
548: * @return the id
549: */
550:
551: public String getId() {
552: return (propertyName != null ? propertyName : super .getId());
553: }
554:
555: // Method invocation error notifier
556: protected static Observable invocationErrorNotifier = (new Observable() {
557: public void notifyObservers(Object obj) {
558: setChanged();
559: super .notifyObservers(obj);
560: }
561: });
562:
563: /**
564: * Gets a method invocation error notifier
565: * @return the notifier
566: */
567: public static Observable getInvocationErrorNotifier() {
568: return invocationErrorNotifier;
569: }
570:
571: public boolean getAttribRuntime(int i) {
572: int na = super .getNumAttributes();
573: if (i == na + GETTER_ATTRIBUTE)
574: return false;
575: else if (i == na + SETTER_ATTRIBUTE)
576: return false;
577: else if (i == na + TRANSIENT_ATTRIBUTE)
578: return false;
579: else
580: return super .getAttribRuntime(i);
581: }
582:
583: public boolean isAttribEditable(int i) {
584: int na = super .getNumAttributes();
585: if (i == na + GETTER_ATTRIBUTE) {
586: return (pojoGetter != null);
587: } else if (i == na + SETTER_ATTRIBUTE) {
588: return (pojoGetter != null);
589: } else if (i == na + TRANSIENT_ATTRIBUTE) {
590: return false;
591: } else {
592: return super .isAttribEditable(i);
593: }
594: }
595:
596: public int getAttribType(int i) {
597: int na = super .getNumAttributes();
598: if (i == na + GETTER_ATTRIBUTE)
599: return ATTRIB_COMBO;
600: else if (i == na + SETTER_ATTRIBUTE)
601: return ATTRIB_COMBO;
602: else if (i == na + TRANSIENT_ATTRIBUTE)
603: return ATTRIB_NEDITABLE;
604: else
605: return super .getAttribType(i);
606: }
607:
608: public String[] getAttribAvailableValues(int i) {
609: int na = super .getNumAttributes();
610: if (i == na + GETTER_ATTRIBUTE) {
611: String[] gbs = null;
612: if (parentModel instanceof XPojoModelVis) {
613: XPojoAdapterEx adapter = ((XPojoModelVis) parentModel)
614: .getAdapter();
615: gbs = adapter.getGettersBySig(getterSig);
616: }
617: return gbs;
618: } else if (i == na + SETTER_ATTRIBUTE) {
619: String[] sbs = null;
620: if (parentModel instanceof XPojoModelVis) {
621: XPojoAdapterEx adapter = ((XPojoModelVis) parentModel)
622: .getAdapter();
623: sbs = adapter.getSetters();
624: }
625: return sbs;
626: } else if (i == na + TRANSIENT_ATTRIBUTE) {
627: return null;
628: } else {
629: return super.getAttribAvailableValues(i);
630: }
631:
632: }
633:
634: }
|