KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * File : $Source: /usr/local/cvs/opencms/src/org/opencms/xml/CmsXmlContentTypeManager.java,v $
3  * Date : $Date: 2006/03/27 14:52:20 $
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.CmsObject;
35 import org.opencms.i18n.CmsEncoder;
36 import org.opencms.main.CmsLog;
37 import org.opencms.main.CmsRuntimeException;
38 import org.opencms.main.OpenCms;
39 import org.opencms.security.CmsRole;
40 import org.opencms.security.CmsRoleViolationException;
41 import org.opencms.util.CmsStringUtil;
42 import org.opencms.widgets.I_CmsWidget;
43 import org.opencms.xml.content.I_CmsXmlContentHandler;
44 import org.opencms.xml.types.CmsXmlNestedContentDefinition;
45 import org.opencms.xml.types.I_CmsXmlSchemaType;
46
47 import java.io.UnsupportedEncodingException JavaDoc;
48 import java.util.ArrayList JavaDoc;
49 import java.util.Collections JavaDoc;
50 import java.util.HashMap JavaDoc;
51 import java.util.Iterator JavaDoc;
52 import java.util.List JavaDoc;
53 import java.util.Map JavaDoc;
54 import java.util.Set JavaDoc;
55
56 import org.apache.commons.collections.FastHashMap;
57 import org.apache.commons.logging.Log;
58
59 import org.dom4j.Document;
60 import org.dom4j.Element;
61
62 /**
63  * Manager class for registered OpenCms XML content types and content collectors.<p>
64  *
65  * @author Alexander Kandzior
66  *
67  * @version $Revision: 1.31 $
68  *
69  * @since 6.0.0
70  */

71 public class CmsXmlContentTypeManager {
72
73     /** The log object for this class. */
74     private static final Log LOG = CmsLog.getLog(CmsXmlContentTypeManager.class);
75
76     /** Stores the initialized XML content handlers. */
77     private Map JavaDoc m_contentHandlers;
78
79     /** Stores the registered content widgets. */
80     private Map JavaDoc m_defaultWidgets;
81
82     /** Stores the registered content types. */
83     private Map JavaDoc m_registeredTypes;
84
85     /** Stores the registed content widgegts by class name. */
86     private Map JavaDoc m_registeredWidgets;
87
88     /** The alias names for the widgets. */
89     private Map JavaDoc m_widgetAliases;
90
91     /**
92      * Creates a new content type manager.<p>
93      */

94     public CmsXmlContentTypeManager() {
95
96         // use the fast hash map implementation since there will be far more read then write accesses
97

98         m_registeredTypes = new HashMap JavaDoc();
99         m_defaultWidgets = new HashMap JavaDoc();
100         m_registeredWidgets = new HashMap JavaDoc();
101         m_widgetAliases = new HashMap JavaDoc();
102
103         FastHashMap fastMap = new FastHashMap();
104         fastMap.setFast(true);
105         m_contentHandlers = fastMap;
106
107         if (CmsLog.INIT.isInfoEnabled()) {
108             CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_START_CONTENT_CONFIG_0));
109         }
110     }
111
112     /**
113      * Returns a statically initialized instance of an XML content type manager (for test cases only).<p>
114      *
115      * @return a statically initialized instance of an XML content type manager
116      */

117     public static CmsXmlContentTypeManager createTypeManagerForTestCases() {
118
119         CmsXmlContentTypeManager typeManager = new CmsXmlContentTypeManager();
120
121         typeManager.addWidget("org.opencms.widgets.CmsCalendarWidget", null);
122         typeManager.addWidget("org.opencms.widgets.CmsHtmlWidget", null);
123         typeManager.addWidget("org.opencms.widgets.CmsInputWidget", null);
124
125         typeManager.addSchemaType("org.opencms.xml.types.CmsXmlDateTimeValue", "org.opencms.widgets.CmsCalendarWidget");
126         typeManager.addSchemaType("org.opencms.xml.types.CmsXmlHtmlValue", "org.opencms.widgets.CmsHtmlWidget");
127         typeManager.addSchemaType("org.opencms.xml.types.CmsXmlLocaleValue", "org.opencms.widgets.CmsInputWidget");
128         typeManager.addSchemaType("org.opencms.xml.types.CmsXmlStringValue", "org.opencms.widgets.CmsInputWidget");
129
130         try {
131             typeManager.initialize(null);
132         } catch (CmsRoleViolationException e) {
133             // this should never happen
134
throw new CmsRuntimeException(Messages.get().container(Messages.ERR_INIT_TYPE_MANAGER_0));
135         }
136         return typeManager;
137     }
138
139     /**
140      * Adds a XML content schema type class to the registerd XML content types.<p>
141      *
142      * @param clazz the XML content schema type class to add
143      *
144      * @return the created instance of the XML content schema type
145      *
146      * @throws CmsXmlException in case the class is not an instance of {@link I_CmsXmlSchemaType}
147      */

