KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > transformation > SourceWritingTransformer


1 /*
2  * Copyright 1999-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.transformation;
17
18 import java.io.IOException JavaDoc;
19 import java.io.OutputStream JavaDoc;
20 import java.util.Map JavaDoc;
21
22 import org.apache.avalon.framework.activity.Disposable;
23 import org.apache.avalon.framework.configuration.Configuration;
24 import org.apache.avalon.framework.configuration.ConfigurationException;
25 import org.apache.avalon.framework.parameters.Parameters;
26 import org.apache.avalon.framework.service.ServiceException;
27 import org.apache.avalon.framework.service.ServiceManager;
28 import org.apache.avalon.framework.service.ServiceSelector;
29 import org.apache.cocoon.ProcessingException;
30 import org.apache.cocoon.components.source.SourceUtil;
31 import org.apache.cocoon.environment.SourceResolver;
32 import org.apache.cocoon.serialization.Serializer;
33 import org.apache.cocoon.xml.XMLUtils;
34 import org.apache.cocoon.xml.dom.DOMStreamer;
35 import org.apache.cocoon.xml.dom.DOMUtil;
36 import org.apache.excalibur.source.ModifiableSource;
37 import org.apache.excalibur.source.Source;
38 import org.apache.excalibur.source.SourceException;
39 import org.apache.excalibur.xml.dom.DOMParser;
40 import org.apache.excalibur.xml.xpath.XPathProcessor;
41 import org.w3c.dom.DOMException JavaDoc;
42 import org.w3c.dom.Document JavaDoc;
43 import org.w3c.dom.DocumentFragment JavaDoc;
44 import org.w3c.dom.Node JavaDoc;
45 import org.w3c.dom.NodeList JavaDoc;
46 import org.xml.sax.Attributes JavaDoc;
47 import org.xml.sax.SAXException JavaDoc;
48
49 /**
50  * @cocoon.sitemap.component.documentation
51  * This transformer allows you to output to a ModifiableSource.
52  *
53  * @cocoon.sitemap.component.name sourcewriting
54  * @cocoon.sitemap.component.logger sitemap.transformer.write-source
55  *
56  * This transformer allows you to output to a ModifiableSource.
57  *
58  * <p>Definition:</p>
59  * <pre>
60  * &lt;map:transformer name="tofile" SRC="org.apache.cocoon.transformation.SourceWritingTransformer"&gt;
61  * &lt;!-- 'xml' is the default Serializer (if your Source needs one, like for instance FileSource) --&gt;
62  * &lt;map:parameter name="serializer" value="xml"/&gt;
63  * &lt;/map:transformer/&gt;
64  * </pre>
65  *
66  * <p>Invocation:</p>
67  * <pre>
68  * &lt;map:transform type="tofile"&gt;
69  * &lt;map:parameter name="serializer" value="xml"/&gt; &lt;!-- you can optionally override the serializer here --&gt;
70  * &lt;/map:transform&gt;
71  * </pre>
72  *
73  * <p>The Tags:</p>
74  * <pre>
75  * &lt;source:write create="[true]|false"&gt; - replaces the entire content of an existing asset, if @create is 'true' (default), a new asset will be created if one does not already exist.
76  * &lt;source:source&gt;The System ID of the asset to be written to&lt;/source:source&gt; - eg: "docs/blah.xml" or "context://blah.xml" etc.
77  * &lt;source:path&gt;[Optional] XPath to specify how your content is wrapped&lt;/source:path&gt; - eg: "doc" (your content is placed inside a &lt;doc/&gt; root tag). NOTE: if this value is omitted, your content MUST have only ONE top-level node.
78  * &lt;source:fragment&gt;The XML Fragment to be written&lt;/source:fragment&gt; - eg: "&lt;foo&gt;&lt;bar id="dogcow"/&gt;&lt;/foo&gt;" or "&lt;foo/&gt;&lt;bar&gt;&lt;dogcow/&gt;&lt;bar/&gt;" etc. NOTE: the second example type, can only be used when the &lt;source:path/&gt; tag has been specified.
79  * &lt;source:write&gt;
80  *
81  * &lt;source:insert create="[true]|false" overwrite="[true]|false"&gt; - inserts content into an existing asset, if @create is 'true' (default), a new asset will be created if one does not already exist. If @overwrite is set to 'true' the data is only inserted if the node specified by the 'replacePath' does not exists.
82  * &lt;source:source&gt;The System ID of the asset to be written to&lt;/source:source&gt; - eg: "docs/blah.xml" or "context://blah.xml" etc.
83  * &lt;source:path&gt;XPath specifying the node into which the content is inserted&lt;/source:path&gt; - eg: "doc" (your content is appended as the last child of the &lt;doc/&gt; root tag), or "doc/section[3]". NOTE: this tag is required in &lt;source:insert/&gt; unlike &lt;source:write/&gt; where it is optional.
84  * &lt;source:replace&gt;[Optional] XPath (relative to &lt;source:path/&gt;) to the node that is replaced by your new content&lt;/source:replace&gt; - eg: "foo/bar/dogcow/@status='cut'" (is equivalent to this in XSLT: select="foo[bar/dogcow/@status='cut']").
85  * &lt;source:reinsert&gt;[Optional] The XPath (relative to &lt;source:replace/&gt;) to backup the overwritten node to&lt;/source:reinsert&gt; - eg: "foo/versions" or "/doc/versions/foo". NOTE: If specified and a node is replaced, all children of this replaced node will be reinserted at the given path.
86  * &lt;source:fragment&gt;The XML Fragment to be written&lt;/source:fragment&gt; - eg: "&lt;foo&gt;&lt;bar id="dogcow"/&gt;&lt;/foo&gt;" or "&lt;foo/&gt;&lt;bar&gt;&lt;dogcow/&gt;&lt;bar/&gt;" etc.
87  * &lt;source:insert&gt;
88  *
89  * &lt;source:delete &gt; - deletes an existing asset.
90  * &lt;source:source&gt;The System ID of the asset to be deleted&lt;/source:source&gt; - eg: "docs/blah.xml" or "context://blah.xml" etc.
91  * &lt;source:path&gt;[Ignored] XPath to specify how your content is wrapped&lt;/source:path&gt;
92  * &lt;source:fragment&gt;[Ignored]The XML Fragment to be written&lt;/source:fragment&gt;
93  * &lt;source:delete&gt;
94  * </pre>
95  *
96  *
97  * <p>Input XML document example (write):</p>
98  * <pre>
99  * &lt;page&gt;
100  * ...
101  * &lt;source:write xmlns:source="http://apache.org/cocoon/source/1.0"&gt;
102  * &lt;source:source&gt;context://doc/editable/my.xml&lt;/source:source&gt;
103  * &lt;source:fragment&gt;&lt;page&gt;
104  * &lt;title&gt;Hello World&lt;/title&gt;
105  * &lt;content&gt;
106  * &lt;p&gt;This is my first paragraph.&lt;/p&gt;
107  * &lt;/content&gt;
108  * &lt;/page&gt;&lt;/source:fragment&gt;
109  * &lt;/source:write&gt;
110  * ...
111  * &lt;/page&gt;
112  * </pre>
113  *
114  * <p>Input XML document example (insert at end):</p>
115  * <pre>
116  * &lt;page&gt;
117  * ...
118  * &lt;source:insert xmlns:source="http://apache.org/cocoon/source/1.0"&gt;
119  * &lt;source:source&gt;context://doc/editable/my.xml&lt;/source:source&gt;
120  * &lt;source:path&gt;page/content&lt;/source:path&gt;
121  * &lt;source:fragment&gt;
122  * &lt;p&gt;This paragraph gets &lt;emp&gt;inserted&lt;/emp&gt;.&lt;/p&gt;
123  * &lt;p&gt;With this one, at the end of the content.&lt;/p&gt;
124  * &lt;/source:fragment&gt;
125  * &lt;/source:insert&gt;
126  * ...
127  * &lt;/page&gt;
128  * </pre>
129  *
130  * <p>Input XML document example (insert at beginning):</p>
131  * <pre>
132  * &lt;page&gt;
133  * ...
134  * &lt;source:insert&gt;
135  * &lt;source:source&gt;context://doc/editable/my.xml&lt;/source:source&gt;
136  * &lt;source:path&gt;page&lt;/source:path&gt;
137  * &lt;source:replace&gt;content&lt;/source:replace&gt;
138  * &lt;source:reinsert&gt;content&lt;/source:reinsert&gt;
139  * &lt;source:fragment&gt;
140  * &lt;content&gt;
141  * &lt;p&gt;This new paragraph gets inserted &lt;emp&gt;before&lt;/emp&gt; the other ones.&lt;/p&gt;
142  * &lt;/content&gt;
143  * &lt;/source:fragment&gt;
144  * &lt;source:insert&gt;
145  * ...
146  * &lt;/page&gt;
147  * </pre>
148  *
149  * <p>Input XML document example (replace):</p>
150  * <pre>
151  * &lt;page&gt;
152  * ...
153  * &lt;source:insert xmlns:source="http://apache.org/cocoon/source/1.0"&gt;
154  * &lt;source:source&gt;context://doc/editable/my.xml"&lt;/source:source&gt;
155  * &lt;source:path&gt;page/content&lt;/source:path&gt;
156  * &lt;source:replace&gt;p[1]&lt;/source:replace&gt;
157  * &lt;source:fragment&gt;
158  * &lt;p&gt;This paragraph &lt;emp&gt;replaces&lt;/emp&gt; the first paragraph.&lt;/p&gt;
159  * &lt;/source:fragment&gt;
160  * &lt;/source:insert&gt;
161  * ...
162  * &lt;/page&gt;
163  * </pre>
164  *
165  * <p>Output XML document example:</p>
166  * <pre>
167  * &lt;page&gt;
168  * ...
169  * &lt;sourceResult xmlns:source="http://apache.org/cocoon/source/1.0"&gt;
170  * &lt;action&gt;new|overwritten|none&lt;/action&gt;
171  * &lt;behaviour&gt;write|insert&lt;behaviour&gt;
172  * &lt;execution&gt;success|failure&lt;/execution&gt;
173  * &lt;serializer&gt;xml&lt;/serializer&gt;
174  * &lt;source&gt;file:/source/specific/path/to/context/doc/editable/my.xml&lt;/source&gt;
175  * &lt;/sourceResult&gt;
176  * ...
177  * &lt;/page&gt;
178  * </pre>
179  *
180  *
181  * The XPath specification is very complicated. So here is an example for the sitemap:
182  * <pre>
183  * &lt;page xmlns:source="http://apache.org/cocoon/source/1.0"&gt;
184  * ...
185  * &lt;source:insert&gt;
186  * &lt;source:source&gt;sitemap.xmap&lt;/source:source&gt;
187  * &lt;source:path&gt;/*[namespace-uri()="http://apache.org/cocoon/sitemap/1.0" and local-name()="sitemap"]/*[namespace-uri()="http://apache.org/cocoon/sitemap/1.0" and local-name()="components"]/*[namespace-uri()="http://apache.org/cocoon/sitemap/1.0" and local-name()="generators"]&lt;/source:path&gt;
188  * &lt;source:fragment&gt;
189  * &lt;generator name="file" xmln="http://apache.org/cocoon/sitemap/1.0"&gt;
190  * &lt;test/&gt;
191  * &lt;/generator&gt;
192  * &lt;/source:fragment&gt;
193  * &lt;source:replace&gt;*[namespace-uri()="http://apache.org/cocoon/sitemap/1.0" and local-name()="generator" and attribute::name="file"]&lt;/source:replace&gt;
194  * &lt;/source:insert&gt;
195  * ...
196  * &lt;/page&gt;
197  * </pre>
198  *
199  * <p>This insert replaces (if it exists) the file generator definition with a new one.
200  * As the sitemap uses namespaces the XPath for the generator is rather complicated.
201  * Due to this it is necessary that the node specified by path exists if namespaces
202  * are used! Otherwise a node with the name * would be created...</p>
203  *
204  * <p>The create attribute of insert. If this is set
205  * to true (default is true), the file is created if it does not exists.
206  * If it is set to false, it is not created, making insert a real insert.
207  * create is only usable for files!</p>
208  * <p>In addition the overwrite attribute is used to check if replacing is allowed.
209  * If overwrite is true (the default) the node is replaced. If it is false
210  * the node is not inserted if the replace node is available.</p>
211  *
212  * <p>[JQ] - the way I understand this, looking at the code:
213  * <pre>
214  * if 'replace' is not specified, your 'fragment' is appended as a child of 'path'.
215  * if 'replace' is specified and it exists and 'overwrite' is true, your 'fragment' is inserted in 'path', before 'replace' and then 'replace' is deleted.
216  * if 'replace' is specified and it exists and 'overwrite' is false, no action occurs.
217  * if 'replace' is specified and it does not exist and 'overwrite' is true, your 'fragment' is appended as a child of 'path'.
218  * if 'replace' is specified and it does not exist and 'overwrite' is false, your 'fragment' is appended as a child of 'path'.
219  * if 'reinsert' is specified and it does not exist, no action occurs.
220  * </pre></p>
221  *
222  * The &lt;source:reinsert&gt; option can be used to
223  * reinsert a replaced node at a given path in the new fragment.
224  *
225  * <b>
226  * TODO: Use the serializer instead of the XMLUtils for inserting of fragments<br/>
227  * TODO: Add a &lt;source:before/&gt; tag.
228  * </b>
229  *
230  * @author <a HREF="mailto:cziegeler@s-und-n.de">Carsten Ziegeler</a>
231  * @author <a HREF="mailto:jeremy@apache.org">Jeremy Quinn</a>
232  * @author <a HREF="mailto:gianugo@apache.org">Gianugo Rabellino</a>
233  * @version $Id: SourceWritingTransformer.java 264814 2005-08-30 17:13:31Z vgritsenko $
234  */

