KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > opencms > template > A_CmsXmlContent


1 /*
2 * File : $Source: /usr/local/cvs/opencms/src-modules/com/opencms/template/A_CmsXmlContent.java,v $
3 * Date : $Date: 2005/06/27 23:22:20 $
4 * Version: $Revision: 1.8 $
5 *
6 * This library is part of OpenCms -
7 * the Open Source Content Mananagement System
8 *
9 * Copyright (C) 2001 The OpenCms Group
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 *
21 * For further information about OpenCms, please see the
22 * OpenCms Website: http://www.opencms.org
23 *
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 */

28
29 package com.opencms.template;
30
31 import org.opencms.file.CmsFile;
32 import org.opencms.file.CmsObject;
33 import org.opencms.main.CmsException;
34 import org.opencms.main.CmsLog;
35 import org.opencms.main.OpenCms;
36
37 import com.opencms.legacy.CmsLegacyException;
38 import com.opencms.legacy.CmsXmlTemplateLoader;
39 import com.opencms.workplace.CmsWorkplaceDefault;
40
41 import java.io.ByteArrayInputStream JavaDoc;
42 import java.io.ByteArrayOutputStream JavaDoc;
43 import java.io.InputStream JavaDoc;
44 import java.io.OutputStream JavaDoc;
45 import java.io.StringReader JavaDoc;
46 import java.io.StringWriter JavaDoc;
47 import java.io.Writer JavaDoc;
48 import java.lang.reflect.InvocationTargetException JavaDoc;
49 import java.lang.reflect.Method JavaDoc;
50 import java.util.Enumeration JavaDoc;
51 import java.util.Hashtable JavaDoc;
52 import java.util.Vector JavaDoc;
53
54 import org.w3c.dom.CDATASection JavaDoc;
55 import org.w3c.dom.Document JavaDoc;
56 import org.w3c.dom.Element JavaDoc;
57 import org.w3c.dom.Node JavaDoc;
58 import org.w3c.dom.NodeList JavaDoc;
59 import org.w3c.dom.Text JavaDoc;
60
61 /**
62  * Abstract class for OpenCms files with XML content.
63  * <P>
64  * This class implements basic functionality for OpenCms XML files.
65  * For each XML file content type (e.g. XML template files, XML
66  * control files, XML news article files, ...) a customized
67  * class extending this abstract class has to be implemented.
68  * <P>
69  * The functionality of this class is:
70  * <UL>
71  * <LI>control the XML parser</LI>
72  * <LI>recognize and handle special XML tags used in OpenCms environment</LI>
73  * <LI>cache parsed documents, so that that they can be re-used</LI>
74  * <LI>provide methods to access XML data</LI>
75  * </UL>
76  * <P>
77  * After creating a new instance of the children of this class it has to be
78  * initialized by calling the init method.
79  * <P>
80  * While initializing the content of the given file will be read
81  * and parsed with the XML parser. After this, the parsed
82  * document will be scanned for INCLUDE tags and for DATA tags.
83  * DATA tags will be stored in an internal Hashtable an can
84  * easily be accessed by the getData methods or by a PROCESS tag.
85  * <P>
86  * Extending classes have to implement the abstract methods
87  * getXmlDocumentTagName() and getContentDescription().
88  *
89  * @author Alexander Lucas
90  * @version $Revision: 1.8 $ $Date: 2005/06/27 23:22:20 $
91  *
92  * @deprecated Will not be supported past the OpenCms 6 release.
93  */

