001: /*
002: * File : $Source: /usr/local/cvs/opencms/src/org/opencms/xml/A_CmsXmlDocument.java,v $
003: * Date : $Date: 2008-02-27 12:05:51 $
004: * Version: $Revision: 1.36 $
005: *
006: * This library is part of OpenCms -
007: * the Open Source Content Management System
008: *
009: * Copyright (c) 2002 - 2008 Alkacon Software GmbH (http://www.alkacon.com)
010: *
011: * This library is free software; you can redistribute it and/or
012: * modify it under the terms of the GNU Lesser General Public
013: * License as published by the Free Software Foundation; either
014: * version 2.1 of the License, or (at your option) any later version.
015: *
016: * This library is distributed in the hope that it will be useful,
017: * but WITHOUT ANY WARRANTY; without even the implied warranty of
018: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: * Lesser General Public License for more details.
020: *
021: * For further information about Alkacon Software GmbH, please see the
022: * company website: http://www.alkacon.com
023: *
024: * For further information about OpenCms, please see the
025: * project website: http://www.opencms.org
026: *
027: * You should have received a copy of the GNU Lesser General Public
028: * License along with this library; if not, write to the Free Software
029: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
030: */
031:
032: package org.opencms.xml;
033:
034: import org.opencms.file.CmsFile;
035: import org.opencms.file.CmsObject;
036: import org.opencms.i18n.CmsLocaleManager;
037: import org.opencms.main.CmsIllegalArgumentException;
038: import org.opencms.main.CmsRuntimeException;
039: import org.opencms.xml.types.I_CmsXmlContentValue;
040: import org.opencms.xml.types.I_CmsXmlSchemaType;
041:
042: import java.io.ByteArrayOutputStream;
043: import java.io.OutputStream;
044: import java.util.ArrayList;
045: import java.util.Collections;
046: import java.util.HashMap;
047: import java.util.HashSet;
048: import java.util.Iterator;
049: import java.util.List;
050: import java.util.Locale;
051: import java.util.Map;
052: import java.util.Set;
053:
054: import org.dom4j.Document;
055: import org.dom4j.Element;
056: import org.xml.sax.EntityResolver;
057:
058: /**
059: * Provides basic XML document handling functions useful when dealing
060: * with XML documents that are stored in the OpenCms VFS.<p>
061: *
062: * @author Alexander Kandzior
063: *
064: * @version $Revision: 1.36 $
065: *
066: * @since 6.0.0
067: */
068: public abstract class A_CmsXmlDocument implements I_CmsXmlDocument {
069:
070: /** The content conversion to use for this XML document. */
071: protected String m_conversion;
072:
073: /** The document object of the document. */
074: protected Document m_document;
075:
076: /** Maps element names to available locales. */
077: protected Map m_elementLocales;
078:
079: /** Maps locales to avaliable element names. */
080: protected Map m_elementNames;
081:
082: /** The encoding to use for this xml document. */
083: protected String m_encoding;
084:
085: /** The file that contains the document data (note: is not set when creating an empty or document based document). */
086: protected CmsFile m_file;
087:
088: /** Set of locales contained in this document. */
089: protected Set m_locales;
090:
091: /** Reference for named elements in the document. */
092: private Map m_bookmarks;
093:
094: /**
095: * Default constructor for a XML document
096: * that initializes some internal values.<p>
097: */
098: protected A_CmsXmlDocument() {
099:
100: m_bookmarks = new HashMap();
101: m_locales = new HashSet();
102: }
103:
104: /**
105: * Creates the bookmark name for a localized element to be used in the bookmark lookup table.<p>
106: *
107: * @param name the element name
108: * @param locale the element locale
109: * @return the bookmark name for a localized element
110: */
111: protected static final String getBookmarkName(String name,
112: Locale locale) {
113:
114: StringBuffer result = new StringBuffer(64);
115: result.append('/');
116: result.append(locale.toString());
117: result.append('/');
118: result.append(name);
119: return result.toString();
120: }
121:
122: /**
123: * @see org.opencms.xml.I_CmsXmlDocument#copyLocale(java.util.List, java.util.Locale)
124: */
125: public void copyLocale(List possibleSources, Locale destination)
126: throws CmsXmlException {
127:
128: if (hasLocale(destination)) {
129: throw new CmsXmlException(Messages.get().container(
130: Messages.ERR_LOCALE_ALREADY_EXISTS_1, destination));
131: }
132: Iterator i = possibleSources.iterator();
133: Locale source = null;
134: while (i.hasNext() && (source == null)) {
135: // check all locales and try to find the first match
136: Locale candidate = (Locale) i.next();
137: if (hasLocale(candidate)) {
138: // locale has been found
139: source = candidate;
140: }
141: }
142: if (source != null) {
143: // found a locale, copy this to the destination
144: copyLocale(source, destination);
145: } else {
146: // no matching locale has been found
147: throw new CmsXmlException(Messages.get().container(
148: Messages.ERR_LOCALE_NOT_AVAILABLE_1,
149: CmsLocaleManager.getLocaleNames(possibleSources)));
150: }
151: }
152:
153: /**
154: * @see org.opencms.xml.I_CmsXmlDocument#copyLocale(java.util.Locale, java.util.Locale)
155: */
156: public void copyLocale(Locale source, Locale destination)
157: throws CmsXmlException {
158:
159: if (!hasLocale(source)) {
160: throw new CmsXmlException(Messages.get().container(
161: Messages.ERR_LOCALE_NOT_AVAILABLE_1, source));
162: }
163: if (hasLocale(destination)) {
164: throw new CmsXmlException(Messages.get().container(
165: Messages.ERR_LOCALE_ALREADY_EXISTS_1, destination));
166: }
167:
168: Element sourceElement = null;
169: Element rootNode = m_document.getRootElement();
170: Iterator i = rootNode.elementIterator();
171: String localeStr = source.toString();
172: while (i.hasNext()) {
173: Element element = (Element) i.next();
174: String language = element
175: .attributeValue(
176: CmsXmlContentDefinition.XSD_ATTRIBUTE_VALUE_LANGUAGE,
177: null);
178: if ((language != null) && (localeStr.equals(language))) {
179: // detach node with the locale
180: sourceElement = element.createCopy();
181: // there can be only one node for the locale
182: break;
183: }
184: }
185:
186: if (sourceElement == null) {
187: // should not happen since this was checked already, just to make sure...
188: throw new CmsXmlException(Messages.get().container(
189: Messages.ERR_LOCALE_NOT_AVAILABLE_1, source));
190: }
191:
192: // switch locale value in attribute of copied node
193: sourceElement.addAttribute(
194: CmsXmlContentDefinition.XSD_ATTRIBUTE_VALUE_LANGUAGE,
195: destination.toString());
196: // attach the copied node to the root node
197: rootNode.add(sourceElement);
198:
199: // re-initialize the document bookmarks
200: initDocument(m_document, m_encoding, getContentDefinition());
201: }
202:
203: /**
204: * Corrects the structure of this XML document.<p>
205: *
206: * @param cms the current OpenCms user context
207: *
208: * @return the file that contains the corrected XML structure
209: *
210: * @throws CmsXmlException if something goes wrong
211: */
212: public CmsFile correctXmlStructure(CmsObject cms)
213: throws CmsXmlException {
214:
215: // iterate over all locales
216: Iterator i = m_locales.iterator();
217: while (i.hasNext()) {
218: Locale locale = (Locale) i.next();
219: List names = getNames(locale);
220: List validValues = new ArrayList();
221:
222: // iterate over all nodes per language
223: Iterator j = names.iterator();
224: while (j.hasNext()) {
225:
226: // this step is required for values that need a processing of their content
227: // an example for this is the HTML value that does link replacement
228: String name = (String) j.next();
229: I_CmsXmlContentValue value = getValue(name, locale);
230: if (value.isSimpleType()) {
231: String content = value.getStringValue(cms);
232: value.setStringValue(cms, content);
233: }
234:
235: // save valid elements for later check
236: validValues.add(value);
237: }
238:
239: if (isAutoCorrectionEnabled()) {
240: // full correction of XML
241:
242: ArrayList roots = new ArrayList();
243: ArrayList rootCds = new ArrayList();
244: ArrayList validElements = new ArrayList();
245:
246: // gather all XML content definitions and their parent nodes
247: Iterator it = validValues.iterator();
248: while (it.hasNext()) {
249: // collect all root elements, also for the nested content definitions
250: I_CmsXmlContentValue value = (I_CmsXmlContentValue) it
251: .next();
252: Element element = value.getElement();
253: validElements.add(element);
254: if (element.supportsParent()) {
255: // get the parent XML node
256: Element root = element.getParent();
257: if ((root != null) && !roots.contains(root)) {
258: // this is a parent node we do not have already in our storage
259: CmsXmlContentDefinition rcd = value
260: .getContentDefinition();
261: if (rcd != null) {
262: // this value has a valid XML content definition
263: roots.add(root);
264: rootCds.add(rcd);
265: } else {
266: // no valid content definition for the XML value
267: throw new CmsXmlException(
268: Messages
269: .get()
270: .container(
271: Messages.ERR_CORRECT_NO_CONTENT_DEF_3,
272: value.getName(),
273: value
274: .getTypeName(),
275: value.getPath()));
276: }
277: }
278: }
279: }
280:
281: for (int le = 0; le < roots.size(); le++) {
282: // iterate all XML content root nodes and correct each XML subtree
283:
284: Element root = (Element) roots.get(le);
285: CmsXmlContentDefinition cd = (CmsXmlContentDefinition) rootCds
286: .get(le);
287:
288: // step 1: first sort the nodes according to the schema, this takes care of re-ordered elements
289: List nodeLists = new ArrayList();
290: Iterator is = cd.getTypeSequence().iterator();
291: while (is.hasNext()) {
292: I_CmsXmlSchemaType type = (I_CmsXmlSchemaType) is
293: .next();
294: String name = type.getName();
295: List elements = root.elements(name);
296: if (elements.size() > type.getMaxOccurs()) {
297: // to many nodes of this type appear according to the current schema definition
298: for (int lo = (elements.size() - 1); lo >= type
299: .getMaxOccurs(); lo--) {
300: elements.remove(lo);
301: }
302: }
303: nodeLists.add(elements);
304: }
305:
306: // step 2: clear the list of nodes (this will remove all invalid nodes)
307: List nodeList = root.elements();
308: nodeList.clear();
309: Iterator in = nodeLists.iterator();
310: while (in.hasNext()) {
311: // now add all valid nodes in the right order
312: List elements = (List) in.next();
313: nodeList.addAll(elements);
314: }
315:
316: // step 3: now append the missing elements according to the XML content definition
317: cd.addDefaultXml(cms, this , root, locale);
318: }
319: }
320: }
321:
322: // write the modified XML back to the VFS file
323: if (m_file != null) {
324: // make sure the file object is available
325: m_file.setContents(marshal());
326: }
327: return m_file;
328: }
329:
330: /**
331: * @see org.opencms.xml.I_CmsXmlDocument#getConversion()
332: */
333: public String getConversion() {
334:
335: return m_conversion;
336: }
337:
338: /**
339: * @see org.opencms.xml.I_CmsXmlDocument#getEncoding()
340: */
341: public String getEncoding() {
342:
343: return m_encoding;
344: }
345:
346: /**
347: * @see org.opencms.xml.I_CmsXmlDocument#getFile()
348: */
349: public CmsFile getFile() {
350:
351: return m_file;
352: }
353:
354: /**
355: * @see org.opencms.xml.I_CmsXmlDocument#getIndexCount(java.lang.String, java.util.Locale)
356: */
357: public int getIndexCount(String path, Locale locale) {
358:
359: List elements = getValues(path, locale);
360: if (elements == null) {
361: return 0;
362: } else {
363: return elements.size();
364: }
365: }
366:
367: /**
368: * @see org.opencms.xml.I_CmsXmlDocument#getLocales()
369: */
370: public List getLocales() {
371:
372: return new ArrayList(m_locales);
373: }
374:
375: /**
376: * Returns a List of all locales that have the named element set in this document.<p>
377: *
378: * If no locale for the given element name is available, an empty list is returned.<p>
379: *
380: * @param path the element to look up the locale List for
381: * @return a List of all Locales that have the named element set in this document
382: */
383: public List getLocales(String path) {
384:
385: Object result = m_elementLocales.get(CmsXmlUtils.createXpath(
386: path, 1));
387: if (result == null) {
388: return Collections.EMPTY_LIST;
389: }
390: return new ArrayList((Set) result);
391: }
392:
393: /**
394: * @see org.opencms.xml.I_CmsXmlDocument#getNames(java.util.Locale)
395: */
396: public List getNames(Locale locale) {
397:
398: Object o = m_elementNames.get(locale);
399: if (o != null) {
400: return new ArrayList((Set) o);
401: }
402: return Collections.EMPTY_LIST;
403: }
404:
405: /**
406: * @see org.opencms.xml.I_CmsXmlDocument#getStringValue(org.opencms.file.CmsObject, java.lang.String, java.util.Locale)
407: */
408: public String getStringValue(CmsObject cms, String path,
409: Locale locale) {
410:
411: I_CmsXmlContentValue value = getValueInternal(CmsXmlUtils
412: .createXpath(path, 1), locale);
413: if (value != null) {
414: return value.getStringValue(cms);
415: }
416: return null;
417: }
418:
419: /**
420: * @see org.opencms.xml.I_CmsXmlDocument#getStringValue(CmsObject, java.lang.String, Locale, int)
421: */
422: public String getStringValue(CmsObject cms, String path,
423: Locale locale, int index) {
424:
425: // directly calling getValueInternal() is more efficient then calling getStringValue(CmsObject, String, Locale)
426: // since the most costs are generated in resolving the xpath name
427: I_CmsXmlContentValue value = getValueInternal(CmsXmlUtils
428: .createXpath(path, index + 1), locale);
429: if (value != null) {
430: return value.getStringValue(cms);
431: }
432: return null;
433: }
434:
435: /**
436: * @see org.opencms.xml.I_CmsXmlDocument#getValue(java.lang.String, java.util.Locale)
437: */
438: public I_CmsXmlContentValue getValue(String path, Locale locale) {
439:
440: return getValueInternal(CmsXmlUtils.createXpath(path, 1),
441: locale);
442: }
443:
444: /**
445: * @see org.opencms.xml.I_CmsXmlDocument#getValue(java.lang.String, java.util.Locale, int)
446: */
447: public I_CmsXmlContentValue getValue(String path, Locale locale,
448: int index) {
449:
450: return getValueInternal(CmsXmlUtils
451: .createXpath(path, index + 1), locale);
452: }
453:
454: /**
455: * @see org.opencms.xml.I_CmsXmlDocument#getValues(java.util.Locale)
456: */
457: public List getValues(Locale locale) {
458:
459: List result = new ArrayList();
460:
461: // bookmarks are stored with the locale as first prefix
462: String prefix = '/' + locale.toString() + '/';
463:
464: // it's better for performance to iterate through the list of bookmarks directly
465: Iterator i = m_bookmarks.keySet().iterator();
466: while (i.hasNext()) {
467: String key = (String) i.next();
468: if (key.startsWith(prefix)) {
469: result.add(m_bookmarks.get(key));
470: }
471: }
472:
473: // sort the result
474: Collections.sort(result);
475:
476: return result;
477: }
478:
479: /**
480: * @see org.opencms.xml.I_CmsXmlDocument#getValues(java.lang.String, java.util.Locale)
481: */
482: public List getValues(String path, Locale locale) {
483:
484: List result = new ArrayList();
485: int count = 1;
486: Object o;
487: String xpath = CmsXmlUtils.createXpath(path, 1);
488: xpath = CmsXmlUtils.removeXpathIndex(xpath);
489: do {
490: String subpath = CmsXmlUtils.createXpathElement(xpath,
491: count);
492: o = getBookmark(subpath, locale);
493: if (o != null) {
494: result.add(o);
495: count++;
496: }
497: } while (o != null);
498:
499: return result;
500: }
501:
502: /**
503: * @see org.opencms.xml.I_CmsXmlDocument#hasLocale(java.util.Locale)
504: */
505: public boolean hasLocale(Locale locale) {
506:
507: if (locale == null) {
508: throw new CmsIllegalArgumentException(Messages.get()
509: .container(Messages.ERR_NULL_LOCALE_0));
510: }
511:
512: return m_locales.contains(locale);
513: }
514:
515: /**
516: * @see org.opencms.xml.I_CmsXmlDocument#hasValue(java.lang.String, java.util.Locale)
517: */
518: public boolean hasValue(String path, Locale locale) {
519:
520: return null != getBookmark(CmsXmlUtils.createXpath(path, 1),
521: locale);
522: }
523:
524: /**
525: * @see org.opencms.xml.I_CmsXmlDocument#hasValue(java.lang.String, java.util.Locale, int)
526: */
527: public boolean hasValue(String path, Locale locale, int index) {
528:
529: return null != getBookmark(CmsXmlUtils.createXpath(path,
530: index + 1), locale);
531: }
532:
533: /**
534: * @see org.opencms.xml.I_CmsXmlDocument#initDocument()
535: */
536: public void initDocument() {
537:
538: initDocument(m_document, m_encoding, getContentDefinition());
539: }
540:
541: /**
542: * @see org.opencms.xml.I_CmsXmlDocument#isEnabled(java.lang.String, java.util.Locale)
543: */
544: public boolean isEnabled(String path, Locale locale) {
545:
546: return hasValue(path, locale);
547: }
548:
549: /**
550: * @see org.opencms.xml.I_CmsXmlDocument#isEnabled(java.lang.String, java.util.Locale, int)
551: */
552: public boolean isEnabled(String path, Locale locale, int index) {
553:
554: return hasValue(path, locale, index);
555: }
556:
557: /**
558: * Marshals (writes) the content of the current XML document
559: * into a byte array using the selected encoding.<p>
560: *
561: * @return the content of the current XML document written into a byte array
562: * @throws CmsXmlException if something goes wrong
563: */
564: public byte[] marshal() throws CmsXmlException {
565:
566: return ((ByteArrayOutputStream) marshal(
567: new ByteArrayOutputStream(), m_encoding)).toByteArray();
568: }
569:
570: /**
571: * @see org.opencms.xml.I_CmsXmlDocument#moveLocale(java.util.Locale, java.util.Locale)
572: */
573: public void moveLocale(Locale source, Locale destination)
574: throws CmsXmlException {
575:
576: copyLocale(source, destination);
577: removeLocale(source);
578: }
579:
580: /**
581: * @see org.opencms.xml.I_CmsXmlDocument#removeLocale(java.util.Locale)
582: */
583: public void removeLocale(Locale locale) throws CmsXmlException {
584:
585: if (!hasLocale(locale)) {
586: throw new CmsXmlException(Messages.get().container(
587: Messages.ERR_LOCALE_NOT_AVAILABLE_1, locale));
588: }
589:
590: Element rootNode = m_document.getRootElement();
591: Iterator i = rootNode.elementIterator();
592: String localeStr = locale.toString();
593: while (i.hasNext()) {
594: Element element = (Element) i.next();
595: String language = element
596: .attributeValue(
597: CmsXmlContentDefinition.XSD_ATTRIBUTE_VALUE_LANGUAGE,
598: null);
599: if ((language != null) && (localeStr.equals(language))) {
600: // detach node with the locale
601: element.detach();
602: // there can be only one node for the locale
603: break;
604: }
605: }
606:
607: // re-initialize the document bookmarks
608: initDocument(m_document, m_encoding, getContentDefinition());
609: }
610:
611: /**
612: * Sets the content conversion mode for this document.<p>
613: *
614: * @param conversion the conversion mode to set for this document
615: */
616: public void setConversion(String conversion) {
617:
618: m_conversion = conversion;
619: }
620:
621: /**
622: * @see java.lang.Object#toString()
623: */
624: public String toString() {
625:
626: try {
627: return CmsXmlUtils.marshal(m_document, m_encoding);
628: } catch (CmsXmlException e) {
629: throw new CmsRuntimeException(Messages.get().container(
630: Messages.ERR_WRITE_XML_DOC_TO_STRING_0), e);
631: }
632: }
633:
634: /**
635: * Validates the XML structure of the document with the DTD or XML schema used by the document.<p>
636: *
637: * This is required in case someone modifies the XML structure of a
638: * document using the "edit control code" option.<p>
639: *
640: * @param resolver the XML entity resolver to use
641: * @throws CmsXmlException if the validation fails
642: */
643: public void validateXmlStructure(EntityResolver resolver)
644: throws CmsXmlException {
645:
646: if (m_file != null) {
647: byte[] xmlData = null;
648: // file is set, use bytes from file directly
649: xmlData = m_file.getContents();
650: CmsXmlUtils.validateXmlStructure(xmlData, resolver);
651: } else {
652: CmsXmlUtils.validateXmlStructure(m_document, m_encoding,
653: resolver);
654: }
655: }
656:
657: /**
658: * Adds a bookmark for the given value.<p>
659: *
660: * @param path the lookup path to use for the bookmark
661: * @param locale the locale to use for the bookmark
662: * @param enabled if true, the value is enabled, if false it is disabled
663: * @param value the value to bookmark
664: */
665: protected void addBookmark(String path, Locale locale,
666: boolean enabled, Object value) {
667:
668: // add the locale (since the locales are a set adding them more then once does not matter)
669: addLocale(locale);
670:
671: // add a bookmark to the provided value
672: m_bookmarks.put(getBookmarkName(path, locale), value);
673:
674: Object o;
675: // update mapping of element name to locale
676: if (enabled) {
677: // only include enabled elements
678: o = m_elementLocales.get(path);
679: if (o != null) {
680: ((Set) o).add(locale);
681: } else {
682: Set set = new HashSet();
683: set.add(locale);
684: m_elementLocales.put(path, set);
685: }
686: }
687: // update mapping of locales to element names
688: o = m_elementNames.get(locale);
689: if (o != null) {
690: ((Set) o).add(path);
691: } else {
692: Set set = new HashSet();
693: set.add(path);
694: m_elementNames.put(locale, set);
695: }
696: }
697:
698: /**
699: * Adds a locale to the set of locales of the XML document.<p>
700: *
701: * @param locale the locale to add
702: */
703: protected void addLocale(Locale locale) {
704:
705: // add the locale to all locales in this dcoument
706: m_locales.add(locale);
707: }
708:
709: /**
710: * Clears the XML document bookmarks.<p>
711: */
712: protected void clearBookmarks() {
713:
714: m_bookmarks.clear();
715: }
716:
717: /**
718: * Returns the bookmarked value for the given bookmark,
719: * which must be a valid bookmark name.
720: *
721: * Use {@link #getBookmarks()} to get the list of all valid bookmark names.<p>
722: *
723: * @param bookmark the bookmark name to look up
724: * @return the bookmarked value for the given bookmark
725: */
726: protected Object getBookmark(String bookmark) {
727:
728: return m_bookmarks.get(bookmark);
729: }
730:
731: /**
732: * Returns the bookmarked value for the given name.<p>
733: *
734: * @param path the lookup path to use for the bookmark
735: * @param locale the locale to get the bookmark for
736: * @return the bookmarked value
737: */
738: protected Object getBookmark(String path, Locale locale) {
739:
740: return m_bookmarks.get(getBookmarkName(path, locale));
741: }
742:
743: /**
744: * Returns the names of all bookmarked elements.<p>
745: *
746: * @return the names of all bookmarked elements
747: */
748: protected Set getBookmarks() {
749:
750: return m_bookmarks.keySet();
751: }
752:
753: /**
754: * Initializes an XML document based on the provided document, encoding and content definition.<p>
755: *
756: * @param document the base XML document to use for initializing
757: * @param encoding the encoding to use when marshalling the document later
758: * @param contentDefinition the content definition to use
759: */
760: protected abstract void initDocument(Document document,
761: String encoding, CmsXmlContentDefinition contentDefinition);
762:
763: /**
764: * Returns <code>true</code> if the auto correction feature is enabled for saving this XML content.<p>
765: *
766: * @return <code>true</code> if the auto correction feature is enabled for saving this XML content
767: */
768: protected boolean isAutoCorrectionEnabled() {
769:
770: // by default, this method always returns false
771: return false;
772: }
773:
774: /**
775: * Marshals (writes) the content of the current XML document
776: * into an output stream.<p>
777: *
778: * @param out the output stream to write to
779: * @param encoding the encoding to use
780: * @return the output stream with the XML content
781: * @throws CmsXmlException if something goes wrong
782: */
783: protected OutputStream marshal(OutputStream out, String encoding)
784: throws CmsXmlException {
785:
786: return CmsXmlUtils.marshal(m_document, out, encoding);
787: }
788:
789: /**
790: * Removes the bookmark for an element with the given name and locale.<p>
791: *
792: * @param path the lookup path to use for the bookmark
793: * @param locale the locale of the element
794: * @return the element removed from the bookmarks or null
795: */
796: protected I_CmsXmlContentValue removeBookmark(String path,
797: Locale locale) {
798:
799: // remove mapping of element name to locale
800: Object o;
801: o = m_elementLocales.get(path);
802: if (o != null) {
803: ((Set) o).remove(locale);
804: }
805: // remove mapping of locale to element name
806: o = m_elementNames.get(locale);
807: if (o != null) {
808: ((Set) o).remove(path);
809: }
810: // remove the bookmark and return the removed element
811: return (I_CmsXmlContentValue) m_bookmarks
812: .remove(getBookmarkName(path, locale));
813: }
814:
815: /**
816: * Internal method to look up a value, requires that the name already has been
817: * "normalized" for the bookmark lookup.
818: *
819: * This is required to find names like "title/subtitle" which are stored
820: * internally as "title[0]/subtitle[0)" in the bookmarks.
821: *
822: * @param path the path to look up
823: * @param locale the locale to look up
824: *
825: * @return the value found in the bookmarks
826: */
827: private I_CmsXmlContentValue getValueInternal(String path,
828: Locale locale) {
829:
830: Object value = getBookmark(path, locale);
831: if (value != null) {
832: return (I_CmsXmlContentValue) value;
833: }
834: return null;
835: }
836: }
|