001: /*
002: *
003: * JMoney - A Personal Finance Manager
004: * Copyright (c) 2004 Nigel Westbury <westbury@users.sourceforge.net>
005: *
006: *
007: * This program is free software; you can redistribute it and/or modify
008: * it under the terms of the GNU General Public License as published by
009: * the Free Software Foundation; either version 2 of the License, or
010: * (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
020: *
021: */
022:
023: package net.sf.jmoney.model2;
024:
025: import java.util.ArrayList;
026: import java.util.Collection;
027: import java.util.HashMap;
028: import java.util.Map;
029: import java.util.Vector;
030:
031: import net.sf.jmoney.JMoneyPlugin;
032:
033: import org.eclipse.swt.graphics.Image;
034:
035: public class ExtendablePropertySet<E extends ExtendableObject> extends
036: PropertySet<E> {
037:
038: ExtendablePropertySet<? super E> basePropertySet;
039:
040: /**
041: * An interface that can be used to construct implementation objects,
042: * or null if this is an abstract property set.
043: */
044: IExtendableObjectConstructors<E> constructors;
045:
046: /**
047: * true if further property sets must be derived from this property set,
048: * false if property sets cannot be derived from this property set.
049: */
050: protected boolean derivable;
051:
052: /**
053: * Set of property sets that are derived from this property set
054: * (either directly or indirectly) and that are not
055: * themselves derivable.
056: */
057: private Map<Class<? extends E>, ExtendablePropertySet<? extends E>> derivedPropertySets = new HashMap<Class<? extends E>, ExtendablePropertySet<? extends E>>();
058:
059: private Collection<ExtendablePropertySet<? extends E>> directlyDerivedPropertySets = new Vector<ExtendablePropertySet<? extends E>>();
060:
061: Map<String, ExtensionPropertySet<?>> extensionPropertySets = null;
062:
063: /**
064: * Localized text describing the type of object represented
065: * by this property set. This property is never null.
066: */
067: protected String objectDescription;
068:
069: /**
070: * This field is valid for extendable property sets only
071: */
072: protected String iconFileName = null;
073:
074: /** cached value */
075: private Image iconImage = null;
076:
077: /**
078: * These arrays are built on first use and then cached.
079: */
080: private Vector<PropertyAccessor> properties2 = null;
081: private Vector<ScalarPropertyAccessor> scalarProperties2 = null;
082: private Vector<ListPropertyAccessor> listProperties2 = null;
083:
084: private Vector<PropertyAccessor> properties3 = null;
085: private Vector<ScalarPropertyAccessor> scalarProperties3 = null;
086: private Vector<ListPropertyAccessor> listProperties3 = null;
087:
088: /**
089: * This field is valid for non-derivable property sets only.
090: */
091: private Vector<PageEntry> pageExtensions = null;
092:
093: /**
094: * Constructs a property set object.
095: *
096: * @param classOfObject
097: * the class of the implementation object
098: * @param objectDescription
099: * a localized description of this object class, suitable for use
100: * in the UI
101: * @param basePropertySet
102: * the property set from which this property set is derived, or
103: * null if we are constructing a base property set
104: * @param constructors
105: * an interface containing methods for constructing
106: * implementation objects, or null if this is an abstract
107: * property set
108: */
109: protected ExtendablePropertySet(Class<E> classOfObject,
110: String objectDescription,
111: ExtendablePropertySet<? super E> basePropertySet,
112: IExtendableObjectConstructors<E> constructors) {
113: this .isExtension = false;
114: this .classOfObject = classOfObject;
115: this .basePropertySet = basePropertySet;
116: this .constructors = constructors;
117:
118: this .derivable = (constructors == null);
119: this .objectDescription = objectDescription;
120: this .iconFileName = null;
121:
122: extensionPropertySets = new HashMap<String, ExtensionPropertySet<?>>();
123: }
124:
125: @Override
126: public void initProperties(String propertySetId) {
127: super .initProperties(propertySetId);
128:
129: // Add to our map that maps ids to ExtendablePropertySet objects.
130: allExtendablePropertySetsMap.put(propertySetId, this );
131:
132: /*
133: * Add to the map that maps the extendable classes to the extendable
134: * property sets. Both final and derived property sets are put in this
135: * map.
136: */
137: if (classToPropertySetMap.containsKey(classOfObject)) {
138: throw new MalformedPluginException(
139: "More than one property set uses " + classOfObject
140: + " as the Java implementation class.");
141: }
142: classToPropertySetMap.put(classOfObject, this );
143:
144: if (basePropertySet != null && !basePropertySet.isDerivable()) {
145: throw new MalformedPluginException(
146: basePropertySet.getImplementationClass().getName()
147: + " is a base property for "
148: + propertySetId
149: + ". However, "
150: + basePropertySet.getImplementationClass()
151: .getName()
152: + " is not derivable (setDerivable() has not been called from the IPropertySetInfo implementation).");
153: }
154:
155: if (!derivable) {
156: pageExtensions = new Vector<PageEntry>();
157:
158: // Add this property set to the list of derived property sets
159: // for this and all the base classes.
160: for (ExtendablePropertySet<? super E> base = this ; base != null; base = base
161: .getBasePropertySet()) {
162: base.derivedPropertySets.put(classOfObject, this );
163: }
164: /*
165: // Build the list of properties that are passed to
166: // the 'new object' constructor and another list that
167: // are passed to the 're-instantiating' constructor.
168:
169: constructorProperties = new Vector<PropertyAccessor>();
170: defaultConstructorProperties = new Vector<PropertyAccessor>();
171: */
172: }
173:
174: if (basePropertySet != null) {
175: basePropertySet.directlyDerivedPropertySets.add(this );
176: }
177: /*
178: // We need to be able to iterate through property sets
179: // starting at the base property set and continuing through
180: // derived property sets until we get to this property set.
181: // To do this, we first build a list of the base property sets
182: // and we can then iterate through these property sets in
183: // reverse order.
184: Vector<ExtendablePropertySet> basePropertySets = new Vector<ExtendablePropertySet>();
185: for (ExtendablePropertySet base = getBasePropertySet(); base != null; base = base.getBasePropertySet()) {
186: basePropertySets.add(base);
187: }
188:
189: // Add the appropriate properties from the base classes to
190: // the constructorParameters and defaultConstructorParameters arrays.
191: int parameterIndex = 3;
192:
193: for (int i = basePropertySets.size()-1; i >= 0; i--) {
194: ExtendablePropertySet<?> base = basePropertySets.get(i);
195:
196: for (PropertyAccessor propertyAccessor: base.properties) {
197: if (!isDerivable()) {
198: constructorProperties.add(propertyAccessor);
199: if (propertyAccessor.isList()) {
200: defaultConstructorProperties.add(propertyAccessor);
201: }
202: }
203:
204: parameterIndex++;
205: }
206: }
207:
208: // Process the properties in this property set.
209: for (PropertyAccessor propertyAccessor: properties) {
210: propertyAccessor.setIndexIntoConstructorParameters(parameterIndex++);
211: if (!isDerivable()) {
212: constructorProperties.add(propertyAccessor);
213: if (propertyAccessor.isList()) {
214: defaultConstructorProperties.add(propertyAccessor);
215: }
216: }
217: }
218: */
219: /*
220: * Set the icon associated with this property set. The icon will already
221: * have been set in any property set for which an icon is specifically
222: * set. However, icons also apply to derived property sets for which no
223: * icon has been set. So, if the icon is null, go up the list of base
224: * property sets until we find a non-null icon.
225: *
226: * This must be done here and not in the constructor because the calling
227: * code must have a change to set an icon before this code is executed.
228: */
229: if (iconFileName == null) {
230: for (ExtendablePropertySet base = getBasePropertySet(); base != null; base = base
231: .getBasePropertySet()) {
232: if (base.getIconFileName() != null) {
233: iconFileName = base.getIconFileName();
234: break;
235: }
236: }
237: }
238: }
239:
240: public void initPropertiesPass2() {
241: int scalarIndex = 0;
242: for (ExtendablePropertySet base = getBasePropertySet(); base != null; base = base
243: .getBasePropertySet()) {
244: scalarIndex += base.getScalarProperties2().size();
245: }
246:
247: for (ScalarPropertyAccessor propertyAccessor : getScalarProperties2()) {
248: propertyAccessor
249: .setIndexIntoScalarProperties(scalarIndex++);
250: }
251:
252: }
253:
254: /**
255: * Gets the set of all property sets that are directly derived from this
256: * property set. This set does not include property sets that are derived
257: * from property sets that are in turn derived from this property set. This
258: * set includes both property sets that are derivable and property sets that
259: * are final.
260: *
261: * This method is useful when the caller needs to know the actual tree
262: * structure of the derived property sets or needs to know about the
263: * intermediate (non-final) property sets. Callers generally would call this
264: * method in a recursive manner.
265: *
266: * If a caller just needs a list of the final property sets,
267: * getDerivedPropertySets() is simpler to use.
268: */
269: public Collection<ExtendablePropertySet<? extends E>> getDirectlyDerivedPropertySets() {
270: return directlyDerivedPropertySets;
271: }
272:
273: /**
274: *
275: * @return the set of all property sets
276: * that are derived from this property set and
277: * that are themselves not derivable
278: */
279: public Collection<ExtendablePropertySet<? extends E>> getDerivedPropertySets() {
280: return derivedPropertySets.values();
281: }
282:
283: /**
284: * Given a class of an object, returns the property
285: * set for that object. The class passed to this method
286: * must be the class of an ExtendableObject that either is
287: * the implementation object for this property set or is
288: * extended from the implemetation class. The class must be
289: * a final class (i.e. a class of an actual object instance,
290: * not an abstract class).
291: *
292: * If this property set is a final property set then this
293: * method will always return this object.
294: *
295: * @return the final property set
296: */
297: public ExtendablePropertySet<? extends E> getActualPropertySet(
298: Class<? extends E> classOfObject) {
299: return derivedPropertySets.get(classOfObject);
300: }
301:
302: /**
303: *
304: * @return If this property set is derived from another property
305: * set then the base property set is returned, otherwise
306: * null is returned.
307: */
308: public ExtendablePropertySet<? super E> getBasePropertySet() {
309: if (isExtension) {
310: throw new RuntimeException(
311: "getBasePropertySet called for an extension.");
312: }
313:
314: return basePropertySet;
315: }
316:
317: /**
318: * @return localized text describing the type of object
319: * represented by this property set
320: */
321: public String getObjectDescription() {
322: return objectDescription;
323: }
324:
325: /**
326: * @return True if this property set can only be used by
327: * deriving another property set from it, false
328: * if property sets cannot be derived from this
329: * property set.
330: */
331: public boolean isDerivable() {
332: return derivable;
333: }
334:
335: /**
336: * Set an icon that is to be shown for objects of this class.
337: * If no icon is set then the icon for the base class will be
338: * used or, if there is no base class, no icon will be shown
339: * for objects of this class.
340: *
341: * @param iconFileName
342: */
343: public void setIcon(String iconFileName) {
344: this .iconFileName = iconFileName;
345: }
346:
347: /** used internally */
348: String getIconFileName() {
349: return iconFileName;
350: }
351:
352: /**
353: * This method creates the image on first call. It is very
354: * important that the image is not created when the this PropertySet
355: * object is initialized. The reason is that this PropertySet is
356: * initialized by a different thread than the UI thread. Images
357: * must be created by UI thread.
358: * <P>
359: * This method is valid for extendable property sets only.
360: *
361: * @return the icon associated with objects that implement
362: * this property set.
363: */
364: public Image getIcon() {
365: if (iconImage == null)
366: iconImage = JMoneyPlugin.createImage(iconFileName);
367: return iconImage;
368: }
369:
370: /**
371: * Gets the accessor for a property given the local name of the property.
372: *
373: * This method searches only this property set and any base
374: * property sets. No extensions are searched.
375: * <P>
376: * This method is used when a column name is persisted in, say, a file
377: * and we are keen to keep the data in the file as simple and short as
378: * possible. We therefore allow local names only to be specified.
379: * Local names may not be unique when extensions are included, so we
380: * must require fully qualified names for extensions.
381: *
382: * @param name The local property name
383: */
384: public PropertyAccessor getPropertyAccessorGivenLocalNameAndExcludingExtensions(
385: String localPropertyName) throws PropertyNotFoundException {
386: ExtendablePropertySet this PropertySet = this ;
387: do {
388: try {
389: return this PropertySet.getProperty(localPropertyName);
390: } catch (PropertyNotFoundException e) {
391: }
392: this PropertySet = this PropertySet.getBasePropertySet();
393: } while (this PropertySet != null);
394:
395: throw new PropertyNotFoundException(propertySetId,
396: localPropertyName);
397: }
398:
399: /**
400: * Returns the set of all properties of the given set of property sets,
401: * including both properties in the extendable object and properties in
402: * extension property sets.
403: * <P>
404: * Properties from base property sets and properties from derived property
405: * sets are not returned.
406: *
407: * @return a collection of <code>PropertyAccessor</code> objects
408: */
409: private Collection<PropertyAccessor> getProperties2() {
410: if (properties2 == null) {
411: properties2 = new Vector<PropertyAccessor>();
412:
413: // Properties in this extendable object
414: for (PropertyAccessor propertyAccessor : properties) {
415: properties2.add(propertyAccessor);
416: }
417:
418: // Properties in the extensions
419: for (PropertySet<?> extensionPropertySet : extensionPropertySets
420: .values()) {
421: for (PropertyAccessor propertyAccessor : extensionPropertySet.properties) {
422: properties2.add(propertyAccessor);
423: }
424: }
425: }
426:
427: return properties2;
428: }
429:
430: /**
431: * Returns the set of all scalar properties (i.e. list properties are
432: * excluded) of the given set of property sets, including both properties in
433: * the extendable object and properties in extension property sets.
434: * <P>
435: * Properties from base property sets and properties from derived property
436: * sets are not returned.
437: *
438: * @return a collection of <code>PropertyAccessor</code> objects
439: */
440: public Collection<ScalarPropertyAccessor> getScalarProperties2() {
441: if (scalarProperties2 == null) {
442: scalarProperties2 = new Vector<ScalarPropertyAccessor>();
443:
444: // Properties in this extendable object
445: for (ScalarPropertyAccessor propertyAccessor : getScalarProperties1()) {
446: scalarProperties2.add(propertyAccessor);
447: }
448:
449: // Properties in the extensions
450: for (PropertySet<?> extensionPropertySet : extensionPropertySets
451: .values()) {
452: for (ScalarPropertyAccessor propertyAccessor : extensionPropertySet
453: .getScalarProperties1()) {
454: scalarProperties2.add(propertyAccessor);
455: }
456: }
457: }
458:
459: return scalarProperties2;
460: }
461:
462: /**
463: * Returns the set of all list properties (i.e. scalar properties are
464: * excluded) of the given set of property sets, including both properties in
465: * the extendable object and properties in extension property sets.
466: * <P>
467: * Properties from base property sets and properties from derived property
468: * sets are not returned.
469: *
470: * @return a collection of <code>PropertyAccessor</code> objects
471: */
472: public Collection<ListPropertyAccessor> getListProperties2() {
473: if (listProperties2 == null) {
474: listProperties2 = new Vector<ListPropertyAccessor>();
475:
476: // Properties in this extendable object
477: for (ListPropertyAccessor propertyAccessor : getListProperties1()) {
478: listProperties2.add(propertyAccessor);
479: }
480:
481: // Properties in the extensions
482: for (PropertySet<?> extensionPropertySet : extensionPropertySets
483: .values()) {
484: for (ListPropertyAccessor propertyAccessor : extensionPropertySet
485: .getListProperties1()) {
486: listProperties2.add(propertyAccessor);
487: }
488: }
489: }
490:
491: return listProperties2;
492: }
493:
494: /**
495: * Returns the set of all properties of the given set of property sets,
496: * including properties in the extendable object, properties in extension
497: * property sets, and all properties in the base property sets including all
498: * extension properties to the base property sets.
499: * <P>
500: * This is the set of properties that can be set against an object that
501: * implements this property set.
502: * <P>
503: * Properties are returned with the properties from the base-most class
504: * first, then properties from the class immediately derived from the
505: * base-most class, and so on with the properties from this property set
506: * being last. This order gives the most intuitive order from the user's
507: * perspective. This order also ensures that a property in a base class has
508: * the same index in the returned order, regardless of the actual derived
509: * property set.
510: */
511: public Collection<PropertyAccessor> getProperties3() {
512: if (properties3 == null) {
513: properties3 = new Vector<PropertyAccessor>();
514:
515: // Properties in this and all the base property sets
516: ExtendablePropertySet<?> extendablePropertySet = this ;
517: do {
518: int index = 0;
519: for (PropertyAccessor propertyAccessor : extendablePropertySet
520: .getProperties2()) {
521: properties3.insertElementAt(propertyAccessor,
522: index++);
523: }
524: extendablePropertySet = extendablePropertySet
525: .getBasePropertySet();
526: } while (extendablePropertySet != null);
527: }
528:
529: return properties3;
530: }
531:
532: /**
533: * Returns the same set of properties as the <code>getProperties3</code>
534: * method but the returned collection includes only the scalar properties
535: * (i.e. list properties are excluded).
536: */
537: public Collection<ScalarPropertyAccessor> getScalarProperties3() {
538: if (scalarProperties3 == null) {
539: scalarProperties3 = new Vector<ScalarPropertyAccessor>();
540:
541: // Properties in this and all the base property sets
542: ExtendablePropertySet<?> extendablePropertySet = this ;
543: do {
544: int index = 0;
545: for (ScalarPropertyAccessor propertyAccessor : extendablePropertySet
546: .getScalarProperties2()) {
547: scalarProperties3.insertElementAt(propertyAccessor,
548: index++);
549: }
550: extendablePropertySet = extendablePropertySet
551: .getBasePropertySet();
552: } while (extendablePropertySet != null);
553: }
554:
555: return scalarProperties3;
556: }
557:
558: /**
559: * Returns the same set of properties as the <code>getProperties3</code>
560: * method but the returned collection includes only the list properties
561: * (i.e. scalar properties are excluded).
562: */
563: public Collection<ListPropertyAccessor> getListProperties3() {
564: if (listProperties3 == null) {
565: listProperties3 = new Vector<ListPropertyAccessor>();
566:
567: // Properties in this and all the base property sets
568: ExtendablePropertySet<?> extendablePropertySet = this ;
569: do {
570: int index = 0;
571: for (ListPropertyAccessor propertyAccessor : extendablePropertySet
572: .getListProperties2()) {
573: listProperties3.insertElementAt(propertyAccessor,
574: index++);
575: }
576: extendablePropertySet = extendablePropertySet
577: .getBasePropertySet();
578: } while (extendablePropertySet != null);
579: }
580:
581: return listProperties3;
582: }
583:
584: /**
585: * Gets a list of all property sets that extend the given property
586: * set. This method is used by the Propagator class only.
587: * <P>
588: * Note:
589: * This method does not return derived property sets.
590: * This method does not return property sets that extend any
591: * property sets from which this property set is derived.
592: *
593: * @return the extension property sets that extend this property
594: * set
595: */
596: public Collection<ExtensionPropertySet<?>> getDirectExtensionPropertySets() {
597: return extensionPropertySets.values();
598: }
599:
600: /**
601: * Gets a list of all property sets that extend the given property set.
602: * Property sets that extend any base property sets are also included.
603: *
604: * Note: This method does not return derived property sets.
605: *
606: * @return the extension property sets that extend this property set
607: */
608: public Collection<ExtensionPropertySet<?>> getExtensionPropertySets() {
609: Collection<ExtensionPropertySet<?>> result = new ArrayList<ExtensionPropertySet<?>>();
610:
611: ExtendablePropertySet<?> propertySet = this ;
612: do {
613: result.addAll(propertySet.extensionPropertySets.values());
614: propertySet = propertySet.getBasePropertySet();
615: } while (propertySet != null);
616:
617: return result;
618: }
619:
620: /**
621: * This method should be used only by plug-ins that implement
622: * a datastore.
623: *
624: * @param constructorParameters an array of values to be passed to
625: * the constructor. If an extendable object is being constructed
626: * then the first three elements of this array must be the
627: * object key, the extension map, and the parent object key.
628: * @return A newly constructed object, constructed from the given
629: * parameters. This object may be an ExtendableObject or
630: * may be an ExtensionObject.
631: */
632: public E constructImplementationObject(IObjectKey objectKey,
633: ListKey<? super E> parentKey, IValues values) {
634: return constructors.construct(objectKey, parentKey, values);
635: }
636:
637: /**
638: * This method should be used only by plug-ins that implement
639: * a datastore.
640: *
641: * @return A newly constructed object, constructed from the given
642: * parameters. This object may be an ExtendableObject or
643: * may be an ExtensionObject.
644: */
645: public E constructDefaultImplementationObject(IObjectKey objectKey,
646: ListKey<? super E> parentKey) {
647: return constructors.construct(objectKey, parentKey);
648: }
649:
650: /**
651: * Returns the set of tabbed pages that are to be shown in the
652: * editor associated with extendable objects of this property set.
653: * <P>
654: * This method is valid only for non-derivable extendable property sets.
655: *
656: * @return a set of objects of type PageEntry
657: */
658: public Vector<PageEntry> getPageFactories() {
659: return pageExtensions;
660: }
661:
662: /**
663: * @param pageEntry
664: */
665: public void addPage(PageEntry newPage) {
666: int addIndex = pageExtensions.size();
667: for (int i = 0; i < pageExtensions.size(); i++) {
668: PageEntry page = pageExtensions.get(i);
669: if (newPage.getPosition() < page.getPosition()) {
670: addIndex = i;
671: break;
672: }
673: }
674: pageExtensions.add(addIndex, newPage);
675: }
676:
677: /**
678: * Utility method to find a property among all properties supported
679: * by objects of this class.
680: *
681: * @param scalarPropertyId
682: * @return
683: */
684: public ScalarPropertyAccessor getScalarProperty(
685: String scalarPropertyId) {
686: for (ScalarPropertyAccessor property : getScalarProperties3()) {
687: if (property.getName().equals(scalarPropertyId)) {
688: return property;
689: }
690: }
691: return null;
692: }
693:
694: /**
695: * Utility method to find a property among all properties supported
696: * by objects of this class.
697: *
698: * @param listPropertyId
699: * @return
700: */
701: public ListPropertyAccessor getListProperty(String listPropertyId) {
702: for (ListPropertyAccessor property : getListProperties3()) {
703: if (property.getName().equals(listPropertyId)) {
704: return property;
705: }
706: }
707: return null;
708: }
709:
710: @Override
711: protected E getImplementationObject(
712: ExtendableObject extendableObject) {
713: return classOfObject.cast(extendableObject);
714: }
715: }
|