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.vdl.classes.*;
018: import java.util.*;
019: import java.io.IOException;
020: import java.io.Writer;
021: import java.io.Serializable;
022:
023: /**
024: * This class implements one of the argument types for parameters passed
025: * to transformations from derivations.
026: *
027: * @author Jens-S. Vöckler
028: * @author Yong Zhao
029: * @version $Revision: 50 $
030: *
031: * @see Value
032: * @see List
033: */
034: public class Scalar extends Value implements Cloneable, Serializable {
035: /**
036: * Any value passed down is an arbitrary mix of the three potential
037: * {@link Leaf} types.
038: *
039: * @see Leaf
040: * @see Text
041: * @see LFN
042: */
043: private ArrayList m_leafList;
044:
045: /**
046: * Creates and returns a copy of this object.
047: * @return a new instance.
048: */
049: public Object clone() {
050: Scalar result = new Scalar();
051: for (int index = 0; index < this .m_leafList.size(); ++index) {
052: result.addLeaf((Leaf) this .getLeaf(index).clone());
053: }
054: return result;
055: }
056:
057: /**
058: * Default ctor.
059: */
060: public Scalar() {
061: super ();
062: this .m_leafList = new ArrayList();
063: }
064:
065: /**
066: * Convenience ctor: Initializes the object with the first child to
067: * be put into the list of values.
068: *
069: * @param firstChild is either a <code>LFN</code> or <code>Text</code>
070: * object.
071: *
072: * @see Leaf
073: * @see LFN
074: * @see Text
075: */
076: public Scalar(Leaf firstChild) {
077: super ();
078: this .m_leafList = new ArrayList();
079: this .m_leafList.add(firstChild);
080: }
081:
082: /**
083: * Accessor: Obtains the value type of this class. By using the abstract
084: * method in the parent class, <code>Scalar</code> objects can be
085: * distinguished from <code>List</code> objects without using the
086: * <code>instanceof</code> operator.
087: *
088: * @return the fixed value of being a scalar.
089: * @see Value#SCALAR
090: */
091: public int getContainerType() {
092: // always
093: return Value.SCALAR;
094: }
095:
096: /**
097: * This method determines which container is being used in the abstract
098: * base class in order to kludgy statements when printing debug info.
099: *
100: * @return the symblic identifier for the type of the Value.
101: */
102: public String getSymbolicType() {
103: // always
104: return new String("Scalar");
105: }
106:
107: /**
108: * Accessor: Adds a <code>Leaf</code> value to the list of values gathered
109: * as the content of a <code>Scalar</code>.
110: *
111: * @param vLeaf is the value to append to the list. Note that only leaf
112: * values of <code>LFN</code> or <code>Text</code> are allowed.
113: * @throws IndexOutOfBoundsException if the value cannot be added.
114: * @see Leaf
115: * @see Text
116: * @see LFN
117: */
118: public void addLeaf(Leaf vLeaf) throws IndexOutOfBoundsException {
119: this .m_leafList.add(vLeaf);
120: }
121:
122: /**
123: * Accessor: Inserts a <code>Leaf</code> value into a specific position
124: * of the list of gathered values.
125: *
126: * @param index is the position to insert the item into
127: * @param vLeaf is the value to append to the list. Note that only leaf
128: * values of <code>LFN</code> or <code>Text</code> are allowed.
129: * @throws IndexOutOfBoundsException if the value cannot be added.
130: *
131: * @see Text
132: * @see LFN
133: */
134: public void addLeaf(int index, Leaf vLeaf)
135: throws IndexOutOfBoundsException {
136: this .m_leafList.add(index, vLeaf);
137: }
138:
139: /**
140: * Accessor: Enumerates the internal values that constitute the content
141: * of the <code>Scalar</code> element.
142: *
143: * @return an enumeration to walk the list with.
144: * @deprecated Use the new Collection based interfaces
145: */
146: public Enumeration enumerateLeaf() {
147: return Collections.enumeration(this .m_leafList);
148: }
149:
150: /**
151: * Determines all LFN instances of a given scalar that match the
152: * specified linkage. This is a higher-level method employing the
153: * given API.
154: *
155: * @param linkage is the linkage to check for, -1 for all filenames.
156: * @return a set of logical filename instances that match the linkage
157: * and were part of the scalar. The result may be an empty set, if no
158: * such result were to be found.
159: *
160: * @see LFN
161: */
162: public java.util.List getAllLFN(int linkage) {
163: java.util.List result = new ArrayList();
164:
165: for (Iterator i = iterateLeaf(); i.hasNext();) {
166: Leaf leaf = (Leaf) i.next();
167: if (leaf instanceof LFN) {
168: LFN lfn = (LFN) leaf;
169: if (linkage == -1 || lfn.getLink() == linkage)
170: result.add(lfn); // add *all* information about this file
171: }
172: }
173: return result;
174: }
175:
176: /**
177: * Determines all LFN instances of a given scalar that match the
178: * specified linkage. This is a higher-level method employing the
179: * given API. Note that also linkage of NONE will not be found in
180: * wildcard search mode.
181: *
182: * @param linkage is the linkage to check for, -1 for all filenames.
183: * @return a set of all logical filenames that match the linkage and
184: * were part of the scalar. The result may be an empty set, if no such
185: * result were to be found. For a linkage of -1, complete LFNs will be
186: * returned, for any other linkage, just the filename will be
187: * returned.
188: *
189: * @see Derivation#getLFNList( int )
190: * @see LFN
191: */
192: public java.util.List getLFNList(int linkage) {
193: java.util.List result = new ArrayList();
194:
195: for (Iterator i = iterateLeaf(); i.hasNext();) {
196: Leaf leaf = (Leaf) i.next();
197: if (leaf instanceof LFN) {
198: LFN local = (LFN) leaf;
199: if (linkage == -1 && local.getLink() != LFN.NONE) {
200: result.add(local); // we need *all* information about this file
201: } else if (local.getLink() == linkage) {
202: result.add(local.getFilename()); // we may know some things
203: }
204: }
205: }
206: return result;
207: }
208:
209: /**
210: * Determines if the scalar contains an LFN of the specified linkage.
211: * The logic uses short-circuit evaluation, thus finding things is
212: * faster than not finding things.
213: *
214: * @param filename is the name of the LFN
215: * @param linkage is the linkage to check for, -1 for any linkage type.
216: * @return true if the LFN is contained in the scalar, false otherwise.
217: *
218: * @see org.griphyn.vdl.classes.LFN
219: */
220: public boolean containsLFN(String filename, int linkage) {
221: // sanity checks
222: if (filename == null)
223: throw new NullPointerException(
224: "You are searching for a non-existing filename");
225:
226: for (Iterator i = this .iterateLeaf(); i.hasNext();) {
227: Leaf leaf = (Leaf) i.next();
228: if (leaf instanceof LFN) {
229: int l_link = ((LFN) leaf).getLink();
230: String l_name = ((LFN) leaf).getFilename();
231: if (linkage == -1 && l_link != LFN.NONE) {
232: if (filename.equals(l_name))
233: return true;
234: } else if (l_link == linkage && filename.equals(l_name)) {
235: return true;
236: }
237: }
238: }
239:
240: // not found
241: return false;
242: }
243:
244: /**
245: * Accessor: Obtains the <code>Leaf</code> at a certain position in the
246: * list of leaf values.
247: *
248: * @param index is the position in the list to obtain a value from
249: * @return The <code>LFN</code> or <code>Text</code> at the position.
250: * @throws IndexOutOfBoundsException if the index points to an elment
251: * in the list that does not contain any elments.
252: *
253: * @see LFN
254: * @see Text
255: */
256: public Leaf getLeaf(int index) throws IndexOutOfBoundsException {
257: //-- check bound for index
258: if ((index < 0) || (index >= this .m_leafList.size()))
259: throw new IndexOutOfBoundsException();
260:
261: return (Leaf) this .m_leafList.get(index);
262: }
263:
264: /**
265: * Accessor: Gets an array of all values that constitute the current
266: * content. This array is a copy to avoid write-through modifications.
267: *
268: * @return an array with a mixture of either <code>Text</code> or
269: * <code>LFN</code> values.
270: *
271: * @see LFN
272: * @see Text
273: * @deprecated Use the new Collection based interfaces
274: */
275: public Leaf[] getLeaf() {
276: int size = this .m_leafList.size();
277: Leaf[] mLeaf = new Leaf[size];
278: System.arraycopy(this .m_leafList.toArray(new Leaf[0]), 0,
279: mLeaf, 0, size);
280: return mLeaf;
281: }
282:
283: /**
284: * Accessor: Obtains the size of the internal list of {@link Leaf}s.
285: *
286: * @return number of elements that an external array needs to be sized to.
287: */
288: public int getLeafCount() {
289: return this .m_leafList.size();
290: }
291:
292: /**
293: * Accessor: Gets an array of all values that constitute the current
294: * content. This list is read-only.
295: *
296: * @return an array with a mixture of either <code>Text</code> or
297: * <code>LFN</code> values.
298: *
299: * @see LFN
300: * @see Text
301: */
302: public java.util.List getLeafList() {
303: return Collections.unmodifiableList(this .m_leafList);
304: }
305:
306: /**
307: * Accessor: Enumerates the internal values that constitute the content
308: * of the <code>Scalar</code> element.
309: *
310: * @return an iterator to walk the list with.
311: */
312: public Iterator iterateLeaf() {
313: return this .m_leafList.iterator();
314: }
315:
316: /**
317: * Accessor: Enumerates the internal values that constitute the content
318: * of the <code>Scalar</code> element.
319: *
320: * @return an iterator to walk the list with.
321: */
322: public ListIterator listIterateLeaf() {
323: return this .m_leafList.listIterator();
324: }
325:
326: /**
327: * Accessor: Enumerates the internal values that constitute the content
328: * of the <code>Scalar</code> element.
329: *
330: * @param start is the start index
331: * @return a list iterator to walk the list with.
332: */
333: public ListIterator listIterateLeaf(int start) {
334: return this .m_leafList.listIterator(start);
335: }
336:
337: /**
338: * Accessor: Removes the content of the <code>Scalar</code>.
339: */
340: public void removeAllLeaf() {
341: this .m_leafList.clear();
342: }
343:
344: /**
345: * Accessor: Remove a single item from the list of nodes. The list is
346: * shrunken in the process.
347: * @param index is the position at which an element is to be removed.
348: * @return the object that was removed. The object is either a
349: * <code>LFN</code> or a <code>Text</code>.
350: *
351: * @see LFN
352: * @see Text
353: */
354: public Leaf removeLeaf(int index) {
355: return (Leaf) this .m_leafList.remove(index);
356: }
357:
358: /**
359: * Accessor: Overwrites a <code>LFN</code> or <code>Text</code> value
360: * at a certain position in the content-constituting list.
361: *
362: * @param index position to overwrite an elment in.
363: * @param vLeaf is either a <code>LFN</code> or <code>Text</code> object.
364: * @throws IndexOutOfBoundsException if the position pointed to is invalid.
365: *
366: * @see LFN
367: * @see Text
368: */
369: public void setLeaf(int index, Leaf vLeaf)
370: throws IndexOutOfBoundsException {
371: //-- check bounds for index
372: if ((index < 0) || (index >= this .m_leafList.size())) {
373: throw new IndexOutOfBoundsException();
374: }
375: this .m_leafList.set(index, vLeaf);
376: }
377:
378: /**
379: * Accessor: Overwrites internal list with an external list representing
380: * a <code>Scalar</code> value.
381: *
382: * @param leafArray is the external list of <code>Text</code> or
383: * <code>LFN</code> objects used to overwrite things.
384: * @see Text
385: * @see LFN
386: * @deprecated Use the new Collection based interfaces
387: */
388: public void setLeaf(Leaf[] leafArray) {
389: this .m_leafList.clear();
390: this .m_leafList.addAll(Arrays.asList(leafArray));
391: }
392:
393: /**
394: * Accessor: Overwrites internal list with an external list representing
395: * a <code>Scalar</code> value.
396: *
397: * @param leaves is the external list of <code>Text</code> or
398: * <code>LFN</code> objects used to overwrite things.
399: * @see Text
400: * @see LFN
401: */
402: public void setLeaf(Collection leaves) {
403: this .m_leafList.clear();
404: this .m_leafList.addAll(leaves);
405: }
406:
407: /**
408: * Converts the object state into textual format for human consumption.
409: *
410: * @return a textual description of the element and its sub-classes.
411: * Be advised that these strings might become large.
412: */
413: public String toString() {
414: StringBuffer result = new StringBuffer(40);
415: for (Iterator i = this .m_leafList.iterator(); i.hasNext();) {
416: result.append(((Leaf) i.next()).toString());
417: }
418: return result.toString();
419: }
420:
421: /**
422: * Converts the object state into textual format for human consumption.
423: *
424: * @param stream is a stream opened and ready for writing. This can also
425: * be a string stream for efficient output.
426: */
427: public void toString(Writer stream) throws IOException {
428: for (Iterator i = this .m_leafList.iterator(); i.hasNext();) {
429: ((Leaf) i.next()).toString(stream);
430: }
431: }
432:
433: /**
434: * Dump the state of the current element as XML output. This function
435: * traverses all sibling classes as necessary, and converts the data
436: * into pretty-printed XML output. The stream interface should be able
437: * to handle large output efficiently, if you use a buffered writer.
438: *
439: * @param stream is a stream opened and ready for writing. This can also
440: * be a string stream for efficient output.
441: * @param indent is a <code>String</code> of spaces used for pretty
442: * printing. The initial amount of spaces should be an empty string.
443: * The parameter is used internally for the recursive traversal.
444: * @param namespace is the XML schema namespace prefix. If neither
445: * empty nor null, each element will be prefixed with this prefix,
446: * and the root element will map the XML namespace.
447: * @exception IOException if something fishy happens to the stream.
448: */
449: public void toXML(Writer stream, String indent, String namespace)
450: throws IOException {
451: String tag = (namespace != null && namespace.length() > 0) ? namespace
452: + ":scalar"
453: : "scalar";
454: String newline = System.getProperty("line.separator", "\r\n");
455:
456: // open tag
457: if (indent != null && indent.length() > 0)
458: stream.write(indent);
459: stream.write('<');
460: stream.write(tag);
461: stream.write('>');
462: if (indent != null)
463: stream.write(newline);
464:
465: // dump content
466: String newindent = indent == null ? null : indent + " ";
467: for (Iterator i = this .m_leafList.iterator(); i.hasNext();) {
468: ((Leaf) i.next()).toXML(stream, newindent, namespace);
469: }
470:
471: // finalize
472: if (indent != null && indent.length() > 0)
473: stream.write(indent);
474: stream.write("</");
475: stream.write(tag);
476: stream.write('>');
477: if (indent != null)
478: stream.write(newline);
479: }
480: }
|