94 public abstract class A_CmsXmlContent implements I_CmsXmlContent {
95
96     /** parameter types for XML node handling methods. */
97     public static final Class JavaDoc[] C_PARAMTYPES_HANDLING_METHODS = new Class JavaDoc[] {Element JavaDoc.class, Object JavaDoc.class, Object JavaDoc.class};
98
99     /** parameter types for user methods called by METHOD tags. */
100     public static final Class JavaDoc[] C_PARAMTYPES_USER_METHODS = new Class JavaDoc[] {CmsObject.class, String JavaDoc.class, A_CmsXmlContent.class, Object JavaDoc.class};
101
102     /** The classname of the super XML content class. */
103     public static final String JavaDoc C_MINIMUM_CLASSNAME = "com.opencms.template.A_CmsXmlContent";
104
105     /** Constant extension of the template-files. */
106     public static final String JavaDoc C_TEMPLATE_EXTENSION = "";
107
108     /** Error message for bad <code>&lt;PROCESS&gt;</code> tags. */
109     public static final String JavaDoc C_ERR_NODATABLOCK = "? UNKNOWN DATABLOCK ";
110
111     /** CmsObject Object for accessing resources. */
112     protected CmsObject m_cms;
113
114     /** All XML tags known by this class. */
115     protected Vector JavaDoc m_knownTags = new Vector JavaDoc();
116
117     /**
118      * This Hashtable contains some XML tags as keys
119      * and the corresponding methods as values.
120      * Used to pass to processNode() to read in
121      * include files and scan for datablocks.
122      */

123     protected Hashtable JavaDoc m_firstRunTags = new Hashtable JavaDoc();
124
125     /**
126      * This Hashtable contains some XML tags as keys
127      * and the corresponding methods as values.
128      * Used to pass to processNode() before generating
129      * HTML output.
130      */

131     protected Hashtable JavaDoc m_mainProcessTags = new Hashtable JavaDoc();
132
133     /** Constant for registering handling tags. */
134     protected static final int C_REGISTER_FIRST_RUN = 1;
135
136     /** Constant for registering handling tags. */
137     protected static final int C_REGISTER_MAIN_RUN = 2;
138
139     /** Boolean for additional debug output control. */
140     private static final boolean C_DEBUG = false;
141
142     /** DOM representaion of the template content. */
143     private Document JavaDoc m_content;
144
145     /** Filename this template was read from. */
146     private String JavaDoc m_filename;
147
148     /** All datablocks in DOM format. */
149     private Hashtable JavaDoc m_blocks = new Hashtable JavaDoc();
150
151     /** Reference all included A_CmsXmlContents. */
152     private Vector JavaDoc m_includedTemplates = new Vector JavaDoc();
153
154     /** Cache for parsed documents. */
155     private static Hashtable JavaDoc m_filecache = new Hashtable JavaDoc();
156
157     /** XML parser. */
158     private static I_CmsXmlParser m_parser = new CmsXmlXercesParser();
159
160     private String JavaDoc m_newEncoding;
161
162     /** Constructor for creating a new instance of this class. */
163     public A_CmsXmlContent() {
164         registerAllTags();
165     }
166
167     /**
168      * Calls a user method in the object callingObject.
169      * Every user method has to user the parameter types defined in
170      * C_PARAMTYPES_USER_METHODS to be recognized by this method.
171      *
172      * @see #C_PARAMTYPES_USER_METHODS
173      * @param methodName Name of the method to be called.
174      * @param parameter Additional parameter passed to the method.
175      * @param callingObject Reference to the object containing the called method.
176      * @param userObj Customizable user object that will be passed through to the user method.
177      * @param resolveMethods If true the methodtags will be resolved even if they have own CacheDirectives.
178      * @return cutomizable user object
179      * @throws CmsException if something goes wrong
180      */

181     protected Object JavaDoc callUserMethod(String JavaDoc methodName, String JavaDoc parameter, Object JavaDoc callingObject, Object JavaDoc userObj, boolean resolveMethods) throws CmsException {
182         Object JavaDoc[] params = new Object JavaDoc[] {m_cms, parameter, this, userObj};
183         Object JavaDoc result = null;
184
185         // Check if the user selected a object where to look for the user method.
186
if (callingObject == null) {
187             throwException("You are trying to call the user method \"" + methodName + "\" without giving an object containing this method. " + "Please select a callingObject in your getProcessedData or getProcessedDataValue call.", CmsLegacyException.C_XML_NO_USER_METHOD);
188         }
189
190         // check if the method has cachedirectives, if so we just return null
191
// this way the methode tag stays in the Element and can be handled like
192
// an normal element. We do this only if elementCache is active.
193
if (CmsXmlTemplateLoader.isElementCacheEnabled() && !resolveMethods) {
194             try {
195                 if (callingObject.getClass().getMethod("getMethodCacheDirectives", new Class JavaDoc[] {CmsObject.class, String JavaDoc.class}).invoke(callingObject, new Object JavaDoc[] {m_cms, methodName}) != null) {
196                     return null;
197                 }
198             } catch (NoSuchMethodException JavaDoc e) {
199                 throwException("Method getMethodeCacheDirectives was not found in class " + callingObject.getClass().getName() + ".", CmsLegacyException.C_XML_NO_USER_METHOD);
200             } catch (InvocationTargetException JavaDoc targetEx) {
201
202                 // the method could be invoked, but throwed a exception
203
// itself. Get this exception and throw it again.
204
Throwable JavaDoc e = targetEx.getTargetException();
205                 if (!(e instanceof CmsException)) {
206                     // Only print an error if this is NO CmsException
207
throwException("Method getMethodeCacheDirectives throwed an exception. " + e, CmsLegacyException.C_UNKNOWN_EXCEPTION);
208                 } else {
209                     // This is a CmsException Error printing should be done previously.
210
throw (CmsException) e;
211                 }
212             } catch (Exception JavaDoc exc2) {
213                 throwException("Method getMethodeCacheDirectives was found but could not be invoked. " + exc2, CmsLegacyException.C_XML_NO_USER_METHOD);
214             }
215         }
216
217         // OK. We have a calling object. Now try to invoke the method
218
try {
219             // try to invoke the method 'methodName'
220
result = getUserMethod(methodName, callingObject).invoke(callingObject, params);
221         } catch (NoSuchMethodException JavaDoc exc) {
222             throwException("User method " + methodName + " was not found in class " + callingObject.getClass().getName() + ".", CmsLegacyException.C_XML_NO_USER_METHOD);
223         } catch (InvocationTargetException JavaDoc targetEx) {
224
225             // the method could be invoked, but throwed a exception
226
// itself. Get this exception and throw it again.
227
Throwable JavaDoc e = targetEx.getTargetException();
228             if (!(e instanceof CmsException)) {
229                 // Only print an error if this is NO CmsException
230
throwException("User method " + methodName + " throwed an exception. " + e, CmsLegacyException.C_UNKNOWN_EXCEPTION);
231             } else {
232                 // This is a CmsException
233
// Error printing should be done previously.
234
throw (CmsException) e;
235             }
236         } catch (Exception JavaDoc exc2) {
237             throwException("User method " + methodName + " was found but could not be invoked. " + exc2, CmsLegacyException.C_XML_NO_USER_METHOD);
238         }
239         if ((result != null) && (!(result instanceof String JavaDoc || result instanceof CmsProcessedString || result instanceof Integer JavaDoc || result instanceof NodeList JavaDoc || result instanceof byte[]))) {
240             throwException("User method " + methodName + " in class " + callingObject.getClass().getName() + " returned an unsupported Object: " + result.getClass().getName(), CmsLegacyException.C_XML_PROCESS_ERROR);
241         }
242         return (result);
243     }
244
245     /**
246      * Deletes all files from the file cache.
247      */

248     public static void clearFileCache() {
249         if (CmsLog.getLog(A_CmsXmlContent.class).isInfoEnabled()) {
250             CmsLog.getLog(A_CmsXmlContent.class).info("Clearing XML file cache.");
251         }
252         m_filecache.clear();
253     }
254
255     /**
256      * Deletes the file represented by the given A_CmsXmlContent from
257      * the file cache.
258      * @param doc A_CmsXmlContent representing the XML file to be deleted.
259      */

260     public static void clearFileCache(A_CmsXmlContent doc) {
261         if (doc != null) {
262             String JavaDoc currentProject = doc.m_cms.getRequestContext().currentProject().getName();
263             String JavaDoc filename = doc.m_cms.getRequestContext().addSiteRoot(doc.getAbsoluteFilename());
264             m_filecache.remove(currentProject + ":" + filename);
265         }
266     }
267
268     /**
269      * Deletes the file with the given key from the file cache.
270      * If no such file exists nothing happens.
271      * @param key Key of the template file to be removed from the cache.
272      */

273     public static void clearFileCache(String JavaDoc key) {
274         m_filecache.remove(key);
275     }
276
277     /**
278      * Creates a clone of this object.
279      * @return cloned object.
280      * @throws CloneNotSupportedException if an error occurs while cloning
281      */

282     public Object JavaDoc clone() throws CloneNotSupportedException JavaDoc {
283         try {
284             A_CmsXmlContent newDoc = (A_CmsXmlContent) getClass().newInstance();
285             newDoc.init(m_cms, (Document JavaDoc) m_content.cloneNode(true), m_filename);
286             return newDoc;
287         } catch (Exception JavaDoc e) {
288             if (CmsLog.getLog(this).isErrorEnabled()) {
289                 CmsLog.getLog(this).error("Error while trying to clone object " + this.getClass().getName());
290             }
291             throw new CloneNotSupportedException JavaDoc(e.toString());
292         }
293     }
294
295     /**
296      * Concats two datablock hashtables and returns the resulting one.
297      *
298      * @param data1 First datablock hashtable.
299      * @param data2 Second datablock hashtable.
300      * @return Concatenated data.
301      */

302     private Hashtable JavaDoc concatData(Hashtable JavaDoc data1, Hashtable JavaDoc data2) {
303         Hashtable JavaDoc retValue = (Hashtable JavaDoc) data1.clone();
304         Enumeration JavaDoc keys = data2.keys();
305         Object JavaDoc key;
306         while (keys.hasMoreElements()) {
307             key = keys.nextElement();
308             retValue.put(key, data2.get(key));
309         }
310         return retValue;
311     }
312
313     /**
314      * Create a new CmsFile object containing an empty XML file of the
315      * current content type.
316      *
317      * The String returned by <code>getXmlDocumentTagName()</code>
318      * will be used to build the XML document element.
319      *
320      * @param cms Current cms object used for accessing system resources.
321      * @param filename Name of the file to be created.
322      * @param documentType Document type of the new file.
323      * @throws CmsException if no absolute filename is given or write access failed.
324      */

325     public void createNewFile(CmsObject cms, String JavaDoc filename, String JavaDoc documentType) throws CmsException {
326         if (!filename.startsWith("/")) {
327
328             // this is no absolute filename.
329
this.throwException("Cannot create new file. Bad name.", CmsLegacyException.C_BAD_NAME);
330         }
331         int pos = filename.lastIndexOf("/") + 1;
332         String JavaDoc folder = filename.substring(0, pos);
333         int type = OpenCms.getResourceManager().getResourceType(documentType).getTypeId();
334         cms.createResource(folder + filename, type);
335         cms.lockResource(filename);
336         m_cms = cms;
337         m_filename = filename;
338         try {
339             m_content = m_parser.createEmptyDocument(getXmlDocumentTagName());
340         } catch (Exception JavaDoc e) {
341             throwException("Cannot create empty XML document for file " + m_filename + ". ", CmsLegacyException.C_XML_PARSING_ERROR);
342         }
343         write();
344     }
345
346     /**
347      * Fast method to replace a datablock.
348      * <P>
349      * <b>USE WITH CARE!</b>
350      * <P>
351      * Using this method only if
352      * <ul>
353      * <li>The tag name is given in lowercase</li>
354      * <li>The datablock already exists (it may be empty)</li>
355      * <li>Neither tag nor data are <code>null</code></li>
356      * <li>You are sure, there will occure no errors</li>
357      * </ul>
358      *
359      * @param tag Key for this datablock.
360      * @param data String to be put in the datablock.
361      */

362     protected void fastSetData(String JavaDoc tag, String JavaDoc data) {
363
364         // fastSetData could have been called with an upper case argument
365
tag = tag.toLowerCase();
366         Element JavaDoc originalBlock = (Element JavaDoc) (m_blocks.get(tag));
367         while (originalBlock.hasChildNodes()) {
368             originalBlock.removeChild(originalBlock.getFirstChild());
369         }
370         originalBlock.appendChild(m_content.createTextNode(data));
371     }
372
373     /**
374      * Gets the absolute filename of the XML file represented by this content class.<p>
375      *
376      * @return Absolute filename
377      */

378     public String JavaDoc getAbsoluteFilename() {
379         return m_filename;
380     }
381
382     /**
383      * Gets all datablocks (the datablock hashtable).
384      * @return Hashtable with all datablocks.
385      */

386     protected Hashtable JavaDoc getAllData() {
387         return m_blocks;
388     }
389
390     /**
391      * This method should be implemented by every extending class.
392      * It returns a short description of the content definition type
393      * (e.g. "OpenCms news article").
394      * @return content description.
395      */

396     public abstract String JavaDoc getContentDescription();
397
398     /**
399      * Gets a complete datablock from the datablock hashtable.
400      *
401      * @param tag Key for the datablocks hashtable.
402      * @return Complete DOM element of the datablock for the given key or null if no datablock is found for this key.
403      * @throws CmsException if something goes wrong
404      */

405     protected Element JavaDoc getData(String JavaDoc tag) throws CmsException {
406         Object JavaDoc result = m_blocks.get(tag.toLowerCase());
407         if (result == null) {
408             String JavaDoc errorMessage = "Unknown Datablock " + tag + " requested.";
409             throwException(errorMessage, CmsLegacyException.C_XML_UNKNOWN_DATA);
410         } else {
411             if (!(result instanceof Element JavaDoc)) {
412                 String JavaDoc errorMessage = "Unexpected object returned as datablock. Requested Tagname: " + tag + ". Returned object: " + result.getClass().getName() + ".";
413                 throwException(errorMessage, CmsLegacyException.C_XML_CORRUPT_INTERNAL_STRUCTURE);
414             }
415         }
416         return (Element JavaDoc) m_blocks.get(tag.toLowerCase());
417     }
418
419     /**
420      * Gets the text and CDATA content of a datablock from the
421      * datablock hashtable.
422      *
423      * @param tag Key for the datablocks hashtable.
424      * @return Datablock content for the given key or null if no datablock is found for this key.
425      * @throws CmsException if something goes wrong
426      */

427     protected String JavaDoc getDataValue(String JavaDoc tag) throws CmsException {
428         Element JavaDoc dataElement = getData(tag);
429         return getTagValue(dataElement);
430     }
431
432     /**
433      * Gets a short filename (without path) of the XML file represented by this content class
434      * of the template file.
435      * @return filename
436      */

437     public String JavaDoc getFilename() {
438         return m_filename.substring(m_filename.lastIndexOf("/") + 1);
439     }
440
441     /**
442      * Gets a processed datablock from the datablock hashtable.
443      *
444      * @param tag Key for the datablocks hashtable.
445      * @return Processed datablock for the given key.
446      * @throws CmsException if something goes wrong
447      */

448     protected Element JavaDoc getProcessedData(String JavaDoc tag) throws CmsException {
449         return getProcessedData(tag, null, null);
450     }
451
452     /**
453      * Gets a processed datablock from the datablock hashtable.
454      *
455      * @param tag Key for the datablocks hashtable.
456      * @param callingObject Object that should be used to look up user methods.
457      * @return Processed datablock for the given key.
458      * @throws CmsException if something goes wrong
459      */

460     protected Element JavaDoc getProcessedData(String JavaDoc tag, Object JavaDoc callingObject) throws CmsException {
461         return getProcessedData(tag, callingObject, null);
462     }
463
464     /**
465      * Gets a processed datablock from the datablock hashtable.
466      * <P>
467      * The userObj Object is passed to all called user methods.
468      * By using this, the initiating class can pass customized data to its methods.
469      *
470      * @param tag Key for the datablocks hashtable.
471      * @param callingObject Object that should be used to look up user methods.
472      * @param userObj any object that should be passed to user methods
473      * @return Processed datablock for the given key.
474      * @throws CmsException if something goes wrong
475      */

476     protected Element JavaDoc getProcessedData(String JavaDoc tag, Object JavaDoc callingObject, Object JavaDoc userObj) throws CmsException {
477         Element JavaDoc dBlock = (Element JavaDoc) getData(tag).cloneNode(true);
478         processNode(dBlock, m_mainProcessTags, null, callingObject, userObj);
479         return dBlock;
480     }
481
482     /**
483      * Gets a processed datablock from the datablock hashtable.
484      * <P>
485      * The userObj Object is passed to all called user methods.
486      * By using this, the initiating class can pass customized data to its methods.
487      *
488      * @param tag Key for the datablocks hashtable.
489      * @param callingObject Object that should be used to look up user methods.
490      * @param userObj any object that should be passed to user methods
491      * @param stream OutputStream that may be used for directly streaming the results or null.
492      * @return Processed datablock for the given key.
493      * @throws CmsException if something goes wrong
494      */

495     protected Element JavaDoc getProcessedData(String JavaDoc tag, Object JavaDoc callingObject, Object JavaDoc userObj, OutputStream JavaDoc stream) throws CmsException {
496         Element JavaDoc dBlock = (Element JavaDoc) getData(tag).cloneNode(true);
497         processNode(dBlock, m_mainProcessTags, null, callingObject, userObj, stream);
498         return dBlock;
499     }
500
501     /**
502      * Gets the text and CDATA content of a processed datablock from the
503      * datablock hashtable.
504      *
505      * @param tag Key for the datablocks hashtable.
506      * @return Processed datablock for the given key.
507      * @throws CmsException if something goes wrong
508      */

509     protected String JavaDoc getProcessedDataValue(String JavaDoc tag) throws CmsException {
510         return getProcessedDataValue(tag, null, null, null);
511     }
512
513     /**
514      * Gets the text and CDATA content of a processed datablock from the
515      * datablock hashtable.
516      *
517      * @param tag Key for the datablocks hashtable.
518      * @param callingObject Object that should be used to look up user methods.
519      * @return Processed datablock for the given key.
520      * @throws CmsException if something goes wrong
521      */

522     protected String JavaDoc getProcessedDataValue(String JavaDoc tag, Object JavaDoc callingObject) throws CmsException {
523         return getProcessedDataValue(tag, callingObject, null, null);
524     }
525
526     /**
527      * Gets the text and CDATA content of a processed datablock from the
528      * datablock hashtable.
529      * <P>
530      * The userObj Object is passed to all called user methods.
531      * By using this, the initiating class can pass customized data to its methods.
532      *
533      * @param tag Key for the datablocks hashtable.
534      * @param callingObject Object that should be used to look up user methods.
535      * @param userObj any object that should be passed to user methods
536      * @return Processed datablock for the given key.
537      * @throws CmsException if something goes wrong
538      */

539     protected String JavaDoc getProcessedDataValue(String JavaDoc tag, Object JavaDoc callingObject, Object JavaDoc userObj) throws CmsException {
540         return getProcessedDataValue(tag, callingObject, userObj, null);
541     }
542
543     /**
544      * Gets the text and CDATA content of a processed datablock from the
545      * datablock hashtable. An eventually given output stream is user for streaming
546      * the generated result directly to the response output stream while processing.
547      * <P>
548      * The userObj Object is passed to all called user methods.
549      * By using this, the initiating class can pass customized data to its methods.
550      *
551      * @param tag Key for the datablocks hashtable.
552      * @param callingObject Object that should be used to look up user methods.
553      * @param userObj any object that should be passed to user methods
554      * @param stream OutputStream that may be used for directly streaming the results or null.
555      * @return Processed datablock for the given key.
556      * @throws CmsException if something goes wrong
557      */

558     protected String JavaDoc getProcessedDataValue(String JavaDoc tag, Object JavaDoc callingObject, Object JavaDoc userObj, OutputStream JavaDoc stream) throws CmsException {
559         // we cant cache the methods here, so we use the other way
560
registerTag("METHOD", A_CmsXmlContent.class, "handleMethodTagForSure", C_REGISTER_MAIN_RUN);
561         Element JavaDoc data = getProcessedData(tag, callingObject, userObj, stream);
562         registerTag("METHOD", A_CmsXmlContent.class, "handleMethodTag", C_REGISTER_MAIN_RUN);
563         return getTagValue(data);
564     }
565
566     /**
567      * Reads all text or CDATA values from the given XML element,
568      * e.g. <code>&lt;ELEMENT&gt;foo blah &lt;![CDATA[&lt;H1&gt;Hello&lt;/H1&gt;]]&gt;&lt;/ELEMENT&gt;</code>.
569      *
570      * @param n Element that should be read out.
571      * @return concatenated string of all text and CDATA nodes or <code>null</code>
572      * if no nodes were found.
573      */

574     protected String JavaDoc getTagValue(Element JavaDoc n) {
575         StringBuffer JavaDoc result = new StringBuffer JavaDoc();
576         if (n != null) {
577             NodeList JavaDoc childNodes = n.getChildNodes();
578             Node JavaDoc child = null;
579             if (childNodes != null) {
580                 int numchilds = childNodes.getLength();
581                 for (int i = 0; i < numchilds; i++) {
582                     child = childNodes.item(i);
583                     String JavaDoc nodeValue = child.getNodeValue();
584
585                     if (nodeValue != null) {
586                         //if(child.getNodeType() == n.TEXT_NODE || child.getNodeType() == n.CDATA_SECTION_NODE) {
587
if (child.getNodeType() == Node.CDATA_SECTION_NODE) {
588                             //result.append(child.getNodeValue());
589
result.append(nodeValue);
590                         } else {
591                             if (child.getNodeType() == Node.TEXT_NODE) {
592                                 //String s = child.getNodeValue().trim();
593
nodeValue = nodeValue.trim();
594                                 //if(!"".equals(s)) {
595
if (!"".equals(nodeValue)) {
596                                     //result.append(child.getNodeValue());
597
result.append(nodeValue);
598                                 }
599                             }
600                         }
601                     }
602                 }
603             }
604         }
605         return result.toString();
606     }
607
608     /**
609      * Looks up a user defined method requested by a "METHOD" tag.
610      * The method is searched in the Object callingObject.
611      * @param methodName Name of the user method
612      * @param callingObject Object that requested the processing of the XML document
613      * @return user method
614      * @throws NoSuchMethodException
615      */

616     private Method JavaDoc getUserMethod(String JavaDoc methodName, Object JavaDoc callingObject) throws NoSuchMethodException JavaDoc {
617         if (methodName == null || "".equals(methodName)) {
618
619             // no valid user method name
620
throw (new NoSuchMethodException JavaDoc("method name is null or empty"));
621         }
622         return callingObject.getClass().getMethod(methodName, C_PARAMTYPES_USER_METHODS);
623     }
624
625     /**
626      * Gets the XML parsed content of this template file as a DOM document.
627      * <P>
628      * <em>WARNING: The returned value is the original DOM document, not a clone.
629      * Any changes will take effect to the behaviour of this class.
630      * Especially datablocks are concerned by this!</em>
631      *
632      * @return the content of this template file.
633      */

634     protected Document JavaDoc getXmlDocument() {
635         return m_content;
636     }
637
638     /**
639      * This method should be implemented by every extending class.
640      * It returns the name of the XML document tag to scan for.
641      * @return name of the XML document tag.
642      */

643     public abstract String JavaDoc getXmlDocumentTagName();
644
645     /**
646      * Gets the currently used XML Parser.
647      * @return currently used parser.
648      */

649     public static I_CmsXmlParser getXmlParser() {
650         return m_parser;
651     }
652
653     /**
654      * Prints the XML parsed content to a String.<p>
655      *
656      * @return String with XML content
657      */

658     public String JavaDoc getXmlText() {
659         StringWriter JavaDoc writer = new StringWriter JavaDoc();
660         getXmlText(writer);
661         return writer.toString();
662     }
663
664     /**
665      * Prints the XML parsed content of this template file
666      * to the given Writer.
667      *
668      * @param out Writer to print to.
669      */

670     public void getXmlText(Writer JavaDoc out) {
671         m_parser.getXmlText(m_content, out);
672     }
673
674     /**
675      * Prints the XML parsed content of the given Node and
676      * its subnodes to the given Writer.<p>
677      *
678      * @param out Writer to print to.
679      * @param n Node that should be printed.
680      */

681     public void getXmlText(Writer JavaDoc out, Node JavaDoc n) {
682         Document JavaDoc tempDoc = (Document JavaDoc) m_content.cloneNode(false);
683         tempDoc.appendChild(m_parser.importNode(tempDoc, n));
684         m_parser.getXmlText(tempDoc, out);
685     }
686     
687     /**
688      * Prints the XML parsed content of the given Node and
689      * its subnodes to the given Writer.<p>
690      *
691      * @param out Stream to print to
692      */

693     public void getXmlText(OutputStream JavaDoc out) {
694         m_parser.getXmlText(m_content, out, m_newEncoding);
695     }
696
697     /**
698      * Prints the XML parsed content of the given Node and
699      * its subnodes to the given Writer.<p>
700      *
701      * @param out Stream to print to
702      * @param n Node that should be printed.
703      */

704     public void getXmlText(OutputStream JavaDoc out, Node JavaDoc n) {
705         Document JavaDoc tempDoc = (Document JavaDoc) m_content.cloneNode(false);
706         tempDoc.appendChild(m_parser.importNode(tempDoc, n));
707         m_parser.getXmlText(tempDoc, out, m_newEncoding);
708     }
709
710     /**
711      * Prints the XML parsed content of a given node and
712      * its subnodes to a String.<p>
713      *
714      * @param n Node that should be printed.
715      * @return String with XML content
716      */

717     public String JavaDoc getXmlText(Node JavaDoc n) {
718         StringWriter JavaDoc writer = new StringWriter JavaDoc();
719         getXmlText(writer, n);
720         return writer.toString();
721     }
722
723     /**
724      * This method is just a hack so that the Eclise IDE will not show the methods listed here
725      * as warnings when the "unused private methods" option is selected,
726      * since they are called only using reclection API.
727      * Do not use this method.
728      *
729      * @throws CmsException if something goes wrong
730      */

731     protected void callAllUncalledMethodsSoThatEclipseDoesntComplainAboutThem() throws CmsException {
732         this.handleDataTag(null, null, null);
733         this.handleIncludeTag(null, null, null);
734         this.handleLinkTag(null, null, null);
735         this.handleMethodTag(null, null, null);
736         this.handleMethodTag(null, null, null);
737         this.handleMethodTagForSure(null, null, null);
738         this.handleProcessTag(null, null, null);
739         this.replaceTagByComment(null, null, null);
740     }
741
742     /**
743      * Handling of "DATA" tags and unknown tags.
744      * A reference to each data tag ist stored in an internal hashtable with
745      * the name of the datablock as key.
746      * Nested datablocks are stored with names like outername.innername
747      *
748      * @param n XML element containing the <code>&lt;DATA&gt;</code> tag.
749      * @param callingObject Reference to the object requesting the node processing.
750      * @param userObj Customizable user object that will be passed through to handling and user methods.
751      */

752     private void handleDataTag(Element JavaDoc n, Object JavaDoc callingObject, Object JavaDoc userObj) {
753         String JavaDoc blockname;
754         String JavaDoc bestFit = null;
755         String JavaDoc parentname = null;
756         Node JavaDoc parent = n.getParentNode();
757         while (parent != null && parent.getNodeType() == Node.ELEMENT_NODE) {
758
759             // check if this datablock is part of a datablock
760

761             // hierarchy like 'language.de.btn_yes'
762

763             // look for the best fitting hierarchy name part, too
764
if (parent.getNodeName().equals("DATA")) {
765                 blockname = ((Element JavaDoc) parent).getAttribute("name");
766             } else {
767                 blockname = parent.getNodeName();
768                 String JavaDoc secondName = ((Element JavaDoc) parent).getAttribute("name");
769                 if (!"".equals(secondName)) {
770                     blockname = blockname + "." + secondName;
771                 }
772             }
773             blockname = blockname.toLowerCase();
774             if (parentname == null) {
775                 parentname = blockname;
776             } else {
777                 parentname = blockname + "." + parentname;
778             }
779             if (m_blocks.containsKey(parentname)) {
780                 bestFit = parentname;
781             }
782             parent = parent.getParentNode();
783         }
784
785         // bestFit now contains the best fitting name part
786

787         // next, look for the tag name (the part behind the last ".")
788
if (n.getNodeName().equals("DATA")) {
789             blockname = n.getAttribute("name");
790         } else {
791             blockname = n.getNodeName();
792             String JavaDoc secondName = n.getAttribute("name");
793             if (!"".equals(secondName)) {
794                 blockname = blockname + "." + secondName;
795             }
796         }
797         blockname = blockname.toLowerCase();
798
799         // now we can build the complete datablock name
800
if (bestFit != null) {
801             blockname = bestFit + "." + blockname;
802         }
803         if (CmsLog.getLog(this).isDebugEnabled() && C_DEBUG) {
804             CmsLog.getLog(this).debug("Reading datablock " + blockname);
805         }
806
807         // finally we cat put the new datablock into the hashtable
808
m_blocks.put(blockname, n);
809
810         //return null;
811
}
812
813     /**
814      * Handling of "INCLUDE" tags.
815      * @param n XML element containing the <code>&lt;INCLUDE&gt;</code> tag.
816      * @param callingObject Reference to the object requesting the node processing.
817      * @param userObj Customizable user object that will be passed through to handling and user methods.
818      */

819     private Object JavaDoc handleIncludeTag(Element JavaDoc n, Object JavaDoc callingObject, Object JavaDoc userObj) throws CmsException {
820         A_CmsXmlContent include = null;
821         String JavaDoc tagcontent = getTagValue(n);
822         include = readIncludeFile(tagcontent);
823         return include.getXmlDocument().getDocumentElement().getChildNodes();
824     }
825
826     /**
827      * Handling of "LINK" tags.
828      * @param n XML element containing the <code>&lt;LINK&gt;</code> tag.
829      * @param callingObject Reference to the object requesting the node processing.
830      * @param userObj Customizable user object that will be passed through to handling and user methods.
831      */

832     private Object JavaDoc handleLinkTag(Element JavaDoc n, Object JavaDoc callingObject, Object JavaDoc userObj) throws CmsException {
833         // get the string and call the getLinkSubstitution method
834
Element JavaDoc dBlock = (Element JavaDoc) n.cloneNode(true);
835         processNode(dBlock, m_mainProcessTags, null, callingObject, userObj, null);
836         String JavaDoc link = getTagValue(dBlock);
837         return OpenCms.getLinkManager().substituteLink(m_cms, link);
838     }
839
840     /**
841      * Handling of the "METHOD name=..." tags.
842      * Name attribute and value of the element are read and the user method
843      * 'name' is invoked with the element value as parameter.
844      *
845      * @param n XML element containing the <code>&lt;METHOD&gt;</code> tag.
846      * @param callingObject Reference to the object requesting the node processing.
847      * @param userObj Customizable user object that will be passed through to handling and user methods.
848      * @return Object returned by the user method
849      * @throws CmsException
850      */

851     private Object JavaDoc handleMethodTag(Element JavaDoc n, Object JavaDoc callingObject, Object JavaDoc userObj) throws CmsException {
852         processNode(n, m_mainProcessTags, null, callingObject, userObj);
853         String JavaDoc tagcontent = getTagValue(n);
854         String JavaDoc method = n.getAttribute("name");
855         Object JavaDoc result = null;
856         try {
857             result = callUserMethod(method, tagcontent, callingObject, userObj, false);
858         } catch (Throwable JavaDoc e1) {
859             if (e1 instanceof CmsException) {
860                 throw (CmsException) e1;
861             } else {
862                 throwException("handleMethodTag() received an exception from callUserMethod() while calling \"" + method + "\" requested by class " + callingObject.getClass().getName() + ": " + e1);
863             }
864         }
865         return result;
866     }
867
868     /**
869      * Handling of the "METHOD name=..." tags.
870      * In contrast to the method handleMethodTag this method resolves
871      * every method even if it has it own CacheDirectives. It is used only for
872      * getProcessedDataValue.
873      * Name attribute and value of the element are read and the user method
874      * 'name' is invoked with the element value as parameter.
875      *
876      * @param n XML element containing the <code>&lt;METHOD&gt;</code> tag.
877      * @param callingObject Reference to the object requesting the node processing.
878      * @param userObj Customizable user object that will be passed through to handling and user methods.
879      * @return Object returned by the user method
880      * @throws CmsException
881      */

882     private Object JavaDoc handleMethodTagForSure(Element JavaDoc n, Object JavaDoc callingObject, Object JavaDoc userObj) throws CmsException {
883         processNode(n, m_mainProcessTags, null, callingObject, userObj);
884         String JavaDoc tagcontent = getTagValue(n);
885         String JavaDoc method = n.getAttribute("name");
886         Object JavaDoc result = null;
887         try {
888             result = callUserMethod(method, tagcontent, callingObject, userObj, true);
889         } catch (Throwable JavaDoc e1) {
890             if (e1 instanceof CmsException) {
891                 throw (CmsException) e1;
892             } else {
893                 throwException("handleMethodTagForSure() received an exception from callUserMethod() while calling \"" + method + "\" requested by class " + callingObject.getClass().getName() + ": " + e1);
894             }
895         }
896         return result;
897     }
898
899     /**
900      * Handling of the "PROCESS" tags.
901      * Looks up the requested datablocks in the internal hashtable and
902      * returns its subnodes.
903      *
904      * @param n XML element containing the <code>&lt;PROCESS&gt;</code> tag.
905      * @param callingObject Reference to the object requesting the node processing.
906      * @param userObj Customizable user object that will be passed through to handling and user methods.
907      */

908     private Object JavaDoc handleProcessTag(Element JavaDoc n, Object JavaDoc callingObject, Object JavaDoc userObj) {
909         String JavaDoc blockname = getTagValue(n).toLowerCase();
910         Element JavaDoc datablock = null;
911         if (CmsLog.getLog(this).isDebugEnabled() && C_DEBUG) {
912             CmsLog.getLog(this).debug("Request for datablock \"" + blockname + "\"");
913         }
914         datablock = ((Element JavaDoc)m_blocks.get(blockname));
915         if (datablock == null) {
916             if (CmsLog.getLog(this).isErrorEnabled()) {
917                 String JavaDoc logUri = "";
918                 try {
919                     logUri = " RequestUri is " + m_cms.getRequestContext().getFolderUri() + m_cms.getRequestContext().getUri() + ".";
920                 } catch (Exception JavaDoc e) {
921                     // noop
922
}
923                 CmsLog.getLog(this).error("Requested datablock \"" + blockname + "\" not found in " + m_filename + " - " + logUri);
924             }
925             return C_ERR_NODATABLOCK + blockname;
926         } else {
927             return datablock.getChildNodes();
928         }
929     }
930
931     /**
932      * Checks if this Template owns a datablock with the given key.
933      * @param key Datablock key to be checked.
934      * @return true if a datablock is found, false otherwise.
935      */

936     protected boolean hasData(String JavaDoc key) {
937         return m_blocks.containsKey(key.toLowerCase());
938     }
939
940     /**
941      * Initialize the XML content class.
942      * Load and parse the content of the given CmsFile object.
943      * @param cms CmsObject Object for accessing resources.
944      * @param file CmsFile object of the file to be loaded and parsed.
945      * @throws CmsException if something goes wrong
946      */

947     public void init(CmsObject cms, CmsFile file) throws CmsException {
948         String JavaDoc filename = cms.getSitePath(file);
949         String JavaDoc currentProject = cms.getRequestContext().currentProject().getName();
950         Document JavaDoc parsedContent = null;
951         m_cms = cms;
952         m_filename = filename;
953         parsedContent = loadCachedDocument(cms.getRequestContext().addSiteRoot(filename));
954         if (parsedContent == null) {
955             byte[] fileContent = file.getContents();
956             if (fileContent == null || fileContent.length <= 1) {
957                 // The file content is empty. Possibly the file object is only
958
// a file header. Re-read the file object and try again
959
file = cms.readFile(filename);
960                 fileContent = file.getContents();
961             }
962             if (fileContent == null || fileContent.length <= 1) {
963                 // The file content is still emtpy.
964
// Start with an empty XML document.
965
try {
966                     parsedContent = getXmlParser().createEmptyDocument(getXmlDocumentTagName());
967                 } catch (Exception JavaDoc e) {
968                     throwException("Could not initialize now XML document " + filename + ". " + e, CmsLegacyException.C_XML_PARSING_ERROR);
969                 }
970             } else {
971                 parsedContent = parse(fileContent);
972             }
973             m_filecache.put(currentProject + ":" + cms.getRequestContext().addSiteRoot(filename), parsedContent.cloneNode(true));
974             fileContent = null;
975         }
976         init(cms, parsedContent, filename);
977     }
978
979     /**
980      * Initialize the XML content class.
981      * Load and parse the content of the given String.
982      *
983      * @param cms CmsObject Object for accessing resources.
984      * @param filename file name to use when storing the parsed XML in the cache
985      * @param content XMl content to parse
986      * @throws CmsException if something goes wrong
987      */

988     public void init(CmsObject cms, String JavaDoc filename, String JavaDoc content) throws CmsException {
989         m_cms = cms;
990         m_filename = filename + com.opencms.core.I_CmsConstants.C_XML_CONTROL_FILE_SUFFIX;
991         init(cms, parse(content.getBytes()), filename);
992     }
993
994     /**
995      * Initialize the XML content class.
996      * Load and parse the content of the given CmsFile object.
997      * <P>
998      * If a previously cached parsed content exists, it will be re-used.
999      * <P>
1000     * If no absolute file name ist given,
1001     * template files will be searched a hierachical order using
1002     * <code>lookupAbsoluteFilename</code>.
1003     *
1004     * @param cms CmsObject Object for accessing resources.
1005     * @param filename CmsFile name of the file to be loaded and parsed.
1006     * @throws CmsException if something goes wrong
1007     */

1008    public void init(CmsObject cms, String JavaDoc filename) throws CmsException {
1009
1010        if (!filename.startsWith("/")) {
1011            throw new CmsLegacyException("A relative path has entered the A_CmsXmlContent class. filename=" + filename + "");
1012        }
1013        String JavaDoc currentProject = cms.getRequestContext().currentProject().getName();
1014        Document JavaDoc parsedContent = null;
1015        m_cms = cms;
1016        m_filename = filename;
1017        parsedContent = loadCachedDocument(cms.getRequestContext().addSiteRoot(filename));
1018        if (parsedContent == null) {
1019            CmsFile file = cms.readFile(filename);
1020            
1021            parsedContent = parse(file.getContents());
1022            m_filecache.put(currentProject + ":" + cms.getRequestContext().addSiteRoot(filename), parsedContent.cloneNode(true));
1023        } else {
1024
1025            // File was found in cache.
1026
// We have to read the file header to check access rights.
1027
cms.readResource(filename);
1028        }
1029
1030        if (C_PRINTNODES) {
1031            if (filename.indexOf(CmsWorkplaceDefault.C_VFS_DIR_LOCALES) != -1) {
1032                System.err.println("\n" + filename);
1033                this.printNode(parsedContent, 0, "");
1034            }
1035        }
1036
1037        init(cms, parsedContent, filename);
1038    }
1039
1040    /** Flag to enable / disable printNode() method. */
1041    private static final boolean C_PRINTNODES = false;
1042
1043    /**
1044     * Prints all nodes of a XML locale file in depth first order split by "."
1045     * to STDOUT.<p>
1046     *
1047     * This method is useful for backward compatibility: you can copy
1048     * the output of this method (which is written to $TOMCAT_HOME/logs/catalina.
1049     * out) to build Java resource bundles. This method is for internal use
1050     * only, should be deactivated on a production system!<p>
1051     *
1052     * Activate this method by setting the value of C_PRINTNODES to true;
1053     *
1054     * @param node the current node in the XML document that is examined
1055     * @param depth the current depth in the XML tree
1056     * @param path the current path of the XML nodes, eg. node1.node2.node3...
1057     */

1058    private void printNode(Node JavaDoc node, int depth, String JavaDoc path) {
1059
1060        if (C_PRINTNODES) {
1061            // Char array for the printNode() method
1062
final String JavaDoc badChars = "\n";
1063
1064            // Char array for the printNode() method
1065
final String JavaDoc[] goodChars = {"\\n"};
1066
1067            int nodeType = node.getNodeType();
1068
1069            if (nodeType == Node.ELEMENT_NODE) {
1070                String JavaDoc nodeName = node.getNodeName();
1071
1072                if (!"".equals(nodeName)) {
1073                    if (depth > 2) {
1074                        path += ".";
1075                    }
1076                    if (depth > 1) {
1077                        path += nodeName;
1078                    }
1079                }
1080            } else if (nodeType == Node.TEXT_NODE) {
1081                String JavaDoc nodeValue = node.getNodeValue();
1082
1083                if (!"".equals(nodeValue)) {
1084                    int nodeValueLength = nodeValue.length();
1085                    String JavaDoc nodeValueNoBadChars = "";
1086
1087                    for (int i = 0; i < nodeValueLength; i++) {
1088                        int index = 0;
1089
1090                        if ((index = badChars.indexOf(nodeValue.charAt(i))) != -1) {
1091                            nodeValueNoBadChars += goodChars[index];
1092                        } else {
1093                            nodeValueNoBadChars += nodeValue.charAt(i);
1094                        }
1095                    }
1096
1097                    if (node.getPreviousSibling() == null) {
1098                        System.out.print(path + "=");
1099                    }
1100
1101                    System.out.print(nodeValueNoBadChars);
1102
1103                    if (node.getNextSibling() == null) {
1104                        System.out.print("\n");
1105                    }
1106                }
1107            } else if (nodeType == Node.CDATA_SECTION_NODE) {
1108                CDATASection JavaDoc cdata = (CDATASection JavaDoc) node;
1109                String JavaDoc nodeValue = cdata.getData();
1110
1111                if (!"".equals(nodeValue)) {
1112                    int nodeValueLength = nodeValue.length();
1113                    String JavaDoc nodeValueNoBadChars = "";
1114
1115                    for (int i = 0; i < nodeValueLength; i++) {
1116                        int index = 0;
1117
1118                        if ((index = badChars.indexOf(nodeValue.charAt(i))) != -1) {
1119                            nodeValueNoBadChars += goodChars[index];
1120                        } else {
1121                            nodeValueNoBadChars += nodeValue.charAt(i);
1122                        }
1123                    }
1124
1125                    if (node.getPreviousSibling() == null) {
1126                        System.out.print(path + "=");
1127                    }
1128
1129                    System.out.print(nodeValueNoBadChars);
1130
1131                    if (node.getNextSibling() == null) {
1132                        System.out.print("\n");
1133                    }
1134                }
1135            }
1136
1137            NodeList JavaDoc nodeChildren = node.getChildNodes();
1138            if (nodeChildren != null) {
1139                for (int i = 0; i < nodeChildren.getLength(); i++) {
1140                    printNode(nodeChildren.item(i), depth + 1, path);
1141                }
1142            }
1143        }
1144    }
1145
1146    /**
1147     * Initialize the class with the given parsed XML DOM document.
1148     * @param cms CmsObject Object for accessing system resources.
1149     * @param content DOM document object containing the parsed XML file.
1150     * @param filename OpenCms filename of the XML file.
1151     * @throws CmsException if something goes wrong
1152     */

1153    public void init(CmsObject cms, Document JavaDoc content, String JavaDoc filename) throws CmsException {
1154        m_cms = cms;
1155        m_content = content;
1156        m_filename = filename;
1157
1158        // First check the document tag. Is this the right document type?
1159
Element JavaDoc docRootElement = m_content.getDocumentElement();
1160        String JavaDoc docRootElementName = docRootElement.getNodeName().toLowerCase();
1161        if (!docRootElementName.equals(getXmlDocumentTagName().toLowerCase())) {
1162
1163            // Hey! This is a wrong XML document!
1164

1165            // We will throw an execption and the document away :-)
1166
removeFromFileCache();
1167            m_content = null;
1168            String JavaDoc errorMessage = "XML document " + getAbsoluteFilename() + " is not of the expected type. This document is \"" + docRootElementName + "\", but it should be \"" + getXmlDocumentTagName() + "\" (" + getContentDescription() + ").";
1169            throwException(errorMessage, CmsLegacyException.C_XML_WRONG_CONTENT_TYPE);
1170        }
1171
1172        // OK. Document tag is fine. Now get the DATA tags and collect them
1173

1174        // in a Hashtable (still in DOM representation!)
1175
try {
1176            processNode(m_content, m_firstRunTags, A_CmsXmlContent.class.getDeclaredMethod("handleDataTag", C_PARAMTYPES_HANDLING_METHODS), null, null);
1177        } catch (CmsException e) {
1178            if (CmsLog.getLog(this).isWarnEnabled()) {
1179                CmsLog.getLog(this).warn("Error while scanning for DATA and INCLUDE tags in file " + getAbsoluteFilename(), e);
1180            }
1181            throw e;
1182        } catch (NoSuchMethodException JavaDoc e2) {
1183            String JavaDoc errorMessage = "XML tag process method \"handleDataTag\" could not be found";
1184            throwException(errorMessage, CmsLegacyException.C_XML_NO_PROCESS_METHOD);
1185        }
1186    }
1187
1188    /**
1189     * Internal method for creating a new datablock.
1190     * <P>
1191     * This method is called by setData() if a new, not existing
1192     * datablock must be created.
1193     * <P>
1194     * <B>Functionality:</B> If a non-hierarchical datablock is given,
1195     * it is inserted at the end of the DOM document.
1196     * If a hierarchical datablock is given, all possible parent
1197     * names are checked in a backward oriented order. If a
1198     * datablock with a name that equals a part of the hierarchy is
1199     * found, the new datablock will be created as a (sub)child
1200     * of this datablock.
1201     *
1202     * @param tag Key for this datablock.
1203     * @param data DOM element node for this datablock.
1204     */

1205    private void insertNewDatablock(String JavaDoc tag, Element JavaDoc data) {
1206
1207        // First check, if this is an extended datablock
1208
// in <NAME1 name="name2>... format, that has to be inserted
1209
// as name1.name2
1210
String JavaDoc nameAttr = data.getAttribute("name");
1211        String JavaDoc workTag = null;
1212        if ((!data.getNodeName().toLowerCase().equals("data")) && nameAttr != null && (!"".equals(nameAttr))) {
1213            // this is an extended datablock
1214
workTag = tag.substring(0, tag.lastIndexOf("."));
1215        } else {
1216            workTag = tag;
1217        }
1218        // Import the node for later inserting
1219
Element JavaDoc importedNode = (Element JavaDoc) m_parser.importNode(m_content, data);
1220
1221        // Check, if this is a simple datablock without hierarchy.
1222
if (workTag.indexOf(".") == -1) {
1223            // Fine. We can insert the new Datablock at the of the document
1224
m_content.getDocumentElement().appendChild(importedNode);
1225            m_blocks.put(tag, importedNode);
1226        } else {
1227            // This is a hierachical datablock tag. We have to search for
1228
// an appropriate place to insert first.
1229
boolean found = false;
1230            String JavaDoc match = "." + workTag;
1231            int dotIndex = match.lastIndexOf(".");
1232            Vector JavaDoc newBlocks = new Vector JavaDoc();
1233            while ((!found) && (dotIndex > 1)) {
1234                match = match.substring(0, dotIndex);
1235                if (hasData(match.substring(1))) {
1236                    found = true;
1237                } else {
1238                    dotIndex = match.lastIndexOf(".");
1239                    newBlocks.addElement(match.substring(dotIndex + 1));
1240                }
1241            }
1242            // newBlocks now contains a (backward oriented) list
1243
// of all datablocks that have to be created, before
1244
// the new datablock named "tag" can be inserted.
1245
String JavaDoc datablockPrefix = "";
1246            if (found) {
1247                datablockPrefix = match.substring(1) + ".";
1248            }
1249            // number of new elements to be created
1250
int numNewBlocks = newBlocks.size();
1251            // used to create the required new elements
1252
Element JavaDoc newElem = null;
1253            // Contains the last existing Element in the hierarchy.
1254
Element JavaDoc lastElem = null;
1255            // now create the new elements backwards
1256
for (int i = numNewBlocks - 1; i >= 0; i--) {
1257                newElem = m_content.createElement("DATA");
1258                newElem.setAttribute("name", (String JavaDoc) newBlocks.elementAt(i));
1259                m_blocks.put(datablockPrefix + (String JavaDoc) newBlocks.elementAt(i), newElem);
1260                if (lastElem != null) {
1261                    lastElem.appendChild(newElem);
1262                } else {
1263                    lastElem = newElem;
1264                }
1265            }
1266            // Now all required parent datablocks are created.
1267
// Finally the given datablock can be inserted.
1268
if (lastElem != null) {
1269                lastElem.appendChild(importedNode);
1270            } else {
1271                lastElem = importedNode;
1272            }
1273            m_blocks.put(datablockPrefix + tag, importedNode);
1274
1275            // lastElem now contains the hierarchical tree of all DATA tags to be
1276
// inserted.
1277
// If we have found an existing part of the hierarchy, get
1278
// this part and append the tree. If no part was found, append the
1279
// tree at the end of the document.
1280
if (found) {
1281                Element JavaDoc parent = (Element JavaDoc) m_blocks.get(match.substring(1));
1282                parent.appendChild(lastElem);
1283            } else {
1284                m_content.getDocumentElement().appendChild(lastElem);
1285            }
1286        }
1287    }
1288
1289    /**
1290     * Reloads a previously cached parsed content.
1291     *
1292     * @param filename Absolute pathname of the file to look for.
1293     * @return DOM parsed document or null if the cached content was not found.
1294     */

1295    private Document JavaDoc loadCachedDocument(String JavaDoc filename) {
1296        Document JavaDoc cachedDoc = null;
1297        String JavaDoc currentProject = m_cms.getRequestContext().currentProject().getName();
1298        Document JavaDoc lookup = (Document JavaDoc) m_filecache.get(currentProject + ":" + filename);
1299        if (lookup != null) {
1300            try {
1301                //cachedDoc = lookup.cloneNode(true).getOwnerDocument();
1302
cachedDoc = (Document JavaDoc) lookup.cloneNode(true);
1303            } catch (Exception JavaDoc e) {
1304                lookup = null;
1305                cachedDoc = null;
1306            }
1307        }
1308        if (CmsLog.getLog(this).isDebugEnabled() && cachedDoc != null) {
1309            CmsLog.getLog(this).debug("Reused previously parsed XML file " + getFilename());
1310        }
1311        return cachedDoc;
1312    }
1313
1314    /**
1315     * Generates a XML comment.
1316     * It's used to replace no longer needed DOM elements by a short XML comment
1317     *
1318     * @param n XML element containing the tag to be replaced <em>(unused)</em>.
1319     * @param callingObject Reference to the object requesting the node processing <em>(unused)</em>.
1320     * @param userObj Customizable user object that will be passed through to handling and user methods <em>(unused)</em>.
1321     * @return the generated XML comment.
1322     */

1323    private NodeList JavaDoc replaceTagByComment(Element JavaDoc n, Object JavaDoc callingObject, Object JavaDoc userObj) {
1324        Element JavaDoc tempNode = (Element JavaDoc) n.cloneNode(false);
1325        while (tempNode.hasChildNodes()) {
1326            tempNode.removeChild(tempNode.getFirstChild());
1327        }
1328        tempNode.appendChild(m_content.createComment("removed " + n.getNodeName()));
1329        return tempNode.getChildNodes();
1330    }
1331    
1332    /**
1333     * Starts the XML parser with the content of the given CmsFile object.
1334     * After parsing the document it is scanned for INCLUDE and DATA tags
1335     * by calling processNode with m_firstRunParameters.
1336     *
1337     * @param content byte array to be parsed
1338     * @return parsed DOM document
1339     * @throws CmsException if something goes wrong
1340     */

1341    protected Document JavaDoc parse(byte[] content) throws CmsException {
1342        return parse(new ByteArrayInputStream JavaDoc(content));
1343    }
1344
1345    /**
1346     * Starts the XML parser with the content of the given CmsFile object.
1347     * After parsing the document it is scanned for INCLUDE and DATA tags
1348     * by calling processNode with m_firstRunParameters.
1349     *
1350     * @param content String to be parsed
1351     * @return Parsed DOM document.
1352     * @throws CmsException if something goes wrong
1353     */

1354    protected Document JavaDoc parse(InputStream JavaDoc content) throws CmsException {
1355        Document JavaDoc parsedDoc = null;
1356
1357        // First parse the String for XML Tags and
1358
// get a DOM representation of the document
1359
try {
1360            parsedDoc = m_parser.parse(content);
1361        } catch (Exception JavaDoc e) {
1362            // Error while parsing the document.
1363
// there ist nothing to do, we cannot go on.
1364
String JavaDoc errorMessage = "Cannot parse XML file \"" + getAbsoluteFilename() + "\". " + e;
1365            throwException(errorMessage, CmsLegacyException.C_XML_PARSING_ERROR);
1366        }
1367        if (parsedDoc == null) {
1368            String JavaDoc errorMessage = "Unknown error. Parsed DOM document is null.";
1369            throwException(errorMessage, CmsLegacyException.C_XML_PARSING_ERROR);
1370        }
1371
1372        // Try to normalize the XML document.
1373
// We should not call the normalize() method in the usual way
1374
// here, since the DOM interface changed at this point between
1375
// Level 1 and Level 2.
1376
// It's better to lookup the normalize() method first using reflection
1377
// API and call it then. So we will get the appropriate method for the
1378
// currently used DOM level and avoid NoClassDefFound exceptions.
1379
try {
1380            Class JavaDoc elementClass = Class.forName("org.w3c.dom.Element");
1381            Method JavaDoc normalizeMethod = elementClass.getMethod("normalize", new Class JavaDoc[] {});
1382            normalizeMethod.invoke(parsedDoc.getDocumentElement(), new Object JavaDoc[] {});
1383        } catch (Exception JavaDoc e) {
1384            // The workaround using reflection API failed.
1385
// We have to throw an exception.
1386
throwException("Normalizing the XML document failed. Possibly you are using concurrent versions of " + "the XML parser with different DOM levels. ", e, CmsLegacyException.C_XML_PARSING_ERROR);
1387        }
1388        // Delete all unnecessary text nodes from the tree.
1389
// These nodes could cause errors when serializing this document otherwise
1390
Node JavaDoc loop = parsedDoc.getDocumentElement();
1391        while (loop != null) {
1392            Node JavaDoc next = treeWalker(parsedDoc.getDocumentElement(), loop);
1393            if (loop.getNodeType() == Node.TEXT_NODE) {
1394                Node JavaDoc leftSibling = loop.getPreviousSibling();
1395                Node JavaDoc rightSibling = loop.getNextSibling();
1396                if (leftSibling == null || rightSibling == null || (leftSibling.getNodeType() == Node.ELEMENT_NODE && rightSibling.getNodeType() == Node.ELEMENT_NODE)) {
1397                    if ("".equals(loop.getNodeValue().trim())) {
1398                        loop.getParentNode().removeChild(loop);
1399                    }
1400                }
1401            }
1402            loop = next;
1403        }
1404        return parsedDoc;
1405    }
1406
1407    /**
1408     * Main processing funtion for the whole XML document.
1409     *
1410     * @param keys Hashtable with XML tags to look for and corresponding methods.
1411     * @param defaultMethod Method to be called if the tag is unknown.
1412     * @param callingObject Reference to the object requesting the node processing.
1413     * @param userObj Customizable user object that will be passed through to handling and user methods.
1414     * @throws CmsException if something goes wrong
1415     */

1416    protected void processDocument(Hashtable JavaDoc keys, Method JavaDoc defaultMethod, Object JavaDoc callingObject, Object JavaDoc userObj) throws CmsException {
1417        processNode(m_content.getDocumentElement(), keys, defaultMethod, callingObject, userObj);
1418    }
1419
1420    /**
1421     * Universal main processing function for parsed XML templates.
1422     * The given node is processed by a tree walk.
1423     * <P>
1424     * Every XML tag will be looked up in the Hashtable "keys".
1425     * If a corresponding entry is found, the tag will be handled
1426     * by the corresponding function returned from the Hashtable.
1427     * <P>
1428     * If an unknown tag is detected the method defaultMethod is called
1429     * instead. Is defaultMethod == null nothing will be done with unknown tags.
1430     * <P>
1431     * The invoked handling methods are allowed to return null or objects
1432     * of the type String, Node, Integer or byte[].
1433     * If the return value is null, nothing happens. In all other cases
1434     * the handled node in the tree will be replaced by a new node.
1435     * The value of this new node depends on the type of the returned value.
1436     *
1437     * @param n Node with its subnodes to process
1438     * @param keys Hashtable with XML tags to look for and corresponding methods.
1439     * @param defaultMethod Method to be called if the tag is unknown.
1440     * @param callingObject Reference to the Object that requested the node processing.
1441     * @param userObj Customizable user object that will be passed to handling and user methods.
1442     * @throws CmsException if something goes wrong
1443     */

1444    protected void processNode(Node JavaDoc n, Hashtable JavaDoc keys, Method JavaDoc defaultMethod, Object JavaDoc callingObject, Object JavaDoc userObj) throws CmsException {
1445        processNode(n, keys, defaultMethod, callingObject, userObj, null);
1446    }
1447
1448    /**
1449     * Universal main processing function for parsed XML templates.
1450     * The given node is processed by a tree walk.
1451     * <P>
1452     * Every XML tag will be looked up in the Hashtable "keys".
1453     * If a corresponding entry is found, the tag will be handled
1454     * by the corresponding function returned from the Hashtable.
1455     * <P>
1456     * If an unknown tag is detected the method defaultMethod is called
1457     * instead. Is defaultMethod == null nothing will be done with unknown tags.
1458     * <P>
1459     * The invoked handling methods are allowed to return null or objects
1460     * of the type String, Node, Integer or byte[].
1461     * If the return value is null, nothing happens. In all other cases
1462     * the handled node in the tree will be replaced by a new node.
1463     * The value of this new node depends on the type of the returned value.
1464     *
1465     * @param n Node with its subnodes to process
1466     * @param keys Hashtable with XML tags to look for and corresponding methods.
1467     * @param defaultMethod Method to be called if the tag is unknown.
1468     * @param callingObject Reference to the Object that requested the node processing.
1469     * @param userObj Customizable user object that will be passed to handling and user methods.
1470     * @param stream the output stream
1471     * @throws CmsException if something goes wrong
1472     */

1473    protected void processNode(Node JavaDoc n, Hashtable JavaDoc keys, Method JavaDoc defaultMethod, Object JavaDoc callingObject, Object JavaDoc userObj, OutputStream JavaDoc stream) throws CmsException {
1474
1475        // Node currently processed
1476
Node JavaDoc child = null;
1477
1478        // Name of the currently processed child
1479
String JavaDoc childName = null;
1480
1481        // Node nextchild needed for the walk through the tree
1482
Node JavaDoc nextchild = null;
1483
1484        // List of new Nodes the current node should be replaced with
1485
NodeList JavaDoc newnodes = null;
1486
1487        // single new Node from newnodes
1488
Node JavaDoc insert = null;
1489
1490        // tag processing method to be called for the current Node
1491
Method JavaDoc callMethod = null;
1492
1493        // Object returned by the tag processing methods
1494
Object JavaDoc methodResult = null;
1495
1496        // Used for streaming mode. Indicates, if the replaced results for the current node are already written to the stream.
1497
boolean newnodesAreAlreadyProcessed = false;
1498
1499        // We should remember the starting node for walking through the tree.
1500
Node JavaDoc startingNode = n;
1501
1502        // only start if there is something to process
1503
if (n != null && n.hasChildNodes()) {
1504            child = n.getFirstChild();
1505            while (child != null) {
1506                childName = child.getNodeName().toLowerCase();
1507
1508                // Get the next node in the tree first
1509
nextchild = treeWalker(startingNode, child);
1510
1511                // Only look for element nodes
1512

1513                // all other nodes are not very interesting
1514
if (child.getNodeType() == Node.ELEMENT_NODE) {
1515                    newnodes = null;
1516                    callMethod = null;
1517                    newnodesAreAlreadyProcessed = false;
1518                    if (keys.containsKey(childName)) {
1519
1520                        // name of this element found in keys Hashtable
1521
callMethod = (Method JavaDoc) keys.get(childName);
1522                    } else {
1523                        if (!m_knownTags.contains(childName)) {
1524
1525                            // name was not found
1526
// and even name is not known as tag
1527
callMethod = defaultMethod;
1528                        }
1529                    }
1530                    if (callMethod != null) {
1531                        methodResult = null;
1532                        try {
1533                            if (C_DEBUG && CmsLog.getLog(this).isDebugEnabled()) {
1534                                CmsLog.getLog(this).debug("<" + childName + "> tag found. Value: " + child.getNodeValue());
1535                                CmsLog.getLog(this).debug("Tag will be handled by method [" + callMethod.getName() + "]. Invoking method NOW.");
1536                            }
1537
1538                            // now invoke the tag processing method.
1539
methodResult = callMethod.invoke(this, new Object JavaDoc[] {child, callingObject, userObj});
1540                        } catch (Exception JavaDoc e) {
1541                            if (e instanceof InvocationTargetException JavaDoc) {
1542                                Throwable JavaDoc thrown = ((InvocationTargetException JavaDoc) e).getTargetException();
1543
1544                                // if the method has thrown a cms exception then
1545
// throw it again
1546
if (thrown instanceof CmsException) {
1547                                    throw (CmsException) thrown;
1548                                } else {
1549                                    throwException("processNode received an exception while handling XML tag \"" + childName + "\" by \"" + callMethod.getName() + "\" for file " + getFilename() + ": " + e, CmsLegacyException.C_XML_PROCESS_ERROR);
1550                                }
1551                            } else {
1552                                throwException("processNode could not invoke the XML tag handling method " + callMethod.getName() + "\" for file " + getFilename() + ": " + e, CmsLegacyException.C_XML_PROCESS_ERROR);
1553                            }
1554                        }
1555
1556                        // Inspect the type of the method return value
1557
// Currently NodeList, String and Integer are
1558
// recognized. All other types will be ignored.
1559
if (methodResult == null) {
1560                            newnodes = null;
1561                        } else {
1562                            if (methodResult instanceof NodeList JavaDoc) {
1563                                newnodes = (NodeList JavaDoc) methodResult;
1564                            } else {
1565                                if (methodResult instanceof String JavaDoc) {
1566                                    newnodes = stringToNodeList((String JavaDoc) methodResult);
1567                                } else {
1568                                    if (methodResult instanceof CmsProcessedString) {
1569                                        newnodes = stringToNodeList(((CmsProcessedString) methodResult).toString());
1570                                        newnodesAreAlreadyProcessed = true;
1571                                    } else {
1572                                        if (methodResult instanceof Integer JavaDoc) {
1573                                            newnodes = stringToNodeList(((Integer JavaDoc) methodResult).toString());
1574                                        } else {
1575                                            if (methodResult instanceof byte[]) {
1576                                                newnodes = stringToNodeList(new String JavaDoc((byte[]) methodResult));
1577                                            } else {
1578
1579                                                // Type not recognized.
1580
if (CmsLog.getLog(this).isErrorEnabled()) {
1581                                                    CmsLog.getLog(this).error("Return type of method " + callMethod.getName() + " not recognized, can not insert value");
1582                                                }
1583                                                newnodes = null;
1584                                            }
1585                                        }
1586                                    }
1587                                }
1588                            }
1589                        }
1590
1591                        // the list of nodes to be inserted could be printed out here.
1592
// uncomment the following to activate this feature.
1593
// printNodeList(newnodes);
1594
if (newnodes != null) {
1595                            // the called method returned a valid result.
1596
// we have do remove the old element from the tree
1597
// and replace it by the new nodes.
1598
// WARNING! Do not remove any subchilds from the old
1599
// element. There could be links to the subchilds
1600
// in our Hashtables (e.g. for datablocks).
1601
// Only remove the child itself from the tree!
1602
int numNewChilds = newnodes.getLength();
1603                            if (numNewChilds > 0) {
1604
1605                                // there are new childs.
1606
// so we can replace the old element
1607
for (int j = 0; j < numNewChilds; j++) {
1608
1609                                    //insert = parser.importNode(m_content, newnodes.item(j));
1610
insert = m_parser.importNode(child.getOwnerDocument(), newnodes.item(j));
1611                                    if (j == 0 && !newnodesAreAlreadyProcessed) {
1612                                        nextchild = insert;
1613                                    }
1614
1615                                    //A_OpenCms.log(c_OPENCMS_DEBUG, "trying to add node " + newnodes.item(j));
1616
child.getParentNode().insertBefore(insert, child);
1617
1618                                    //A_OpenCms.log(c_OPENCMS_DEBUG, "Node " + newnodes.item(j) + " added.");
1619
}
1620
1621                                if (newnodesAreAlreadyProcessed) {
1622                                    // We just have inserted new nodes that were processed prviously.
1623
// So we hav to recalculate the next child.
1624
nextchild = treeWalkerWidth(startingNode, child);
1625                                }
1626
1627                            } else {
1628
1629                                // the list of the new childs is empty.
1630
// so we have to re-calculate the next node
1631
// in the tree since the old nextchild will be deleted
1632
// been deleted.
1633
nextchild = treeWalkerWidth(startingNode, child);
1634                            }
1635
1636                            // now delete the old child and get the next one.
1637
child.getParentNode().removeChild(child);
1638                        }
1639                    }
1640                } else if (stream != null) {
1641                    /* We are in HTTP streaming mode.
1642                    So we can put the content of the current node directly into
1643                    the output stream. */

1644                    String JavaDoc streamResults = null;
1645                    if (child.getNodeType() == Node.CDATA_SECTION_NODE) {
1646                        streamResults = child.getNodeValue();
1647                    } else {
1648                        if (child.getNodeType() == Node.TEXT_NODE) {
1649                            String JavaDoc s = child.getNodeValue().trim();
1650                            if (!"".equals(s)) {
1651                                streamResults = child.getNodeValue();
1652                            }
1653                        }
1654                    }
1655                    if (streamResults != null) {
1656                        try {
1657                            stream.write(streamResults.getBytes(m_cms.getRequestContext().getEncoding()));
1658                        } catch (Exception JavaDoc e) {
1659                            throw new CmsLegacyException(CmsLegacyException.C_UNKNOWN_EXCEPTION, e);
1660                        }
1661                    }
1662                }
1663                child = nextchild;
1664            }
1665        }
1666    }
1667
1668    /**
1669     * Read the datablocks of the given content file and include them
1670     * into the own Hashtable of datablocks.
1671     *
1672     * @param include completely initialized A_CmsXmlObject to be included
1673     * @throws CmsException if something goes wrong
1674     */

1675    public void readIncludeFile(A_CmsXmlContent include) throws CmsException {
1676        m_includedTemplates.addElement(include);
1677        m_blocks = concatData(m_blocks, include.getAllData());
1678    }
1679
1680    /**
1681     * Parses the given file and stores it in the internal list of included files and
1682     * appends the relevant data structures of the new file to its own structures.
1683     *
1684     * @param filename file name of the XML file to be included
1685     * @return file with xml conten
1686     * @throws CmsException if something goes wrong
1687     */

1688    public A_CmsXmlContent readIncludeFile(String JavaDoc filename) throws CmsException {
1689        A_CmsXmlContent include = null;
1690        if (CmsLog.getLog(this).isDebugEnabled() && C_DEBUG) {
1691            CmsLog.getLog(this).debug("Including file: " + filename);
1692        }
1693        try {
1694            include = (A_CmsXmlContent) getClass().newInstance();
1695            include.init(m_cms, filename);
1696        } catch (Exception JavaDoc e) {
1697            if (CmsLog.getLog(this).isErrorEnabled()) {
1698                CmsLog.getLog(this).error("Error include file: " + filename, e);
1699            }
1700        }
1701        readIncludeFile(include);
1702        return include;
1703    }
1704
1705    /**
1706     * Internal method registering all special tags relevant for the basic functionality of
1707     * this abstract class.
1708     * <P>
1709     * OpenCms special tags are:
1710     * <UL>
1711     * <LI><CODE>INCLUDE: </CODE> used to include other XML files</LI>
1712     * <LI><CODE>DATA: </CODE> used to define a datablock that can be handled
1713     * by getData or processed by getProcessedData or <code>PROCESS</CODE></LI>
1714     * <LI><CODE>PROCESS: </CODE> used to insert earlier or external defined datablocks</LI>
1715     * <LI><CODE>METHOD: </CODE> used to call customized methods in the initiating user object</LI>
1716     * </UL>
1717     * All unknown tags will be treated as a shortcut for <code>&lt;DATA name="..."&gt;</code>.
1718     */

1719    private void registerAllTags() {
1720
1721        // register tags for scanning "INCLUDE" and "DATA"
1722
registerTag("INCLUDE", A_CmsXmlContent.class, "handleIncludeTag", C_REGISTER_FIRST_RUN);
1723        registerTag("DATA", A_CmsXmlContent.class, "handleDataTag", C_REGISTER_FIRST_RUN);
1724
1725        // register tags for preparing HTML output
1726
registerTag("METHOD", A_CmsXmlContent.class, "handleMethodTag", C_REGISTER_MAIN_RUN);
1727        registerTag("PROCESS", A_CmsXmlContent.class, "handleProcessTag", C_REGISTER_MAIN_RUN);
1728        registerTag("LINK", A_CmsXmlContent.class, "handleLinkTag", C_REGISTER_MAIN_RUN);
1729        registerTag("INCLUDE", A_CmsXmlContent.class, "replaceTagByComment", C_REGISTER_MAIN_RUN);
1730        registerTag("DATA", A_CmsXmlContent.class, "replaceTagByComment", C_REGISTER_MAIN_RUN);
1731        registerTag(getXmlDocumentTagName());
1732    }
1733
1734    /**
1735     * Registers the given tag to be "known" by the system.
1736     * So this tag will not be handled by the default method of processNode.
1737     * Under normal circumstances this feature will only be used for
1738     * the XML document tag.
1739     * @param tagname Tag name to register.
1740     */

1741    public void registerTag(String JavaDoc tagname) {
1742        if (!(m_knownTags.contains(tagname.toLowerCase()))) {
1743            m_knownTags.addElement(tagname.toLowerCase());
1744        }
1745    }
1746
1747    /**
1748     * Registeres a tagname together with a corresponding method for processing
1749     * with processNode. Tags can be registered for two different runs of the processNode
1750     * method. This can be selected by the runSelector.
1751     * <P>
1752     * C_REGISTER_FIRST_RUN registeres the given tag for the first
1753     * run of processNode, just after parsing a XML document. The basic functionality
1754     * of this class uses this run to scan for INCLUDE and DATA tags.
1755     * <P>
1756     * C_REGISTER_MAIN_RUN registeres the given tag for the main run of processNode.
1757     * This will be initiated by getProcessedData(), processDocument() or any
1758     * PROCESS tag.
1759     *
1760     * @param tagname Tag name to register.
1761     * @param c Class containing the handling method.
1762     * @param methodName Name of the method that should handle a occurance of tag "tagname".
1763     * @param runSelector see description above.
1764     */

1765    public void registerTag(String JavaDoc tagname, Class JavaDoc c, String JavaDoc methodName, int runSelector) {
1766        Hashtable JavaDoc selectedRun = null;
1767        switch (runSelector) {
1768            case C_REGISTER_FIRST_RUN :
1769                selectedRun = m_firstRunTags;
1770                break;
1771
1772            case C_REGISTER_MAIN_RUN :
1773            default:
1774                selectedRun = m_mainProcessTags;
1775                break;
1776        }
1777        try {
1778            selectedRun.put(tagname.toLowerCase(), c.getDeclaredMethod(methodName, C_PARAMTYPES_HANDLING_METHODS));
1779        } catch (Exception JavaDoc e) {
1780            if (CmsLog.getLog(this).isWarnEnabled()) {
1781                CmsLog.getLog(this).warn("Exception in register tag ", e);
1782            }
1783        }
1784        registerTag(tagname);
1785    }
1786
1787    /**
1788     * Remove a datablock from the internal hashtable and
1789     * from the XML document.<p>
1790     * @param tag Key of the datablock to delete.
1791     */

1792    protected void removeData(String JavaDoc tag) {
1793        Element JavaDoc e = (Element JavaDoc) m_blocks.get(tag.toLowerCase());
1794        if (e != null) {
1795            m_blocks.remove(tag.toLowerCase());
1796            Element JavaDoc parent = (Element JavaDoc) e.getParentNode();
1797            if (parent != null) {
1798                parent.removeChild(e);
1799            }
1800        }
1801    }
1802
1803    /**
1804     * Deletes this object from the internal XML file cache.<p>
1805     */

1806    public void removeFromFileCache() {
1807        String JavaDoc currentProject = m_cms.getRequestContext().currentProject().getName();
1808        m_filecache.remove(currentProject + ":" + m_cms.getRequestContext().addSiteRoot(getAbsoluteFilename()));
1809    }
1810
1811    /**
1812     * Creates a datablock consisting of a single TextNode containing
1813     * data and stores this block into the datablock-hashtable.
1814     *
1815     * @param tag Key for this datablock.
1816     * @param data String to be put in the datablock.
1817     */

1818    protected void setData(String JavaDoc tag, String JavaDoc data) {
1819        // create new XML Element to store the data
1820
String JavaDoc attribute = tag;
1821        int dotIndex = tag.lastIndexOf(".");
1822        if (dotIndex != -1) {
1823            attribute = attribute.substring(dotIndex + 1);
1824        }
1825        Element JavaDoc newElement = m_content.createElement(attribute);
1826        if (data == null || "".equals(data)) {
1827            // empty string or null are given.
1828
// put an empty datablock without any text nodes.
1829
setData(tag, newElement);
1830        } else {
1831            // Fine. String is not empty.
1832
// So we can add a new text node containig the string data.
1833
// Leading spaces are removed before creating the text node.
1834
newElement.appendChild(m_content.createTextNode(data.trim()));
1835            setData(tag, newElement);
1836        }
1837    }
1838
1839    /**
1840     * Stores a given datablock element in the datablock hashtable.
1841     *
1842     * @param tag Key for this datablock.
1843     * @param data DOM element node for this datablock.
1844     */

1845    protected void setData(String JavaDoc tag, Element JavaDoc data) {
1846
1847        // If we got a null data, give this request to setData(Strig, String)
1848
// to create a new text node.
1849
if (data == null) {
1850            setData(tag, "");
1851        } else {
1852            // Now we can be sure to have a correct Element
1853
tag = tag.toLowerCase();
1854            Element JavaDoc newElement = (Element JavaDoc) data.cloneNode(true);
1855            if (CmsLog.getLog(this).isDebugEnabled() && C_DEBUG) {
1856                CmsLog.getLog(this).debug("Putting datablock " + tag + " into internal Hashtable");
1857            }
1858            if (!(m_blocks.containsKey(tag))) {
1859                // This is a brand new datablock. It can be inserted directly.
1860
//m_blocks.put(tag, newElement);
1861
insertNewDatablock(tag, newElement);
1862            } else {
1863                // datablock existed before, so the childs of the old
1864
// one can be replaced.
1865
if (CmsLog.getLog(this).isDebugEnabled() && C_DEBUG) {
1866                    CmsLog.getLog(this).debug("Datablock existed before, replacing");
1867                }
1868                // Look up the old datablock and remove all its childs.
1869
Element JavaDoc originalBlock = (Element JavaDoc) (m_blocks.get(tag));
1870                while (originalBlock.hasChildNodes()) {
1871                    originalBlock.removeChild(originalBlock.getFirstChild());
1872                }
1873                // And now add all childs of the new node
1874
NodeList JavaDoc newNodes = data.getChildNodes();
1875                int len = newNodes.getLength();
1876                for (int i = 0; i < len; i++) {
1877                    Node JavaDoc newElement2 = newNodes.item(i).cloneNode(true);
1878                    originalBlock.appendChild(m_parser.importNode(originalBlock.getOwnerDocument(), newElement2));
1879                }
1880            }
1881        }
1882    }
1883
1884    /**
1885     * Creates a datablock element by parsing the data string
1886     * and stores this block into the datablock-hashtable.
1887     *
1888     * @param tag Key for this datablock.
1889     * @param data String to be put in the datablock.
1890     * @throws CmsException if something goes wrong
1891     */

1892    public void setParsedData(String JavaDoc tag, String JavaDoc data) throws CmsException {
1893
1894        StringBuffer JavaDoc tempXmlString = new StringBuffer JavaDoc();
1895        tempXmlString.append("<?xml version=\"1.0\"?>\n");
1896        tempXmlString.append("<" + getXmlDocumentTagName() + ">");
1897        tempXmlString.append("<" + tag + ">\n");
1898        tempXmlString.append("<![CDATA[");
1899        tempXmlString.append(data);
1900        tempXmlString.append("]]>");
1901        tempXmlString.append("</" + tag + ">\n");
1902        tempXmlString.append("</" + getXmlDocumentTagName() + ">\n");
1903        StringReader JavaDoc parserReader = new StringReader JavaDoc(tempXmlString.toString());
1904        Document JavaDoc tempDoc = null;
1905        try {
1906            tempDoc = m_parser.parse(parserReader);
1907        } catch (Exception JavaDoc e) {
1908            throwException("PARSING ERROR! " + e.toString(), CmsLegacyException.C_XML_PARSING_ERROR);
1909        }
1910        Element JavaDoc templateNode = (Element JavaDoc) tempDoc.getDocumentElement().getFirstChild();
1911        setData(tag, templateNode);
1912    }
1913
1914    /**
1915     * Utility method for converting a String to a NodeList containing
1916     * a single TextNode.
1917     * @param s String to convert
1918     * @return NodeList containing a TextNode with s
1919     */

1920    private NodeList JavaDoc stringToNodeList(String JavaDoc s) {
1921        Element JavaDoc tempNode = m_content.createElement("TEMP");
1922        Text JavaDoc text = m_content.createTextNode(s);
1923        tempNode.appendChild(text);
1924        return tempNode.getChildNodes();
1925    }
1926
1927    /**
1928     * Help method that handles any occuring exception by writing
1929     * an error message to the OpenCms logfile and throwing a
1930     * CmsException of the type "unknown".
1931     * @param errorMessage String with the error message to be printed.
1932     * @throws CmsException if something goes wrong
1933     */

1934    protected void throwException(String JavaDoc errorMessage) throws CmsException {
1935        throwException(errorMessage, CmsLegacyException.C_UNKNOWN_EXCEPTION);
1936    }
1937
1938    /**
1939     * Help method that handles any occuring exception by writing
1940     * an error message to the OpenCms logfile and throwing a
1941     * CmsException of the given type.
1942     * @param errorMessage String with the error message to be printed.
1943     * @param type Type of the exception to be thrown.
1944     * @throws CmsLegacyException if something goes wrong
1945     */

1946    protected void throwException(String JavaDoc errorMessage, int type) throws CmsLegacyException {
1947        if (CmsLog.getLog(this).isErrorEnabled()) {
1948            CmsLog.getLog(this).error(errorMessage);
1949        }
1950        throw new CmsLegacyException(errorMessage, type);
1951    }
1952
1953    /**
1954     * Help method that handles any occuring exception by writing
1955     * an error message to the OpenCms logfile and throwing a
1956     * CmsException of the type "unknown".
1957     * @param errorMessage String with the error message to be printed.
1958     * @param e Original exception.
1959     * @throws CmsException if something goes wrong
1960     */

1961    protected void throwException(String JavaDoc errorMessage, Exception JavaDoc e) throws CmsException {
1962        throwException(errorMessage, e, CmsLegacyException.C_UNKNOWN_EXCEPTION);
1963    }
1964
1965    /**
1966     * Help method that handles any occuring exception by writing
1967     * an error message to the OpenCms logfile and throwing a
1968     * CmsException of the type "unknown".
1969     * @param errorMessage String with the error message to be printed.
1970     * @param e Original exception.
1971     * @param type Type of the exception to be thrown.
1972     * @throws CmsException if something goes wrong
1973     */

1974    protected void throwException(String JavaDoc errorMessage, Exception JavaDoc e, int type) throws CmsException {
1975        if (CmsLog.getLog(this).isErrorEnabled()) {
1976            CmsLog.getLog(this).error(errorMessage, e);
1977        }
1978        if (e instanceof CmsException) {
1979            throw (CmsException) e;
1980        } else {
1981            throw new CmsLegacyException(errorMessage, type, e);
1982        }
1983    }
1984
1985    /**
1986     * Gets a string representation of this object.
1987     * @return String representation of this object.
1988     */

1989    public String JavaDoc toString() {
1990        StringBuffer JavaDoc output = new StringBuffer JavaDoc();
1991        output.append("[XML file]: ");
1992        output.append(getFilename());
1993        output.append(", content type: ");
1994        output.append(getContentDescription());
1995        return output.toString();
1996    }
1997
1998    /**
1999     * Help method to walk through the DOM document tree.
2000     * First it will be looked for children of the given node.
2001     * If there are no children, the siblings and the siblings of our parents
2002     * are examined. This will be done by calling treeWalkerWidth.
2003     * @param root the root node
2004     * @param n Node representing the actual position in the tree
2005     * @return next node
2006     */

2007    protected Node JavaDoc treeWalker(Node JavaDoc root, Node JavaDoc n) {
2008        Node JavaDoc nextnode = null;
2009        if (n.hasChildNodes()) {
2010            // child has child notes itself
2011
// process these first in the next loop
2012
nextnode = n.getFirstChild();
2013        } else {
2014            // child has no subchild.
2015
// so we take the next sibling
2016
nextnode = treeWalkerWidth(root, n);
2017        }
2018        return nextnode;
2019    }
2020
2021    /**
2022     * Help method to walk through the DOM document tree by a
2023     * width-first-order.
2024     * @param root the root node
2025     * @param n Node representing the actual position in the tree
2026     * @return next node
2027     */

2028    protected Node JavaDoc treeWalkerWidth(Node JavaDoc root, Node JavaDoc n) {
2029        if (n == root) {
2030            return null;
2031        }
2032        Node JavaDoc nextnode = null;
2033        Node JavaDoc parent = null;
2034        nextnode = n.getNextSibling();
2035        parent = n.getParentNode();
2036        while (nextnode == null && parent != null && parent != root) {
2037
2038            // child has sibling
2039
// last chance: we take our parent's sibling
2040
// (or our grandparent's sibling...)
2041
nextnode = parent.getNextSibling();
2042            parent = parent.getParentNode();
2043        }
2044        return nextnode;
2045    }
2046
2047    /**
2048     * Writes the XML document back to the OpenCms system.
2049     * @throws CmsException if something goes wrong
2050     */

2051    public void write() throws CmsException {
2052        ByteArrayOutputStream JavaDoc os = new ByteArrayOutputStream JavaDoc();
2053        getXmlText(os);
2054        byte[] xmlContent = os.toByteArray();
2055
2056        // Get the CmsFile object to write to
2057
String JavaDoc filename = getAbsoluteFilename();
2058        CmsFile file = m_cms.readFile(filename);
2059
2060        // Set the new content and write the file
2061
file.setContents(xmlContent);
2062        m_cms.writeFile(file);
2063        xmlContent = null;
2064
2065        // update the internal parsed content cache with the new file data.
2066
String JavaDoc currentProject = m_cms.getRequestContext().currentProject().getName();
2067        m_filecache.put(currentProject + ":" + m_cms.getRequestContext().addSiteRoot(filename), m_content.cloneNode(true));
2068    }
2069
2070    /**
2071     * Returns current XML document encoding.
2072     * @return String encoding of XML document
2073     */

2074    public String JavaDoc getEncoding() {
2075        return m_parser.getOriginalEncoding(m_content);
2076    }
2077
2078    /**
2079     * Sets new encoding for XML document.
2080     * @param encoding the encoding to set
2081     */

2082    public void setEncoding(String JavaDoc encoding) {
2083        m_newEncoding = encoding;
2084    }
2085
2086}
2087
Popular Tags