148     public I_CmsXmlSchemaType addContentType(Class JavaDoc clazz) throws CmsXmlException {
149
150         I_CmsXmlSchemaType type;
151         try {
152             type = (I_CmsXmlSchemaType)clazz.newInstance();
153         } catch (InstantiationException JavaDoc e) {
154             throw new CmsXmlException(Messages.get().container(Messages.ERR_INVALID_XCC_TYPE_REGISTERED_0));
155         } catch (IllegalAccessException JavaDoc e) {
156             throw new CmsXmlException(Messages.get().container(Messages.ERR_INVALID_XCC_TYPE_REGISTERED_0));
157         } catch (ClassCastException JavaDoc e) {
158             throw new CmsXmlException(Messages.get().container(Messages.ERR_INVALID_XCC_TYPE_REGISTERED_0));
159         }
160         m_registeredTypes.put(type.getTypeName(), type);
161         return type;
162     }
163
164     /**
165      * Adds a new XML content type schema class and XML widget to the manager by class names.<p>
166      *
167      * @param className class name of the XML content schema type class to add
168      * @param defaultWidget class name of the default XML widget class for the added XML content type
169      */

170     public void addSchemaType(String JavaDoc className, String JavaDoc defaultWidget) {
171
172         Class JavaDoc classClazz;
173         // init class for schema type
174
try {
175             classClazz = Class.forName(className);
176         } catch (ClassNotFoundException JavaDoc e) {
177             LOG.error(
178                 Messages.get().getBundle().key(Messages.LOG_XML_CONTENT_SCHEMA_TYPE_CLASS_NOT_FOUND_1, className),
179                 e);
180             return;
181         }
182
183         // create the schema type and add it to the internal list
184
I_CmsXmlSchemaType type;
185         try {
186             type = addContentType(classClazz);
187         } catch (Exception JavaDoc e) {
188             LOG.error(Messages.get().getBundle().key(
189                 Messages.LOG_INIT_XML_CONTENT_SCHEMA_TYPE_CLASS_ERROR_1,
190                 classClazz.getName()), e);
191             return;
192         }
193
194         // add the editor widget for the schema type
195
I_CmsWidget widget = getWidget(defaultWidget);
196         if (widget == null) {
197             LOG.error(Messages.get().getBundle().key(
198                 Messages.LOG_INIT_DEFAULT_WIDGET_FOR_CONTENT_TYPE_2,
199                 defaultWidget,
200                 type.getTypeName()));
201             return;
202         }
203
204         // store the registered default widget
205
m_defaultWidgets.put(type.getTypeName(), widget);
206
207         if (CmsLog.INIT.isInfoEnabled()) {
208             CmsLog.INIT.info(Messages.get().getBundle().key(
209                 Messages.INIT_ADD_ST_USING_WIDGET_2,
210                 type.getTypeName(),
211                 widget.getClass().getName()));
212         }
213     }
214
215     /**
216      * Adds a XML content editor widget class, making this widget available for the XML content editor.<p>
217      *
218      * @param className the widget class to add
219      * @param aliasName the (optional) alias name to use for the widget class
220      */

