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: package org.griphyn.vdl.classes;
016:
017: import org.griphyn.common.util.Separator;
018: import java.util.*;
019: import java.io.IOException;
020: import java.io.Writer;
021: import java.io.Serializable;
022:
023: /**
024: * <code>Derivation</code> is an implementation of an abstract VDL
025: * <code>Definition</code>. A derivation describes the mutable part
026: * concerning input, processing, and output (IPO) when calling an
027: * application. The environment is part of the capture.
028: *
029: * A derivation parametrizes the template provided by a
030: * <code>Transformation</code> with actual values. Think of a derivation
031: * as something akin to a C function call. The derivation provides the
032: * actual parameter to a job. The immutable parts are hidden in a
033: * <code>Transformation</code>.
034: *
035: * @author Jens-S. Vöckler
036: * @author Yong Zhao
037: * @version $Revision: 50 $
038: *
039: * @see Definition
040: * @see Definitions
041: * @see Transformation
042: */
043: public class Derivation extends Definition // thus implements VDL
044: implements HasPass, Serializable {
045: /**
046: * Though most <code>Derivation</code>s may have a name of their own,
047: * most of the times, though, derivations are anonymous. A derivation
048: * must provide the name of the <code>Transformation</code> that
049: * it calls, though.
050: *
051: * @see Definition
052: * @see Transformation
053: */
054: private String m_uses;
055:
056: /**
057: * The namespace in which a derivation resides can differ from the
058: * namespace that the transformation lives in. This argument provides
059: * the namespace of the <code>Transformation</code> to call.
060: * @see Definition
061: * @see Transformation
062: */
063: private String m_usesspace;
064:
065: /**
066: * Any <code>Transformation</code> may exist in multiple versions.
067: * This argument specifies the minimum permissable version that can
068: * be used. FIXME: versioning is not really supported.
069: */
070: private String m_maxIncludeVersion;
071:
072: /**
073: * Any <code>Transformation</code> may exist in multiple versions.
074: * This argument specifies the maximum permissable version that can
075: * be used. FIXME: versioning is not really supported.
076: */
077: private String m_minIncludeVersion;
078:
079: /**
080: * Actual arguments used when calling a {@link Transformation} are
081: * matched up with the formal arguments of the transformation by their
082: * names.
083: */
084: private TreeMap m_passMap;
085:
086: /**
087: * Type accessor for quick distinction between definitions.
088: * @return the value of {@link Definition#DERIVATION}
089: */
090: public int getType() {
091: return Definition.DERIVATION;
092: }
093:
094: /**
095: * ctor.
096: */
097: public Derivation() {
098: super ();
099: this .m_passMap = new TreeMap();
100: }
101:
102: /**
103: * Convenience ctor: Names a derivation and the used
104: * <code>Transformation</code>
105: *
106: * @param name is the name of the <code>Derivation</code>
107: * @param uses is the name of the <code>Transformation</code>
108: * @see Transformation
109: */
110: public Derivation(String name, String uses) {
111: super (name);
112: this .m_passMap = new TreeMap();
113: this .m_uses = uses;
114: }
115:
116: /**
117: * Convenience ctor: Names a derivation and supplies the used
118: * <code>Transformation</code> as well as the permissable version
119: * range.
120: *
121: * @param name is the name of the <code>Derivation</code>.
122: * @param uses is the name of the <code>Transformation</code>.
123: * @param min is the minimum inclusive permissable version.
124: * @param max is the maximum inclusive permissable version.
125: * @see Transformation
126: */
127: public Derivation(String name, String uses, String min, String max) {
128: super (name);
129: this .m_passMap = new TreeMap();
130: this .m_uses = uses;
131: this .m_minIncludeVersion = min;
132: this .m_maxIncludeVersion = max;
133: }
134:
135: /**
136: * Complete ctor: Constructs the full three part <code>Derivation</code>
137: * identifier, and four part <code>Transformation</code> mapper.
138: *
139: * @param ns is then namespace of the <code>Derivation</code>.
140: * @param name is the name of the <code>Derivation</code>.
141: * @param version is the version of the <code>Derivation</code>.
142: * @param us is the namespace to search for a <code>Transformation</code>.
143: * @param uses is the name of the <code>Transformation</code>.
144: * @param min is the minimum inclusive permissable version.
145: * @param max is the maximum inclusive permissable version.
146: * @see Transformation
147: */
148: public Derivation(String ns, String name, String version,
149: String us, String uses, String min, String max) {
150: super (ns, name, version);
151: this .m_usesspace = us;
152: this .m_uses = uses;
153: this .m_minIncludeVersion = min;
154: this .m_maxIncludeVersion = max;
155: this .m_passMap = new TreeMap();
156: }
157:
158: /**
159: * Convenience ctor: Names the derivation and supplies the used
160: * <code>Transformation</code>, and the first actual argument.
161: *
162: * @param name is the name of the <code>Derivation</code>.
163: * @param uses is the name of the <code>Transformation</code>.
164: * @param firstChild is a first (possibly only) actual argument.
165: * @see Transformation
166: * @see Pass
167: */
168: public Derivation(String name, String uses, Pass firstChild) {
169: super (name);
170: this .m_passMap = new TreeMap();
171: this .m_passMap.put(firstChild.getBind(), firstChild);
172: this .m_uses = uses;
173: }
174:
175: /**
176: * Accessor: Adds an actual argument to the bag of arguments.
177: *
178: * @param vPass is the new actual argument to add.
179: * @see Pass
180: */
181: public void addPass(Pass vPass) {
182: this .m_passMap.put(vPass.getBind(), vPass);
183: }
184:
185: /*
186: * won't work with maps
187: *
188: public void addPass( int index, Pass vPass )
189: throws IndexOutOfBoundsException
190: { this.m_passList.insertElementAt(vPass, index); }
191: */
192:
193: /**
194: * Accessor: Provides an iterator for the bag of actual arguments.
195: * @return the iterator for <code>Pass</code> elements.
196: * @see Pass
197: * @see java.util.Enumeration
198: * @deprecated Use the new Collection based interfaces
199: */
200: public Enumeration enumeratePass() {
201: // return this.m_passMap.elements();
202: return Collections.enumeration(this .m_passMap.values());
203: }
204:
205: /**
206: * Determines all LFN instances from the actual arguments of a given
207: * derivation that match the specified linkage. This is a higher-level
208: * method employing the given interface. Note that also linkage of
209: * NONE will not be found in wildcard search mode.
210: *
211: * @param linkage is the linkage type to match against, -1 for all
212: * files.
213: * @return a list of logical filenames from the given derivation which
214: * match the given linkage. For a linkage of -1, complete LFNs will be
215: * returned, for any other linkage, just the filename will be returned.
216: *
217: * @see Value#getLFNList( int )
218: * @see LFN
219: */
220: public java.util.List getLFNList(int linkage) {
221: java.util.List result = new ArrayList();
222:
223: for (Iterator i = this .iteratePass(); i.hasNext();) {
224: Value value = ((Pass) i.next()).getValue();
225: result.addAll(value.getLFNList(linkage));
226: }
227:
228: return result;
229: }
230:
231: /**
232: * Determines if the list contains an LFN of the specified linkage.
233: * The logic uses short-circuit evaluation, thus finding things is
234: * faster than not finding things. Searching a list is a potentially
235: * expensive method.
236: *
237: * @param filename is the name of the LFN
238: * @param linkage is the linkage to check for, -1 for any linkage type.
239: * @return true if the LFN is contained in the scalar, false otherwise.
240: *
241: * @see Value#containsLFN( String, int )
242: * @see LFN
243: */
244: public boolean containsLFN(String filename, int linkage) {
245: for (Iterator i = this .iteratePass(); i.hasNext();) {
246: Value actual = ((Pass) i.next()).getValue();
247: if (actual.containsLFN(filename, linkage))
248: return true;
249: }
250:
251: return false;
252: }
253:
254: /**
255: * Accessor: Obtains the maximum inclusive version permissable for
256: * binding to a {@link Transformation}.
257: *
258: * @return the maximum inclusive version number.
259: * @see #setMaxIncludeVersion( java.lang.String )
260: */
261: public String getMaxIncludeVersion() {
262: return this .m_maxIncludeVersion;
263: }
264:
265: /**
266: * Accessor: Obtains the minimum inclusive version permissable for
267: * binding to a {@link Transformation}.
268: *
269: * @return the minimum inclusive version number.
270: * @see #setMinIncludeVersion( java.lang.String )
271: */
272: public String getMinIncludeVersion() {
273: return this .m_minIncludeVersion;
274: }
275:
276: /**
277: * Accessor: Obtains an actual argument identified by the bound variable.
278: *
279: * @param name is the binding name.
280: * @return the bound value to the given name.
281: * @see Pass
282: */
283: public Pass getPass(String name) {
284: return (Pass) this .m_passMap.get(name);
285: }
286:
287: /**
288: * Accessor: Obtains the bag of actual arguments as array. Note that the
289: * order is arbitrary.
290: *
291: * @return an array containing all bound variables.
292: * @see Pass
293: * @deprecated Use the new Collection based interfaces
294: */
295: public Pass[] getPass() {
296: int size = this .m_passMap.size();
297: Pass[] mPass = new Pass[size];
298: this .m_passMap.values().toArray(mPass);
299: return mPass;
300: }
301:
302: /**
303: * Accessor: Counts the number of actual arguments.
304: *
305: * @return the number of actual arguments in the internal bag.
306: */
307: public int getPassCount() {
308: return this .m_passMap.size();
309: }
310:
311: /**
312: * Accessor: Gets an array of all values that constitute the current
313: * content. This list is read-only.
314: *
315: * @return an array with <code>Pass</code> elements.
316: * @see Pass
317: */
318: public java.util.List getPassList() {
319: return Collections.unmodifiableList(new ArrayList(
320: this .m_passMap.values()));
321: }
322:
323: /**
324: * Accessor: Obtains all actual arguments. The map is a read-only
325: * map to avoid modifications outside the API.
326: *
327: * @return a map will all actual arguments.
328: * @see Pass
329: */
330: public java.util.Map getPassMap() {
331: return Collections.unmodifiableMap(this .m_passMap);
332: }
333:
334: /**
335: * Accessor: Obtains the name of the logical {@link Transformation}
336: * that this derivation refers to.
337: *
338: * @see #setUses( java.lang.String )
339: */
340: public java.lang.String getUses() {
341: return this .m_uses;
342: }
343:
344: /**
345: * Accessor: Obtains the namespace of the logical {@link Transformation}
346: * that this derivation refers to.
347: *
348: * @see #setUsesspace( java.lang.String )
349: */
350: public java.lang.String getUsesspace() {
351: return this .m_usesspace;
352: }
353:
354: /**
355: * Accessor: Provides an iterator for the bag of actual arguments.
356: * @return an iterator to walk the <code>Pass</code> list with.
357: * @see Pass
358: */
359: public Iterator iteratePass() {
360: return this .m_passMap.values().iterator();
361: }
362:
363: /* NOT APPLICABLE
364: *
365: * Accessor: Provides an iterator for the bag of actual arguments.
366: * @return an iterator to walk the <code>Pass</code> list with.
367: * @see Pass
368: *
369: public ListIterator listIteratePass()
370: {
371: return (new ArrayList( this.m_passMap.values() ).listIterator());
372: }
373: */
374:
375: /**
376: * Matches an external version string against the internal range. This
377: * predicate function uses inclusive matches. Special interpretation
378: * will be applied to <code>null</code> values, internally as well as
379: * an external null value.<p>
380: *
381: * <pre>
382: * vers. min max result
383: * ----- ---- ----- ------
384: * null null null true
385: * null * null true
386: * null null * true
387: * null * * true
388: *
389: * * null null true
390: *
391: * "A" "B" null false
392: * "B" "B" null true
393: * "C" "B" null true
394: * "A" null "B" true
395: * "B" null "B" true
396: * "C" null "B" false
397: * "A" "B" "B" false
398: * "B" "B" "B" true
399: * "C" "B" "B" false
400: * </pre>
401: *
402: * @param version is an externally supplied version to be checked,
403: * if it is within the inclusive interval of min and max.
404: * @param minInc is the minimum inclusive version of the range.
405: * @param maxInc is the maximum inclusive version of the range.
406: * @return true, if the version is in range, false otherwise.
407: */
408: public static boolean match(String version, String minInc,
409: String maxInc) {
410: // special null combinations first.
411: if (minInc == null && maxInc == null || version == null)
412: return true;
413:
414: String ver = version.trim();
415: String min = minInc == null ? "" : minInc;
416: String max = maxInc == null ? "" : maxInc;
417: return (ver.compareTo(min) >= 0 && ver.compareTo(max) <= 0);
418: }
419:
420: /**
421: * Instance method for matching an external version against the inclusive
422: * version range.
423: * @param version is an externally supplied version to be checked,
424: * if it is within the inclusive interval of min and max.
425: * @return true, if the version is in range, false otherwise.
426: * @see Derivation#match( String, String, String )
427: */
428: public boolean match(String version) {
429: return Derivation.match(version, this .m_minIncludeVersion,
430: this .m_maxIncludeVersion);
431: }
432:
433: /**
434: * Accessor: Removes all actual arguments. Effectively empties the bag.
435: */
436: public void removeAllPass() {
437: this .m_passMap.clear();
438: }
439:
440: /**
441: * Accessor: Removes a specific actual argument.
442: *
443: * @param name is the bound variable name of the argument to remove.
444: * @return the object that was removed, or null, if not found.
445: * @see Pass
446: */
447: public Pass removePass(String name) {
448: return (Pass) this .m_passMap.remove(name);
449: }
450:
451: /**
452: * Accessor: Sets the maximum inclusive permissable version of
453: * a logical transformation to run with.
454: *
455: * @param miv is the (new) maximum inclusive version.
456: * @see #getMaxIncludeVersion()
457: */
458: public void setMaxIncludeVersion(String miv) {
459: this .m_maxIncludeVersion = miv == null ? null : miv.trim();
460: }
461:
462: /**
463: * Accessor: Sets the minimum inclusive permissable version of
464: * a logical transformation to run with.
465: *
466: * @param miv is the (new) minimum inclusive version.
467: * @see #getMinIncludeVersion()
468: */
469: public void setMinIncludeVersion(String miv) {
470: this .m_minIncludeVersion = miv == null ? null : miv.trim();
471: }
472:
473: /**
474: * Accessor: Adds a new or overwrites an existing actual argument.
475: *
476: * @param vPass is a new actual argument with bound name and value.
477: * @see Pass
478: */
479: public void setPass(Pass vPass) {
480: this .m_passMap.put(vPass.getBind(), vPass);
481: }
482:
483: /**
484: * Accessor: Replaces the bag of actual argument with new arguments.
485: *
486: * @param passArray is the new actual argument list.
487: * @see Pass
488: * @deprecated Use the new Collection based interfaces
489: */
490: public void setPass(Pass[] passArray) {
491: //-- copy array
492: this .m_passMap.clear();
493: for (int i = 0; i < passArray.length; i++) {
494: this .m_passMap.put(passArray[i].getBind(), passArray[i]);
495: }
496: }
497:
498: /**
499: * Accessor: Replaces the bag of actual argument with a bag of
500: * new arguments.
501: *
502: * @param passes is the new actual argument collection.
503: * @see Pass
504: */
505: public void setPass(Collection passes) {
506: this .m_passMap.clear();
507: for (Iterator i = passes.iterator(); i.hasNext();) {
508: Pass p = (Pass) i.next();
509: this .m_passMap.put(p.getBind(), p);
510: }
511: }
512:
513: /**
514: * Accessor: Replaces the bag of actual argument with a map of
515: * new arguments.
516: *
517: * @param passes is the new actual argument map.
518: * @see Pass
519: */
520: public void setPass(Map passes) {
521: this .m_passMap.clear();
522: this .m_passMap.putAll(passes);
523: }
524:
525: /**
526: * Accessor: Sets a new name for a logical <code>Transformation</code>
527: * to call.
528: *
529: * @param uses is the new name of the <code>Transformation</code> to use.
530: * @see #getUses()
531: * @see Transformation
532: */
533: public void setUses(String uses) {
534: this .m_uses = uses;
535: }
536:
537: /**
538: * Accessor: Sets a new namespace identifier for a logical
539: * <code>Transformation</code> to call.
540: *
541: * @param usesspace is the new namespace of the
542: * <code>Transformation</code>.
543: * @see #getUsesspace()
544: * @see Transformation
545: */
546: public void setUsesspace(String usesspace) {
547: this .m_usesspace = usesspace;
548: }
549:
550: /**
551: * Constructs dynamically a short descriptive, hopefully unique
552: * identifier for this derivation. Recent modification add the
553: * complete identification in terms of derivation name, namespace,
554: * and version as well as the called transformation name, namespace
555: * and version range.
556: * FIXME: Anonymous derivations get their hash code, which is well
557: * for the first versions working without database. Later versions
558: * with database must use some unique sequence mechanism instead.
559: *
560: * @return a string describing the derivation
561: * @see Object#hashCode()
562: */
563: public String identify() {
564: StringBuffer result = new StringBuffer();
565:
566: result.append(shortID());
567: result.append("->");
568: result.append(Separator.combine(this .m_usesspace, this .m_uses,
569: this .getMinIncludeVersion(), this
570: .getMaxIncludeVersion()));
571:
572: // // and now for the called part
573: // result.append( shortID(null, this.m_usesspace, this.m_uses, null) );
574: //
575: // String vmin = this.getMinIncludeVersion();
576: // String vmax = this.getMaxIncludeVersion();
577: // if ( vmin != null && vmin.length() > 0 &&
578: // vmax != null && vmax.length() > 0 ) {
579: // result.append(Separator.NAME);
580: // if ( vmin != null ) result.append(vmin);
581: // result.append(Separator.VERSION);
582: // if ( vmax != null ) result.append(vmax);
583: // }
584:
585: // result
586: return result.toString();
587: }
588:
589: /**
590: * Dumps the content of the given element into a string. This function
591: * traverses all sibling classes as necessary and converts the
592: * data into textual output. Note that order of the actual arguments
593: * is not preserved.
594: *
595: * @param stream is a stream opened and ready for writing. This can also
596: * be a string stream for efficient output.
597: * @exception IOException if something fishy happens to the stream.
598: */
599: public void toString(Writer stream) throws IOException {
600: String newline = System.getProperty("line.separator", "\r\n");
601:
602: stream.write("DV ");
603: stream.write(this .identify());
604: stream.write('(');
605:
606: // write arguments
607: if (this .m_passMap.size() > 0) {
608: stream.write(newline);
609: for (Iterator i = this .m_passMap.values().iterator(); i
610: .hasNext();) {
611: stream.write(" ");
612: ((Pass) i.next()).toString(stream);
613: if (i.hasNext())
614: stream.write("," + newline);
615: }
616: }
617:
618: stream.write(" );");
619: stream.write(newline);
620: }
621:
622: /**
623: * Dump the state of the current element as XML output. This function
624: * traverses all sibling classes as necessary, and converts the data
625: * into pretty-printed XML output. The stream interface should be able
626: * to handle large output efficiently, if you use a buffered writer.
627: *
628: * @param stream is a stream opened and ready for writing. This can also
629: * be a string stream for efficient output.
630: * @param indent is a <code>String</code> of spaces used for pretty
631: * printing. The initial amount of spaces should be an empty string.
632: * The parameter is used internally for the recursive traversal.
633: * If a <code>null</code> value is specified, no indentation nor
634: * linefeeds will be generated.
635: * @param namespace is the XML schema namespace prefix. If neither
636: * empty nor null, each element will be prefixed with this prefix,
637: * and the root element will map the XML namespace.
638: * @see org.griphyn.vdl.Chimera#writeAttribute( Writer, String, String )
639: * @exception IOException if something fishy happens to the stream.
640: */
641: public void toXML(Writer stream, String indent, String namespace)
642: throws IOException {
643: String newline = System.getProperty("line.separator", "\r\n");
644: String tag = (namespace != null && namespace.length() > 0) ? namespace
645: + ":derivation"
646: : "derivation";
647:
648: // open tag
649: if (indent != null && indent.length() > 0)
650: stream.write(indent);
651: stream.write('<');
652: stream.write(tag);
653: super .toXML(stream);
654: writeAttribute(stream, " usesspace=\"", this .m_usesspace);
655: writeAttribute(stream, " uses=\"", this .m_uses);
656: writeAttribute(stream, " minIncludeVersion=\"",
657: this .m_minIncludeVersion);
658: writeAttribute(stream, " maxIncludeVersion=\"",
659: this .m_maxIncludeVersion);
660:
661: if (this .m_passMap.size() == 0) {
662: // no actual arguments
663: stream.write("/>");
664: } else {
665: // there are actual arguments
666: stream.write('>');
667: if (indent != null)
668: stream.write(newline);
669:
670: String newindent = indent == null ? null : indent + " ";
671: for (Iterator i = this .m_passMap.values().iterator(); i
672: .hasNext();) {
673: ((Pass) i.next()).toXML(stream, newindent, namespace);
674: }
675:
676: if (indent != null && indent.length() > 0)
677: stream.write(indent);
678: stream.write("</");
679: stream.write(tag);
680: stream.write('>');
681: }
682: if (indent != null)
683: stream.write(newline);
684: }
685: }
|