001: /*
002: * <copyright>
003: *
004: * Copyright 2002-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: package org.cougaar.tools.csmart.ui.viewer;
027:
028: import org.cougaar.tools.csmart.core.db.ExperimentDB;
029: import org.cougaar.tools.csmart.core.property.ConfigurableComponent;
030: import org.cougaar.tools.csmart.experiment.DBExperiment;
031: import org.cougaar.tools.csmart.experiment.Experiment;
032: import org.cougaar.tools.csmart.recipe.RecipeComponent;
033: import org.cougaar.tools.csmart.util.XMLUtils;
034: import org.cougaar.util.log.Logger;
035: import org.w3c.dom.Document;
036: import org.w3c.dom.Element;
037: import org.w3c.dom.Node;
038: import org.w3c.dom.NodeList;
039:
040: import javax.swing.tree.DefaultMutableTreeNode;
041: import javax.xml.parsers.DocumentBuilder;
042: import javax.xml.parsers.DocumentBuilderFactory;
043: import javax.xml.parsers.ParserConfigurationException;
044: import java.io.File;
045: import java.util.Enumeration;
046: import java.util.Map;
047:
048: /**
049: * Write out to XML enough content to recreate a users workspace. Also used to read it in,
050: * loading from the DB the named experiments and recipes. Note that societies are not captured.
051: **/
052: public class OrganizerXML extends XMLUtils {
053: public static final String WORKSPACE_NODE = "Workspace";
054: public static final String FOLDER_NODE = "Folder";
055: public static final String EXPERIMENT_NODE = "Experiment";
056: public static final String RECIPE_NODE = "Recipe";
057: public static final String RESULTDIR_NODE = "ResultDir";
058: public static final String NAME_ATTR = "Name";
059: public static final String ID_ATTR = "ID";
060:
061: private Logger log;
062:
063: private Document doc;
064: private Organizer organizer;
065:
066: public OrganizerXML() {
067: log = CSMART
068: .createLogger("org.cougaar.tools.csmart.ui.viewer.OrganizerXML");
069: }
070:
071: /**
072: * Given the root node of the workspace and the global result directory name,
073: * generate an XML document for the workspace.
074: *
075: * @param rootNode a <code>DefaultMutableTreeNode</code> root of the workspace organizer
076: * @param resultDirName a <code>String</code> global result directory name
077: * @return a <code>Document</code> capturing the necc info
078: */
079: public Document createXMLDocument(DefaultMutableTreeNode rootNode,
080: String resultDirName) {
081: if (rootNode == null)
082: return null;
083:
084: Document doc = null;
085:
086: DocumentBuilderFactory dbf = DocumentBuilderFactory
087: .newInstance();
088: try {
089: DocumentBuilder db = dbf.newDocumentBuilder();
090:
091: doc = db.newDocument();
092:
093: Element root = doc.createElement(WORKSPACE_NODE);
094: // this should be same as the name of the file, so leave it out
095: // root.setAttribute(NAME_ATTR, rootNode.getUserObject());
096: doc.appendChild(root);
097: if (resultDirName != null) {
098: Element rsFile = doc.createElement(RESULTDIR_NODE);
099: rsFile.setAttribute(NAME_ATTR, resultDirName);
100: root.appendChild(rsFile);
101: }
102:
103: // walk the tree -- for each child of the root node:
104: Enumeration kids = rootNode.children();
105: while (kids.hasMoreElements()) {
106: DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) kids
107: .nextElement();
108: Element next = getNextElement(childNode, doc);
109: if (next != null)
110: root.appendChild(next);
111: }
112: } catch (ParserConfigurationException pce) {
113: if (log.isErrorEnabled()) {
114: log.error("Exception creating DocumentBuilder.", pce);
115: }
116: }
117: return doc;
118: }
119:
120: /**
121: * Generate the next element for the XML version of the workspace.
122: *
123: * @param nextNode a <code>DefaultMutableTreeNode</code> to write out
124: * @param doc a <code>Document</code> to use to generate elements
125: * @return an <code>Element</code> to add (added by the caller), possibly null
126: */
127: private Element getNextElement(DefaultMutableTreeNode nextNode,
128: Document doc) {
129: Object o = nextNode.getUserObject();
130: if (o instanceof Experiment) {
131: Element exp = doc.createElement(EXPERIMENT_NODE);
132: exp.setAttribute(NAME_ATTR, ((Experiment) o)
133: .getExperimentName());
134: exp.setAttribute(ID_ATTR, ((DBExperiment) o)
135: .getExperimentID());
136: // what about experiment result directory?
137: File eResDir = ((Experiment) o).getResultDirectory();
138: if (eResDir != null) {
139: Element resDir = doc.createElement(RESULTDIR_NODE);
140: resDir.setAttribute(NAME_ATTR, eResDir.getPath());
141: exp.appendChild(resDir);
142: }
143: return exp;
144: } else if (o instanceof RecipeComponent) {
145: Element rec = doc.createElement(RECIPE_NODE);
146: rec.setAttribute(NAME_ATTR, ((RecipeComponent) o)
147: .getRecipeName());
148: return rec;
149: } else if (o instanceof ConfigurableComponent) {
150: // A society or something to skip
151: return null;
152: } else {
153: // A folder
154: Element folder = doc.createElement(FOLDER_NODE);
155: folder.setAttribute(NAME_ATTR, o.toString());
156: Enumeration kids = nextNode.children();
157: while (kids.hasMoreElements()) {
158: DefaultMutableTreeNode childNode = (DefaultMutableTreeNode) kids
159: .nextElement();
160: Element next = getNextElement(childNode, doc);
161: if (next != null)
162: folder.appendChild(next);
163: }
164: return folder;
165: }
166: }
167:
168: /**
169: * Load the given XML file, creating the workspace in the given Organizer.
170: * The caller is expected to have set up the organizer with a workspace,
171: * TreeModel, and root Node.
172: *
173: * @param workspacefilename a <code>String</code> file name to load
174: * @param organizer an <code>Organizer</code> to fill in
175: * @return a <code>DefaultMutableTreeNode</code> root node in the workspace.
176: */
177: public DefaultMutableTreeNode populateWorkspace(
178: String workspacefilename, Organizer organizer) {
179: if (organizer == null)
180: return null;
181: doc = loadXMLFile(workspacefilename);
182: if (doc == null) {
183: if (log.isWarnEnabled())
184: log
185: .warn("Not restoring workspace "
186: + workspacefilename);
187: return null;
188: }
189: this .organizer = organizer;
190:
191: return parse(doc.getDocumentElement(), organizer.root);
192: }
193:
194: // Parsing an XML workspace representation.
195: // Given a root element (that has been handled), and a root Workspace
196: // node that has been added, add any children
197: private DefaultMutableTreeNode parse(Element element,
198: DefaultMutableTreeNode parentNode) {
199: NodeList children = element.getChildNodes();
200: for (int i = 0; i < children.getLength(); i++) {
201: Node child = children.item(i);
202: if (child.getNodeType() == Node.ELEMENT_NODE) {
203: if (child.getNodeName().equals(WORKSPACE_NODE)) {
204: // The root workspace node has already been added
205: parse(((Element) child), parentNode);
206: } else if (child.getNodeName().equals(RESULTDIR_NODE)) {
207: // There are 2 kinds of result directories - global and experiment
208: String resdir = ((Element) child)
209: .getAttribute(NAME_ATTR);
210: if (resdir != null) {
211: if (element.getNodeName()
212: .equals(WORKSPACE_NODE)) {
213: // If the parent element is the hi-level workspace,
214: // then this is the global result node
215: organizer.csmart.setResultFile(resdir);
216: } else {
217: // Otherwise we're trying to set the resultdir on an experiment
218: if (parentNode != null
219: && parentNode.getUserObject() instanceof Experiment) {
220: Experiment exp = (Experiment) parentNode
221: .getUserObject();
222: exp
223: .setResultDirectory(new File(
224: resdir));
225: }
226: }
227: }
228: } else if (child.getNodeName().equals(EXPERIMENT_NODE)) {
229: // For experiments, get the name and ID
230: String eName = ((Element) child)
231: .getAttribute(NAME_ATTR);
232: String eID = ((Element) child)
233: .getAttribute(ID_ATTR);
234: if (log.isInfoEnabled())
235: log.info("Adding experiment " + eName + "("
236: + eID + ") to workspace.");
237:
238: // Double check that there is an experiment of that name/ID in the DB, and do something if not? Popup?
239: // If name is wrong but ID is Ok, we'll lose the recipes at least!
240: Map expNamesMap = ExperimentDB.getExperimentNames();
241: String dbid = (String) expNamesMap.get(eName);
242: if (dbid == null) {
243: if (log.isWarnEnabled()) {
244: log.warn("XML file Exp name " + eName
245: + " not found in DB!");
246: log
247: .warn("Will skip loading that experiment. Look for it manually later.");
248: }
249: // Or perhaps load it anyhow, losing recipes & whatnot?
250: } else if (!dbid.equals(eID)) {
251: if (log.isWarnEnabled()) {
252: log
253: .warn("XML file Exp "
254: + eName
255: + " lists experiment ID of "
256: + eID
257: + " but DB says experiment with that name has ID "
258: + dbid + "!");
259: log
260: .warn("Will skip loading that experiment. Load it manually later if you really want it.");
261: }
262: } else {
263: // This will load the experiment (incl CMTDialog),
264: // create the Node, add it to the workspace, and also
265: // add any recipes & societies & whatnot to the workspace
266: // FIXME: This method does a GUIUtils delay, which means
267: // the recipes get added to the workspace
268: // before the experiment has been fully loaded and added,
269: // which means all the experiments end up at the bottom,
270: // even if the user's organizer had listed it earlier
271: organizer.selectGivenExperimentFromDatabase(
272: eName, eID, false);
273: DefaultMutableTreeNode eNode = organizer
274: .getSelectedNode();
275: // But we recurse in case there is a resultdir
276: parse(((Element) child), eNode);
277: }
278: } else if (child.getNodeName().equals(RECIPE_NODE)) {
279: // For recipes, we load them be name
280: String rName = ((Element) child)
281: .getAttribute(NAME_ATTR);
282: RecipeComponent rc = organizer.helper
283: .loadRecipeNamed(rName);
284: // However, make sure it's not already in the workspace
285: // before adding it to the workspace
286: // -- this will create the node, add it, and reset the selection
287: if (rc == null) {
288: if (log.isErrorEnabled())
289: log.error("Could not find recipe named "
290: + rName
291: + " in database! Skipping...");
292: // Popup?
293: } else if (!organizer.isInWorkspace(rc)) {
294: if (log.isInfoEnabled())
295: log.info("Adding recipe " + rName
296: + " to workspace.");
297: organizer.addRecipeToWorkspace(rc, parentNode);
298: }
299: } else if (child.getNodeName().equals(FOLDER_NODE)) {
300: String fName = ((Element) child)
301: .getAttribute(NAME_ATTR);
302: if (log.isInfoEnabled())
303: log.info("Adding folder " + fName
304: + " to workspace.");
305: DefaultMutableTreeNode fNode = organizer
306: .addFolderToWorkspace(fName, parentNode);
307: // (this will set the new selected node to be that folder)
308: // also, it returns the new workspace node
309: // recurse into the folder contents
310: parse(((Element) child), fNode);
311: }
312: }
313: }
314: return parentNode;
315: }
316:
317: /**
318: * Given the root node in an XML document, find the result dir name, if any.
319: *
320: * @param root a <code>Node</code> at the root of an XML document
321: * @return a <code>String</code> result directory name, possibly null
322: */
323: public String parseResultDirName(Node root) {
324: NodeList children = root.getChildNodes();
325: for (int i = 0; i < children.getLength(); i++) {
326: Node child = children.item(i);
327: if (child.getNodeType() == Node.ELEMENT_NODE) {
328: if (child.getNodeName().equals(RESULTDIR_NODE)) {
329: return ((Element) child).getAttribute(NAME_ATTR);
330: } else if (child.getNodeName().equals(WORKSPACE_NODE)) {
331: return parseResultDirName(child);
332: }
333: }
334: }
335: return null;
336: }
337:
338: }
|