/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cocoon.generation; import org.apache.avalon.framework.parameters.ParameterException; import org.apache.avalon.framework.parameters.Parameterizable; import org.apache.avalon.framework.parameters.Parameters; import org.apache.cocoon.ProcessingException; import org.apache.cocoon.caching.CacheableProcessingComponent; import org.apache.cocoon.environment.SourceResolver; import org.apache.commons.lang.SystemUtils; import org.apache.excalibur.source.Source; import org.apache.excalibur.source.SourceException; import org.apache.excalibur.source.SourceValidity; import org.xml.sax.SAXException; import org.xml.sax.helpers.AttributesImpl; import org.xml.sax.helpers.LocatorImpl; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.io.Serializable; import java.util.Map; /** * Read a plain text file and produce a valid XML file. * <pre> * <text xmlns="http://chaperon.sourceforge.net/schema/text/1.0"> * Text 123 bla * </text> * </pre> * * @author <a href="mailto:stephan@apache.org">Stephan Michels </a> * @author <a href="mailto:rolf.schumacher@hamburg.de">Rolf Schumacher</a> * @version CVS $Id$ */ public class TextGenerator extends ServiceableGenerator implements Parameterizable, CacheableProcessingComponent { /** The URI of the text element */ public static final String URI = "http://chaperon.sourceforge.net/schema/text/1.0"; private static final char[] initNonXmlChars = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', // 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }; /** The input source */ private Source inputSource; private String encoding; private char[] nonXmlChars; private boolean localizable = false; /** * Recycle this component. All instance variables are set to <code>null</code>. */ public void recycle() { if (inputSource!=null) super.resolver.release(inputSource); inputSource = null; encoding = null; nonXmlChars = null; super.recycle(); } /** * Provide component with parameters. * * @param parameters the parameters * * @throws ParameterException if parameters are invalid */ public void parameterize(Parameters parameters) throws ParameterException { this.localizable = parameters.getParameterAsBoolean("localizable", false); } /** * Set the SourceResolver, objectModel Map, the source and sitemap Parameters used to process the * request. * * @param resolver Source resolver * @param objectmodel Object model * @param src Source * @param parameters Parameters * * @throws IOException * @throws ProcessingException * @throws SAXException */ public void setup(SourceResolver resolver, Map objectmodel, String src, Parameters parameters) throws ProcessingException, SAXException, IOException { super.setup(resolver, objectmodel, src, parameters); try { this.encoding = parameters.getParameter("encoding", null); this.inputSource = resolver.resolveURI(src); String nXmlCh = parameters.getParameter("nonXmlChars", String.valueOf(initNonXmlChars)); if (nXmlCh.length() != initNonXmlChars.length) throw new ProcessingException("Error during resolving of '"+src+"'.", new SourceException("length of attribute string 'nonXmlChars' is "+ nXmlCh.length()+" where it should be "+ initNonXmlChars.length+"!")); this.nonXmlChars = nXmlCh.toCharArray(); } catch (SourceException se) { throw new ProcessingException("Error during resolving of '"+src+"'.", se); } } /** * Generate the unique key. This key must be unique inside the space of this component. * * @return The generated key hashes the src */ public Serializable getKey() { return inputSource.getURI() + ";localizable=" + localizable + ";encoding=" + encoding; } /** * Generate the validity object. * * @return The generated validity object or <code>null</code> if the component is currently not * cacheable. */ public SourceValidity getValidity() { return this.inputSource.getValidity(); } /** * Generate XML data. * * @throws IOException * @throws ProcessingException * @throws SAXException */ public void generate() throws IOException, SAXException, ProcessingException { InputStreamReader in = null; try { final InputStream sis = this.inputSource.getInputStream(); if (sis == null) { throw new ProcessingException("Source '" + this.inputSource.getURI() + "' not found"); } if (encoding != null) { in = new InputStreamReader(sis, encoding); } else { in = new InputStreamReader(sis); } } catch (SourceException se) { throw new ProcessingException("Error during resolving of '" + this.source + "'.", se); } LocatorImpl locator = new LocatorImpl(); locator.setSystemId(this.inputSource.getURI()); locator.setLineNumber(1); locator.setColumnNumber(1); contentHandler.setDocumentLocator(locator); contentHandler.startDocument(); contentHandler.startPrefixMapping("", URI); AttributesImpl atts = new AttributesImpl(); if (localizable) { atts.addAttribute("", "source", "source", "CDATA", locator.getSystemId()); atts.addAttribute("", "line", "line", "CDATA", String.valueOf(locator.getLineNumber())); atts.addAttribute("", "column", "column", "CDATA", String.valueOf(locator.getColumnNumber())); } contentHandler.startElement(URI, "text", "text", atts); LineNumberReader reader = new LineNumberReader(in); String line; String newline = null; while (true) { if (newline==null) { line = convertNonXmlChars(reader.readLine()); } else { line = newline; } if (line==null) { break; } newline = convertNonXmlChars(reader.readLine()); if (newline != null) { line += SystemUtils.LINE_SEPARATOR; } locator.setLineNumber(reader.getLineNumber()); locator.setColumnNumber(1); contentHandler.characters(line.toCharArray(), 0, line.length()); if (newline==null) { break; } } reader.close(); contentHandler.endElement(URI, "text", "text"); contentHandler.endPrefixMapping(""); contentHandler.endDocument(); } private String convertNonXmlChars(String s) { if (s != null) { int nv; char[] sc = s.toCharArray(); for (int i = 0; i<sc.length; i++) { nv = sc[i]; if ((nv>=0) && (nv<nonXmlChars.length)) { //do not convert white space characters if ((nv!=9) && (nv!=10) && (nv!=13)) sc[i] = nonXmlChars[nv]; } } return String.valueOf(sc); } else { return null; } } }