/* * 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 java.io.IOException; import java.util.Map; import org.apache.avalon.framework.parameters.Parameters; import org.apache.cocoon.ProcessingException; import org.apache.cocoon.components.source.InspectableSource; import org.apache.cocoon.components.source.helpers.SourceProperty; import org.apache.cocoon.environment.SourceResolver; import org.apache.excalibur.source.Source; import org.w3c.dom.Element; import org.w3c.dom.DocumentFragment; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.Attributes; import org.xml.sax.SAXException; /** * This transformer allows you to set and remove properties on an InspectableSource. * * <p>Input XML document example:</p> * <pre> * <page> * ... * <source:patch xmlns:source="http://apache.org/cocoon/propwrite/1.0"> * <source:source>webdav://localhost/webdav/step1/repo/contentA.xml</source:source> * <source:set> * <myns:author xmlns:myns="meta">me</myns:author> * </source:set> * <source:remove> * <myns:title xmlns:myns="meta"/> * </source:remove> * </source:patch> * ... * </page> * </pre> * * @author <a href="mailto:gcasper@s-und-n.de">Guido Casper</a> * @version $Id$ */ public class SourcePropsWritingTransformer extends AbstractSAXTransformer { public static final String SPWT_URI = "http://apache.org/cocoon/propwrite/1.0"; /** incoming elements */ public static final String PATCH_ELEMENT = "patch"; public static final String SOURCE_ELEMENT = "source"; public static final String SET_ELEMENT = "set"; public static final String REMOVE_ELEMENT = "remove"; /** The current state */ private static final int STATE_OUTSIDE = 0; private static final int STATE_PATCH = 1; private static final int STATE_SOURCE = 2; private static final int STATE_SET = 3; private static final int STATE_REMOVE = 4; private int state; /** * Constructor * Set the namespace */ public SourcePropsWritingTransformer() { super.defaultNamespaceURI = SPWT_URI; } public void recycle() { this.state = STATE_OUTSIDE; super.recycle(); } /** * Set the <code>SourceResolver</code>, objectModel <code>Map</code>, * the source and sitemap <code>Parameters</code> used to process the request. * * @param resolver Source Resolver * @param objectModel Object model * @param src URI of the source attribute * @param par Parameters for the transformer */ public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par) throws ProcessingException, SAXException, IOException { super.setup(resolver, objectModel, src, par); this.state = STATE_OUTSIDE; } /** * Receive notification of the beginning of an element. * * @param uri The Namespace URI, or the empty string if the element has no * Namespace URI or if Namespace * processing is not being performed. * @param name The local name (without prefix), or the empty string if * Namespace processing is not being performed. * @param raw The raw XML 1.0 name (with prefix), or the empty string if * raw names are not available. * @param attr The attributes attached to the element. If there are no * attributes, it shall be an empty Attributes object. */ public void startTransformingElement(String uri, String name, String raw, Attributes attr) throws SAXException, IOException, ProcessingException { if (this.getLogger().isDebugEnabled()) { this.getLogger().debug("BEGIN startTransformingElement uri=" + uri + ", name=" + name + ", raw=" + raw + ", attr=" + attr); } // Element: patch if (this.state == STATE_OUTSIDE && name.equals(PATCH_ELEMENT)) { this.state = STATE_PATCH; this.stack.push("END"); // Element: source } else if (this.state == STATE_PATCH && name.equals(SOURCE_ELEMENT)) { this.state = STATE_SOURCE; this.startTextRecording(); // Element: props } else if (this.state == STATE_PATCH && name.equals(SET_ELEMENT)) { this.state = STATE_SET; this.startRecording(); } else if (this.state == STATE_PATCH && name.equals(REMOVE_ELEMENT)) { this.state = STATE_REMOVE; this.startRecording(); } else { super.startTransformingElement(uri, name, raw, attr); } if (this.getLogger().isDebugEnabled() == true) { this.getLogger().debug("END startTransformingElement"); } } /** * Receive notification of the end of an element. * * @param uri The Namespace URI, or the empty string if the element has no * Namespace URI or if Namespace * processing is not being performed. * @param name The local name (without prefix), or the empty string if * Namespace processing is not being performed. * @param raw The raw XML 1.0 name (with prefix), or the empty string if * raw names are not available. */ public void endTransformingElement(String uri, String name, String raw) throws SAXException, IOException, ProcessingException { if (this.getLogger().isDebugEnabled() == true) { this.getLogger().debug("BEGIN endTransformingElement uri=" + uri + ", name=" + name + ", raw=" + raw); } // Element: patch if ((this.state == STATE_PATCH && name.equals(PATCH_ELEMENT))) { this.state = STATE_OUTSIDE; String sourceName = null; String tag = null; DocumentFragment setfrag = null; DocumentFragment removefrag = null; do { tag = (String)this.stack.pop(); if (tag.equals(SOURCE_ELEMENT)) { sourceName = (String)this.stack.pop(); } else if (tag.equals(SET_ELEMENT)) { setfrag = (DocumentFragment)this.stack.pop(); } else if (tag.equals(REMOVE_ELEMENT)) { removefrag = (DocumentFragment)this.stack.pop(); } } while ( !tag.equals("END") ); if (setfrag != null) { NodeList list = setfrag.getChildNodes(); Node node = null; for (int i=0; i<list.getLength(); i++) { node = list.item(i); if (node instanceof Element) { this.setProperty(sourceName, (Element) node); } } } if (removefrag != null) { NodeList list = removefrag.getChildNodes(); Node node = null; for (int i=0; i<list.getLength(); i++) { node = list.item(i); if (node instanceof Element) { this.removeProperty(sourceName, (Element) node); } } } // Element: source } else if (this.state == STATE_SOURCE && name.equals(SOURCE_ELEMENT)) { this.state = STATE_PATCH; String sourceName = this.endTextRecording(); this.stack.push(sourceName); this.stack.push(SOURCE_ELEMENT); // Element: set } else if (this.state == STATE_SET && name.equals(SET_ELEMENT)) { this.state = STATE_PATCH; this.stack.push(this.endRecording()); this.stack.push(SET_ELEMENT); // Element: remove } else if (this.state == STATE_REMOVE && name.equals(REMOVE_ELEMENT)) { this.state = STATE_PATCH; this.stack.push(this.endRecording()); this.stack.push(REMOVE_ELEMENT); // default } else { super.endTransformingElement(uri, name, raw); } if (this.getLogger().isDebugEnabled() == true) { this.getLogger().debug("END endTransformingElement"); } } private void setProperty(String src, Element element) throws ProcessingException { if (src != null && element != null) { try { Source source = this.resolver.resolveURI(src); if (source instanceof InspectableSource) { SourceProperty property = new SourceProperty(element); ((InspectableSource)source).setSourceProperty(property); } else { getLogger().error("Cannot set properties on " + src + ": not an inspectable source"); } } catch (Exception e) { throw new ProcessingException("Error setting properties on "+src, e); } } else { getLogger().error("Error setting properties on "+src); } } private void removeProperty(String src, Element element) throws ProcessingException { if (src != null && element != null) { try { Source source = this.resolver.resolveURI(src); if (source instanceof InspectableSource) { ((InspectableSource)source).removeSourceProperty( element.getNamespaceURI(),element.getLocalName()); } else { getLogger().error("Cannot remove properties on " + src + ": not an inspectable source"); } } catch (Exception e) { throw new ProcessingException("Error removing properties on "+src, e); } } else { getLogger().error("Error removing properties on "+src); } } }