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