001: /*
002:
003: This software is OSI Certified Open Source Software.
004: OSI Certified is a certification mark of the Open Source Initiative.
005:
006: The license (Mozilla version 1.0) can be read at the MMBase site.
007: See http://www.MMBase.org/license
008:
009: */
010: package org.mmbase.util.transformers;
011:
012: import java.io.*;
013: import java.util.*;
014: import org.mmbase.util.ThreadPools;
015:
016: import org.mmbase.util.logging.*;
017:
018: /**
019: * A CharTransformer which wraps N other CharTransformers, and links them with N - 1 new Threads,
020: * effectively working as a 'chained' transformer.
021: *
022: * The first transformation is done by the ChainedCharTransformer instance itself, after starting
023: * the N - 1 Threads for the other N - 1 transformations.
024: *
025: * If no CharTransformers are added, and 'transform' is called, logically, nothing will happen. Add
026: * the CopyCharTransformer if necessary.
027: *
028: * Schematicly:
029: *
030: <pre>
031:
032: new ChainedCharTransformer().add(T1).add(T2)....add(TN).transform(R, W);
033:
034: ___________ __________ _________
035: / \/ \ / \
036: | R --> PW - PR --> PW -...- PR --> W |
037: | T1 | T2 | | TN |
038: \___________/ \_________/ \_________/
039:
040:
041: R: reader, PR: piped reader, W: writer, PW, piped writer, T1 - TN: transformers
042:
043: </pre>
044: *
045: * @author Michiel Meeuwissen
046: * @since MMBase-1.7
047: * @version $Id: ChainedCharTransformer.java,v 1.26 2007/06/13 07:51:41 michiel Exp $
048: */
049:
050: public class ChainedCharTransformer extends ReaderTransformer implements
051: CharTransformer {
052: private static Logger log = Logging
053: .getLoggerInstance(ChainedCharTransformer.class);
054:
055: private List<CharTransformer> charTransformers = new ArrayList<CharTransformer>();
056:
057: public ChainedCharTransformer() {
058: super ();
059: }
060:
061: /**
062: * Adds a CharTranformer to the chain of CharTransformers. If the
063: * CharTransformer is a ChainedCharTransformer, then it will not
064: * be added itself, but its elements will be added.
065: */
066: public ChainedCharTransformer add(CharTransformer ct) {
067: if (ct instanceof ChainedCharTransformer) {
068: addAll(((ChainedCharTransformer) ct).charTransformers);
069: } else {
070: charTransformers.add(ct);
071: }
072: return this ;
073: }
074:
075: /**
076: * Adds a Collection of CharTranformers to the chain of CharTransformers.
077: *
078: * @throws ClassCastException if collection does not contain only CharTransformers
079: */
080: public ChainedCharTransformer addAll(Collection<CharTransformer> col) {
081: for (CharTransformer c : col) {
082: add(c);
083: }
084: return this ;
085: }
086:
087: /**
088: * @since MMBase-1.9
089: */
090: public ChainedCharTransformer add(CharTransformer... col) {
091: for (CharTransformer c : col) {
092: add(c);
093: }
094: return this ;
095: }
096:
097: /**
098: * Implementation without Threads. Not needed when transforming by String.
099: */
100: public String transform(String string) {
101: for (CharTransformer ct : charTransformers) {
102: string = ct.transform(string);
103: }
104: return string;
105:
106: }
107:
108: // javadoc inherited
109: public Writer transform(Reader startReader, Writer endWriter) {
110: try {
111: PipedReader r = null;
112: Writer w = endWriter;
113: boolean closeWriterAfterUse = false; // This boolean indicates if 'w' must be flushed/closed after use.
114:
115: List<CharTransformerLink> links = new ArrayList<CharTransformerLink>();
116: // keep track of the started threads, needing to wait for them later.
117:
118: // going to loop backward through the list of CharTransformers, and starting threads for
119: // every transformation, besides the last one (which is the first in the chain). This
120: // transformation is performed, and the then started other Threads catch the result.
121:
122: ListIterator<CharTransformer> i = charTransformers
123: .listIterator(charTransformers.size());
124: while (i.hasPrevious()) {
125: CharTransformer ct = i.previous();
126: if (i.hasPrevious()) { // needing a new Thread!
127: r = new PipedReader();
128: CharTransformerLink link = new CharTransformerLink(
129: ct, r, w, closeWriterAfterUse);
130: links.add(link);
131: w = new PipedWriter(r);
132: closeWriterAfterUse = true;
133: ThreadPools.filterExecutor.execute(link);
134: } else { // arrived at first in chain, start transforming
135: ct.transform(startReader, w);
136: if (closeWriterAfterUse) {
137: w.close();
138: }
139: }
140: }
141: // wait until all threads are ready, because only then this transformation is actually
142: // ready
143: for (CharTransformerLink l : links) {
144: try {
145: while (!l.ready()) {
146: synchronized (l) { // make sure we have the lock.
147: l.wait();
148: }
149: }
150: } catch (InterruptedException ie) {
151: log.warn("" + ie);
152: }
153: }
154: } catch (IOException e) {
155: log.error(e.toString());
156: log.info(Logging.stackTrace(e));
157: }
158: return endWriter;
159: }
160:
161: public String toString() {
162: return "CHAINED" + charTransformers;
163: }
164:
165: // main for testing purposes
166: public static void main(String[] args) throws IOException {
167: ChainedCharTransformer t = new ChainedCharTransformer().add(
168: new UnicodeEscaper()).add(new SpaceReducer()).add(
169: new UpperCaser()).add(new Trimmer());
170: System.out.println("Starting transform");
171:
172: t.transform(new InputStreamReader(System.in),
173: new OutputStreamWriter(System.out)).flush();
174: //System.out.println(t.transform(new StringReader("hello world")));
175:
176: System.out.println(t.transform("test test test test "));
177:
178: System.out.println("Finished transform");
179:
180: }
181:
182: }
|