blob: a29b63717fd628274d854f1fa52ee9e35bb05642 [file] [log] [blame]
/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed 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.sitemap;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.caching.CacheableProcessingComponent;
import org.apache.cocoon.components.source.SourceUtil;
import org.apache.cocoon.environment.SourceResolver;
import org.apache.cocoon.generation.Generator;
import org.apache.cocoon.xml.ContentHandlerWrapper;
import org.apache.cocoon.xml.XMLConsumer;
import org.apache.excalibur.source.Source;
import org.apache.excalibur.source.SourceException;
import org.apache.excalibur.source.SourceValidity;
import org.apache.excalibur.source.impl.validity.AggregatedValidity;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Map;
/**
* This generator implements the sitemap content aggregation.
* It combines several parts into one big XML document which is streamed
* into the pipeline.
*
* @author <a href="mailto:giacomo@apache.org">Giacomo Pati</a>
* @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
* @version CVS $Id: ContentAggregator.java,v 1.6 2004/03/05 13:02:58 bdelacretaz Exp $
*/
public class ContentAggregator
extends ContentHandlerWrapper
implements Generator, CacheableProcessingComponent, Serviceable {
/** the root element of the aggregated content */
protected Element rootElement;
/** the parts */
protected ArrayList parts = new ArrayList();
/** Empty Attributes */
private AttributesImpl emptyAttrs = new AttributesImpl();
/** Indicates the position in the stack of the root element of the aggregated content */
private int rootElementIndex;
/** The element used for the current part */
protected Element currentElement;
/** The SourceResolver */
protected SourceResolver resolver;
/** The component manager */
protected ServiceManager manager;
/** This object holds the part parts :) */
protected final class Part {
public String uri;
public Element element;
public Source source;
boolean stripRootElement;
public Part(String uri, Element element, String stripRoot) {
this.uri = uri;
this.element = element;
this.stripRootElement = (stripRoot.equals("yes") || stripRoot.equals("true"));
}
}
/** This object holds an element definition */
protected final class Element {
public String namespace;
public String prefix;
public String name;
public Element(String name, String namespace, String prefix) {
this.namespace = namespace;
this.prefix = prefix;
this.name = name;
}
}
/**
* generates the content
*/
public void generate()
throws IOException, SAXException, ProcessingException {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Generating aggregated content");
}
this.contentHandler.startDocument();
this.startElem(this.rootElement);
try {
for (int i = 0; i < this.parts.size(); i++) {
final Part part = (Part)this.parts.get(i);
this.rootElementIndex = (part.stripRootElement ? -1 : 0);
if (part.element != null) {
this.currentElement = part.element;
this.startElem(part.element);
} else {
this.currentElement = this.rootElement;
}
try {
SourceUtil.parse(this.manager, part.source, this);
} finally {
if (part.element != null) {
this.endElem(part.element);
}
}
}
} finally {
this.endElem(this.rootElement);
this.contentHandler.endDocument();
}
getLogger().debug("Finished aggregating content");
}
/**
* Generate the unique key.
* This key must be unique inside the space of this component.
*
* @return The generated key hashes the src
*/
public java.io.Serializable getKey() {
try {
StringBuffer buffer = new StringBuffer();
buffer.append("CA(")
.append(this.rootElement.prefix).append(':')
.append(this.rootElement.name).append('<')
.append(this.rootElement.namespace).append(">)");
Source current;
for (int i = 0; i < this.parts.size(); i++) {
final Part part = (Part)this.parts.get(i);
current = part.source;
if (part.element == null) {
buffer.append("P=")
.append(part.stripRootElement).append(':')
.append(current.getURI()).append(';');
} else {
buffer.append("P=")
.append(part.element.prefix).append(':')
.append(part.element.name)
.append('<').append(part.element.namespace).append(">:")
.append(part.stripRootElement).append(':')
.append(current.getURI()).append(';');
}
}
return buffer.toString();
} catch (Exception e) {
getLogger().error("Could not generateKey", e);
return null;
}
}
/**
* Generate the validity object.
*
* @return The generated validity object or <code>null</code> if the
* component is currently not cacheable.
*/
public SourceValidity getValidity() {
try {
AggregatedValidity v = new AggregatedValidity();
Source current;
for (int i = 0; i < this.parts.size(); i++) {
current = ((Part)this.parts.get(i)).source;
SourceValidity sv = current.getValidity();
if (sv == null) {
return null;
} else {
v.add(sv);
}
}
return v;
} catch (Exception e) {
getLogger().error("Could not getValidity", e);
return null;
}
}
/**
* Set the root element. Please make sure that the parameters are not null!
*/
public void setRootElement(String element, String namespace, String prefix) {
this.rootElement = new Element(element,
namespace,
prefix);
if (getLogger().isDebugEnabled()) {
getLogger().debug("Root element='" + element +
"' ns='" + namespace + "' prefix='" + prefix + "'");
}
}
/**
* Add a part. Please make sure that the parameters are not null!
*/
public void addPart(String uri,
String element,
String namespace,
String stripRootElement,
String prefix) {
Element elem = null;
if (!element.equals("")) {
if (namespace.equals("")) {
elem = new Element(element,
this.rootElement.namespace,
this.rootElement.prefix);
} else {
elem = new Element(element,
namespace,
prefix);
}
}
this.parts.add(new Part(uri,
elem,
stripRootElement));
if (getLogger().isDebugEnabled()) {
getLogger().debug("Part uri='" + uri +
"' element='" + element + "' ns='" + namespace +
"' stripRootElement='" + stripRootElement + "' prefix='" + prefix + "'");
}
}
/**
* Set the <code>XMLConsumer</code> that will receive XML data.
* <br>
* This method will simply call <code>setContentHandler(consumer)</code>
* and <code>setLexicalHandler(consumer)</code>.
*/
public void setConsumer(XMLConsumer consumer) {
this.setContentHandler(consumer);
this.setLexicalHandler(consumer);
}
/**
* Recycle the producer by removing references
*/
public void recycle() {
super.recycle();
this.rootElement = null;
for(int i=0; i<this.parts.size();i++) {
final Part current = (Part)this.parts.get(i);
if (current.source != null) {
if (getLogger().isDebugEnabled()) {
getLogger().debug("Releasing " + String.valueOf(current.source));
}
this.resolver.release(current.source);
}
}
this.parts.clear();
this.currentElement = null;
this.resolver = null;
}
/**
* Set the <code>SourceResolver</code>, object model <code>Map</code>,
* the source and sitemap <code>Parameters</code> used to process the request.
*/
public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par)
throws ProcessingException, SAXException, IOException {
this.resolver = resolver;
// get the Source for each part
try {
for(int i=0; i<this.parts.size();i++) {
final Part current = (Part)this.parts.get(i);
current.source = resolver.resolveURI(current.uri);
}
} catch (SourceException se) {
throw SourceUtil.handle("Unable to resolve source.", se);
}
}
/**
* Private method generating startElement event for the aggregated parts
* and the root element
*/
private void startElem(Element element)
throws SAXException {
final String qname = (element.prefix.equals("")) ? element.name : element.prefix + ':' + element.name;
if (!element.namespace.equals("")) {
this.contentHandler.startPrefixMapping(element.prefix, element.namespace);
}
this.contentHandler.startElement(element.namespace, element.name, qname, this.emptyAttrs);
}
/**
* Private method generating endElement event for the aggregated parts
* and the root element
*/
private void endElem(Element element) throws SAXException {
final String qname = (element.prefix.equals("")) ? element.name : element.prefix + ':' + element.name;
this.contentHandler.endElement(element.namespace, element.name, qname);
if (!element.namespace.equals("")) {
this.contentHandler.endPrefixMapping(element.prefix);
}
}
/**
* Ignore start and end document events
*/
public void startDocument() throws SAXException {
}
/**
* Ignore start and end document events
*/
public void endDocument() throws SAXException {
}
/**
* Override startElement() event to add namespace and prefix
*/
public void startElement(String namespaceURI, String localName, String raw, Attributes atts)
throws SAXException {
this.rootElementIndex++;
if (this.rootElementIndex == 0) {
getLogger().debug("Skipping root element start event.");
return;
}
if (namespaceURI == null || namespaceURI.equals("")) {
final String qname = this.currentElement.prefix.equals("") ? localName : this.currentElement.prefix + ':' + localName;
this.contentHandler.startElement(this.currentElement.namespace, localName, qname, atts);
} else {
this.contentHandler.startElement(namespaceURI, localName, raw, atts);
}
}
/**
* Override startElement() event to add namespace and prefix
*/
public void endElement(String namespaceURI, String localName, String raw) throws SAXException {
this.rootElementIndex--;
if (this.rootElementIndex == -1) {
getLogger().debug("Skipping root element end event.");
return;
}
if (namespaceURI == null || namespaceURI.equals("")) {
final String qname = this.currentElement.prefix.equals("") ? localName : this.currentElement.prefix + ':' + localName;
this.contentHandler.endElement(this.currentElement.namespace, localName, qname);
} else {
this.contentHandler.endElement(namespaceURI, localName, raw);
}
}
/* (non-Javadoc)
* @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
*/
public void service(ServiceManager manager) throws ServiceException {
this.manager = manager;
}
}