235 public class SourceWritingTransformer extends AbstractSAXTransformer
236                                       implements Disposable {
237
238     public static final String JavaDoc SWT_URI = "http://apache.org/cocoon/source/1.0";
239     public static final String JavaDoc DEFAULT_SERIALIZER = "xml";
240
241     /** incoming elements */
242     public static final String JavaDoc WRITE_ELEMENT = "write";
243     public static final String JavaDoc INSERT_ELEMENT = "insert";
244     public static final String JavaDoc PATH_ELEMENT = "path";
245     public static final String JavaDoc FRAGMENT_ELEMENT = "fragment";
246     public static final String JavaDoc REPLACE_ELEMENT = "replace";
247     public static final String JavaDoc DELETE_ELEMENT = "delete";
248     public static final String JavaDoc SOURCE_ELEMENT = "source";
249     public static final String JavaDoc REINSERT_ELEMENT = "reinsert";
250     /** outgoing elements */
251     public static final String JavaDoc RESULT_ELEMENT = "sourceResult";
252     public static final String JavaDoc EXECUTION_ELEMENT = "execution";
253     public static final String JavaDoc BEHAVIOUR_ELEMENT = "behaviour";
254     public static final String JavaDoc ACTION_ELEMENT = "action";
255     public static final String JavaDoc MESSAGE_ELEMENT = "message";
256     public static final String JavaDoc SERIALIZER_ELEMENT = "serializer";
257     /** main (write or insert) tag attributes */
258     public static final String JavaDoc SERIALIZER_ATTRIBUTE = "serializer";
259     public static final String JavaDoc CREATE_ATTRIBUTE = "create";
260     public static final String JavaDoc OVERWRITE_ATTRIBUTE = "overwrite";
261     /** results */
262     public static final String JavaDoc RESULT_FAILED = "failed";
263     public static final String JavaDoc RESULT_SUCCESS = "success";
264     public static final String JavaDoc ACTION_NONE = "none";
265     public static final String JavaDoc ACTION_NEW = "new";
266     public static final String JavaDoc ACTION_OVER = "overwritten";
267     public static final String JavaDoc ACTION_DELETE = "deleted";
268     /** The current state */
269     private static final int STATE_OUTSIDE = 0;
270     private static final int STATE_INSERT = 1;
271     private static final int STATE_PATH = 3;
272     private static final int STATE_FRAGMENT = 4;
273     private static final int STATE_REPLACE = 5;
274     private static final int STATE_FILE = 6;
275     private static final int STATE_REINSERT = 7;
276     private static final int STATE_WRITE = 8;
277     private static final int STATE_DELETE = 9;
278     private int state;
279     private int parent_state;
280
281     /** The configured serializer name */
282     protected String JavaDoc configuredSerializerName;
283
284     /** The XPath processor */
285     protected XPathProcessor xpathProcessor;
286
287     /**
288      * Constructor. Set the namespace.
289      */

290     public SourceWritingTransformer() {
291         this.defaultNamespaceURI = SWT_URI;
292     }
293
294     /**
295      * Get the current <code>Configuration</code> instance used by this
296      * <code>Configurable</code>.
297      */

298     public void configure(Configuration configuration)
299     throws ConfigurationException {
300         super.configure(configuration);
301         this.configuredSerializerName = configuration.getChild(SERIALIZER_ATTRIBUTE).getValue(DEFAULT_SERIALIZER);
302     }
303
304     /**
305      * Get the <code>Parameter</code> called "serializer" from the
306      * <code>Transformer</code> invocation.
307      */

308     public void setup(SourceResolver resolver, Map JavaDoc objectModel, String JavaDoc src, Parameters par)
309     throws ProcessingException, SAXException JavaDoc, IOException JavaDoc {
310         super.setup(resolver, objectModel, src, par);
311
312         this.configuredSerializerName = par.getParameter(SERIALIZER_ATTRIBUTE,
313                                                          this.configuredSerializerName);
314         this.state = STATE_OUTSIDE;
315     }
316
317     /**
318      * Receive notification of the beginning of an element.
319      *
320      * @param uri The Namespace URI, or the empty string if the element has no
321      * Namespace URI or if Namespace
322      * processing is not being performed.
323      * @param name The local name (without prefix), or the empty string if
324      * Namespace processing is not being performed.
325      * @param raw The raw XML 1.0 name (with prefix), or the empty string if
326      * raw names are not available.
327      * @param attr The attributes attached to the element. If there are no
328      * attributes, it shall be an empty Attributes object.
329      */

330     public void startTransformingElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw, Attributes JavaDoc attr)
331     throws SAXException JavaDoc, IOException JavaDoc, ProcessingException {
332         if (getLogger().isDebugEnabled()) {
333             getLogger().debug("Start transforming element. uri=" + uri +
334                               ", name=" + name + ", raw=" + raw + ", attr=" + attr);
335         }
336
337         // Element: insert
338
if (this.state == STATE_OUTSIDE
339             && (name.equals(INSERT_ELEMENT) || name.equals(WRITE_ELEMENT))) {
340
341             this.state = (name.equals(INSERT_ELEMENT) ? STATE_INSERT : STATE_WRITE);
342             this.parent_state = this.state;
343             if (attr.getValue(CREATE_ATTRIBUTE) != null
344                 && attr.getValue(CREATE_ATTRIBUTE).equals("false")) {
345                 this.stack.push("false");
346             } else {
347                 this.stack.push("true"); // default value
348
}
349             if (attr.getValue(OVERWRITE_ATTRIBUTE) != null
350                 && attr.getValue(OVERWRITE_ATTRIBUTE).equals("false")) {
351                 this.stack.push("false");
352             } else {
353                 this.stack.push("true"); // default value
354
}
355             this.stack.push(attr.getValue(SERIALIZER_ATTRIBUTE));
356             this.stack.push("END");
357
358         // Element: delete
359
} else if (this.state == STATE_OUTSIDE && name.equals(DELETE_ELEMENT)) {
360             this.state = STATE_DELETE;
361             this.parent_state = state;
362             this.stack.push("END");
363         // Element: file
364
} else if (name.equals(SOURCE_ELEMENT)
365                    && (this.state == STATE_INSERT || this.state == STATE_WRITE || this.state == STATE_DELETE)) {
366             this.state = STATE_FILE;
367             this.startTextRecording();
368
369         // Element: path
370
} else if (name.equals(PATH_ELEMENT)
371                    && (this.state == STATE_INSERT || this.state == STATE_WRITE || this.state == STATE_DELETE)) {
372             this.state = STATE_PATH;
373             this.startTextRecording();
374
375         // Element: replace
376
} else if (name.equals(REPLACE_ELEMENT)
377                    && this.state == STATE_INSERT) {
378             this.state = STATE_REPLACE;
379             this.startTextRecording();
380
381         // Element: fragment
382
} else if (name.equals(FRAGMENT_ELEMENT)
383                    && (this.state == STATE_INSERT || this.state == STATE_WRITE || this.state == STATE_DELETE)) {
384             this.state = STATE_FRAGMENT;
385             this.startRecording();
386
387         // Element: reinsert
388
} else if (name.equals(REINSERT_ELEMENT)
389                    && this.state == STATE_INSERT) {
390             this.state = STATE_REINSERT;
391             this.startTextRecording();
392
393         } else {
394             super.startTransformingElement(uri, name, raw, attr);
395         }
396     }
397
398
399     /**
400      * Receive notification of the end of an element.
401      *
402      * @param uri The Namespace URI, or the empty string if the element has no
403      * Namespace URI or if Namespace
404      * processing is not being performed.
405      * @param name The local name (without prefix), or the empty string if
406      * Namespace processing is not being performed.
407      * @param raw The raw XML 1.0 name (with prefix), or the empty string if
408      * raw names are not available.
409      */

