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;
016:
017: import java.io.*;
018: import java.text.*;
019: import org.griphyn.vdl.util.Logging;
020:
021: /**
022: * This abstract class defines a common base for all JAPI Chimera objects.
023: * All VDL-related classes must conform to this interface, in order to
024: * make various instances available as a reference to this class.
025: *
026: * @author Jens-S. Vöckler
027: * @author Yong Zhao
028: * @version $Revision: 50 $
029: */
030: public abstract class Chimera {
031: /**
032: * Escapes certain characters inappropriate for textual output.
033: *
034: * @param original is a string that needs to be quoted
035: * @return a string that is "safe" to print.
036: */
037: static public String escape(String original) {
038: if (original == null)
039: return null;
040: StringBuffer result = new StringBuffer(2 * original.length());
041: StringCharacterIterator i = new StringCharacterIterator(
042: original);
043: for (char ch = i.first(); ch != i.DONE; ch = i.next()) {
044: if (ch == '\r') {
045: result.append("\\r");
046: } else if (ch == '\n') {
047: result.append("\\n");
048: } else if (ch == '\t') {
049: result.append("\\t");
050: } else {
051: // Chimera bugzilla bug#21
052: // Do not escape apostrophe unless it is required to escape
053: // it in the input.
054: if (ch == '\"' || ch == '\\')
055: result.append('\\');
056: result.append(ch);
057: }
058: }
059:
060: return result.toString();
061: }
062:
063: /**
064: * Escapes certain characters inappropriate for XML content output.
065: * FIXME: Quotes within attribute values are still not handled correctly.
066: *
067: * @param original is a string that needs to be quoted
068: * @param isAttribute denotes an attributes value, if set to true.
069: * If false, it denotes regular XML content outside of attributes.
070: * @return a string that is "safe" to print as XML.
071: */
072: static public String quote(String original, boolean isAttribute) {
073: if (original == null)
074: return null;
075: StringBuffer result = new StringBuffer(2 * original.length());
076: StringCharacterIterator i = new StringCharacterIterator(
077: original);
078: for (char ch = i.first(); ch != i.DONE; ch = i.next()) {
079: switch (ch) {
080: case '<':
081: result.append("<");
082: break;
083: case '&':
084: result.append("&");
085: break;
086: case '>':
087: result.append(">");
088: break;
089: case '\'':
090: result.append("'");
091: break;
092: case '\"':
093: result.append(""");
094: break;
095: default:
096: result.append(ch);
097: break;
098: }
099: }
100:
101: return result.toString();
102: }
103:
104: /**
105: * Dumps content of the given element into a string. This function
106: * traverses all sibling classes as necessary and converts the
107: * data into textual output.<p>
108: *
109: * Sibling classes which represent small leaf objects, and can return
110: * the necessary data more efficiently, are encouraged to overwrite
111: * this method.
112: *
113: * @return a textual description of the element and its sub-classes.
114: * Be advised that these strings might become large.
115: */
116: public String toString() {
117: StringWriter sw = new StringWriter();
118: try {
119: this .toString(sw);
120: sw.flush();
121: } catch (IOException ioe) {
122: Logging.instance().log("default", 0, ioe.toString());
123: }
124: return sw.toString();
125: }
126:
127: /**
128: * Dumps the content of the given element into a stream. This function
129: * traverses all sibling classes as necessary and converts the
130: * data into textual output.
131: *
132: * @param s is a stream opened and ready for writing. This can also be
133: * a string stream for efficient output. The stream interface should
134: * be able to handle large elements efficiently.
135: * @exception IOException if something fishy happens to the stream.
136: */
137: public abstract void toString(Writer s) throws IOException;
138:
139: /**
140: * XML write helper method writes a quoted attribute onto a stream.
141: * The terminating quote will be appended automatically. Values will
142: * be XML-escaped. No action will be taken, if the value is null.
143: *
144: * @param stream is the stream to append to
145: * @param key is the attribute including initial space, attribute name,
146: * equals sign, and opening quote.
147: * @param value is a string value, which will be put within the quotes
148: * and which will be escaped. If the value is null, no action will be
149: * taken
150: * @exception IOException for stream errors.
151: */
152: public void writeAttribute(Writer stream, String key, String value)
153: throws IOException {
154: if (value != null) {
155: stream.write(key);
156: stream.write(quote(value, true));
157: stream.write('"');
158: }
159: }
160:
161: /**
162: * Dumps the state of the current element as XML output. This function
163: * traverses all sibling classes as necessary, and converts the data
164: * into pretty-printed XML output.<p>
165: *
166: * Sibling classes which represent small leaf objects, and can return
167: * the necessary data more efficiently, are encouraged to overwrite
168: * this method.
169: *
170: * @param indent is a <code>String</code> of spaces used for pretty
171: * printing. The initial amount of spaces should be an empty string.
172: * The parameter is used internally for the recursive traversal.
173: * If null, avoidable whitespaces in the output will be avoided.
174: * @param namespace is the XML schema namespace prefix. If neither
175: * empty nor null, each element will be prefixed with this prefix,
176: * and the root element will map the XML namespace.
177: * @return a String which contains the state of the current class
178: * and its siblings using XML. Note that these strings might become large.
179: */
180: public String toXML(String indent, String namespace) {
181: StringWriter sw = new StringWriter();
182: try {
183: this .toXML(sw, indent, namespace);
184: sw.flush();
185: } catch (IOException ioe) {
186: Logging.instance().log("default", 0, ioe.toString());
187: }
188: return sw.toString();
189: }
190:
191: /**
192: * Provides backward compatibility.
193: * <pre>
194: * toXML( stream, indent, (String) null );
195: * </pre>
196: *
197: * @param stream is a stream opened and ready for writing. This can also
198: * be a string stream for efficient output.
199: * @param indent is a <code>String</code> of spaces used for pretty
200: * printing. The initial amount of spaces should be an empty string.
201: * The parameter is used internally for the recursive traversal.
202: * If a <code>null</code> value is specified, no indentation nor
203: * linefeeds will be generated.
204: * @exception IOException if something fishy happens to the stream.
205: * @see #toXML( Writer, String, String )
206: */
207: public void toXML(Writer stream, String indent) throws IOException {
208: toXML(stream, indent, (String) null);
209: }
210:
211: /**
212: * Dump the state of the current element as XML output. This function
213: * traverses all sibling classes as necessary, and converts the data
214: * into pretty-printed XML output. The stream interface should be able
215: * to handle large output efficiently, if you used a buffered writer.
216: *
217: * @param stream is a stream opened and ready for writing. This can also
218: * be a string stream for efficient output.
219: * @param indent is a <code>String</code> of spaces used for pretty
220: * printing. The initial amount of spaces should be an empty string.
221: * The parameter is used internally for the recursive traversal.
222: * If a <code>null</code> value is specified, no indentation nor
223: * linefeeds will be generated.
224: * @param namespace is the XML schema namespace prefix. If neither
225: * empty nor null, each element will be prefixed with this prefix,
226: * and the root element will map the XML namespace.
227: * @exception IOException if something fishy happens to the stream.
228: * @see java.io.BufferedWriter
229: */
230: public abstract void toXML(Writer stream, String indent,
231: String namespace) throws IOException;
232: }
|