You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by pi...@apache.org on 2004/09/29 00:00:16 UTC
svn commit: rev 47427 - cocoon/trunk/src/blocks/scratchpad/java/org/apache/cocoon/transformation
Author: pier
Date: Tue Sep 28 15:00:12 2004
New Revision: 47427
Modified:
cocoon/trunk/src/blocks/scratchpad/java/org/apache/cocoon/transformation/IncludeTransformer.java
Log:
Merging my and Vadim's patches in the IncludeTransformer
Modified: cocoon/trunk/src/blocks/scratchpad/java/org/apache/cocoon/transformation/IncludeTransformer.java
==============================================================================
--- cocoon/trunk/src/blocks/scratchpad/java/org/apache/cocoon/transformation/IncludeTransformer.java (original)
+++ cocoon/trunk/src/blocks/scratchpad/java/org/apache/cocoon/transformation/IncludeTransformer.java Tue Sep 28 15:00:12 2004
@@ -17,6 +17,9 @@
import java.io.IOException;
import java.io.Serializable;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.HashMap;
import java.util.Map;
import org.apache.avalon.framework.parameters.Parameters;
@@ -28,6 +31,7 @@
import org.apache.cocoon.components.source.SourceUtil;
import org.apache.cocoon.components.source.impl.MultiSourceValidity;
import org.apache.cocoon.environment.SourceResolver;
+import org.apache.cocoon.util.NetUtils;
import org.apache.cocoon.xml.IncludeXMLConsumer;
import org.apache.cocoon.xml.NamespacesTable;
import org.apache.excalibur.source.Source;
@@ -44,8 +48,31 @@
*
* <p>Example:</p>
*
- * <p><code><incl:include xmlns="http://apache.org/cocoon/include/1.0"
- * src="cocoon://path/to/include"/></code></p>
+ * <pre>
+ * <incl:include xmlns:incl="http://apache.org/cocoon/include/1.0"
+ * src="cocoon://path/to/include"/>
+ * </pre>
+ *
+ * <p>Parameters to be passed to the included sources can be specified in two ways:
+ * the first one is to encode them onto the source itelf, for example:</p>
+ *
+ * <pre>
+ * <incl:include xmlns:incl="http://apache.org/cocoon/include/1.0"
+ * src="cocoon://path/to/include?paramA=valueA&paramB=valueB"/>
+ * </pre>
+ *
+ * <p>Another approach allows the encoding of parameters to be done automatically by
+ * the transformer, so that one can easily pass parameter name or values containing
+ * the <code>&</code> (amperstand) or <code>=</code> (equals) character, which are
+ * reserved characters in URIs. An example:</p>
+ *
+ * <pre>
+ * <incl:include xmlns:incl="http://apache.org/cocoon/include/1.0"
+ * src="cocoon://path/to/include">
+ * <incl:parameter name="firstParameterName" value="firstParameterValue"/>
+ * <incl:parameter name="other&Para=Name" value="other=Para&Value"/>
+ * </incl:include>
+ * </pre>
*
* <p>An interesting feature of this {@link Transformer} is that it implements the
* {@link CacheableProcessingComponent} interface and provides full support for
@@ -63,15 +90,40 @@
public class IncludeTransformer extends AbstractTransformer
implements Serviceable, Transformer, CacheableProcessingComponent {
+ /** <p>The namespace URI of the elements recognized by this transformer.</p> */
private static final String NS_URI = "http://apache.org/cocoon/include/1.0";
+ /** <p>The name of the element triggering inclusion of sources.</p> */
private static final String INCLUDE_ELEMENT = "include";
+ /** <p>The name of the element defining an included subrequest parameter.</p> */
+ private static final String PARAMETER_ELEMENT = "parameter";
+ /** <p>The name of the attribute indicating the included source URI.</p> */
private static final String SRC_ATTRIBUTE = "src";
+ /** <p>The name of the attribute indicating the parameter name.</p> */
+ private static final String NAME_ATTRIBUTE = "name";
+ /** <p>The name of the attribute indicating the parameter name.</p> */
+ private static final String VALUE_ATTRIBUTE = "value";
+
+ /** <p>The encoding to use for parameter names and values.</p> */
+ private static final String ENCODING = "US-ASCII";
+ /** <p>The {@link ServiceManager} instance associated with this instance.</p> */
private ServiceManager m_manager;
+ /** <p>The {@link SourceResolver} used to resolve included URIs.</p> */
private SourceResolver m_resolver;
+ /** <p>The {@link SourceValidity} instance associated with this request.</p> */
private MultiSourceValidity m_validity;
+ /** <p>A {@link NamespacesTable} used to filter namespace declarations.</p> */
private NamespacesTable m_namespaces;
+ /** <p>A {@link Map} of the parameters to supply to the included source.</p> */
+ private Map x_parameters;
+ /** <p>The source to be included declared in an include element.</p> */
+ private String x_source;
+ /** <p>The current parameter name captured.</p> */
+ private String x_parameter;
+ /** <p>The current parameter value (as a {@link StringBuffer}).</p> */
+ private StringBuffer x_value;
+
/**
* <p>Create a new {@link IncludeTransformer} instance.</p>
*/
@@ -96,9 +148,11 @@
*/
public void setup(SourceResolver resolver, Map om, String src, Parameters parameters)
throws ProcessingException, SAXException, IOException {
+ this.m_namespaces = new NamespacesTable();
this.m_resolver = resolver;
this.m_validity = null;
- this.m_namespaces = new NamespacesTable();
+ this.x_parameters = null;
+ this.x_value = null;
}
/**
@@ -107,10 +161,12 @@
* @see org.apache.avalon.excalibur.pool.Recyclable#recycle()
*/
public void recycle() {
- super.recycle();
+ this.m_namespaces = new NamespacesTable();
this.m_resolver = null;
this.m_validity = null;
- this.m_namespaces = new NamespacesTable();
+ this.x_parameters = null;
+ this.x_value = null;
+ super.recycle();
}
/**
@@ -178,39 +234,93 @@
}
/**
+ * <p>Receive notification of characters.</p>
+ *
+ * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+ */
+ public void characters(char data[], int offset, int length)
+ throws SAXException {
+ /* If we have a parameter value to add to, let's add this chunk */
+ if (this.x_parameter != null) {
+ if (this.x_value == null) this.x_value = new StringBuffer();
+ this.x_value.append(data, offset, length);
+
+ /* Forward this only if we are not inside an include tag */
+ } else if (this.x_source == null) {
+ super.characters(data, offset, length);
+ }
+ }
+
+ /**
* <p>Receive notification of the start of an element.</p>
*
- * @see org.xml.sax.ContentHandler#startElement(String, String, String, org.xml.sax.Attributes)
+ * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
*/
public void startElement(String uri, String localName, String qName, Attributes atts)
throws SAXException {
+ /* Check the namespace declaration */
if (NS_URI.equals(uri)) {
+ /* Inclusion will not happen here but when we close this tag */
if (INCLUDE_ELEMENT.equals(localName)) {
- String src = atts.getValue(SRC_ATTRIBUTE);
- Source source = null;
- try {
- source = m_resolver.resolveURI(src);
- if (m_validity != null) {
- m_validity.addSource(source);
- }
- SourceUtil.toSAX(m_manager, source, "text/xml",
- new IncludeXMLConsumer(super.contentHandler));
+ /* Check before we include (we don't want nested stuff) */
+ if (this.x_source != null) {
+ throw new SAXException("Invalid include nested in another");
}
- catch (IOException e) {
- throw new SAXException(e);
+
+ /* Remember the source we are trying to include */
+ this.x_source = atts.getValue(SRC_ATTRIBUTE);
+ if ((this.x_source == null) || (this.x_source.length() == 0)) {
+ throw new SAXException("Attribute \"" + SRC_ATTRIBUTE
+ + "\" not specified");
}
- catch (ProcessingException e) {
- throw new SAXException(e);
+
+ /* Whatever list of parameters we got before, we wipe it! */
+ this.x_parameters = null;
+ this.x_value = null;
+ this.x_parameter = null;
+
+ /* Done with this element */
+ return;
+ }
+
+ /* If this is a parameter, then make sure we prepare. */
+ if (PARAMETER_ELEMENT.equals(localName)) {
+ /* Check if we are in the right context */
+ if (this.x_source == null) {
+ throw new SAXException("Parameter specified outside of include");
}
- finally {
- if (source != null) {
- m_resolver.release(source);
- }
+ if (this.x_parameter != null) {
+ throw new SAXException("Invalid parameter nested in another");
+ }
+
+ /* Get and process the parameter name */
+ this.x_parameter = atts.getValue(NAME_ATTRIBUTE);
+ if ((this.x_parameter == null) || (this.x_parameter.length() == 0)) {
+ throw new SAXException("Attribute \"" + NAME_ATTRIBUTE
+ + "\" not specified");
}
+
+ /* Make some room for the parameter value */
+ String value = atts.getValue(VALUE_ATTRIBUTE);
+ if (value != null) this.x_value = new StringBuffer(value);
+
+ /* Done with this element */
+ return;
}
- } else {
+
+ /* We don't have a clue of why we got here (wrong element?) */
+ if (this.getLogger().isWarnEnabled()) {
+ this.getLogger().warn("Unknown element \"" + localName + "\"");
+ }
+ return;
+ }
+
+ /* Not our namespace, simply check and pass this element on! */
+ if (this.x_source == null) {
super.startElement(uri, localName, qName, atts);
+ return;
}
+ throw new SAXException("Element <" + qName + "/> invalid inside include");
}
/**
@@ -220,7 +330,69 @@
*/
public void endElement(String uri, String localName, String qName)
throws SAXException {
- if (!NS_URI.equals(uri)) {
+ /* Check the namespace declaration */
+ if (NS_URI.equals(uri)) {
+
+ /* Inclusion will happen here, when we close the include element */
+ if (INCLUDE_ELEMENT.equals(localName)) {
+
+ /* Get the source discovered opening the element and include */
+ Source source = null;
+ try {
+ if (this.x_parameter != null) {
+ this.x_source = NetUtils.parameterize(this.x_source,
+ this.x_parameters);
+ }
+ source = this.m_resolver.resolveURI(this.x_source);
+ if (this.m_validity != null) this.m_validity.addSource(source);
+ SourceUtil.toSAX(this.m_manager, source, "text/xml",
+ new IncludeXMLConsumer(super.contentHandler));
+ } catch (IOException e) {
+ /* Something bad happenend processing a stream */
+ throw new SAXException(e);
+ } catch (ProcessingException e) {
+ /* Something bad happened processing a pipeline */
+ throw new SAXException(e);
+ } finally {
+ /* In any case, make sure we release the source */
+ if (source != null) this.m_resolver.release(source);
+ }
+
+ /* We are done with the include element */
+ this.x_parameters = null;
+ this.x_value = null;
+ this.x_parameter = null;
+ this.x_source = null;
+ return;
+ }
+
+ /* Addition of parameters happens here (so that we can capture chars) */
+ if (PARAMETER_ELEMENT.equals(localName)) {
+ String value = (this.x_value != null? this.x_value.toString(): "");
+
+ /* Store the parameter name and value */
+ try {
+ /*
+ * Note: the parameter name and value are URL encoded, so that
+ * weird characters such as "&" or "=" (have special meaning)
+ * are passed through flawlessly.
+ */
+ if (this.x_parameters == null) this.x_parameters = new HashMap();
+ this.x_parameters.put(URLEncoder.encode(this.x_parameter, ENCODING),
+ URLEncoder.encode(value, ENCODING));
+ } catch (UnsupportedEncodingException e) {
+ throw new SAXException("Your platform does not support the "
+ + ENCODING + " encoding", e);
+ }
+
+ /* We are done with this parameter element */
+ this.x_value = null;
+ this.x_parameter = null;
+ return;
+ }
+
+ } else {
+ /* This is not our namespace, pass the event on! */
super.endElement(uri, localName, qName);
}
}
@@ -231,8 +403,10 @@
* @see CacheableProcessingComponent#getKey()
*/
public Serializable getKey() {
- // FIXME: In case of including "cocoon://" or other dynamic sources
- // key has to be dynamic.
+ /*
+ * FIXME: In case of including "cocoon://" or other dynamic sources
+ * key has to be dynamic.
+ */
return "I";
}