410     public void endTransformingElement(String JavaDoc uri, String JavaDoc name, String JavaDoc raw)
411     throws SAXException JavaDoc, IOException JavaDoc, ProcessingException {
412         if (getLogger().isDebugEnabled()) {
413             getLogger().debug("End transforming element. uri=" + uri +
414                               ", name=" + name + ", raw=" + raw);
415         }
416
417         if ((name.equals(INSERT_ELEMENT) && this.state == STATE_INSERT)
418             || (name.equals(WRITE_ELEMENT) && this.state == STATE_WRITE)) {
419
420             // get the information from the stack
421
DocumentFragment JavaDoc fragment = null;
422             String JavaDoc tag;
423             String JavaDoc sourceName = null;
424             String JavaDoc path = (this.state == STATE_INSERT ? null : "/");
425                                  // source:write's path can be empty
426
String JavaDoc replacePath = null;
427             String JavaDoc reinsert = null;
428             do {
429                 tag = (String JavaDoc)this.stack.pop();
430                 if (tag.equals("PATH")) {
431                     path = (String JavaDoc)this.stack.pop();
432                 } else if (tag.equals("FILE")) {
433                     sourceName = (String JavaDoc)this.stack.pop();
434                 } else if (tag.equals("FRAGMENT")) {
435                     fragment = (DocumentFragment JavaDoc)this.stack.pop();
436                 } else if (tag.equals("REPLACE")) {
437                     replacePath = (String JavaDoc)this.stack.pop();
438                 } else if (tag.equals("REINSERT")) {
439                     reinsert = (String JavaDoc)this.stack.pop();
440                 }
441             } while ( !tag.equals("END") );
442
443             final String JavaDoc localSerializer = (String JavaDoc)this.stack.pop();
444             final boolean overwrite = this.stack.pop().equals("true");
445             final boolean create = this.stack.pop().equals("true");
446
447             this.insertFragment(sourceName,
448                                     path,
449                                     fragment,
450                                     replacePath,
451                                     create,
452                                     overwrite,
453                                     reinsert,
454                                     localSerializer,
455                                     name);
456
457             this.state = STATE_OUTSIDE;
458
459         // Element: delete
460
} else if (name.equals(DELETE_ELEMENT) && this.state == STATE_DELETE) {
461             String JavaDoc sourceName = null;
462             String JavaDoc tag;
463             do {
464                 tag = (String JavaDoc)this.stack.pop();
465                 if (tag.equals("FILE")) {
466                     sourceName = (String JavaDoc)this.stack.pop();
467                 } else if (tag.equals("FRAGMENT")) {
468                     //Get rid of it
469
this.stack.pop();
470                 }
471             } while ( !tag.equals("END"));
472
473             this.deleteSource(sourceName);
474             this.state = STATE_OUTSIDE;
475         // Element: file
476
} else if (name.equals(SOURCE_ELEMENT) && this.state == STATE_FILE) {
477             this.state = this.parent_state;
478             this.stack.push(this.endTextRecording());
479             this.stack.push("FILE");
480
481         // Element: path
482
} else if (name.equals(PATH_ELEMENT) && this.state == STATE_PATH) {
483             this.state = this.parent_state;
484             this.stack.push(this.endTextRecording());
485             this.stack.push("PATH");
486
487         // Element: replace
488
} else if (name.equals(REPLACE_ELEMENT) && this.state == STATE_REPLACE) {
489             this.state = this.parent_state;
490             this.stack.push(this.endTextRecording());
491             this.stack.push("REPLACE");
492
493         // Element: fragment
494
} else if (name.equals(FRAGMENT_ELEMENT) && this.state == STATE_FRAGMENT) {
495             this.state = this.parent_state;
496             this.stack.push(this.endRecording());
497             this.stack.push("FRAGMENT");
498
499         // Element: reinsert
500
} else if (name.equals(REINSERT_ELEMENT) && this.state == STATE_REINSERT) {
501             this.state = this.parent_state;
502             this.stack.push(this.endTextRecording());
503             this.stack.push("REINSERT");
504
505         // default
506
} else {
507             super.endTransformingElement(uri, name, raw);
508         }
509     }
510
511     /**
512      * Deletes a source
513      * @param systemID
514      */