221     public void addWidget(String JavaDoc className, String JavaDoc aliasName) {
222
223         Class JavaDoc widgetClazz;
224         I_CmsWidget widget;
225         try {
226             widgetClazz = Class.forName(className);
227             widget = (I_CmsWidget)widgetClazz.newInstance();
228         } catch (Exception JavaDoc e) {
229             LOG.error(Messages.get().getBundle().key(Messages.LOG_XML_WIDGET_INITIALIZING_ERROR_1, className), e);
230             return;
231         }
232
233         m_registeredWidgets.put(widgetClazz.getName(), widget);
234
235         if (aliasName != null) {
236             m_widgetAliases.put(aliasName, widgetClazz.getName());
237         }
238
239         if (CmsLog.INIT.isInfoEnabled()) {
240             if (aliasName != null) {
241                 CmsLog.INIT.info(Messages.get().getBundle().key(Messages.INIT_ADD_WIDGET_1, widgetClazz.getName()));
242             } else {
243                 CmsLog.INIT.info(Messages.get().getBundle().key(
244                     Messages.INIT_ADD_WIDGET_ALIAS_2,
245                     widgetClazz.getName(),
246                     aliasName));
247             }
248         }
249     }
250
251     /**
252      * Returns the XML content handler instance class for the specified class name.<p>
253      *
254      * Only one instance of an XML content handler class per content definition name will be generated,
255      * and that instance will be cached and re-used for all operations.<p>
256      *
257      * @param className the name of the XML content handler to return
258      * @param schemaLocation the schema location of the XML content definition that handler belongs to
259      *
260      * @return the XML content handler class
261      *
262      * @throws CmsXmlException if something goes wrong
263      */

264     public I_CmsXmlContentHandler getContentHandler(String JavaDoc className, String JavaDoc schemaLocation) throws CmsXmlException {
265
266         // create a unique key for the content deinition / class name combo
267
StringBuffer JavaDoc buffer = new StringBuffer JavaDoc(128);
268         buffer.append(schemaLocation);
269         buffer.append('#');
270         buffer.append(className);
271         String JavaDoc key = buffer.toString();
272
273         // look up the content handler from the cache
274
I_CmsXmlContentHandler contentHandler = (I_CmsXmlContentHandler)m_contentHandlers.get(key);
275         if (contentHandler != null) {
276             return contentHandler;
277         }
278
279         // generate an instance for the content handler
280
try {
281             contentHandler = (I_CmsXmlContentHandler)Class.forName(className).newInstance();
282         } catch (InstantiationException JavaDoc e) {
283             throw new CmsXmlException(Messages.get().container(Messages.ERR_INVALID_CONTENT_HANDLER_1, key));
284         } catch (IllegalAccessException JavaDoc e) {
285             throw new CmsXmlException(Messages.get().container(Messages.ERR_INVALID_CONTENT_HANDLER_1, key));
286         } catch (ClassCastException JavaDoc e) {
287             throw new CmsXmlException(Messages.get().container(Messages.ERR_INVALID_CONTENT_HANDLER_1, key));
288         } catch (ClassNotFoundException JavaDoc e) {
289             throw new CmsXmlException(Messages.get().container(Messages.ERR_INVALID_CONTENT_HANDLER_1, key));
290         }
291
292         // cache and return the content handler instance
293
m_contentHandlers.put(key, contentHandler);
294         return contentHandler;
295     }
296
297     /**
298      * Generates an initialized instance of a XML content type definition
299      * from the given XML schema element.<p>
300      *
301      * @param typeElement the element to generate the XML content type definition from
302      * @param nestedDefinitions the nested (included) XML content sub-definitions
303      *
304      * @return an initialized instance of a XML content type definition
305      * @throws CmsXmlException in case the element does not describe a valid XML content type definition
306      */

