001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026:
027: package org.cougaar.planning.ldm;
028:
029: import java.lang.reflect.Field;
030: import java.lang.reflect.Method;
031: import java.util.HashMap;
032: import java.util.HashSet;
033: import java.util.Map;
034: import java.util.Set;
035:
036: import org.cougaar.core.domain.Factory;
037: import org.cougaar.core.domain.FactoryException;
038: import org.cougaar.planning.ldm.asset.Asset;
039: import org.cougaar.planning.ldm.asset.AssetFactory;
040: import org.cougaar.planning.ldm.asset.EssentialAssetFactory;
041: import org.cougaar.planning.ldm.asset.NewTypeIdentificationPG;
042: import org.cougaar.planning.ldm.asset.PropertyGroup;
043: import org.cougaar.planning.ldm.asset.PropertyGroupFactory;
044: import org.cougaar.planning.ldm.plan.ClusterObjectFactory;
045: import org.cougaar.planning.ldm.plan.ClusterObjectFactoryImpl;
046:
047: /**
048: * Factory methods for all LDM objects.
049: **/
050: class PlanningFactoryImpl extends ClusterObjectFactoryImpl implements
051: Factory, PlanningFactory, ClusterObjectFactory {
052:
053: /** map of propertyclassname to factorymethod **/
054: private final Map propertyNames = new HashMap(89);
055:
056: /** map of assetname -> assetClass **/
057: private final Map assetClasses = new HashMap(89);
058:
059: /** set of property(group) factory classes **/
060: private final Set propertyFactories = new HashSet(11);
061:
062: /** set of assetFactories **/
063: private final Set assetFactories = new HashSet(11);
064:
065: /**
066: * Constructor. Create a new instance of the Factory.
067: * @param ldm LDM object so that Factory can provide convenience aliases to
068: * prototype cache, etc.
069: **/
070: public PlanningFactoryImpl(LDMServesPlugin ldm) {
071: super (ldm, ldm.getMessageAddress());
072:
073: // add the asset factories
074: addAssetFactory(new AssetFactory());
075: addPropertyGroupFactory(new PropertyGroupFactory());
076: }
077:
078: /** register a propertyfactory with us so that short (no package!)
079: * property group names may be used in createPropertyGroup(String).
080: * Either a PropertyGroupFactory class or an instance of such may be passed in.
081: **/
082: public final void addPropertyGroupFactory(Object pf) {
083: try {
084: Class pfc;
085: if (pf instanceof Class) {
086: pfc = (Class) pf;
087: } else {
088: pfc = pf.getClass();
089: }
090:
091: synchronized (propertyFactories) {
092: if (propertyFactories.contains(pfc)) {
093: return;
094: } else {
095: propertyFactories.add(pfc);
096: }
097: }
098:
099: Field f = pfc.getField("properties");
100: String[][] properties = (String[][]) f.get(pf);
101: int l = properties.length;
102: for (int i = 0; i < l; i++) {
103: String fullname = properties[i][0];
104: //Class pc = Class.forName(fullname);
105: loadClass(fullname);
106: String name = trimPackage(fullname);
107:
108: /*
109: * Don't support explicitly creating PropertyGroupSchedules through createPropertyGroup.
110: * Can create TimePhasedPropertyGroup through createPropertyGroup.
111: *
112: * Schedules created implicitly by creating a TimePhasedPropertyGroup and then adding to
113: * the Asset. Schedules created explicitly by using PropertyGroupFactory methods.
114: */
115: if (!name.equals("PropertyGroupSchedule")) {
116:
117: Method fm = pfc.getMethod(properties[i][1], null);
118:
119: Object old = propertyNames.put(name.intern(), fm);
120: if (old != null) {
121: System.err
122: .println("Warning: PropertyGroupFactory "
123: + pf
124: + " overlaps with another propertyFactory at "
125: + name);
126: }
127: propertyNames.put(fullname.intern(), fm);
128: }
129: }
130: } catch (Exception e) {
131: System.err
132: .println("addPropertyGroupFactory of non-PropertyGroupFactory:");
133: e.printStackTrace();
134: }
135: }
136:
137: /** @return true iff the factory parameter is already registered as a
138: * propertygroup factory.
139: **/
140: public final boolean containsPropertyGroupFactory(Object pf) {
141: Class pfc;
142: if (pf instanceof Class) {
143: pfc = (Class) pf;
144: } else {
145: pfc = pf.getClass();
146: }
147: synchronized (propertyFactories) {
148: return propertyFactories.contains(pfc);
149: }
150: }
151:
152: private final Method findPropertyGroupFactoryMethod(String name) {
153: return (Method) propertyNames.get(name);
154: }
155:
156: /** register an assetfactory with us so that we can
157: * (1) find an asset class from an asset name and (2)
158: * can figure out which factory to use for a given
159: * asset class.
160: **/
161: public final void addAssetFactory(EssentialAssetFactory af) {
162: try {
163: // check for redundant add
164: synchronized (assetFactories) {
165: if (assetFactories.contains(af)) {
166: return;
167: } else {
168: assetFactories.add(af);
169: }
170: }
171:
172: Class afc = af.getClass();
173: Field f = afc.getField("assets");
174: String[] assets = (String[]) f.get(af);
175: int l = assets.length;
176: for (int i = 0; i < l; i++) {
177: String fullname = assets[i];
178: Class ac = loadClass(fullname);
179: String name = trimPackage(fullname);
180: Object old = assetClasses.put(name.intern(), ac);
181: if (old != null) {
182: System.err
183: .println("Warning: AssetFactory "
184: + af
185: + " overlaps with another PropertyGroupFactory at "
186: + name);
187: }
188: assetClasses.put(fullname.intern(), ac);
189: }
190: } catch (Exception e) {
191: System.err
192: .println("addAssetFactory of non-functional AssetFactory "
193: + af);
194: e.printStackTrace();
195: }
196: }
197:
198: public final boolean containsAssetFactory(Object f) {
199: return assetFactories.contains(f);
200: }
201:
202: private String trimPackage(String classname) {
203: int i = classname.lastIndexOf(".");
204: if (i < 0)
205: return classname;
206: else
207: return classname.substring(i + 1);
208: }
209:
210: private Class findAssetClass(String name) {
211: return (Class) assetClasses.get(name);
212: }
213:
214: /** Find a prototype Asset based on it's typeid description,
215: * (e.g. "NSN/1234567890123") either by looking up an existing
216: * object or by creating one of the appropriate type.
217: *
218: * Shorthand for LDMServesPlugin.getPrototype(aTypeName);
219: **/
220: public final Asset getPrototype(String aTypeName) {
221: return ldm.getPrototype(aTypeName);
222: }
223:
224: /** Create a raw Asset instance for use by LDM Plugins
225: * which are PrototypeProviders.
226: * The asset created will have *no* propertygroups.
227: * This *always* creates a prototype of the specific class.
228: * most plugins want to call getPrototype(String typeid);
229: *
230: * @param classname One of the defined LDM class names. This must
231: * be the actual class name without the package path. For example,
232: * "Container" is correct, "org.cougaar.planning.ldm.asset.Container" is not.
233: **/
234: public final Asset createAsset(String classname) {
235: if (classname == null)
236: throw new IllegalArgumentException(
237: "Classname must be non-null");
238: try {
239: Class ac = findAssetClass(classname);
240: if (ac == null) {
241: throw new IllegalArgumentException(
242: "createAsset(String): \""
243: + classname
244: + "\" is not a known Asset class. This may be due to a misloaded LDM domain.");
245: }
246: Asset asset = createAsset(ac);
247: return asset;
248: } catch (Exception e) {
249: throw new FactoryException("Could not createAsset("
250: + classname + "): " + e, e);
251: }
252: }
253:
254: /** Create a raw Asset instance for use by LDM Plugins
255: * which are PrototypeProviders.
256: * The asset created will have *no* propertygroups.
257: * This *always* creates a prototype of the specific class.
258: * most plugins want to call getPrototype(String typeid);
259: *
260: * @param assetClass an LDM Asset class.
261: **/
262: public final Asset createAsset(Class assetClass) {
263: if (assetClass == null)
264: throw new IllegalArgumentException(
265: "assetClass must be non-null");
266:
267: try {
268: Asset asset = (Asset) assetClass.newInstance();
269: asset.registerWithLDM(ldm);
270: return asset;
271: } catch (Exception e) {
272: throw new FactoryException("Could not createAsset("
273: + assetClass + "): " + e, e);
274: }
275: }
276:
277: /** convenience routine for creating prototype assets.
278: * does a createAsset followed by setting the TypeIdentification
279: * to the specified string.
280: **/
281: public final Asset createPrototype(String classname, String typeid) {
282: if (classname == null)
283: throw new IllegalArgumentException(
284: "classname must be non-null");
285: //if (typeid == null) throw new IllegalArgumentException("typeid must be non-null");
286:
287: Asset proto = createAsset(classname);
288: NewTypeIdentificationPG tip = (NewTypeIdentificationPG) proto
289: .getTypeIdentificationPG();
290: tip.setTypeIdentification(typeid);
291: return proto;
292: }
293:
294: /** convenience routine for creating prototype assets.
295: * does a createAsset followed by setting the TypeIdentification
296: * to the specified string.
297: **/
298: public final Asset createPrototype(Class assetclass, String typeid) {
299: if (assetclass == null)
300: throw new IllegalArgumentException(
301: "assetclass must be non-null");
302: //if (typeid == null) throw new IllegalArgumentException("typeid must be non-null");
303:
304: Asset proto = createAsset(assetclass);
305: NewTypeIdentificationPG tip = (NewTypeIdentificationPG) proto
306: .getTypeIdentificationPG();
307: tip.setTypeIdentification(typeid);
308: return proto;
309: }
310:
311: /** convenience routine for creating prototype assets.
312: * does a createAsset followed by setting the TypeIdentification
313: * and the nomenclature to the specified string.
314: **/
315: public final Asset createPrototype(String classname, String typeid,
316: String nomen) {
317: if (classname == null)
318: throw new IllegalArgumentException(
319: "classname must be non-null");
320: //if (typeid == null) throw new IllegalArgumentException("typeid must be non-null");
321:
322: Asset proto = createAsset(classname);
323: NewTypeIdentificationPG tip = (NewTypeIdentificationPG) proto
324: .getTypeIdentificationPG();
325: tip.setTypeIdentification(typeid);
326: tip.setNomenclature(nomen);
327: return proto;
328: }
329:
330: /** Create an instance of a prototypical asset.
331: * This variation does <em>not</em> add an ItemIdentificationCode
332: * to the constructed asset instance. Without itemIDs,
333: * multiple instances of a prototype will test as .equals(), and
334: * can be confusing if they're added to the logplan.
335: * Most users will find #createInstance(Asset, String) more convenient.
336: **/
337: public final Asset createInstance(Asset prototypeAsset) {
338: Asset asset = prototypeAsset.createInstance();
339: asset.registerWithLDM(ldm);
340: return asset;
341: }
342:
343: /** Create an instance of a prototypical asset.
344: * This variation does <em>not</em> add an ItemIdentificationCode
345: * to the constructed asset instance. Without itemIDs,
346: * multiple instances of a prototype will test as .equals(), and
347: * can be confusing if they're added to the logplan.
348: * Most users will find #createInstance(String, String) more convenient.
349: **/
350: public final Asset createInstance(String prototypeAssetTypeId) {
351: if (prototypeAssetTypeId == null)
352: throw new IllegalArgumentException(
353: "prototypeAssetTypeId must be non-null");
354:
355: Asset proto = ldm.getPrototype(prototypeAssetTypeId);
356: if (proto == null)
357: throw new FactoryException(
358: "Could not find a prototype with TypeId = "
359: + prototypeAssetTypeId);
360: Asset asset = proto.createInstance();
361: asset.registerWithLDM(ldm);
362: return asset;
363: }
364:
365: /** Create an instance of a prototypical asset, specifying an initial
366: * UniqueID for its itemIdentificationPG .
367: **/
368: public final Asset createInstance(Asset prototypeAsset,
369: String uniqueId) {
370: Asset asset = prototypeAsset.createInstance(uniqueId);
371: asset.registerWithLDM(ldm);
372: return asset;
373: }
374:
375: /** Create an instance of a prototypical asset, specifying an initial UniqueID
376: * for its itemIdentificationPG.
377: **/
378: public final Asset createInstance(String prototypeAssetTypeId,
379: String uniqueId) {
380: if (prototypeAssetTypeId == null)
381: throw new IllegalArgumentException(
382: "prototypeAssetTypeId must be non-null");
383:
384: Asset proto = ldm.getPrototype(prototypeAssetTypeId);
385: if (proto == null)
386: throw new FactoryException(
387: "Could not find a prototype with TypeId = "
388: + prototypeAssetTypeId);
389: Asset asset = proto.createInstance(uniqueId);
390: asset.registerWithLDM(ldm);
391: return asset;
392: }
393:
394: /** Make a copy of an instance. The result will be a shallow copy
395: * of the original - that is, it will share most PropertyGroups with the
396: * original instance. The differences will be that the copy's PGs will
397: * be locked and the copy will have a different UID.
398: * The copy will truly be a different asset which happens to (initially) have
399: * identical propertygroups.
400: * This method should be used to create new assets which are very much
401: * like another instance. The use of this method is a less-desirable alternative
402: * to creating a new instance of your original's prototype and then adding back
403: * any extra properties. This is less desirable because it doesn't allow the
404: * LDM to participate in the construction of the copy.
405: **/
406: public final Asset copyInstance(Asset asset) {
407: Asset copy = asset.copy();
408: copy.registerWithLDM(ldm);
409: return copy;
410: }
411:
412: /** make an evil twin of an instance. The result will be a shallow copy of the
413: * original (as in copyInstance), with locked PropertyGroups. The copy
414: * <em> will </em> have the same UID as the original, so will, in a systems sense
415: * actually be the same asset. It could be very bad for multiple clones of an
416: * asset to show up in someone's Blackboard.
417: * This method should be used when subsetting the capabilities of an asset
418: * for some other consumer. Eg. when you want to allow a client to use just one
419: * capability of your organization.
420: * Note: This method name may change.
421: **/
422: public final Asset cloneInstance(Asset asset) {
423: Asset copy = asset.copy();
424: // We need to reuse the original UID so we do this:
425: copy.setUID(asset.getUID());
426: copy.bindToLDM(ldm);
427: // instead of:
428: // copy.registerWithLDM(ldm);
429: return copy;
430: }
431:
432: /** Create an aggregate asset instance of a prototypical asset.
433: **/
434: public final Asset createAggregate(Asset prototypeAsset,
435: int quantity) {
436: Asset asset = prototypeAsset.createAggregate(quantity);
437: asset.registerWithLDM(ldm);
438: return asset;
439: }
440:
441: /** Create an aggregate asset instance of a prototypical asset.
442: **/
443: public final Asset createAggregate(String prototypeAssetTypeId,
444: int quantity) {
445: if (prototypeAssetTypeId == null)
446: throw new IllegalArgumentException(
447: "prototypeAssetTypeId must be non-null");
448:
449: Asset proto = ldm.getPrototype(prototypeAssetTypeId);
450: if (proto == null)
451: throw new FactoryException(
452: "Could not find a prototype with TypeId = "
453: + prototypeAssetTypeId);
454: Asset asset = proto.createAggregate(quantity);
455: asset.registerWithLDM(ldm);
456: return asset;
457: }
458:
459: /** Create an aggregate asset instance of a prototypical asset.
460: **/
461: public Asset createInstance(String prototypeAssetTypeId,
462: int quantity) {
463: if (prototypeAssetTypeId == null)
464: throw new IllegalArgumentException(
465: "prototypeAssetTypeId must be non-null");
466:
467: Asset proto = ldm.getPrototype(prototypeAssetTypeId);
468: if (proto == null)
469: throw new FactoryException(
470: "Could not find a prototype with TypeId = "
471: + prototypeAssetTypeId);
472: Asset asset = proto.createAggregate(quantity);
473: asset.registerWithLDM(ldm);
474: return asset;
475: }
476:
477: /** create a new property group, given a PropertyGroup name.
478: * The name should not have any package prefix and should
479: * be the cannonical name (not the implementation class name).
480: **/
481: public final PropertyGroup createPropertyGroup(String propertyName) {
482: try {
483: Method factoryMethod = findPropertyGroupFactoryMethod(propertyName);
484: if (factoryMethod == null)
485: throw new IllegalArgumentException(propertyName
486: + " is not a known PropertyGroup name");
487:
488: return (PropertyGroup) factoryMethod.invoke(null, null); // class/static method, no args
489: } catch (Exception e) {
490: throw new FactoryException("Could not createPropertyGroup("
491: + propertyName + "): " + e, e);
492: }
493: }
494:
495: /** create a new property group, given a PropertyGroupGroup name.
496: * The name should not have any package prefix and should
497: * be the cannonical name (not the implementation class name).
498: **/
499: public final PropertyGroup createPropertyGroup(Class propertyClass) {
500: try {
501: // we got the interface name, not the impl - find and run the
502: // correct property factory method.
503: Field f = propertyClass.getField("factoryMethod");
504: String fmname = (String) f.get(null);
505: Field ff = propertyClass.getField("factoryClass");
506: Class factory = (Class) ff.get(null);
507: Method fm = factory.getMethod(fmname, null);
508: return (PropertyGroup) fm.invoke(null, null);
509: } catch (Exception e) {
510: throw new FactoryException("Could not createPropertyGroup("
511: + propertyClass + "): " + e, e);
512: }
513: }
514:
515: /** @return a copy of another property group **/
516: public final PropertyGroup createPropertyGroup(
517: PropertyGroup originalProperty) {
518: return originalProperty.copy();
519: }
520:
521: /** dummy for create(String) **/
522: public Object create(String objectname) {
523: return null;
524: }
525:
526: /** dummy for create(Class) **/
527: public Object create(Class objectclass) {
528: return null;
529: }
530: }
|