KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > opencms > xml > A_CmsXmlDocument


1 /*
2  * File : $Source: /usr/local/cvs/opencms/src/org/opencms/xml/A_CmsXmlDocument.java,v $
3  * Date : $Date: 2006/04/28 15:20:52 $
4  * Version: $Revision: 1.31 $
5  *
6  * This library is part of OpenCms -
7  * the Open Source Content Mananagement System
8  *
9  * Copyright (c) 2005 Alkacon Software GmbH (http://www.alkacon.com)
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 Alkacon Software GmbH, please see the
22  * company website: http://www.alkacon.com
23  *
24  * For further information about OpenCms, please see the
25  * project website: http://www.opencms.org
26  *
27  * You should have received a copy of the GNU Lesser General Public
28  * License along with this library; if not, write to the Free Software
29  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30  */

31
32 package org.opencms.xml;
33
34 import org.opencms.file.CmsFile;
35 import org.opencms.file.CmsObject;
36 import org.opencms.main.CmsIllegalArgumentException;
37 import org.opencms.main.CmsRuntimeException;
38 import org.opencms.xml.types.I_CmsXmlContentValue;
39
40 import java.io.ByteArrayOutputStream JavaDoc;
41 import java.io.OutputStream JavaDoc;
42 import java.util.ArrayList JavaDoc;
43 import java.util.Collections JavaDoc;
44 import java.util.HashMap JavaDoc;
45 import java.util.HashSet JavaDoc;
46 import java.util.Iterator JavaDoc;
47 import java.util.List JavaDoc;
48 import java.util.Locale JavaDoc;
49 import java.util.Map JavaDoc;
50 import java.util.Set JavaDoc;
51
52 import org.dom4j.Document;
53 import org.dom4j.Element;
54 import org.xml.sax.EntityResolver JavaDoc;
55
56 /**
57  * Provides basic XML document handling functions useful when dealing
58  * with XML documents that are stored in the OpenCms VFS.<p>
59  *
60  * @author Alexander Kandzior
61  *
62  * @version $Revision: 1.31 $
63  *
64  * @since 6.0.0
65  */