515     private void deleteSource(String JavaDoc systemID) throws ProcessingException, IOException JavaDoc, SAXException JavaDoc {
516         Source source = null;
517         try {
518             source = resolver.resolveURI(systemID);
519             if (!(source instanceof ModifiableSource)) {
520                 throw new ProcessingException("Source '" + systemID + "' is not writeable.");
521             }
522
523             ((ModifiableSource)source).delete();
524             reportResult("none",
525                          "delete",
526                          "source deleted successfully",
527                          systemID,
528                          RESULT_SUCCESS,
529                          ACTION_DELETE);
530         } catch (SourceException se) {
531             if (getLogger().isDebugEnabled()) {
532                 getLogger().debug("FAIL exception: " + se, se);
533             }
534             reportResult("none",
535                          "delete",
536                          "unable to delete source: " + se.getMessage(),
537                          systemID,
538                          RESULT_FAILED,
539                          ACTION_DELETE);
540         } finally {
541             resolver.release(source);
542         }
543     }
544
545     /**
546      * Insert a fragment into a file.
547      * The file is loaded by the resource connector.
548      *
549      * @param systemID The name of the xml file.
550      * @param path The XPath specifying the node under which the data is inserted
551      * @param fragment The data to be inserted.
552      * @param replacePath Optional XPath relative to <CODE>path</CODE>. This path
553      * can specify a node which will be removed if it exists.
554      * So insertFragment can be used as a replace utility.
555      * @param create If the file does not exists and this is set to
556      * <CODE>false</CODE> nothing is inserted. If it is set
557      * to <CODE>true</CODE> the file is created and the data
558      * is inserted.
559      * @param overwrite If this is set to <CODE>true</CODE> the data is only
560      * inserted if the node specified by the <CODE>replacePath</CODE>
561      * does not exists.
562      * @param reinsertPath If specified and a node is replaced , all children of
563      * this replaced node will be reinserted at the given path.
564      * @param localSerializer The serializer used to serialize the XML
565      * @param tagname The name of the tag that triggered me 'insert' or 'write'
566      */