307     public I_CmsXmlSchemaType getContentType(Element typeElement, Set JavaDoc nestedDefinitions) throws CmsXmlException {
308
309         if (!CmsXmlContentDefinition.XSD_NODE_ELEMENT.equals(typeElement.getQName())) {
310             throw new CmsXmlException(Messages.get().container(Messages.ERR_INVALID_CD_SCHEMA_STRUCTURE_0));
311         }
312         if (typeElement.elements().size() > 0) {
313             throw new CmsXmlException(Messages.get().container(Messages.ERR_INVALID_CD_SCHEMA_STRUCTURE_0));
314         }
315
316         int todo = 0;
317         // TODO: Use validation methods from CmsXmlContentDefinition here
318

319         String JavaDoc elementName = typeElement.attributeValue(CmsXmlContentDefinition.XSD_ATTRIBUTE_NAME);
320         String JavaDoc typeName = typeElement.attributeValue(CmsXmlContentDefinition.XSD_ATTRIBUTE_TYPE);
321         String JavaDoc defaultValue = typeElement.attributeValue(CmsXmlContentDefinition.XSD_ATTRIBUTE_DEFAULT);
322         String JavaDoc maxOccrs = typeElement.attributeValue(CmsXmlContentDefinition.XSD_ATTRIBUTE_MAX_OCCURS);
323         String JavaDoc minOccrs = typeElement.attributeValue(CmsXmlContentDefinition.XSD_ATTRIBUTE_MIN_OCCURS);
324
325         if (CmsStringUtil.isEmpty(elementName) || CmsStringUtil.isEmpty(typeName)) {
326             throw new CmsXmlException(Messages.get().container(Messages.ERR_INVALID_CD_SCHEMA_STRUCTURE_0));
327         }
328
329         boolean simpleType = true;
330         I_CmsXmlSchemaType schemaType = (I_CmsXmlSchemaType)m_registeredTypes.get(typeName);
331         if (schemaType == null) {
332
333             // the name is not a simple type, try to resolve from the nested schemas
334
Iterator JavaDoc i = nestedDefinitions.iterator();
335             while (i.hasNext()) {
336
337                 CmsXmlContentDefinition cd = (CmsXmlContentDefinition)i.next();
338                 if (typeName.equals(cd.getTypeName())) {
339
340                     simpleType = false;
341                     return new CmsXmlNestedContentDefinition(cd, elementName, minOccrs, maxOccrs);
342                 }
343             }
344
345             if (simpleType) {
346                 throw new CmsXmlException(Messages.get().container(Messages.ERR_UNKNOWN_SCHEMA_1, typeName));
347             }
348         }
349
350         if (simpleType) {
351             schemaType = schemaType.newInstance(elementName, minOccrs, maxOccrs);
352
353             if (CmsStringUtil.isNotEmpty(defaultValue)) {
354                 schemaType.setDefault(defaultValue);
355             }
356         }
357
358         return schemaType;
359     }
360
361     /**
362      * Returns the content type registered with the given name, or <code>null</code>.<p>
363      *
364      * @param typeName the name to look up the content type for
365      * @return the content type registered with the given name, or <code>null</code>
366      */

367     public I_CmsXmlSchemaType getContentType(String JavaDoc typeName) {
368
369         return (I_CmsXmlSchemaType)m_registeredTypes.get(typeName);
370     }
371
372     /**
373      * Retruns an alphabetically sorted list of all configured XML content schema types.<p>
374      *
375      * @return an alphabetically sorted list of all configured XML content schema types
376      */

377     public List JavaDoc getRegisteredSchemaTypes() {
378
379         List JavaDoc result = new ArrayList JavaDoc(m_registeredTypes.values());
380         Collections.sort(result);
381         return result;
382     }
383
384     /**
385      * Returns the alias for the given Widget class name, may be <code>null</code> if no alias is defined for
386      * the class.<p>
387      *
388      * @param className the name of the widget
389      * @return the alias for the given Widget class name, may be <code>null</code> if no alias is defined for
390      * the class
391      */

392     public String JavaDoc getRegisteredWidgetAlias(String JavaDoc className) {
393
394         // this implementation could be improved for performance,
395
// but since it's very seldom used it's currently just a straight map iteration
396
Iterator JavaDoc i = m_widgetAliases.keySet().iterator();
397         while (i.hasNext()) {
398             String JavaDoc aliasName = (String JavaDoc)i.next();
399             String JavaDoc clazzName = (String JavaDoc)m_widgetAliases.get(aliasName);
400             if (clazzName.equals(className)) {
401                 // the alias mapping was found
402
return aliasName;
403             }
404         }
405         return null;
406     }
407
408     /**
409      * Retruns an alphabetically sorted list of the class names of all configured XML widgets.<p>
410      *
411      * @return an alphabetically sorted list of the class names of all configured XML widgets
412      */

413     public List JavaDoc getRegisteredWidgetNames() {
414
415         List JavaDoc result = new ArrayList JavaDoc(m_registeredWidgets.keySet());
416         Collections.sort(result);
417         return result;
418     }
419
420     /**
421      * Returns an initialized widget class by it's class name or by it's alias.<p>
422      *
423      * @param name the class name or alias name to get the widget for
424      * @return the widget instance for the class name
425      */

