001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.transformation;
018:
019: import java.io.IOException;
020: import java.util.Map;
021:
022: import org.apache.avalon.framework.parameters.Parameters;
023: import org.apache.cocoon.ProcessingException;
024: import org.apache.cocoon.components.source.InspectableSource;
025: import org.apache.cocoon.components.source.helpers.SourceProperty;
026: import org.apache.cocoon.environment.SourceResolver;
027: import org.apache.excalibur.source.Source;
028: import org.w3c.dom.Element;
029: import org.w3c.dom.DocumentFragment;
030: import org.w3c.dom.Node;
031: import org.w3c.dom.NodeList;
032: import org.xml.sax.Attributes;
033: import org.xml.sax.SAXException;
034:
035: /**
036: * This transformer allows you to set and remove properties on an InspectableSource.
037: *
038: * <p>Input XML document example:</p>
039: * <pre>
040: * <page>
041: * ...
042: * <source:patch xmlns:source="http://apache.org/cocoon/propwrite/1.0">
043: * <source:source>webdav://localhost/webdav/step1/repo/contentA.xml</source:source>
044: * <source:set>
045: * <myns:author xmlns:myns="meta">me</myns:author>
046: * </source:set>
047: * <source:remove>
048: * <myns:title xmlns:myns="meta"/>
049: * </source:remove>
050: * </source:patch>
051: * ...
052: * </page>
053: * </pre>
054: *
055: * @author <a href="mailto:gcasper@s-und-n.de">Guido Casper</a>
056: * @version $Id: SourcePropsWritingTransformer.java 433543 2006-08-22 06:22:54Z crossley $
057: */
058: public class SourcePropsWritingTransformer extends
059: AbstractSAXTransformer {
060:
061: public static final String SPWT_URI = "http://apache.org/cocoon/propwrite/1.0";
062:
063: /** incoming elements */
064: public static final String PATCH_ELEMENT = "patch";
065: public static final String SOURCE_ELEMENT = "source";
066: public static final String SET_ELEMENT = "set";
067: public static final String REMOVE_ELEMENT = "remove";
068:
069: /** The current state */
070: private static final int STATE_OUTSIDE = 0;
071: private static final int STATE_PATCH = 1;
072: private static final int STATE_SOURCE = 2;
073: private static final int STATE_SET = 3;
074: private static final int STATE_REMOVE = 4;
075:
076: private int state;
077:
078: /**
079: * Constructor
080: * Set the namespace
081: */
082: public SourcePropsWritingTransformer() {
083: super .defaultNamespaceURI = SPWT_URI;
084: }
085:
086: public void recycle() {
087: this .state = STATE_OUTSIDE;
088: super .recycle();
089: }
090:
091: /**
092: * Set the <code>SourceResolver</code>, objectModel <code>Map</code>,
093: * the source and sitemap <code>Parameters</code> used to process the request.
094: *
095: * @param resolver Source Resolver
096: * @param objectModel Object model
097: * @param src URI of the source attribute
098: * @param par Parameters for the transformer
099: */
100: public void setup(SourceResolver resolver, Map objectModel,
101: String src, Parameters par) throws ProcessingException,
102: SAXException, IOException {
103: super .setup(resolver, objectModel, src, par);
104: this .state = STATE_OUTSIDE;
105: }
106:
107: /**
108: * Receive notification of the beginning of an element.
109: *
110: * @param uri The Namespace URI, or the empty string if the element has no
111: * Namespace URI or if Namespace
112: * processing is not being performed.
113: * @param name The local name (without prefix), or the empty string if
114: * Namespace processing is not being performed.
115: * @param raw The raw XML 1.0 name (with prefix), or the empty string if
116: * raw names are not available.
117: * @param attr The attributes attached to the element. If there are no
118: * attributes, it shall be an empty Attributes object.
119: */
120: public void startTransformingElement(String uri, String name,
121: String raw, Attributes attr) throws SAXException,
122: IOException, ProcessingException {
123: if (this .getLogger().isDebugEnabled()) {
124: this .getLogger().debug(
125: "BEGIN startTransformingElement uri=" + uri
126: + ", name=" + name + ", raw=" + raw
127: + ", attr=" + attr);
128: }
129: // Element: patch
130: if (this .state == STATE_OUTSIDE && name.equals(PATCH_ELEMENT)) {
131: this .state = STATE_PATCH;
132: this .stack.push("END");
133:
134: // Element: source
135: } else if (this .state == STATE_PATCH
136: && name.equals(SOURCE_ELEMENT)) {
137: this .state = STATE_SOURCE;
138: this .startTextRecording();
139:
140: // Element: props
141: } else if (this .state == STATE_PATCH
142: && name.equals(SET_ELEMENT)) {
143: this .state = STATE_SET;
144: this .startRecording();
145: } else if (this .state == STATE_PATCH
146: && name.equals(REMOVE_ELEMENT)) {
147: this .state = STATE_REMOVE;
148: this .startRecording();
149: } else {
150: super .startTransformingElement(uri, name, raw, attr);
151: }
152:
153: if (this .getLogger().isDebugEnabled() == true) {
154: this .getLogger().debug("END startTransformingElement");
155: }
156: }
157:
158: /**
159: * Receive notification of the end of an element.
160: *
161: * @param uri The Namespace URI, or the empty string if the element has no
162: * Namespace URI or if Namespace
163: * processing is not being performed.
164: * @param name The local name (without prefix), or the empty string if
165: * Namespace processing is not being performed.
166: * @param raw The raw XML 1.0 name (with prefix), or the empty string if
167: * raw names are not available.
168: */
169: public void endTransformingElement(String uri, String name,
170: String raw) throws SAXException, IOException,
171: ProcessingException {
172: if (this .getLogger().isDebugEnabled() == true) {
173: this .getLogger().debug(
174: "BEGIN endTransformingElement uri=" + uri
175: + ", name=" + name + ", raw=" + raw);
176: }
177:
178: // Element: patch
179: if ((this .state == STATE_PATCH && name.equals(PATCH_ELEMENT))) {
180: this .state = STATE_OUTSIDE;
181: String sourceName = null;
182: String tag = null;
183: DocumentFragment setfrag = null;
184: DocumentFragment removefrag = null;
185: do {
186: tag = (String) this .stack.pop();
187: if (tag.equals(SOURCE_ELEMENT)) {
188: sourceName = (String) this .stack.pop();
189: } else if (tag.equals(SET_ELEMENT)) {
190: setfrag = (DocumentFragment) this .stack.pop();
191: } else if (tag.equals(REMOVE_ELEMENT)) {
192: removefrag = (DocumentFragment) this .stack.pop();
193: }
194: } while (!tag.equals("END"));
195: if (setfrag != null) {
196: NodeList list = setfrag.getChildNodes();
197: Node node = null;
198: for (int i = 0; i < list.getLength(); i++) {
199: node = list.item(i);
200: if (node instanceof Element) {
201: this .setProperty(sourceName, (Element) node);
202: }
203: }
204: }
205: if (removefrag != null) {
206: NodeList list = removefrag.getChildNodes();
207: Node node = null;
208: for (int i = 0; i < list.getLength(); i++) {
209: node = list.item(i);
210: if (node instanceof Element) {
211: this .removeProperty(sourceName, (Element) node);
212: }
213: }
214: }
215:
216: // Element: source
217: } else if (this .state == STATE_SOURCE
218: && name.equals(SOURCE_ELEMENT)) {
219: this .state = STATE_PATCH;
220: String sourceName = this .endTextRecording();
221: this .stack.push(sourceName);
222: this .stack.push(SOURCE_ELEMENT);
223:
224: // Element: set
225: } else if (this .state == STATE_SET && name.equals(SET_ELEMENT)) {
226: this .state = STATE_PATCH;
227: this .stack.push(this .endRecording());
228: this .stack.push(SET_ELEMENT);
229:
230: // Element: remove
231: } else if (this .state == STATE_REMOVE
232: && name.equals(REMOVE_ELEMENT)) {
233: this .state = STATE_PATCH;
234: this .stack.push(this .endRecording());
235: this .stack.push(REMOVE_ELEMENT);
236:
237: // default
238: } else {
239: super .endTransformingElement(uri, name, raw);
240: }
241:
242: if (this .getLogger().isDebugEnabled() == true) {
243: this .getLogger().debug("END endTransformingElement");
244: }
245: }
246:
247: private void setProperty(String src, Element element)
248: throws ProcessingException {
249: if (src != null && element != null) {
250: try {
251: Source source = this .resolver.resolveURI(src);
252: if (source instanceof InspectableSource) {
253: SourceProperty property = new SourceProperty(
254: element);
255: ((InspectableSource) source)
256: .setSourceProperty(property);
257:
258: } else {
259: getLogger().error(
260: "Cannot set properties on " + src
261: + ": not an inspectable source");
262: }
263: } catch (Exception e) {
264: throw new ProcessingException(
265: "Error setting properties on " + src, e);
266: }
267: } else {
268: getLogger().error("Error setting properties on " + src);
269: }
270: }
271:
272: private void removeProperty(String src, Element element)
273: throws ProcessingException {
274:
275: if (src != null && element != null) {
276: try {
277: Source source = this .resolver.resolveURI(src);
278: if (source instanceof InspectableSource) {
279: ((InspectableSource) source).removeSourceProperty(
280: element.getNamespaceURI(), element
281: .getLocalName());
282:
283: } else {
284: getLogger().error(
285: "Cannot remove properties on " + src
286: + ": not an inspectable source");
287: }
288: } catch (Exception e) {
289: throw new ProcessingException(
290: "Error removing properties on " + src, e);
291: }
292: } else {
293: getLogger().error("Error removing properties on " + src);
294: }
295: }
296: }
|