001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.cocoon.generation;
019:
020: import org.apache.avalon.framework.parameters.ParameterException;
021: import org.apache.avalon.framework.parameters.Parameterizable;
022: import org.apache.avalon.framework.parameters.Parameters;
023:
024: import org.apache.cocoon.ProcessingException;
025: import org.apache.cocoon.caching.CacheableProcessingComponent;
026: import org.apache.cocoon.environment.SourceResolver;
027: import org.apache.commons.lang.SystemUtils;
028:
029: import org.apache.excalibur.source.Source;
030: import org.apache.excalibur.source.SourceException;
031: import org.apache.excalibur.source.SourceValidity;
032:
033: import org.xml.sax.SAXException;
034: import org.xml.sax.helpers.AttributesImpl;
035: import org.xml.sax.helpers.LocatorImpl;
036:
037: import java.io.IOException;
038: import java.io.InputStream;
039: import java.io.InputStreamReader;
040: import java.io.LineNumberReader;
041: import java.io.Serializable;
042:
043: import java.util.Map;
044:
045: /**
046: * Read a plain text file and produce a valid XML file.
047: * <pre>
048: * <text xmlns="http://chaperon.sourceforge.net/schema/text/1.0">
049: * Text 123 bla
050: * </text>
051: * </pre>
052: *
053: * @author <a href="mailto:stephan@apache.org">Stephan Michels </a>
054: * @author <a href="mailto:rolf.schumacher@hamburg.de">Rolf Schumacher</a>
055: * @version CVS $Id: TextGenerator.java 433543 2006-08-22 06:22:54Z crossley $
056: */
057: public class TextGenerator extends ServiceableGenerator implements
058: Parameterizable, CacheableProcessingComponent {
059: /** The URI of the text element */
060: public static final String URI = "http://chaperon.sourceforge.net/schema/text/1.0";
061: private static final char[] initNonXmlChars = { ' ', ' ', ' ', ' ',
062: ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
063: // 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
064: ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
065: ' ', ' ', ' ', ' ' };
066:
067: /** The input source */
068: private Source inputSource;
069: private String encoding;
070: private char[] nonXmlChars;
071: private boolean localizable = false;
072:
073: /**
074: * Recycle this component. All instance variables are set to <code>null</code>.
075: */
076: public void recycle() {
077: if (inputSource != null)
078: super .resolver.release(inputSource);
079:
080: inputSource = null;
081: encoding = null;
082: nonXmlChars = null;
083:
084: super .recycle();
085: }
086:
087: /**
088: * Provide component with parameters.
089: *
090: * @param parameters the parameters
091: *
092: * @throws ParameterException if parameters are invalid
093: */
094: public void parameterize(Parameters parameters)
095: throws ParameterException {
096: this .localizable = parameters.getParameterAsBoolean(
097: "localizable", false);
098: }
099:
100: /**
101: * Set the SourceResolver, objectModel Map, the source and sitemap Parameters used to process the
102: * request.
103: *
104: * @param resolver Source resolver
105: * @param objectmodel Object model
106: * @param src Source
107: * @param parameters Parameters
108: *
109: * @throws IOException
110: * @throws ProcessingException
111: * @throws SAXException
112: */
113: public void setup(SourceResolver resolver, Map objectmodel,
114: String src, Parameters parameters)
115: throws ProcessingException, SAXException, IOException {
116: super .setup(resolver, objectmodel, src, parameters);
117: try {
118: this .encoding = parameters.getParameter("encoding", null);
119: this .inputSource = resolver.resolveURI(src);
120:
121: String nXmlCh = parameters.getParameter("nonXmlChars",
122: String.valueOf(initNonXmlChars));
123: if (nXmlCh.length() != initNonXmlChars.length)
124: throw new ProcessingException(
125: "Error during resolving of '" + src + "'.",
126: new SourceException(
127: "length of attribute string 'nonXmlChars' is "
128: + nXmlCh.length()
129: + " where it should be "
130: + initNonXmlChars.length + "!"));
131:
132: this .nonXmlChars = nXmlCh.toCharArray();
133: } catch (SourceException se) {
134: throw new ProcessingException("Error during resolving of '"
135: + src + "'.", se);
136: }
137: }
138:
139: /**
140: * Generate the unique key. This key must be unique inside the space of this component.
141: *
142: * @return The generated key hashes the src
143: */
144: public Serializable getKey() {
145: return inputSource.getURI() + ";localizable=" + localizable
146: + ";encoding=" + encoding;
147: }
148:
149: /**
150: * Generate the validity object.
151: *
152: * @return The generated validity object or <code>null</code> if the component is currently not
153: * cacheable.
154: */
155: public SourceValidity getValidity() {
156: return this .inputSource.getValidity();
157: }
158:
159: /**
160: * Generate XML data.
161: *
162: * @throws IOException
163: * @throws ProcessingException
164: * @throws SAXException
165: */
166: public void generate() throws IOException, SAXException,
167: ProcessingException {
168: InputStreamReader in = null;
169:
170: try {
171: final InputStream sis = this .inputSource.getInputStream();
172: if (sis == null) {
173: throw new ProcessingException("Source '"
174: + this .inputSource.getURI() + "' not found");
175: }
176:
177: if (encoding != null) {
178: in = new InputStreamReader(sis, encoding);
179: } else {
180: in = new InputStreamReader(sis);
181: }
182: } catch (SourceException se) {
183: throw new ProcessingException("Error during resolving of '"
184: + this .source + "'.", se);
185: }
186:
187: LocatorImpl locator = new LocatorImpl();
188:
189: locator.setSystemId(this .inputSource.getURI());
190: locator.setLineNumber(1);
191: locator.setColumnNumber(1);
192:
193: contentHandler.setDocumentLocator(locator);
194: contentHandler.startDocument();
195: contentHandler.startPrefixMapping("", URI);
196:
197: AttributesImpl atts = new AttributesImpl();
198: if (localizable) {
199: atts.addAttribute("", "source", "source", "CDATA", locator
200: .getSystemId());
201: atts.addAttribute("", "line", "line", "CDATA", String
202: .valueOf(locator.getLineNumber()));
203: atts.addAttribute("", "column", "column", "CDATA", String
204: .valueOf(locator.getColumnNumber()));
205: }
206:
207: contentHandler.startElement(URI, "text", "text", atts);
208:
209: LineNumberReader reader = new LineNumberReader(in);
210: String line;
211: String newline = null;
212:
213: while (true) {
214: if (newline == null) {
215: line = convertNonXmlChars(reader.readLine());
216: } else {
217: line = newline;
218: }
219: if (line == null) {
220: break;
221: }
222: newline = convertNonXmlChars(reader.readLine());
223: if (newline != null) {
224: line += SystemUtils.LINE_SEPARATOR;
225: }
226: locator.setLineNumber(reader.getLineNumber());
227: locator.setColumnNumber(1);
228: contentHandler.characters(line.toCharArray(), 0, line
229: .length());
230: if (newline == null) {
231: break;
232: }
233: }
234: reader.close();
235: contentHandler.endElement(URI, "text", "text");
236: contentHandler.endPrefixMapping("");
237: contentHandler.endDocument();
238: }
239:
240: private String convertNonXmlChars(String s) {
241: if (s != null) {
242: int nv;
243: char[] sc = s.toCharArray();
244:
245: for (int i = 0; i < sc.length; i++) {
246: nv = sc[i];
247:
248: if ((nv >= 0) && (nv < nonXmlChars.length)) {
249: //do not convert white space characters
250: if ((nv != 9) && (nv != 10) && (nv != 13))
251: sc[i] = nonXmlChars[nv];
252: }
253: }
254: return String.valueOf(sc);
255: } else {
256: return null;
257: }
258: }
259: }
|