426     public I_CmsWidget getWidget(String JavaDoc name) {
427
428         // first look up by class name
429
I_CmsWidget result = (I_CmsWidget)m_registeredWidgets.get(name);
430         if (result == null) {
431             // not found by class name, look up an alias
432
String JavaDoc className = (String JavaDoc)m_widgetAliases.get(name);
433             if (className != null) {
434                 result = (I_CmsWidget)m_registeredWidgets.get(className);
435             }
436         }
437         if (result != null) {
438             result = result.newInstance();
439         }
440         return result;
441     }
442
443     /**
444      * Returns the editor widget for the specified XML content type.<p>
445      *
446      * @param typeName the name of the XML content type to get the widget for
447      * @return the editor widget for the specified XML content type
448      */

449     public I_CmsWidget getWidgetDefault(String JavaDoc typeName) {
450
451         I_CmsWidget result = (I_CmsWidget)m_defaultWidgets.get(typeName);
452         if (result != null) {
453             result = result.newInstance();
454         }
455         return result;
456     }
457
458     /**
459      * Initializes XML content types managed in this XML content type manager.<p>
460      *
461      * @param cms an initialized OpenCms user context with "Administrator" role permissions
462      *
463      * @throws CmsRoleViolationException in case the provided OpenCms user context doea not have "Administrator" role permissions
464      */

465     public synchronized void initialize(CmsObject cms) throws CmsRoleViolationException {
466
467         if (OpenCms.getRunLevel() > OpenCms.RUNLEVEL_1_CORE_OBJECT) {
468
469             // simple test cases don't require this check
470
cms.checkRole(CmsRole.ADMINISTRATOR);
471         }
472
473         // initilaize the special entity resolver
474
CmsXmlEntityResolver.initialize(cms, getSchemaBytes());
475
476         if (CmsLog.INIT.isInfoEnabled()) {
477             CmsLog.INIT.info(Messages.get().getBundle().key(
478                 Messages.INIT_NUM_ST_INITIALIZED_1,
479                 new Integer JavaDoc(m_registeredTypes.size())));
480         }
481     }
482
483     /**
484      * Returns a byte array to be used as input source for the configured XML content types.<p>
485      *
486      * @return a byte array to be used as input source for the configured XML content types
487      */

488     private byte[] getSchemaBytes() {
489
490         StringBuffer JavaDoc schema = new StringBuffer JavaDoc(512);
491         schema.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
492         schema.append("<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" elementFormDefault=\"qualified\">");
493         Iterator JavaDoc i = m_registeredTypes.values().iterator();
494         while (i.hasNext()) {
495             I_CmsXmlSchemaType type = (I_CmsXmlSchemaType)i.next();
496             schema.append(type.getSchemaDefinition());
497         }
498         schema.append("</xsd:schema>");
499         String JavaDoc schemaStr = schema.toString();
500
501         try {
502             // pretty print the XML schema
503
// this helps in debugging the auto-generated schema includes
504
// since it makes them more human-readable
505
Document doc = CmsXmlUtils.unmarshalHelper(schemaStr, null);
506             schemaStr = CmsXmlUtils.marshal(doc, CmsEncoder.ENCODING_UTF_8);
507         } catch (CmsXmlException e) {
508             // should not ever happen
509
LOG.error(Messages.get().getBundle().key(Messages.LOG_PRETTY_PRINT_SCHEMA_BYTES_ERROR_0), e);
510         }
511         if (LOG.isInfoEnabled()) {
512             LOG.info(Messages.get().getBundle().key(
513                 Messages.LOG_XML_TYPE_DEFINITION_XSD_2,
514                 CmsXmlContentDefinition.XSD_INCLUDE_OPENCMS,
515                 schemaStr));
516         }
517         try {
518             return schemaStr.getBytes(CmsEncoder.ENCODING_UTF_8);
519         } catch (UnsupportedEncodingException JavaDoc e) {
520             // should not happen since the default encoding of UTF-8 is always valid
521
LOG.error(Messages.get().getBundle().key(Messages.LOG_CONVERTING_SCHEMA_BYTES_ERROR_0), e);
522         }
523         return null;
524     }
525 }
Popular Tags