| /* |
| * 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.xml; |
| |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.io.Writer; |
| import java.io.IOException; |
| |
| import org.xml.sax.ContentHandler; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.Attributes; |
| |
| /** |
| * Modification of the SAX buffer with parameterization capabilities. |
| * |
| * Any <code>{name}</code> expression inside of the character events can be |
| * replaced by the content of another SaxBuffer if it is present in the map |
| * passed to the {@link #toSAX(ContentHandler, Map)} method. |
| * |
| * @author <a href="mailto:vgritsenko@apache.org">Vadim Gritsenko</a> |
| * @version CVS $Id$ |
| */ |
| public class ParamSaxBuffer extends SaxBuffer { |
| |
| /** |
| * If ch (in characters()) contains an unmatched '{' then |
| * we save the chars from '{' onward in previous_ch. |
| * Next call to characters() prepends the saved chars to ch before processing |
| * (and sets previous_ch to null). |
| */ |
| private char[] previous_ch = null; |
| |
| /** |
| * Creates empty SaxBuffer |
| */ |
| public ParamSaxBuffer() { |
| } |
| |
| /** |
| * Creates copy of another SaxBuffer |
| */ |
| public ParamSaxBuffer(SaxBuffer saxBuffer) { |
| super(saxBuffer); |
| } |
| |
| /** |
| * Parses text and extracts <code>{name}</code> parameters for later |
| * substitution. |
| * '{' is escaped with a preceding '\'. |
| */ |
| public void characters(char ch[], int start, int length) throws SAXException { |
| |
| if (previous_ch != null) { |
| // prepend char's from previous_ch to ch |
| char[] buf = new char[length + previous_ch.length]; |
| System.arraycopy(previous_ch, 0, buf, 0, previous_ch.length); |
| System.arraycopy(ch, start, buf, previous_ch.length, length); |
| ch = buf; |
| start = 0; |
| length += previous_ch.length; |
| previous_ch = null; |
| } |
| |
| final int end = start + length; |
| for (int i = start; i < end; i++) { |
| if (ch[i] == '\\') { |
| if (i + 1 == end) { |
| // if '\' is the last char, flush chars and let's wait to see if a '{' or '\' will come next |
| addBit(new Characters(ch, start, i - start)); |
| previous_ch = new char[]{ch[i]}; |
| return; |
| } |
| if (ch[i + 1] == '{' || ch[i + 1] == '\\') { |
| // Encountered "\{" or "\\", meaning that the next char should be escaped |
| addBit(new Characters(ch, start, i - start)); |
| addBit(new Characters(new char[]{ch[i + 1]}, 0, 1)); |
| i++; |
| start = i + 1; |
| continue; |
| } |
| } |
| else if (ch[i] == '{') { |
| // Send any collected characters so far |
| if (i > start) { |
| addBit(new Characters(ch, start, i - start)); |
| } |
| |
| // Find closing brace, and construct parameter name |
| StringBuffer name = new StringBuffer(); |
| int j = i + 1; |
| for (; j < end; j++) { |
| if (ch[j] == '}') { |
| break; |
| } |
| name.append(ch[j]); |
| } |
| if (j == end) { |
| // '{' without a closing '}' |
| // save char's from '{' in previous_ch in case the following call to characters() |
| // provides the '}' |
| previous_ch = new char[end - i]; |
| System.arraycopy(ch, i, previous_ch, 0, end - i); |
| return; |
| } |
| addBit(new Parameter(name.toString())); |
| |
| // Continue processing |
| i = j; |
| start = j + 1; |
| continue; |
| } |
| } |
| |
| // Send any tailing characters |
| if (start < end) { |
| addBit(new Characters(ch, start, end - start)); |
| } |
| } |
| |
| public void endElement(String namespaceURI, String localName, String qName) throws SAXException { |
| flushChars(); |
| super.endElement(namespaceURI, localName, qName); |
| } |
| |
| public void ignorableWhitespace(char ch[], int start, int length) throws SAXException { |
| flushChars(); |
| super.ignorableWhitespace(ch, start, length); |
| } |
| |
| public void processingInstruction(String target, String data) throws SAXException { |
| flushChars(); |
| super.processingInstruction(target, data); |
| } |
| |
| public void startDocument() throws SAXException { |
| flushChars(); |
| super.startDocument(); |
| } |
| |
| public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { |
| flushChars(); |
| super.startElement(namespaceURI, localName, qName, atts); |
| } |
| |
| public void endDocument() throws SAXException { |
| flushChars(); |
| super.endDocument(); |
| } |
| |
| public void comment(char ch[], int start, int length) throws SAXException { |
| flushChars(); |
| super.comment(ch, start, length); |
| } |
| |
| public void endDTD() throws SAXException { |
| flushChars(); |
| super.endDTD(); |
| } |
| |
| public void startDTD(String name, String publicId, String systemId) throws SAXException { |
| flushChars(); |
| super.startDTD(name, publicId, systemId); |
| } |
| |
| private void flushChars() { |
| // Handle saved chars (in case we had a '{' with no matching '}'). |
| if (previous_ch != null) { |
| addBit(new Characters(previous_ch, 0, previous_ch.length)); |
| previous_ch = null; |
| } |
| } |
| |
| /** |
| * @param parameters map containing SaxBuffers |
| */ |
| public void toSAX(ContentHandler contentHandler, Map parameters) throws SAXException { |
| for (Iterator i = bits(); i.hasNext();) { |
| SaxBit saxbit = (SaxBit)i.next(); |
| if (saxbit instanceof Parameter) { |
| ((Parameter)saxbit).send(contentHandler, parameters); |
| } else { |
| saxbit.send(contentHandler); |
| } |
| } |
| } |
| |
| /** |
| * @param parameters map containing SaxBuffers |
| */ |
| public String toString(Map parameters) throws SAXException { |
| final StringBuffer buffer = new StringBuffer(); |
| for (Iterator i = bits(); i.hasNext();) { |
| SaxBit saxbit = (SaxBit)i.next(); |
| if (saxbit instanceof Parameter) { |
| ((Parameter)saxbit).toString(buffer, parameters); |
| } else if (saxbit instanceof Characters) { |
| ((Characters) saxbit).toString(buffer); |
| } |
| } |
| return buffer.toString(); |
| } |
| |
| |
| final static class Parameter implements SaxBit { |
| private final String name; |
| |
| public Parameter(String name) { |
| this.name = name; |
| } |
| |
| public void send(ContentHandler contentHandler) { |
| } |
| |
| public void send(ContentHandler contentHandler, Map parameters) throws SAXException { |
| SaxBuffer value = (SaxBuffer)parameters.get(name); |
| if (value != null) { |
| value.toSAX(contentHandler); |
| } |
| } |
| |
| public void toString(StringBuffer result, Map parameters) throws SAXException { |
| String value = (String)parameters.get(name); |
| if (value != null) { |
| result.append(value); |
| } |
| } |
| |
| public void dump(Writer writer) throws IOException { |
| writer.write("[Parameter] name=" + name); |
| } |
| } |
| } |