001: /*
002: * This file or a portion of this file is licensed under the terms of
003: * the Globus Toolkit Public License, found in file GTPL, or at
004: * http://www.globus.org/toolkit/download/license.html. This notice must
005: * appear in redistributions of this file, with or without modification.
006: *
007: * Redistributions of this Software, with or without modification, must
008: * reproduce the GTPL in: (1) the Software, or (2) the Documentation or
009: * some other similar material which is provided with the Software (if
010: * any).
011: *
012: * Copyright 1999-2004 University of Chicago and The University of
013: * Southern California. All rights reserved.
014: */
015:
016: package org.griphyn.vdl.util;
017:
018: import java.util.TreeMap;
019: import java.util.Map;
020: import java.util.Iterator;
021: import java.io.*;
022: import org.griphyn.common.util.Separator;
023: import org.griphyn.vdl.dax.*;
024: import org.griphyn.vdl.classes.LFN;
025:
026: /**
027: * Convert a dag structure into GraphViz dot format.
028: *
029: * @author Jens-S. Vöckler
030: * @author Yong Zhao
031: * @version $Revision: 50 $
032: */
033: public class DAX2DOT {
034: /**
035: * Separator for strings.
036: */
037: public static final String SEPARATOR = "/";
038:
039: /**
040: * Linefeed element for labels.
041: */
042: public static final String LINEFEED = "\\n";
043:
044: /**
045: * height in inches?
046: */
047: private double m_height;
048:
049: /**
050: * width in inches?
051: */
052: private double m_width;
053:
054: /**
055: * predicate to show the derivation (DV) name.
056: */
057: private boolean m_showDV;
058:
059: /**
060: * predicate to show the transformation (TR) name.
061: */
062: private boolean m_showTR;
063:
064: /**
065: * Maintains namespace to color mappings.
066: */
067: private Map m_color;
068:
069: /**
070: * Maintains the color cycle.
071: */
072: private int m_index;
073:
074: /**
075: * Map of default colors to cycle through for
076: * coloration of job nodes by TR namespace.
077: */
078: private static final String c_color[] = { "#FFAAFF", "#FFFFAA",
079: "#FFAAAA" };
080:
081: /**
082: * Constructor
083: */
084: public DAX2DOT() {
085: m_height = 10;
086: m_width = 8;
087: m_showDV = false;
088: m_showTR = true;
089:
090: m_color = new TreeMap();
091: m_index = 0;
092: }
093:
094: /**
095: * Convenience constructor sets the size of the graph.
096: *
097: * @param h is the height in inches
098: * @param w is the width in inches
099: */
100: public DAX2DOT(double h, double w) {
101: m_height = h;
102: m_width = w;
103: m_showDV = false;
104: m_showTR = true;
105:
106: m_color = new TreeMap();
107: m_index = 0;
108: }
109:
110: /**
111: * Sets the size of the graph.
112: *
113: * @param h is the height in inches
114: * @param w is the width in inches
115: * @see #getHeight()
116: * @see #getWidth()
117: */
118: public void setSize(double h, double w) {
119: m_height = h;
120: m_width = w;
121: }
122:
123: /**
124: * Determines the height of the graph.
125: * @return height in inches
126: * @see #setSize( double, double )
127: * @see #getWidth()
128: */
129: public double getHeight() {
130: return m_height;
131: }
132:
133: /**
134: * Determines the width of the graph.
135: * @return width in inches
136: * @see #setSize( double, double )
137: * @see #getHeight()
138: */
139: public double getWidth() {
140: return m_width;
141: }
142:
143: /**
144: * Determines, if DV identifiers are show.
145: *
146: * @return true, if the DV identifier is shown
147: * @see #setShowDV( boolean )
148: */
149: public boolean getShowDV() {
150: return m_showDV;
151: }
152:
153: /**
154: * Sets the showing of derivation names.
155: *
156: * @param showDV is true to show derivation identifiers.
157: * @see #getShowDV()
158: */
159: public void setShowDV(boolean showDV) {
160: m_showDV = showDV;
161: }
162:
163: /**
164: * Determines, if TR identifiers are show.
165: *
166: * @return true, if the TR identifier is shown
167: * @see #setShowTR( boolean )
168: */
169: public boolean getShowTR() {
170: return m_showTR;
171: }
172:
173: /**
174: * Sets the showing of derivation names.
175: *
176: * @param showTR is true to show derivation identifiers.
177: * @see #getShowTR()
178: */
179: public void setShowTR(boolean showTR) {
180: m_showTR = showTR;
181: }
182:
183: /**
184: * Generates GraphViz .dot format from the specified ADAG
185: * @param adag is the ADAG instance
186: * @return a string representing .dot format
187: */
188: public String toDOT(ADAG adag) throws IOException {
189: // do not show files in the graph by default
190: StringWriter sw = new StringWriter();
191: toDOT(adag, sw, false);
192: return sw.toString();
193: }
194:
195: /**
196: * Generates GraphViz .dot format from the specified ADAG
197: * @param adag is the ADAG instance
198: * @param showFiles if set to true, then display files in the graph
199: * @return a string representing .dot format
200: * @see #toDOT( ADAG, Writer, boolean )
201: * @see #toDOT( ADAG, Writer, boolean, String, String )
202: */
203: public String toDOT(ADAG adag, boolean showFiles)
204: throws IOException {
205: StringWriter sw = new StringWriter();
206: toDOT(adag, sw, showFiles);
207: return sw.toString();
208: }
209:
210: /**
211: * Generates GraphViz .dot format from the specified ADAG
212: * @param adag is the ADAG instance
213: * @param writer is the target to output the dot specification
214: * @param showFiles if set to true, then display files in the graph
215: * @see #toDOT( ADAG, Writer, boolean, String, String )
216: */
217: public void toDOT(ADAG adag, Writer writer, boolean showFiles)
218: throws IOException {
219: toDOT(adag, writer, showFiles, null, null);
220: }
221:
222: /**
223: * Prepares and prints the job node of the graph. The job's unique
224: * identifier assigned in the DAX is taken as the job's identifier,
225: * but the TR, ID, and DV are used as a label.
226: *
227: * @param w is the open file writer to print to
228: * @param j is a Job element.
229: * @param url is the job URL, which may be <code>null</code>.
230: * @return the identifier for the job to connect the graph.
231: */
232: private String showJob(Writer w, Job j, String url)
233: throws IOException {
234: StringBuffer label = new StringBuffer(48);
235: String id = j.getID();
236: String tr = Separator.combine(j.getNamespace(), j.getName(), j
237: .getVersion());
238:
239: label.append(id);
240: if (m_showTR && tr != null)
241: label.append(LINEFEED).append("TR ").append(tr);
242: if (m_showDV) {
243: String dv = Separator.combine(j.getDVNamespace(), j
244: .getDVName(), j.getDVVersion());
245: if (dv != null)
246: label.append(LINEFEED).append("DV ").append(dv);
247: }
248:
249: //
250: // Doug's wish: color by namespace
251: //
252: String color = null;
253: String ns = j.getNamespace(); // may be null!
254: if (ns != null) {
255: if (m_color.containsKey(ns)) {
256: // existing namespace, recycle color
257: color = (String) m_color.get(ns);
258: } else {
259: // insert new color for new namespace
260: color = c_color[m_index];
261: m_index = (m_index + 1) % c_color.length;
262: m_color.put(ns, color);
263: }
264: }
265:
266: // write output for job node
267: w.write(" \"");
268: w.write(id);
269: w.write("\" [label=\"");
270: w.write(label.toString());
271: if (url != null) {
272: w.write("\" URL=\"");
273: w.write(url);
274: w.write(tr);
275: }
276: if (color != null) {
277: w.write("\" color=\"");
278: w.write(color);
279: }
280: w.write("\"]\n");
281: return id;
282: }
283:
284: /**
285: * Prepares and prints the file node of the graph. The file's LFN
286: * will be its unique identifier, and its label.
287: *
288: * @param w is the open file writer to print to
289: * @param f is a Filename element.
290: * @param url is the file URL, which may be <code>null</code>.
291: * @return the identifier for the file to connect the graph.
292: */
293: private String showFile(Writer w, Filename f, String url)
294: throws IOException {
295: String lfn = f.getFilename();
296:
297: // write output for filename node
298: w.write(" \"");
299: w.write(lfn);
300: w.write("\" [color=\"#88");
301: w.write(((f.getLink() & LFN.INPUT) > 0 ? "FF" : "AA"));
302: w.write(((f.getLink() & LFN.OUTPUT) > 0 ? "FF" : "AA"));
303:
304: if (url != null) {
305: w.write("\" URL=\"");
306: w.write(url);
307: w.write(lfn);
308: }
309: w.write("\"]\n");
310: return lfn;
311: }
312:
313: /**
314: * Generates GraphViz .dot format from the specified ADAG, also generates
315: * the client side HTML map for nodes.
316: * @param adag is the ADAG instance
317: * @param writer is the target to output the dot specification
318: * @param showFiles if set to true, then display files in the graph
319: * @param jobURL is the base URL for jobs
320: * @param fileURL is the base URL for files
321: */
322: public void toDOT(ADAG adag, Writer writer, boolean showFiles,
323: String jobURL, String fileURL) throws IOException {
324: this .m_index = 0;
325:
326: writer.write("digraph DAG {\n");
327: writer.write(" size=\"" + m_width + "," + m_height + "\"\n");
328: writer.write(" ratio = fill\n");
329:
330: if (showFiles) {
331: writer.write(" node[shape=parallelogram]\n");
332: for (Iterator i = adag.iterateFilename(); i.hasNext();) {
333: Filename fn = (Filename) i.next();
334: String lfn = showFile(writer, fn, fileURL);
335: }
336:
337: writer
338: .write(" node [shape=ellipse, color=orange, style=filled]\n");
339: for (Iterator i = adag.iterateJob(); i.hasNext();) {
340: Job job = (Job) i.next();
341: String jid = showJob(writer, job, jobURL);
342:
343: for (Iterator j = job.iterateUses(); j.hasNext();) {
344: Filename fn = (Filename) j.next();
345: String lfn = fn.getFilename();
346:
347: // this covers in, out, and io (two arrows)
348: if ((fn.getLink() & LFN.INPUT) > 0)
349: writer.write(" \"" + lfn + "\" -> \"" + jid
350: + "\"\n");
351: if ((fn.getLink() & LFN.OUTPUT) > 0)
352: writer.write(" \"" + jid + "\" -> \"" + lfn
353: + "\"\n");
354:
355: }
356: }
357: } else {
358: writer
359: .write(" node [shape=ellipse, color=orange, style=filled]\n");
360: for (Iterator i = adag.iterateJob(); i.hasNext();) {
361: Job job = (Job) i.next();
362: String jid = showJob(writer, job, jobURL);
363: }
364:
365: for (Iterator c = adag.iterateChild(); c.hasNext();) {
366: Child chld = (Child) c.next();
367: String ch = chld.getChild();
368: Job cjob = adag.getJob(ch);
369: String cid = cjob.getID();
370:
371: for (Iterator p = chld.iterateParent(); p.hasNext();) {
372: String pr = (String) p.next();
373: Job pjob = adag.getJob(pr);
374: String pid = pjob.getID();
375: writer.write(" \"" + pid + "\" -> \"" + cid
376: + "\"\n");
377: }
378: }
379: }
380:
381: writer.write("}\n");
382: writer.flush();
383: }
384:
385: /**
386: * Simple test
387: */
388: public static void main(String[] args) throws IOException {
389: ADAG adag = new ADAG();
390: Job A = new Job("ns1", "trA", null, "ID000001");
391: Job B = new Job("ns2", "trB", null, "ID000002");
392: Job C = new Job("ns3", "trC", null, "ID000003");
393: Job D = new Job(null, "trD", null, "ID000004");
394: A.setDV("ns2", "dvA", null);
395: B.setDV("ns2", "dvB", null);
396: C.setDV("ns3", "dvC", null);
397: D.setDV("ns3", "dvD", null);
398:
399: A.addUses(new Filename("f.1", LFN.INPUT));
400: adag
401: .addFilename("f.1", true, "true", false,
402: LFN.XFER_MANDATORY);
403: A.addUses(new Filename("f.2", LFN.OUTPUT));
404: adag.addFilename("f.2", false, "true", false,
405: LFN.XFER_MANDATORY);
406:
407: B.addUses(new Filename("f.2", LFN.INPUT));
408: adag
409: .addFilename("f.2", true, "true", false,
410: LFN.XFER_MANDATORY);
411: B.addUses(new Filename("f.3", LFN.OUTPUT));
412: adag.addFilename("f.3", false, "true", false,
413: LFN.XFER_MANDATORY);
414:
415: C.addUses(new Filename("f.2", LFN.INPUT));
416: adag
417: .addFilename("f.2", true, "true", false,
418: LFN.XFER_MANDATORY);
419: C.addUses(new Filename("f.4", LFN.OUTPUT));
420: adag.addFilename("f.4", false, "true", false,
421: LFN.XFER_MANDATORY);
422:
423: D.addUses(new Filename("f.3", LFN.INPUT));
424: adag
425: .addFilename("f.3", true, "true", false,
426: LFN.XFER_MANDATORY);
427: D.addUses(new Filename("f.4", LFN.INPUT));
428: adag
429: .addFilename("f.4", true, "true", false,
430: LFN.XFER_MANDATORY);
431: D.addUses(new Filename("f.5", LFN.OUTPUT));
432: adag.addFilename("f.5", false, "true", false,
433: LFN.XFER_MANDATORY);
434:
435: adag.addJob(A);
436: adag.addJob(B);
437: adag.addJob(C);
438: adag.addJob(D);
439: adag.addChild("ID000003", "ID000001");
440: adag.addChild("ID000003", "ID000002");
441: adag.addChild("ID000004", "ID000003");
442: DAX2DOT d2d = new DAX2DOT(5, 5);
443: d2d.setShowDV(true);
444: String dot = d2d.toDOT(adag, true);
445: System.out.println(dot);
446: }
447: }
|