blob: a70f86c07bab76ffc873f0e17cb62c35ee2d46f7 [file] [log] [blame]
/*
* 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.transformation;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.Composable;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.caching.CacheValidity;
import org.apache.cocoon.caching.Cacheable;
import org.apache.cocoon.caching.IncludeCacheValidity;
import org.apache.cocoon.components.source.SourceUtil;
import org.apache.cocoon.environment.SourceResolver;
import org.apache.cocoon.xml.IncludeXMLConsumer;
import org.apache.cocoon.xml.XMLUtils;
import org.apache.excalibur.source.Source;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.util.Map;
/**
* <p>This transformer triggers for the element <code>include</code> in the
* namespace "http://apache.org/cocoon/include/1.0".
* The <code>src</code> attribute contains the url which points to
* an xml resource which is include instead of the element.
* With the attributes <code>element</code>, <code>ns</code> and
* <code>prefix</code> it is possible to specify an element
* which surrounds the included content.</p>
*
* <p>Validity of cached pipelines is calculated not by comparing old and new
* IncludeCacheValidity objects (as in AggregatedCacheValidity) but by comparing
* timestamps. Validity object of cached pipeline contain two lists: source urls
* and timestamps. When it comes to checking validity of cached pipeline we know
* that generation/transformation steps before CIncludeTransformer are valid (otherwise
* we would have had discarded cached pipeline already) so source url list
* of new validity will be the same as of old one. Only timestamps have to be
* recalculated and compared.</p>
*
* @see IncludeTransformer (scratchpad)
* @author <a href="mailto:maciejka@tiger.com.pl">Maciek Kaminski</a>
* @deprecated This transformer violates the avalon/cocoon design principles. Use IncludeTransformer.
* @version $Id$
*/
public class CachingCIncludeTransformer extends AbstractTransformer
implements Composable, Cacheable {
public static final String CINCLUDE_NAMESPACE_URI = "http://apache.org/cocoon/include/1.0";
public static final String CINCLUDE_INCLUDE_ELEMENT = "include";
public static final String CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE = "src";
public static final String CINCLUDE_INCLUDE_ELEMENT_ELEMENT_ATTRIBUTE = "element";
public static final String CINCLUDE_INCLUDE_ELEMENT_NS_ATTRIBUTE = "ns";
public static final String CINCLUDE_INCLUDE_ELEMENT_PREFIX_ATTRIBUTE = "prefix";
/** The <code>SourceResolver</code> */
protected SourceResolver sourceResolver;
/** The current <code>ComponentManager</code>. */
protected ComponentManager manager = null;
/** The current <code>IncludeCacheValidity</code>. */
protected IncludeCacheValidity currentCacheValidity;
/** The current <code>IncludeXMLConsumer</code> that ommits start and endDocument events. */
protected IncludeXMLConsumer consumer;
/**
* Setup the component.
*/
public void setup(SourceResolver resolver, Map objectModel,
String source, Parameters parameters)
throws ProcessingException, SAXException, IOException {
this.sourceResolver = resolver;
}
/**
* Composable Interface
*/
public final void compose(final ComponentManager manager) throws ComponentException {
this.manager = manager;
}
/**
* Recycle the component
*/
public void recycle() {
super.recycle();
this.sourceResolver = null;
this.currentCacheValidity = null;
}
public void startElement(String uri, String name, String raw, Attributes attr)
throws SAXException {
if (uri != null && name != null
&& uri.equals(CINCLUDE_NAMESPACE_URI)
&& name.equals(CINCLUDE_INCLUDE_ELEMENT)) {
this.processCIncludeElement(attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_SRC_ATTRIBUTE),
attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_ELEMENT_ATTRIBUTE),
attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_NS_ATTRIBUTE),
attr.getValue("",CINCLUDE_INCLUDE_ELEMENT_PREFIX_ATTRIBUTE));
} else {
super.startElement(uri, name, raw, attr);
}
}
public void endElement(String uri, String name, String raw) throws SAXException {
if (uri != null && name != null
&& uri.equals(CINCLUDE_NAMESPACE_URI)
&& name.equals(CINCLUDE_INCLUDE_ELEMENT)) {
return;
}
super.endElement(uri, name, raw);
}
public void endDocument()
throws SAXException {
super.endDocument();
if(currentCacheValidity != null) {
currentCacheValidity.setIsNew2False();
}
}
protected void processCIncludeElement(String src, String element, String ns, String prefix)
throws SAXException {
if (element == null) element="";
if (ns == null) ns="";
if (prefix == null) prefix="";
if (this.getLogger().isDebugEnabled()) {
getLogger().debug("Processing CInclude element: src=" + src
+ ", element=" + element
+ ", ns=" + ns
+ ", prefix=" + prefix);
}
// complete validity information
if(currentCacheValidity != null ) {
Source temp = null;
try {
temp = sourceResolver.resolveURI(src);
currentCacheValidity.add(src, temp.getLastModified());
if (this.getLogger().isDebugEnabled()) {
getLogger().debug("currentCacheValidity: " + currentCacheValidity);
}
} catch (Exception e) {
throw new SAXException("CachingCIncludeTransformer could not resolve resource", e);
} finally {
sourceResolver.release(temp);
}
}
if (!"".equals(element)) {
if (!ns.equals("")) {
super.startPrefixMapping(prefix, ns);
}
super.startElement(ns,
element,
(!ns.equals("") && !prefix.equals("") ? prefix+":"+element : element),
XMLUtils.EMPTY_ATTRIBUTES);
}
Source source = null;
try {
source = this.sourceResolver.resolveURI(src);
SourceUtil.parse(this.manager, source, getConsumer());
} catch (Exception e) {
throw new SAXException("CachingCIncludeTransformer could not read resource", e);
} finally {
sourceResolver.release(source);
}
if (!"".equals(element)) {
super.endElement(ns, element, (!ns.equals("") && !prefix.equals("") ? prefix+":"+element : element));
if (!ns.equals("")) {
super.endPrefixMapping(prefix);
}
}
}
/**
* Generate the unique key.
* This key must be unique inside the space of this component.
* CachingCIncludeTransformer always generates the same key since which documents
* are included depends only on former generation/transformation stages.
*
* @return The generated key hashes the src
*/
public long generateKey() {
return 1;
}
/**
* Generate the validity object.
* CachingCIncludeTransformer generates "empty" IncludeCacheValidity
* and completes it with validity data during transformation.
* See processCIncludeElement method.
*
* @return The generated validity object or <code>null</code> if the
* component is currently not cacheable.
*/
public CacheValidity generateValidity() {
try {
currentCacheValidity = new IncludeCacheValidity(sourceResolver);
return currentCacheValidity;
} catch (RuntimeException e) {
getLogger().warn("CachingCIncludeTransformer: could not generateKey", e);
return null;
}
}
protected IncludeXMLConsumer getConsumer() {
if(consumer == null) {
consumer = new IncludeXMLConsumer(this);
}
return consumer;
}
}