66 public abstract class A_CmsXmlDocument implements I_CmsXmlDocument {
67
68     /** The content conversion to use for this xml document. */
69     protected String JavaDoc m_conversion;
70
71     /** The document object of the document. */
72     protected Document m_document;
73
74     /** Maps element names to available locales. */
75     protected Map JavaDoc m_elementLocales;
76
77     /** Maps locales to avaliable element names. */
78     protected Map JavaDoc m_elementNames;
79
80     /** The encoding to use for this xml document. */
81     protected String JavaDoc m_encoding;
82
83     /** The file that contains the document data (note: is not set when creating an empty or document based document). */
84     protected CmsFile m_file;
85
86     /** Set of locales contained in this document. */
87     protected Set JavaDoc m_locales;
88
89     /** Reference for named elements in the document. */
90     private Map JavaDoc m_bookmarks;
91
92     /**
93      * Default constructor for a XML document
94      * that initializes some internal values.<p>
95      */

96     protected A_CmsXmlDocument() {
97
98         m_bookmarks = new HashMap JavaDoc();
99         m_locales = new HashSet JavaDoc();
100     }
101
102     /**
103      * Creates the bookmark name for a localized element to be used in the bookmark lookup table.<p>
104      *
105      * @param name the element name
106      * @param locale the element locale
107      * @return the bookmark name for a localized element
108      */

109     protected static final String JavaDoc getBookmarkName(String JavaDoc name, Locale JavaDoc locale) {
110
111         StringBuffer JavaDoc result = new StringBuffer JavaDoc(64);
112         result.append('/');
113         result.append(locale.toString());
114         result.append('/');
115         result.append(name);
116         return result.toString();
117     }
118
119     /**
120      * @see org.opencms.xml.I_CmsXmlDocument#copyLocale(java.util.Locale, java.util.Locale)
121      */

122     public void copyLocale(Locale JavaDoc source, Locale JavaDoc destination) throws CmsXmlException {
123
124         if (!hasLocale(source)) {
125             throw new CmsXmlException(Messages.get().container(Messages.ERR_LOCALE_NOT_AVAILABLE_1, source));
126         }
127         if (hasLocale(destination)) {
128             throw new CmsXmlException(Messages.get().container(Messages.ERR_LOCALE_ALREADY_EXISTS_1, destination));
129         }
130
131         Element sourceElement = null;
132         Element rootNode = m_document.getRootElement();
133         Iterator JavaDoc i = rootNode.elementIterator();
134         String JavaDoc localeStr = source.toString();
135         while (i.hasNext()) {
136             Element element = (Element)i.next();
137             String JavaDoc language = element.attributeValue(CmsXmlContentDefinition.XSD_ATTRIBUTE_VALUE_LANGUAGE, null);
138             if ((language != null) && (localeStr.equals(language))) {
139                 // detach node with the locale
140
sourceElement = element.createCopy();
141                 // there can be only one node for the locale
142
break;
143             }
144         }
145
146         if (sourceElement == null) {
147             // should not happen since this was checked already, just to make sure...
148
throw new CmsXmlException(Messages.get().container(Messages.ERR_LOCALE_NOT_AVAILABLE_1, source));
149         }
150
151         // switch locale value in attribute of copied node
152
sourceElement.addAttribute(CmsXmlContentDefinition.XSD_ATTRIBUTE_VALUE_LANGUAGE, destination.toString());
153         // attach the copied node to the root node
154
rootNode.add(sourceElement);
155
156         // re-initialize the document bookmarks
157
initDocument(m_document, m_encoding, getContentDefinition());
158     }
159
160     /**
161      * Corrects the structure of this XML content.<p>
162      *
163      * @param cms the current cms object
164      * @return the file that contains the corrected XML structure
165      *
166      * @throws CmsXmlException if something goes wrong
167      */

168     public CmsFile correctXmlStructure(CmsObject cms) throws CmsXmlException {
169
170         // iterate over all locales
171
Iterator JavaDoc i = m_locales.iterator();
172         while (i.hasNext()) {
173             Locale JavaDoc locale = (Locale JavaDoc)i.next();
174             List JavaDoc names = getNames(locale);
175
176             // iterate over all nodes per language
177
Iterator JavaDoc j = names.iterator();
178             while (j.hasNext()) {
179
180                 // this step is required for values that need a processing of their content
181
// an example for this is the HTML value that does link replacement
182
String JavaDoc name = (String JavaDoc)j.next();
183                 I_CmsXmlContentValue value = getValue(name, locale);
184                 if (value.isSimpleType()) {
185                     String JavaDoc content = value.getStringValue(cms);
186                     value.setStringValue(cms, content);
187                 }
188             }
189         }
190
191         // write the modifed xml back to the VFS file
192
m_file.setContents(marshal());
193         return m_file;
194     }
195
196     /**
197      * Returns the content converison used for the page content.<p>
198      *
199      * @return the content converison used for the page content
200      */

201     public String JavaDoc getConversion() {
202
203         return m_conversion;
204     }
205
206     /**
207      * @see org.opencms.xml.I_CmsXmlDocument#getEncoding()
208      */

209     public String JavaDoc getEncoding() {
210
211         return m_encoding;
212     }
213
214     /**
215      * Returns the file with the xml page content or <code>null</code> if not set.<p>
216      *
217      * @return the file with the xml page content
218      */

219     public CmsFile getFile() {
220
221         return m_file;
222     }
223
224     /**
225      * @see org.opencms.xml.I_CmsXmlDocument#getIndexCount(java.lang.String, java.util.Locale)
226      */

227     public int getIndexCount(String JavaDoc name, Locale JavaDoc locale) {
228
229         List JavaDoc elements = getValues(name, locale);
230         if (elements == null) {
231             return 0;
232         } else {
233             return elements.size();
234         }
235     }
236
237     /**
238      * @see org.opencms.xml.I_CmsXmlDocument#getLocales()
239      */

240     public List JavaDoc getLocales() {
241
242         return new ArrayList JavaDoc(m_locales);
243     }
244
245     /**
246      * Returns a List of all locales that have the named element set in this document.<p>
247      *
248      * If no locale for the given element name is available, an empty list is returned.<p>
249      *
250      * @param name the element to look up the locale List for
251      * @return a List of all Locales that have the named element set in this document
252      */

253     public List JavaDoc getLocales(String JavaDoc name) {
254
255         Object JavaDoc result = m_elementLocales.get(CmsXmlUtils.createXpath(name, 1));
256         if (result == null) {
257             return Collections.EMPTY_LIST;
258         }
259         return new ArrayList JavaDoc((Set JavaDoc)result);
260     }
261
262     /**
263      * @see org.opencms.xml.I_CmsXmlDocument#getNames(java.util.Locale)
264      */

265     public List JavaDoc getNames(Locale JavaDoc locale) {
266
267         Object JavaDoc o = m_elementNames.get(locale);
268         if (o != null) {
269             return new ArrayList JavaDoc((Set JavaDoc)o);
270         }
271         return Collections.EMPTY_LIST;
272     }
273
274     /**
275      * @see org.opencms.xml.I_CmsXmlDocument#getStringValue(org.opencms.file.CmsObject, java.lang.String, java.util.Locale)
276      */

277     public String JavaDoc getStringValue(CmsObject cms, String JavaDoc name, Locale JavaDoc locale) {
278
279         I_CmsXmlContentValue value = getValueInternal(CmsXmlUtils.createXpath(name, 1), locale);
280         if (value != null) {
281             return value.getStringValue(cms);
282         }
283         return null;
284     }
285
286     /**
287      * @see org.opencms.xml.I_CmsXmlDocument#getStringValue(CmsObject, java.lang.String, Locale, int)
288      */

289     public String JavaDoc getStringValue(CmsObject cms, String JavaDoc name, Locale JavaDoc locale, int index) {
290
291         // directly calling getValueInternal() is more efficient then calling getStringValue(CmsObject, String, Locale)
292
// since the most costs are generated in resolving the Xpath name
293
I_CmsXmlContentValue value = getValueInternal(CmsXmlUtils.createXpath(name, index + 1), locale);
294         if (value != null) {
295             return value.getStringValue(cms);
296         }
297         return null;
298     }
299
300     /**
301      * @see org.opencms.xml.I_CmsXmlDocument#getValue(java.lang.String, java.util.Locale)
302      */

303     public I_CmsXmlContentValue getValue(String JavaDoc name, Locale JavaDoc locale) {
304
305         return getValueInternal(CmsXmlUtils.createXpath(name, 1), locale);
306     }
307
308     /**
309      * @see org.opencms.xml.I_CmsXmlDocument#getValue(java.lang.String, java.util.Locale, int)
310      */

311     public I_CmsXmlContentValue getValue(String JavaDoc name, Locale JavaDoc locale, int index) {
312
313         return getValueInternal(CmsXmlUtils.createXpath(name, index + 1), locale);
314     }
315
316     /**
317      * @see org.opencms.xml.I_CmsXmlDocument#getValues(java.util.Locale)
318      */

319     public List JavaDoc getValues(Locale JavaDoc locale) {
320
321         List JavaDoc result = new ArrayList JavaDoc();
322
323         // bookmarks are stored with the locale as first prefix
324
String JavaDoc prefix = '/' + locale.toString() + '/';
325
326         // it's better for performance to iterate through the list of bookmarks directly
327
Iterator JavaDoc i = m_bookmarks.keySet().iterator();
328         while (i.hasNext()) {
329             String JavaDoc key = (String JavaDoc)i.next();
330             if (key.startsWith(prefix)) {
331                 result.add(m_bookmarks.get(key));
332             }
333         }
334
335         // sort the result
336
Collections.sort(result);
337
338         return result;
339     }
340
341     /**
342      * @see org.opencms.xml.I_CmsXmlDocument#getValues(java.lang.String, java.util.Locale)
343      */

344     public List JavaDoc getValues(String JavaDoc name, Locale JavaDoc locale) {
345
346         List JavaDoc result = new ArrayList JavaDoc();
347         int count = 1;
348         Object JavaDoc o;
349         name = CmsXmlUtils.removeXpathIndex(name);
350         do {
351             String JavaDoc path = CmsXmlUtils.createXpathElement(name, count);
352             o = getBookmark(path, locale);
353             if (o != null) {
354                 result.add(o);
355                 count++;
356             }
357         } while (o != null);
358
359         return result;
360     }
361
362     /**
363      * @see org.opencms.xml.I_CmsXmlDocument#hasLocale(java.util.Locale)
364      */

365     public boolean hasLocale(Locale JavaDoc locale) {
366
367         if (locale == null) {
368             throw new CmsIllegalArgumentException(Messages.get().container(Messages.ERR_NULL_LOCALE_0));
369         }
370
371         return m_locales.contains(locale);
372     }
373
374     /**
375      * @see org.opencms.xml.I_CmsXmlDocument#hasValue(java.lang.String, java.util.Locale)
376      */

377     public boolean hasValue(String JavaDoc name, Locale JavaDoc locale) {
378
379         return null != getBookmark(CmsXmlUtils.createXpath(name, 1), locale);
380     }
381
382     /**
383      * @see org.opencms.xml.I_CmsXmlDocument#hasValue(java.lang.String, java.util.Locale, int)
384      */

385     public boolean hasValue(String JavaDoc name, Locale JavaDoc locale, int index) {
386
387         return null != getBookmark(CmsXmlUtils.createXpath(name, index + 1), locale);
388     }
389
390     /**
391      * @see org.opencms.xml.I_CmsXmlDocument#initDocument()
392      */

393     public void initDocument() {
394
395         initDocument(m_document, m_encoding, getContentDefinition());
396     }
397
398     /**
399      * @see org.opencms.xml.I_CmsXmlDocument#isEnabled(java.lang.String, java.util.Locale)
400      */

401     public boolean isEnabled(String JavaDoc name, Locale JavaDoc locale) {
402
403         return hasValue(name, locale);
404     }
405
406     /**
407      * @see org.opencms.xml.I_CmsXmlDocument#isEnabled(java.lang.String, java.util.Locale, int)
408      */

409     public boolean isEnabled(String JavaDoc name, Locale JavaDoc locale, int index) {
410
411         return hasValue(name, locale, index);
412     }
413
414     /**
415      * Marshals (writes) the content of the current XML document
416      * into a byte array using the selected encoding.<p>
417      *
418      * @return the content of the current XML document written into a byte array
419      * @throws CmsXmlException if something goes wrong
420      */

421     public byte[] marshal() throws CmsXmlException {
422
423         return ((ByteArrayOutputStream JavaDoc)marshal(new ByteArrayOutputStream JavaDoc(), m_encoding)).toByteArray();
424     }
425
426     /**
427      * @see org.opencms.xml.I_CmsXmlDocument#moveLocale(java.util.Locale, java.util.Locale)
428      */

429     public void moveLocale(Locale JavaDoc source, Locale JavaDoc destination) throws CmsXmlException {
430
431         copyLocale(source, destination);
432         removeLocale(source);
433     }
434
435     /**
436      * @see org.opencms.xml.I_CmsXmlDocument#removeLocale(java.util.Locale)
437      */

438     public void removeLocale(Locale JavaDoc locale) throws CmsXmlException {
439
440         if (!hasLocale(locale)) {
441             throw new CmsXmlException(Messages.get().container(Messages.ERR_LOCALE_NOT_AVAILABLE_1, locale));
442         }
443
444         Element rootNode = m_document.getRootElement();
445         Iterator JavaDoc i = rootNode.elementIterator();
446         String JavaDoc localeStr = locale.toString();
447         while (i.hasNext()) {
448             Element element = (Element)i.next();
449             String JavaDoc language = element.attributeValue(CmsXmlContentDefinition.XSD_ATTRIBUTE_VALUE_LANGUAGE, null);
450             if ((language != null) && (localeStr.equals(language))) {
451                 // detach node with the locale
452
element.detach();
453                 // there can be only one node for the locale
454
break;
455             }
456         }
457
458         // re-initialize the document bookmarks
459
initDocument(m_document, m_encoding, getContentDefinition());
460     }
461
462     /**
463      * Sets the content conversion mode for this document.<p>
464      *
465      * @param conversion the conversion mode to set for this document
466      */

467     public void setConversion(String JavaDoc conversion) {
468
469         m_conversion = conversion;
470     }
471
472     /**
473      * @see java.lang.Object#toString()
474      */

475     public String JavaDoc toString() {
476
477         try {
478             return CmsXmlUtils.marshal(m_document, m_encoding);
479         } catch (CmsXmlException e) {
480             throw new CmsRuntimeException(Messages.get().container(Messages.ERR_WRITE_XML_DOC_TO_STRING_0), e);
481         }
482     }
483
484     /**
485      * Validates the xml structure of the document with the DTD ot schema used by the document.<p>
486      *
487      * This is required in case someone modifies the xml structure of a
488      * document using the "edit control code" option.<p>
489      *
490      * @param resolver the XML entitiy resolver to use
491      * @throws CmsXmlException if the validation fails
492      */

493     public void validateXmlStructure(EntityResolver JavaDoc resolver) throws CmsXmlException {
494
495         if (m_file != null) {
496             byte[] xmlData = null;
497             // file is set, use bytes from file directly
498
xmlData = m_file.getContents();
499             CmsXmlUtils.validateXmlStructure(xmlData, resolver);
500         } else {
501             CmsXmlUtils.validateXmlStructure(m_document, m_encoding, resolver);
502         }
503     }
504
505     /**
506      * Adds a bookmark for the given value.<p>
507      *
508      * @param name the name to use for the bookmark
509      * @param locale the locale to use for the bookmark
510      * @param enabled if true, the value is enabled, if false it is disabled
511      * @param value the value to bookmark
512      */

513     protected void addBookmark(String JavaDoc name, Locale JavaDoc locale, boolean enabled, Object JavaDoc value) {
514
515         // add the locale (since the locales are a set adding them more then once does not matter)
516
addLocale(locale);
517
518         // add a bookmark to the provided value
519
m_bookmarks.put(getBookmarkName(name, locale), value);
520
521         Object JavaDoc o;
522         // update mapping of element name to locale
523
if (enabled) {
524             // only include enabled elements
525
o = m_elementLocales.get(name);
526             if (o != null) {
527                 ((Set JavaDoc)o).add(locale);
528             } else {
529                 Set JavaDoc set = new HashSet JavaDoc();
530                 set.add(locale);
531                 m_elementLocales.put(name, set);
532             }
533         }
534         // update mapping of locales to element names
535
o = m_elementNames.get(locale);
536         if (o != null) {
537             ((Set JavaDoc)o).add(name);
538         } else {
539             Set JavaDoc set = new HashSet JavaDoc();
540             set.add(name);
541             m_elementNames.put(locale, set);
542         }
543     }
544
545     /**
546      * Adds a locale to the set of locales of the XML document.<p>
547      *
548      * @param locale the locale to add
549      */

550     protected void addLocale(Locale JavaDoc locale) {
551
552         // add the locale to all locales in this dcoument
553
m_locales.add(locale);
554     }
555
556     /**
557      * Clears the XML document bookmarks.<p>
558      */

559     protected void clearBookmarks() {
560
561         m_bookmarks.clear();
562     }
563
564     /**
565      * Returns the bookmarked value for the given bookmark,
566      * which must be a valid bookmark name.
567      *
568      * Use {@link #getBookmarks()} to get the list of all valid bookmark names.<p>
569      *
570      * @param bookmark the bookmark name to look up
571      * @return the bookmarked value for the given bookmark
572      */

573     protected Object JavaDoc getBookmark(String JavaDoc bookmark) {
574
575         return m_bookmarks.get(bookmark);
576     }
577
578     /**
579      * Returns the bookmarked value for the given name.<p>
580      *
581      * @param name the name to get the bookmark for
582      * @param locale the locale to get the bookmark for
583      * @return the bookmarked value
584      */

585     protected Object JavaDoc getBookmark(String JavaDoc name, Locale JavaDoc locale) {
586
587         return m_bookmarks.get(getBookmarkName(name, locale));
588     }
589
590     /**
591      * Returns the names of all bookmarked elements.<p>
592      *
593      * @return the names of all bookmarked elements
594      */

595     protected Set JavaDoc getBookmarks() {
596
597         return m_bookmarks.keySet();
598     }
599
600     /**
601      * Initializes an XML document based on the provided document, encoding and content definition.<p>
602      *
603      * @param document the base XML document to use for initializing
604      * @param encoding the encoding to use when marshalling the document later
605      * @param contentDefinition the content definition to use
606      */

607     protected abstract void initDocument(Document document, String JavaDoc encoding, CmsXmlContentDefinition contentDefinition);
608
609     /**
610      * Marshals (writes) the content of the current XML document
611      * into an output stream.<p>
612      *
613      * @param out the output stream to write to
614      * @param encoding the encoding to use
615      * @return the output stream with the xml content
616      * @throws CmsXmlException if something goes wrong
617      */

618     protected OutputStream JavaDoc marshal(OutputStream JavaDoc out, String JavaDoc encoding) throws CmsXmlException {
619
620         return CmsXmlUtils.marshal(m_document, out, encoding);
621     }
622
623     /**
624      * Removes the bookmark for an element with the given name and locale.<p>
625      *
626      * @param name the name of the element
627      * @param locale the locale of the element
628      * @return the element removed from the bookmarks or null
629      */

630     protected I_CmsXmlContentValue removeBookmark(String JavaDoc name, Locale JavaDoc locale) {
631
632         // remove mapping of element name to locale
633
Object JavaDoc o;
634         o = m_elementLocales.get(name);
635         if (o != null) {
636             ((Set JavaDoc)o).remove(locale);
637         }
638         // remove mapping of locale to element name
639
o = m_elementNames.get(locale);
640         if (o != null) {
641             ((Set JavaDoc)o).remove(name);
642         }
643         // remove the bookmark and return the removed element
644
return (I_CmsXmlContentValue)m_bookmarks.remove(getBookmarkName(name, locale));
645     }
646
647     /**
648      * Internal method to look up a value, requires that the name already has been
649      * "normalized" for the bookmark lookup.
650      *
651      * This is required to find names like "title/subtitle" which are stored
652      * internally as "title[0]/subtitle[0)" in the bookmarks.
653      *
654      * @param name the name to look up
655      * @param locale the locale to look up
656      *
657      * @return the value found in the bookmarks
658      */

659     private I_CmsXmlContentValue getValueInternal(String JavaDoc name, Locale JavaDoc locale) {
660
661         Object JavaDoc value = getBookmark(name, locale);
662         if (value != null) {
663             return (I_CmsXmlContentValue)value;
664         }
665         return null;
666     }
667 }
Popular Tags