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.lib.xml.parser;
028:
029: import java.lang.reflect.InvocationTargetException;
030: import java.lang.reflect.Method;
031: import java.util.Collection;
032: import java.util.Hashtable;
033:
034: import org.cougaar.planning.ldm.LDMServesPlugin;
035: import org.cougaar.planning.ldm.asset.Asset;
036: import org.cougaar.planning.ldm.asset.NewTypeIdentificationPG;
037: import org.cougaar.planning.ldm.asset.TypeIdentificationPG;
038: import org.cougaar.util.log.Logger;
039: import org.w3c.dom.Node;
040: import org.w3c.dom.NodeList;
041:
042: /**
043: * Parses fields of objects.
044: *
045: * (This is quite involved and complicated, actually.)
046: */
047: public class FieldParser {
048:
049: public FieldParser(Logger l, ObjectParser objectParser) {
050: logger = l;
051: valueParser = new ValueParser(l, objectParser);
052: }
053:
054: /**
055: * set a field given by node in an object obj
056: */
057: public Object setField(LDMServesPlugin ldm, Node node, Object obj) {
058: if (node.getNodeName().equals("field")) {
059: Class objClass = obj.getClass();
060: String fieldName = node.getAttributes()
061: .getNamedItem("name").getNodeValue();
062: String fieldType = node.getAttributes()
063: .getNamedItem("type").getNodeValue();
064: String setterName = null;
065:
066: // Get the field setter method
067: Method fieldSetter = null;
068: Class fieldClass = null;
069: Class origFieldClass = null; // for reporting errors only
070: Object fieldValue = null;
071:
072: // check for correct field type
073: if (fieldType.equals("object")) {
074: fieldType = getObjectType(node);
075: } else if (fieldType.equals("String")) {
076: fieldType = "java.lang." + fieldType;
077: }
078:
079: // assuming normal bean-like setters
080: if (fieldSetter == null) {
081: try {
082: setterName = "set" + fieldName;
083: fieldClass = getFieldClass(fieldType);
084: origFieldClass = fieldClass;
085: //fieldSetter = objClass.getMethod(setterName, fieldClass);
086: fieldSetter = getSetterMethod(objClass, setterName,
087: fieldClass);
088:
089: if (fieldSetter != null) {
090: if (fieldName.equals("TypeIdentificationPG")) {
091: fieldValue = getTypeIdentificationPG(ldm,
092: node, (Asset) obj);
093: } else {
094: fieldValue = ((javaUtilCollectionClass
095: .isAssignableFrom(fieldClass)) ? getFieldCollection(
096: ldm, node, fieldClass)
097: : getFieldValue(ldm, node));
098: callMethod(obj, fieldSetter, fieldValue,
099: fieldType);
100: }
101: }
102: } catch (Exception e) {
103: // BOZO, need to do something here.
104: fieldSetter = null;
105:
106: //logger.error(e.getMessage());
107: //e.printStackTrace();
108: //System.exit(1);
109: }
110: }
111:
112: // assume that it is an org.cougaar.planning.ldm.asset and set using addOtherPropertyGroup()
113: if (fieldSetter == null) {
114: try {
115: setterName = "addOtherPropertyGroup";
116: fieldClass = getFieldClass("org.cougaar.planning.ldm.asset.PropertyGroup");
117: //fieldSetter = objClass.getMethod(setterName, fieldClass);
118: fieldSetter = getSetterMethod(objClass, setterName,
119: fieldClass);
120:
121: if (fieldSetter != null) {
122: fieldValue = ((javaUtilCollectionClass
123: .isAssignableFrom(fieldClass)) ? getFieldCollection(
124: ldm, node, fieldClass)
125: : getFieldValue(ldm, node));
126: callMethod(obj, fieldSetter, fieldValue,
127: fieldType);
128: }
129: } catch (Exception e) {
130: // BOZO, need to do something here.
131: fieldSetter = null;
132:
133: //logger.error(e.getMessage());
134: //e.printStackTrace();
135: //System.exit(1);
136: }
137: }
138:
139: // assume that we are setting with a new measure class
140: if (fieldSetter == null) {
141: try {
142: objClass = (Class) obj; // for this very special case
143: setterName = "new" + fieldName;
144: fieldClass = getFieldClass(fieldType);
145: //fieldSetter = objClass.getMethod(setterName, fieldClass);
146: fieldSetter = getSetterMethod(objClass, setterName,
147: fieldClass);
148:
149: if (fieldSetter != null) {
150: fieldValue = ((javaUtilCollectionClass
151: .isAssignableFrom(fieldClass)) ? getFieldCollection(
152: ldm, node, fieldClass)
153: : getFieldValue(ldm, node));
154: obj = callMethod(null, fieldSetter, fieldValue,
155: fieldType);
156: }
157: } catch (Exception e) {
158: // BOZO, need to do something here.
159: //logger.error(e.getMessage());
160: //e.printStackTrace();
161: fieldSetter = null;
162:
163: //logger.error(e.getMessage());
164: //e.printStackTrace();
165: //System.exit(1);
166: }
167: }
168:
169: // we are hosed
170: if (fieldSetter == null) {
171: logger.error("");
172: logger
173: .error("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
174: logger.error("XXXX Method not found: set" + fieldName
175: + "(" + origFieldClass.getName() + ")");
176: logger.error("XXXX: In object: " + obj);
177: logger.error("XXXX: Please revise your ldm.xml file");
178: logger
179: .error("XXXX: NOTE: If you are suddenly having unexplained problems with ");
180: logger
181: .error("XXXX: this code, please contact gvidaver@bbn.com.");
182: // logger.error("XXXX: method oldGetSetterMethod to the name getSetterMethod in");
183: // logger.error("XXXX: xmlparser.FieldParser");
184: logger
185: .error("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
186: logger.error("");
187: // System.exit(1);
188: }
189: }
190: return obj;
191: }
192:
193: private static Class javaUtilCollectionClass;
194: static {
195: try {
196: javaUtilCollectionClass = Class
197: .forName("java.util.Collection");
198: } catch (Exception e) {
199: }
200: }
201:
202: static final Class PG_CLASS = org.cougaar.planning.ldm.asset.PropertyGroup.class;
203:
204: /**
205: * <pre>
206: * Wrapper arround the java.lang.Class functionality to allow to
207: * get a method on an object that is called with a superclass of its
208: * argument. Assumes only one argument per method (good for setters).
209: *
210: * NOTE: It is actually faster to do the algorithm implemented below rather
211: * than call class.getMethods() and look for a method with the appropriate
212: * (isAssignableFrom(argtype)) argument.
213: * For several societies tested 100% of the arguments of the methods desired
214: * were either equal to their arguments or were propertygroups. It is still
215: * an open question whether it is faster to do the compare (endsWith("PG"))
216: * before looking for the method or whether one should just let the
217: * NoSuchMethodException be thrown before trying the PG approach.
218: * </pre>
219: */
220: private Method getSetterMethod(Class objc, String name,
221: Class argtype) {
222: Method method = null;
223: Class[] argclasses = { argtype };
224: // For strict accuracy we should also add the argtype to the
225: // hash_str, but since most classes have only one method with
226: // the given type of arg, we can just fail in the exceptional
227: // case and pick it up later.
228: String classname = objc.getName();
229:
230: // Preprocessing for speed!
231: if (classname.equals("java.lang.Class"))
232: return null;
233: if (argtype.getName().endsWith("PG"))
234: argtype = PG_CLASS;
235:
236: String hash_str = classname.concat(name); // + argtype.getName();
237:
238: // This 3-level nesting is somewhat arbitrary but gets most
239: // methods (statistically)
240: // That is, if we haven't seen this method for this class before,
241: // try to find it within the class. If that fails, try the arg's
242: // super, then that super's super(class), then revert to the old
243: // slow way of doing things
244: if ((method = (Method) seen_classmethods.get(hash_str)) == null) {
245: try {
246: method = objc.getMethod(name, argclasses);
247: } catch (NoSuchMethodException nsme) {
248:
249: argclasses[0] = argclasses[0].getSuperclass();
250: try {
251: if (argclasses[0] != null)
252: method = objc.getMethod(name, argclasses);
253: else
254: method = oldGetSetterMethod(objc, name, argtype);
255: } catch (NoSuchMethodException nsme2) {
256: argclasses[0] = argclasses[0].getSuperclass();
257: try {
258: if (argclasses[0] != null)
259: method = objc.getMethod(name, argclasses);
260: else
261: method = oldGetSetterMethod(objc, name,
262: argtype);
263: } catch (NoSuchMethodException nsme3) {
264: method = oldGetSetterMethod(objc, name, argtype);
265: }
266: }
267: }
268: if (method == null)
269: return null;
270:
271: seen_classmethods.put(hash_str, method);
272: }
273:
274: return method;
275: }
276:
277: /**
278: * Wrapper arround the java.lang.Class functionality to allow to
279: * get a method on an object that is called with a superclass of its
280: * argument. Assumes only one argument per method (good for setters).
281: */
282: // private static Method oldGetSetterMethod(Class objc, String name, Class argtype){
283: // return getSetterMethod(objc, name, argtype);
284: // }
285: Hashtable seen_classes = new Hashtable();
286:
287: /**
288: * Wrapper arround the java.lang.Class functionality to allow to
289: * get a method on an object that is called with a superclass of its
290: * argument. Assumes only one argument per method (good for setters).
291: */
292: private Method oldGetSetterMethod(Class objc, String name,
293: Class argtype) {
294: Method[] methods = null;
295: Class[] argclass = new Class[1];
296:
297: argclass[0] = argtype;
298: if ((methods = (Method[]) seen_classes.get(objc)) == null) {
299: methods = objc.getMethods();
300: seen_classes.put(objc, methods);
301: }
302:
303: for (int i = 0; i < methods.length; i++) {
304: if (name.equals(methods[i].getName())) {
305: Class[] params = methods[i].getParameterTypes();
306: //logger.debug(":XXX:: " + methods[i] + " (" + params[0] + ")");
307: if (params[0].isAssignableFrom(argtype))
308: return methods[i];
309:
310: }
311: }
312: return null;
313: }
314:
315: /**
316: * Calls a method inside of an object with the given
317: * parameter of the given type.
318: */
319: private Object callMethod(Object object, Method fieldSetter,
320: Object fieldValue, String fieldType)
321: throws IllegalAccessException, InvocationTargetException {
322:
323: Object[] buffer = new Object[1];
324: Object retval = null;
325:
326: if (fieldType.equals("byte")) {
327: buffer[0] = Byte.valueOf((String) fieldValue);
328: retval = fieldSetter.invoke(object, buffer);
329: } else if (fieldType.equals("char")) {
330: buffer[0] = new Character(((String) fieldValue).charAt(0));
331: retval = fieldSetter.invoke(object, buffer);
332: } else if (fieldType.equals("boolean")) {
333: buffer[0] = Boolean.valueOf((String) fieldValue);
334: retval = fieldSetter.invoke(object, buffer);
335: } else if (fieldType.equals("short")) {
336: buffer[0] = Short.valueOf((String) fieldValue);
337: retval = fieldSetter.invoke(object, buffer);
338: } else if (fieldType.equals("int")) {
339: buffer[0] = Integer.valueOf((String) fieldValue);
340: retval = fieldSetter.invoke(object, buffer);
341: } else if (fieldType.equals("long")) {
342: buffer[0] = Long.valueOf((String) fieldValue);
343: retval = fieldSetter.invoke(object, buffer);
344: } else if (fieldType.equals("float")) {
345: buffer[0] = Float.valueOf((String) fieldValue);
346: retval = fieldSetter.invoke(object, buffer);
347: } else if (fieldType.equals("double")) {
348: buffer[0] = Double.valueOf((String) fieldValue);
349: retval = fieldSetter.invoke(object, buffer);
350: } else {
351: buffer[0] = fieldValue;
352: retval = fieldSetter.invoke(object, buffer);
353: }
354: return retval;
355: }
356:
357: /**
358: * This is a "loook ahead" function to get the object type
359: * for a field that contains an object.
360: */
361: private String getObjectType(Node node) {
362: String retval = null;
363: NodeList nlist = node.getChildNodes();
364: int nlength = nlist.getLength();
365:
366: for (int i = 0; i < nlength; i++) {
367: Node child = nlist.item(i);
368: String childname = child.getNodeName();
369:
370: if (child.getNodeType() == Node.ELEMENT_NODE) {
371: if (childname.equals("value")) {
372:
373: NodeList nnlist = child.getChildNodes();
374:
375: for (int ii = 0; ii < nlength; ii++) {
376: Node cchild = nnlist.item(ii);
377: String cchildname = cchild.getNodeName();
378:
379: if (cchild.getNodeType() == Node.ELEMENT_NODE) {
380: if (cchildname.equals("object")) {
381:
382: retval = cchild.getAttributes()
383: .getNamedItem("class")
384: .getNodeValue();
385: }
386: }
387: }
388: }
389: }
390: }
391: return retval;
392: }
393:
394: /**
395: * Get the value of a field given by Node. Java primitives
396: * are returned as strings, it is the callers responsibility
397: * to do the conversion.
398: */
399: private Object getFieldValue(LDMServesPlugin ldm, Node node) {
400: Object retval = null;
401: NodeList nlist = node.getChildNodes();
402: int nlength = nlist.getLength();
403:
404: for (int i = 0; i < nlength; i++) {
405: Node child = nlist.item(i);
406: String childname = child.getNodeName();
407:
408: if (child.getNodeType() == Node.ELEMENT_NODE) {
409: if (childname.equals("value")) {
410: retval = valueParser.getValue(ldm, child);
411: }
412: }
413: }
414: return retval;
415: }
416:
417: /**
418: * Get the Collection of fields given by Node. Java primitives
419: * are returned as strings, it is the callers responsibility
420: * to do the conversion.
421: */
422: private Collection getFieldCollection(LDMServesPlugin ldm,
423: Node node, Class collectionClass) {
424: Collection retcoll;
425: NodeList nlist = node.getChildNodes();
426: int nlength = nlist.getLength();
427:
428: try {
429: retcoll = (Collection) collectionClass.newInstance();
430: } catch (Exception noInstanceE) {
431: logger.error(
432: "Unable to create instance of Collection Class "
433: + collectionClass, noInstanceE);
434: return null;
435: }
436:
437: for (int i = 0; i < nlength; i++) {
438: Node child = nlist.item(i);
439: String childname = child.getNodeName();
440:
441: if (child.getNodeType() == Node.ELEMENT_NODE) {
442: if (childname.equals("value")) {
443: Object collObj = valueParser.getValue(ldm, child);
444: if (collObj != null) {
445: retcoll.add(collObj);
446: }
447: }
448: }
449: }
450: return retcoll;
451: }
452:
453: private TypeIdentificationPG getTypeIdentificationPG(
454: LDMServesPlugin ldm, Node node, Asset obj) {
455: NewTypeIdentificationPG ntip = (NewTypeIdentificationPG) obj
456: .getTypeIdentificationPG();
457: TypeIdentificationPG tip = null;
458:
459: NodeList nlist = node.getChildNodes();
460: int nlength = nlist.getLength();
461:
462: for (int i = 0; i < nlength; i++) {
463: Node child = nlist.item(i);
464: String childname = child.getNodeName();
465:
466: if (child.getNodeType() == Node.ELEMENT_NODE) {
467: if (childname.equals("value")) {
468: tip = (TypeIdentificationPG) valueParser.getValue(
469: ldm, child);
470: // OK, this is not the most general way, I could dynamicallly
471: // get all the methods that TypeIdentification has dynamically
472: // and then call the setters, but hell...
473: ntip.setTypeIdentification(tip
474: .getTypeIdentification());
475: ntip.setNomenclature(tip.getNomenclature());
476: ntip.setAlternateTypeIdentification(tip
477: .getAlternateTypeIdentification());
478: tip = null;
479: }
480: }
481: }
482: return ntip;
483: }
484:
485: /**
486: * Get the Class of a field taking into account that it
487: * could be a Java primitive.
488: */
489: private Class getFieldClass(String fieldType) {
490: Class retval = null;
491:
492: if (fieldType.equals("byte")) {
493: retval = Byte.TYPE;
494: } else if (fieldType.equals("char")) {
495: retval = Character.TYPE;
496: } else if (fieldType.equals("boolean")) {
497: retval = Boolean.TYPE;
498: } else if (fieldType.equals("short")) {
499: retval = Short.TYPE;
500: } else if (fieldType.equals("int")) {
501: retval = Integer.TYPE;
502: } else if (fieldType.equals("long")) {
503: retval = Long.TYPE;
504: } else if (fieldType.equals("float")) {
505: retval = Float.TYPE;
506: } else if (fieldType.equals("double")) {
507: retval = Double.TYPE;
508: } else {
509: try {
510: retval = Class.forName(fieldType);
511: } catch (ClassNotFoundException e) {
512: logger.error("XXXXXXX: could not find class: "
513: + fieldType, e);
514: }
515: }
516: return retval;
517: }
518:
519: protected Logger logger;
520:
521: protected Hashtable seen_classmethods = new Hashtable();
522: protected ValueParser valueParser;
523: }
|