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.vishnu.client;
028:
029: import java.util.Collection;
030: import java.util.Date;
031: import java.util.HashMap;
032: import java.util.Iterator;
033: import java.util.List;
034: import java.util.Map;
035: import java.util.HashSet;
036: import java.util.Set;
037:
038: import org.apache.xerces.dom.DocumentImpl;
039:
040: import org.cougaar.core.util.UID;
041: import org.cougaar.core.util.UniqueObject;
042: import org.cougaar.core.util.PropertyNameValue;
043:
044: import org.cougaar.util.log.Logger;
045:
046: import org.w3c.dom.Document;
047: import org.w3c.dom.Element;
048: import org.w3c.dom.NodeList;
049:
050: /**
051: * Create XML document in the Vishnu ObjectFormat format,
052: * directly from ALP objects.
053: * <p>
054: * Create and return xml for first class log plan objects.
055: * <p>
056: * Element name is extracted from object class, by taking the
057: * last field of the object class, and dropping a trailing "Impl",
058: * if it exists.
059: */
060:
061: public class FormatXMLize extends BaseXMLize implements XMLizer {
062: protected Class collectionClass;
063: protected Class enumerationClass;
064: protected Class dateClass;
065: protected Class latitudeClass;
066: protected Class longitudeClass;
067: protected Class UIDClass;
068: protected String numberString = "number";
069: protected String booleanString = "boolean";
070: protected String dateString = "datetime";
071: protected String latitudeString = "latlong";
072:
073: // controls how deep down the object hierarchy to go
074: protected final int RECURSION_DEPTH = 9;
075:
076: public FormatXMLize(Logger logger) {
077: super (logger);
078: try {
079: collectionClass = Class.forName("java.util.Collection");
080: dateClass = Class.forName("java.util.Date");
081: latitudeClass = Class
082: .forName("org.cougaar.planning.ldm.measure.Latitude");
083: longitudeClass = Class
084: .forName("org.cougaar.planning.ldm.measure.Longitude");
085: UIDClass = Class.forName("org.cougaar.core.util.UID");
086: enumerationClass = Class.forName("java.util.Enumeration");
087: } catch (ClassNotFoundException cnfe) {
088: }
089: }
090:
091: /**
092: * <b>Recursively</b> introspect and add nodes to the XML document.
093: * <p>
094: * Keeps a Set of objects (as these can be circular) and stops
095: * when it tries to introspect over an object a second time.
096: * <p>
097: * Also keeps a depth counter, decrements for each call to addNodes,
098: * and stops when the counter is zero. Use Integer.MAX_VALUE to
099: * indicate an unlimited search.
100: */
101:
102: Map unique = new HashMap();
103: Set globals = new HashSet();
104:
105: public Map getUnique() {
106: return unique;
107: }
108:
109: protected void addNodes(Document doc, Object obj,
110: Element parentElement, int searchDepth,
111: Collection createdNodes) {
112: if (obj == null) {
113: return;
114: }
115:
116: if (logger.isDebugEnabled()) {
117: if (obj instanceof org.cougaar.planning.ldm.asset.PropertyGroup) {
118: if (unique.containsKey(obj))
119: unique.put(obj, new Integer(((Integer) unique
120: .get(obj)).intValue() + 1));
121: else
122: unique.put(obj, new Integer(1));
123: }
124: }
125:
126: if (logger.isDebugEnabled())
127: logger.debug("addNodes - class " + obj.getClass());
128:
129: if (ignoreClass(obj.getClass())) {
130: if (logger.isDebugEnabled())
131: logger.debug("----> ignored!");
132: return;
133: }
134:
135: // debug ("Search depth " + searchDepth);
136: if (searchDepth <= 0) {
137: generateElementReachedMaxDepth(doc, parentElement, obj);
138: return;
139: }
140:
141: Map listProps = new HashMap();
142: List propertyNameValues = getProperties(obj, listProps);
143:
144: boolean foundDateAspect = false;
145:
146: // add the nodes for the properties and values
147: for (int i = 0; i < propertyNameValues.size();) {
148: PropertyNameValue pnv = (PropertyNameValue) propertyNameValues
149: .get(i);
150: foundDateAspect = checkForDateAspect(pnv, foundDateAspect);
151:
152: boolean isFirst = false;
153: Integer numListElems = (Integer) listProps.get(pnv.name);
154: boolean isList = (numListElems != null);
155:
156: if (isList) {
157: isFirst = true;
158: for (int j = 0; j < numListElems.intValue(); j++) {
159: pnv = (PropertyNameValue) propertyNameValues
160: .get(i++);
161: generateElem(doc, parentElement, pnv.name,
162: pnv.value, searchDepth, isList, isFirst,
163: createdNodes);
164: isFirst = false;
165: }
166: } else {
167: generateElem(doc, parentElement, pnv.name, pnv.value,
168: searchDepth, isList, isFirst, createdNodes);
169: i++;
170: }
171: }
172: }
173:
174: protected boolean checkForDateAspect(PropertyNameValue pnv,
175: boolean foundDateAspect) {
176: if (foundDateAspect && pnv.name.charAt(0) == 'v') {
177: if (pnv.name.equals("value")) {
178: double millis = Double.parseDouble((String) pnv.value);
179: pnv.value = new Date((long) millis);
180: }
181: } else if (pnv.name.charAt(0) == 'a') {
182: if (pnv.name.equals("aspectType")) {
183: String value = (String) pnv.value;
184: char first = value.charAt(0);
185: if ((first == '0')
186: || ((first == '1') && (value.length() == 1))
187: || ((first == '1') && (value.charAt(1) == '.' || value
188: .charAt(1) == '3')))
189: foundDateAspect = true;
190: }
191: }
192: return foundDateAspect;
193: }
194:
195: /** subclass to generate different tag */
196: protected Element createRootNode(Document doc, String tag,
197: boolean isTask, boolean isResource, Object obj,
198: String resourceClassName) {
199: return createObjectFormat(doc, tag, isTask, isResource, obj,
200: resourceClassName);
201: }
202:
203: /**
204: * Already seen this object or reached maximum depth.
205: * Write the UID if possible, otherwise write the "toString".
206: */
207: protected void generateElementReachedMaxDepth(Document doc,
208: Element parentElement, Object obj) {
209: if (logger.isDebugEnabled())
210: logger.debug("Object traversed already/max depth: "
211: + obj.getClass().toString() + " " + obj);
212: if (isUniqueObject(obj)) {
213: Element item = createFieldFormat(doc, "UID", "string",
214: false, false);
215: if (logger.isDebugEnabled())
216: logger.debug("maxDepth - " + "UID" + " - " + "string");
217:
218: parentElement.appendChild(item);
219: } else {
220: parentElement.appendChild(createFieldFormat(doc,
221: getTypeFor(obj), obj, false, false));
222: if (logger.isDebugEnabled())
223: logger.debug("maxDepth - " + obj);
224: }
225: }
226:
227: protected void generateLeaf(Document doc, Element parentElement,
228: String propertyName, Object propertyValue) {
229: Element item = createFieldFormat(doc, propertyName,
230: propertyValue, false, false);
231:
232: parentElement.appendChild(item);
233:
234: // only set keys on tasks and resources
235: correctKey(parentElement, item);
236:
237: if (logger.isDebugEnabled())
238: logger.debug("isLeaf - " + propertyName + " - "
239: + propertyValue);
240: }
241:
242: protected void generateNonLeaf(Document doc, Element parentElement,
243: String propertyName, Object propertyValue, int searchDepth,
244: boolean isList, boolean isFirst, Collection createdNodes) {
245: // this removes the class name following the $ for Locked classes
246: int index = propertyName.indexOf('$');
247: if (index > 0) {
248: propertyName = propertyName.substring(0, index);
249: }
250: boolean skip = skipObject(propertyValue);
251:
252: Element fieldNode = null;
253:
254: if (logger.isDebugEnabled()
255: && !((isList && isFirst) || !isList))
256: logger.debug("skipping field format for - " + propertyName
257: + " - " + propertyValue);
258:
259: if (!ignoreObject(propertyValue)
260: && ((isList && isFirst) || !isList)) {
261: fieldNode = createFieldFormat(doc, propertyName,
262: propertyValue, isList, !skip);
263: parentElement.appendChild(fieldNode);
264: // only set keys on tasks and resources
265: correctKey(parentElement, fieldNode);
266: if (logger.isDebugEnabled())
267: logger.debug("nonLeaf - " + propertyName + " - "
268: + propertyValue);
269: }
270:
271: if (!skip) {
272: if (propertyValue instanceof org.cougaar.planning.ldm.asset.PropertyGroup) {
273: if (globals.contains(propertyValue)) {
274: return;
275: }
276: }
277:
278: Element item = createObjectFormat(doc, propertyName, false,
279: false, propertyValue, null);
280: createdNodes.add(item);
281: // recurse!
282:
283: addNodes(doc, propertyValue, item, (searchDepth - 1),
284: createdNodes);
285: if (item.getChildNodes().getLength() == 0) {
286: // createdNodes.remove (item);
287: // parentElement.removeChild (fieldNode);
288: if (logger.isDebugEnabled())
289: logger.debug("nonLeaf - REMOVING? - "
290: + propertyName + " - " + propertyValue);
291:
292: }
293: if ((propertyName.charAt(0) == 'r')
294: && propertyName.equals("roleSchedule")) {
295: removeChildNamed(item, "scheduleElements");
296: removeChildNamed(item, "availableSchedule");
297:
298: addIntervalNamed("scheduleElements", doc, item);
299: addIntervalNamed("availableSchedule", doc, item);
300: }
301:
302: globals.add(propertyValue);
303: }
304: }
305:
306: protected void removeChildNamed(Element roleScheduleImpl,
307: String childName) {
308: NodeList children = roleScheduleImpl.getChildNodes();
309: for (int i = 0; i < children.getLength(); i++) {
310: Element child = (Element) children.item(i);
311: if (child.getAttribute("name").equals(childName)) {
312: if (logger.isDebugEnabled())
313: logger.debug("found " + childName + ", removing.");
314:
315: roleScheduleImpl.removeChild(child);
316: break;
317: }
318: }
319: }
320:
321: /** all property groups become globals, except for item id pg */
322: protected boolean isGlobal(Object obj) {
323: boolean isPropertyGroup = (obj instanceof org.cougaar.planning.ldm.asset.PropertyGroup);
324: boolean isItemIDPropertyGroup = (obj instanceof org.cougaar.planning.ldm.asset.ItemIdentificationPG);
325: // boolean isPrepPhrase = (obj instanceof org.cougaar.planning.ldm.plan.PrepositionalPhrase);
326: boolean isGlobal = isPropertyGroup && !isItemIDPropertyGroup; // || isPrepPhrase;
327: return isGlobal;
328: }
329:
330: protected void correctKey(Element parentElement, Element item) {
331: // only set keys on tasks and resources
332: if ((parentElement.getAttribute("is_task").charAt(0) == 'f')
333: && (parentElement.getAttribute("is_resource").charAt(0) == 'f')
334: && (item.getAttribute("is_key").charAt(0) == 't')) {
335: if (logger.isDebugEnabled())
336: logger.debug("correctKey - CORRECTING key");
337: item.setAttribute("is_key", "false");
338: }
339:
340: if (logger.isDebugEnabled()) {
341: boolean allFalse = parentElement.getAttribute("is_task")
342: .charAt(0) == 'f'
343: & parentElement.getAttribute("is_resource").charAt(
344: 0) == 'f'
345: & item.getAttribute("is_key").charAt(0) == 'f';
346: if (!allFalse)
347: logger.debug("generateLeaf - parent task "
348: + parentElement.getAttribute("is_task").charAt(
349: 0)
350: + " resource "
351: + parentElement.getAttribute("is_resource")
352: .charAt(0) + " item key "
353: + item.getAttribute("is_key").charAt(0));
354: }
355: }
356:
357: /**
358: * need to have special code to add plan element intervals to role schedule format, since
359: * bean properties don't include them
360: */
361: protected void addScheduleElements(Document doc, Element item) {
362: addIntervalNamed("scheduleElements", doc, item);
363: }
364:
365: protected void addIntervalNamed(String name, Document doc,
366: Element item) {
367: Element elem = doc.createElement("FIELDFORMAT");
368: elem.setAttribute("name", name);
369: elem.setAttribute("is_subobject", "true");
370: elem.setAttribute("is_list", "true");
371: elem.setAttribute("is_key", "false");
372: elem.setAttribute("datatype", "interval");
373: item.appendChild(elem);
374: }
375:
376: /**
377: * implemented for XMLizer interface <p>
378: *
379: * creates an DOM Document out of the collection of items passed in, <br>
380: * with the name of the resource class.
381: **/
382: public Document createDoc(Collection items,
383: Collection ignoredChangedItems, String assetClassName) {
384: Document doc = new DocumentImpl();
385: Element root = doc.createElement("PROBLEM");
386: doc.appendChild(root);
387: Element df = doc.createElement("DATAFORMAT");
388: root.appendChild(df);
389:
390: Element element = null;
391: for (Iterator iter = items.iterator(); iter.hasNext();) {
392: Collection nodes = getPlanObjectXMLNodes(iter.next(), doc,
393: RECURSION_DEPTH, assetClassName);
394: for (Iterator iter2 = nodes.iterator(); iter2.hasNext();)
395: df.appendChild((Element) iter2.next());
396: }
397:
398: if (logger.isDebugEnabled()) {
399: int i = 0;
400: for (Iterator iter = unique.keySet().iterator(); iter
401: .hasNext();) {
402: Object thing = iter.next();
403: logger.debug("" + i++ + " - " + thing.getClass()
404: + " - " + unique.get(thing));
405: }
406: }
407: return doc;
408: }
409:
410: protected Element createObjectFormat(Document doc, String name,
411: boolean isTask, boolean isResource, Object theObj,
412: String resourceClassName) {
413: Element elem = doc.createElement("OBJECTFORMAT");
414: elem.setAttribute("is_task", (isTask) ? "true" : "false");
415: elem.setAttribute("is_resource", (isResource) ? "true"
416: : "false");
417: String cn;
418:
419: if (isResource)
420: cn = resourceClassName;
421: else {
422: cn = "" + theObj.getClass();
423: cn = cn.substring(cn.lastIndexOf('.') + 1);
424: cn = cn.substring(cn.lastIndexOf('$') + 1);
425: }
426:
427: elem.setAttribute("name", (isTask) ? name : cn);
428:
429: return elem;
430: }
431:
432: protected Element createFieldFormat(Document doc, String name,
433: Object theObj, boolean isList, boolean isSubobject) {
434: Element elem = doc.createElement("FIELDFORMAT");
435: elem.setAttribute("name", name);
436: boolean isKey = isKey(theObj);
437:
438: elem.setAttribute("is_subobject",
439: ((isSubobject && !isKey) || (latitudeClass
440: .isInstance(theObj))) ? "true" : "false");
441: String cn = "" + theObj.getClass();
442: if (isSubobject) {
443: cn = cn.substring(cn.lastIndexOf('.') + 1);
444: cn = cn.substring(cn.lastIndexOf('$') + 1);
445: elem.setAttribute("datatype", cn);
446: } else
447: elem.setAttribute("datatype", getTypeFor(theObj));
448: elem.setAttribute("is_list", (isList) ? "true" : "false");
449: elem.setAttribute("is_key", (isKey(theObj)) ? "true" : "false");
450:
451: // if it's a global set it's attr
452: if (isGlobal(theObj)) {
453: elem.setAttribute("is_globalptr", "true");
454: elem.setAttribute("is_subobject", "false");
455: }
456:
457: if (logger.isDebugEnabled() && isList(theObj) && !isList)
458: logger.debug("WARNING - disaggrement over " + name + " - "
459: + name.getClass());
460:
461: return elem;
462: }
463:
464: protected boolean isList(Object theObj) {
465: return collectionClass.isInstance(theObj)
466: || theObj.getClass().isArray()
467: || enumerationClass.isInstance(theObj);
468: }
469:
470: protected String getTypeFor(Object theObj) {
471: if (numberClass.isInstance(theObj))
472: return numberString;
473: if (dateClass.isInstance(theObj))
474: return dateString;
475: if (latitudeClass.isInstance(theObj))
476: return latitudeString;
477: if (booleanClass.isInstance(theObj))
478: return booleanString;
479:
480: String objAsString = theObj.toString();
481:
482: if (objAsString.length() == 0)
483: return "string";
484:
485: char firstChar = objAsString.charAt(0);
486:
487: if (((firstChar == 't') || (firstChar == 'f'))
488: && (objAsString.equals("true") || objAsString
489: .equals("false"))) {
490: // if (logger.isDebugEnabled())
491: // logger.debug ("got here - " + objAsString + " but not boolean - " + theObj.getClass ());
492: return booleanString;
493: }
494:
495: if (Character.isDigit(firstChar)) {
496: boolean isNumber = false;
497: try {
498: Integer.parseInt(objAsString);
499: isNumber = true;
500: } catch (NumberFormatException nfe) {
501: }
502:
503: try {
504: Double.parseDouble(objAsString);
505: isNumber = true;
506: } catch (NumberFormatException nfe) {
507: }
508: if (isNumber) {
509: // if (logger.isDebugEnabled())
510: // logger.debug ("got here - " + objAsString + " but not number - " + theObj.getClass ());
511: return numberString;
512: }
513: }
514:
515: if (firstChar == 'I' || firstChar == '-') {
516: if (objAsString.equals("Infinity"))
517: return numberString;
518: else if (objAsString.equals("-Infinity"))
519: return numberString;
520: }
521:
522: return "string";
523: }
524:
525: protected boolean skipObject(Object theObj) {
526: return (dateClass.isInstance(theObj)
527: || latitudeClass.isInstance(theObj)
528: || longitudeClass.isInstance(theObj) || UIDClass
529: .isInstance(theObj));
530: }
531:
532: protected boolean ignoreObject(Object theObj) {
533: return longitudeClass.isInstance(theObj);
534: }
535:
536: protected boolean isKey(Object theObj) {
537: return UIDClass.isInstance(theObj);
538: }
539: }
|