567     protected void insertFragment(String JavaDoc systemID,
568                                   String JavaDoc path,
569                                   DocumentFragment JavaDoc fragment,
570                                   String JavaDoc replacePath,
571                                   boolean create,
572                                   boolean overwrite,
573                                   String JavaDoc reinsertPath,
574                                   String JavaDoc localSerializer,
575                                   String JavaDoc tagname)
576     throws SAXException JavaDoc, IOException JavaDoc, ProcessingException {
577         // no sync req
578
if (getLogger().isDebugEnabled()) {
579             getLogger().debug("Insert fragment. systemID=" + systemID +
580                               ", path=" + path +
581                               ", replace=" + replacePath +
582                               ", create=" + create +
583                               ", overwrite=" + overwrite +
584                               ", reinsert=" + reinsertPath +
585                               ", fragment=" + (fragment == null ? "null" : XMLUtils.serializeNode(fragment)));
586         }
587
588         // test parameter
589
if (systemID == null) {
590             throw new ProcessingException("insertFragment: systemID is required.");
591         }
592         if (path == null) {
593             throw new ProcessingException("insertFragment: path is required.");
594         }
595         if (path.startsWith("/")) {
596             path = path.substring(1);
597         }
598         if (fragment == null) {
599             throw new ProcessingException("insertFragment: fragment is required.");
600         }
601
602         // first: read the source as a DOM
603
Source source = null;
604         Document JavaDoc resource = null;
605         boolean failed = true;
606         boolean exists = false;
607         String JavaDoc message = "";
608         String JavaDoc target = systemID;
609         try {
610             source = this.resolver.resolveURI(systemID);
611             if (! (source instanceof ModifiableSource)) {
612                 throw new ProcessingException("Source '" + systemID + "' is not writeable.");
613             }
614             ModifiableSource ws = (ModifiableSource) source;
615             exists = ws.exists();
616             target = source.getURI();
617
618             // Insert?
619
if (exists && this.state == STATE_INSERT) {
620                 message = "content inserted at: " + path;
621                 resource = SourceUtil.toDOM(source);
622                 // import the fragment
623
Node JavaDoc importNode = resource.importNode(fragment, true);
624                 // get the node
625
Node JavaDoc parent = DOMUtil.selectSingleNode(resource, path, this.xpathProcessor);
626
627                 // replace?
628
if (replacePath != null) {
629                     try {
630                         Node JavaDoc replaceNode = DOMUtil.getSingleNode(parent, replacePath, this.xpathProcessor);
631                         // now get the parent of this node until it is the parent node for insertion
632
while (replaceNode != null && !replaceNode.getParentNode().equals(parent)) {
633                            replaceNode = replaceNode.getParentNode();
634                         }
635
636                         if (replaceNode != null) {
637                             if (overwrite) {
638                                 if (parent.getNodeType() == Node.DOCUMENT_NODE) {
639                                     // replacing of the document element is not allowed
640
resource = newDocument();
641                                     resource.appendChild(resource.importNode(importNode, true));
642                                     parent = resource;
643                                     replaceNode = resource.importNode(replaceNode, true);
644                                 } else {
645                                     parent.replaceChild(importNode, replaceNode);
646                                 }
647                                 message += ", replacing: " + replacePath;
648                                 if (reinsertPath != null) {
649                                     Node JavaDoc insertAt = DOMUtil.getSingleNode(parent, reinsertPath, this.xpathProcessor);
650                                     if (insertAt != null) {
651                                         while (replaceNode.hasChildNodes()) {
652                                             insertAt.appendChild(replaceNode.getFirstChild());
653                                         }
654                                     } else { // reinsert point null
655
message = "replace failed, could not find your reinsert path: " + reinsertPath;
656                                         resource = null;
657                                     }
658                                 }
659                             } else { // overwrite was false
660
message = "replace failed, no overwrite allowed.";
661                                 resource = null;
662                             }
663                         } else { // specified replaceNode was not found
664
parent.appendChild(importNode);
665                         }
666                     } catch (javax.xml.transform.TransformerException JavaDoc sax) {
667                         throw new ProcessingException("TransformerException: " + sax, sax);
668                     }
669                 } else { // no replace path, just do an insert at end
670
parent.appendChild(importNode);
671                 }
672
673             // Create?
674
} else if (create) {
675                 // Create new document
676
resource = newDocument();
677
678                 // Import the fragment
679
Node JavaDoc importNode = resource.importNode(fragment, true);
680
681                 if (path.equals("")) {
682                     // Parent node is document itself
683
NodeList JavaDoc nodes = importNode.getChildNodes();
684                     for (int i = 0; i < nodes.getLength();) {
685                         Node JavaDoc node = nodes.item(i);
686                         switch (node.getNodeType()) {
687                             case Node.ELEMENT_NODE:
688                                 // May throw exception if fragment has more than one element
689
resource.appendChild(node);
690                                 break;
691
692                             case Node.DOCUMENT_TYPE_NODE:
693                             case Node.PROCESSING_INSTRUCTION_NODE:
694                             case Node.COMMENT_NODE:
695                                 resource.appendChild(node);
696                                 break;
697
698                             default:
699                                 // Ignore all other nodes
700
i++;
701                                 break;
702                         }
703                     }
704                     message = "entire source overwritten";
705
706                 } else {
707                     // Get the parent node
708
Node JavaDoc parent = DOMUtil.selectSingleNode(resource, path, this.xpathProcessor);
709                     // Add a fragment
710
parent.appendChild(importNode);
711                     message = "content appended to: " + path;
712                 }
713
714             // Oops: Document does not exist and create is not allowed.
715
} else {
716                 message = "create not allowed";
717                 resource = null;/**/
718             }
719
720
721             // Write source
722
if (resource != null) {
723                 resource.normalize();
724                 // use serializer
725
if (localSerializer == null) {
726                     localSerializer = this.configuredSerializerName;
727                 }
728
729                 if (localSerializer != null) {
730                     // Lookup the Serializer
731
ServiceSelector selector = null;
732                     Serializer serializer = null;
733                     OutputStream JavaDoc oStream = null;
734                     try {
735                         selector = (ServiceSelector)manager.lookup(Serializer.ROLE + "Selector");
736                         serializer = (Serializer)selector.select(localSerializer);
737                         oStream = ws.getOutputStream();
738                         serializer.setOutputStream(oStream);
739                         DOMStreamer streamer = new DOMStreamer(serializer);
740                         streamer.stream(resource);
741                     } finally {
742                         if (oStream != null) {
743                             oStream.flush();
744                             try {
745                                 oStream.close();
746                                 failed = false;
747                             } catch (Throwable JavaDoc t) {
748                                 if (getLogger().isDebugEnabled()) {
749                                     getLogger().debug("FAIL (oStream.close) exception"+t, t);
750                                 }
751                                 throw new ProcessingException("Could not process your document.", t);
752                             } finally {
753                                 if (selector != null) {
754                                     selector.release(serializer);
755                                     this.manager.release(selector);
756                                 }
757                             }
758                         }
759                     }
760                 } else {
761                     if (getLogger().isDebugEnabled()) {
762                         getLogger().debug("ERROR: No serializer");
763                     }
764                     //throw new ProcessingException("No serializer specified for writing to source " + systemID);
765
message = "That source requires a serializer, please add the appropirate tag to your code.";
766                 }
767             }
768         } catch (DOMException JavaDoc de) {
769             if (getLogger().isDebugEnabled()) {
770                 getLogger().debug("FAIL exception: "+de, de);
771             }
772             message = "There was a problem manipulating your document: " + de;
773         } catch (ServiceException ce) {
774             if (getLogger().isDebugEnabled()) {
775                 getLogger().debug("FAIL exception: "+ce, ce);
776             }
777             message = "There was a problem looking up a component: " + ce;
778         } catch (SourceException se) {
779             if (getLogger().isDebugEnabled()) {
780                 getLogger().debug("FAIL exception: "+se, se);
781             }
782             message = "There was a problem resolving that source: [" + systemID + "] : " + se;
783         } finally {
784             this.resolver.release(source);
785         }
786
787         // Report result
788
String JavaDoc result = (failed) ? RESULT_FAILED : RESULT_SUCCESS;
789         String JavaDoc action = ACTION_NONE;
790         if (!failed) {
791             action = (exists) ? ACTION_OVER : ACTION_NEW;
792         }
793
794         reportResult(localSerializer, tagname, message, target, result, action);
795     }
796
797     private void reportResult(String JavaDoc localSerializer,
798                                 String JavaDoc tagname,
799                                 String JavaDoc message,
800                                 String JavaDoc target,
801                                 String JavaDoc result,
802                                 String JavaDoc action)
803     throws SAXException JavaDoc {
804         sendStartElementEvent(RESULT_ELEMENT);
805             sendStartElementEvent(EXECUTION_ELEMENT);
806                 sendTextEvent(result);
807             sendEndElementEvent(EXECUTION_ELEMENT);
808             sendStartElementEvent(MESSAGE_ELEMENT);
809                 sendTextEvent(message);
810             sendEndElementEvent(MESSAGE_ELEMENT);
811             sendStartElementEvent(BEHAVIOUR_ELEMENT);
812                 sendTextEvent(tagname);
813             sendEndElementEvent(BEHAVIOUR_ELEMENT);
814             sendStartElementEvent(ACTION_ELEMENT);
815                 sendTextEvent(action);
816             sendEndElementEvent(ACTION_ELEMENT);
817             sendStartElementEvent(SOURCE_ELEMENT);
818                 sendTextEvent(target);
819             sendEndElementEvent(SOURCE_ELEMENT);
820             if (localSerializer != null) {
821                 sendStartElementEvent(SERIALIZER_ELEMENT);
822                     sendTextEvent(localSerializer);
823                 sendEndElementEvent(SERIALIZER_ELEMENT);
824             }
825         sendEndElementEvent(RESULT_ELEMENT);
826     }
827
828     private Document JavaDoc newDocument() throws SAXException JavaDoc, ServiceException {
829         DOMParser parser = (DOMParser) this.manager.lookup(DOMParser.ROLE);
830         try {
831             return parser.createDocument();
832         } finally {
833             this.manager.release(parser);
834         }
835     }
836
837     /* (non-Javadoc)
838      * @see org.apache.avalon.framework.service.Serviceable#service(ServiceManager)
839      */

840     public void service(ServiceManager manager) throws ServiceException {
841         super.service(manager);
842         this.xpathProcessor = (XPathProcessor) this.manager.lookup(XPathProcessor.ROLE);
843     }
844
845     /* (non-Javadoc)
846      * @see org.apache.avalon.framework.activity.Disposable#dispose()
847      */

848     public void dispose() {
849         if (this.manager != null) {
850             this.manager.release(this.xpathProcessor);
851             this.xpathProcessor = null;
852         }
853         super.dispose();
854     }
855 }
856
Popular Tags