0001: /*
0002: * File : $Source: /usr/local/cvs/opencms/src/org/opencms/xml/CmsXmlContentDefinition.java,v $
0003: * Date : $Date: 2008-02-27 12:05:50 $
0004: * Version: $Revision: 1.41 $
0005: *
0006: * This library is part of OpenCms -
0007: * the Open Source Content Management System
0008: *
0009: * Copyright (c) 2002 - 2008 Alkacon Software GmbH (http://www.alkacon.com)
0010: *
0011: * This library is free software; you can redistribute it and/or
0012: * modify it under the terms of the GNU Lesser General Public
0013: * License as published by the Free Software Foundation; either
0014: * version 2.1 of the License, or (at your option) any later version.
0015: *
0016: * This library is distributed in the hope that it will be useful,
0017: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0018: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0019: * Lesser General Public License for more details.
0020: *
0021: * For further information about Alkacon Software GmbH, please see the
0022: * company website: http://www.alkacon.com
0023: *
0024: * For further information about OpenCms, please see the
0025: * project website: http://www.opencms.org
0026: *
0027: * You should have received a copy of the GNU Lesser General Public
0028: * License along with this library; if not, write to the Free Software
0029: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
0030: */
0031:
0032: package org.opencms.xml;
0033:
0034: import org.opencms.file.CmsObject;
0035: import org.opencms.main.OpenCms;
0036: import org.opencms.util.CmsStringUtil;
0037: import org.opencms.xml.content.CmsDefaultXmlContentHandler;
0038: import org.opencms.xml.content.I_CmsXmlContentHandler;
0039: import org.opencms.xml.types.CmsXmlLocaleValue;
0040: import org.opencms.xml.types.CmsXmlNestedContentDefinition;
0041: import org.opencms.xml.types.I_CmsXmlContentValue;
0042: import org.opencms.xml.types.I_CmsXmlSchemaType;
0043:
0044: import java.io.IOException;
0045: import java.util.ArrayList;
0046: import java.util.Arrays;
0047: import java.util.Collections;
0048: import java.util.HashMap;
0049: import java.util.HashSet;
0050: import java.util.Iterator;
0051: import java.util.List;
0052: import java.util.Locale;
0053: import java.util.Map;
0054: import java.util.Set;
0055:
0056: import org.dom4j.Attribute;
0057: import org.dom4j.Document;
0058: import org.dom4j.DocumentHelper;
0059: import org.dom4j.Element;
0060: import org.dom4j.Namespace;
0061: import org.dom4j.QName;
0062: import org.xml.sax.EntityResolver;
0063: import org.xml.sax.InputSource;
0064: import org.xml.sax.SAXException;
0065:
0066: /**
0067: * Describes the structure definition of an XML content object.<p>
0068: *
0069: * @author Alexander Kandzior
0070: *
0071: * @version $Revision: 1.41 $
0072: *
0073: * @since 6.0.0
0074: */
0075: public class CmsXmlContentDefinition implements Cloneable {
0076:
0077: /** Constant for the XML schema attribute "mapto". */
0078: public static final String XSD_ATTRIBUTE_DEFAULT = "default";
0079:
0080: /** Constant for the XML schema attribute "elementFormDefault". */
0081: public static final String XSD_ATTRIBUTE_ELEMENT_FORM_DEFAULT = "elementFormDefault";
0082:
0083: /** Constant for the XML schema attribute "maxOccurs". */
0084: public static final String XSD_ATTRIBUTE_MAX_OCCURS = "maxOccurs";
0085:
0086: /** Constant for the XML schema attribute "minOccurs". */
0087: public static final String XSD_ATTRIBUTE_MIN_OCCURS = "minOccurs";
0088:
0089: /** Constant for the XML schema attribute "name". */
0090: public static final String XSD_ATTRIBUTE_NAME = "name";
0091:
0092: /** Constant for the XML schema attribute "schemaLocation". */
0093: public static final String XSD_ATTRIBUTE_SCHEMA_LOCATION = "schemaLocation";
0094:
0095: /** Constant for the XML schema attribute "type". */
0096: public static final String XSD_ATTRIBUTE_TYPE = "type";
0097:
0098: /** Constant for the XML schema attribute "use". */
0099: public static final String XSD_ATTRIBUTE_USE = "use";
0100:
0101: /** Constant for the XML schema attribute value "language". */
0102: public static final String XSD_ATTRIBUTE_VALUE_LANGUAGE = "language";
0103:
0104: /** Constant for the XML schema attribute value "optional". */
0105: public static final String XSD_ATTRIBUTE_VALUE_OPTIONAL = "optional";
0106:
0107: /** Constant for the XML schema attribute value "qualified". */
0108: public static final String XSD_ATTRIBUTE_VALUE_QUALIFIED = "qualified";
0109:
0110: /** Constant for the XML schema attribute value "required". */
0111: public static final String XSD_ATTRIBUTE_VALUE_REQUIRED = "required";
0112:
0113: /** Constant for the XML schema attribute value "unbounded". */
0114: public static final String XSD_ATTRIBUTE_VALUE_UNBOUNDED = "unbounded";
0115:
0116: /** Constant for the XML schema attribute value "0". */
0117: public static final String XSD_ATTRIBUTE_VALUE_ZERO = "0";
0118:
0119: /** The opencms default type definition include. */
0120: public static final String XSD_INCLUDE_OPENCMS = CmsXmlEntityResolver.OPENCMS_SCHEME
0121: + "opencms-xmlcontent.xsd";
0122:
0123: /** The schema definition namespace. */
0124: public static final Namespace XSD_NAMESPACE = Namespace.get("xsd",
0125: "http://www.w3.org/2001/XMLSchema");
0126:
0127: /** Constant for the "annotation" node in the XML schema namespace. */
0128: public static final QName XSD_NODE_ANNOTATION = QName.get(
0129: "annotation", XSD_NAMESPACE);
0130:
0131: /** Constant for the "appinfo" node in the XML schema namespace. */
0132: public static final QName XSD_NODE_APPINFO = QName.get("appinfo",
0133: XSD_NAMESPACE);
0134:
0135: /** Constant for the "attribute" node in the XML schema namespace. */
0136: public static final QName XSD_NODE_ATTRIBUTE = QName.get(
0137: "attribute", XSD_NAMESPACE);
0138:
0139: /** Constant for the "complexType" node in the XML schema namespace. */
0140: public static final QName XSD_NODE_COMPLEXTYPE = QName.get(
0141: "complexType", XSD_NAMESPACE);
0142:
0143: /** Constant for the "element" node in the XML schema namespace. */
0144: public static final QName XSD_NODE_ELEMENT = QName.get("element",
0145: XSD_NAMESPACE);
0146:
0147: /** Constant for the "include" node in the XML schema namespace. */
0148: public static final QName XSD_NODE_INCLUDE = QName.get("include",
0149: XSD_NAMESPACE);
0150:
0151: /** Constant for the "schema" node in the XML schema namespace. */
0152: public static final QName XSD_NODE_SCHEMA = QName.get("schema",
0153: XSD_NAMESPACE);
0154:
0155: /** Constant for the "sequence" node in the XML schema namespace. */
0156: public static final QName XSD_NODE_SEQUENCE = QName.get("sequence",
0157: XSD_NAMESPACE);
0158:
0159: /** The XML content handler. */
0160: private I_CmsXmlContentHandler m_contentHandler;
0161:
0162: /** The set of included additional XML content definitions. */
0163: private Set m_includes;
0164:
0165: /** The inner element name of the content definition (type sequence). */
0166: private String m_innerName;
0167:
0168: /** The outer element name of the content definition (languange sequence). */
0169: private String m_outerName;
0170:
0171: /** The location from which the XML schema was read (XML system id). */
0172: private String m_schemaLocation;
0173:
0174: /** The main type name of this XML content definition. */
0175: private String m_typeName;
0176:
0177: /** The Map of configured types. */
0178: private Map m_types;
0179:
0180: /** The type sequence. */
0181: private List m_typeSequence;
0182:
0183: /**
0184: * Creates a new XML content definition.<p>
0185: *
0186: * @param innerName the inner element name to use for the content definiton
0187: * @param schemaLocation the location from which the XML schema was read (system id)
0188: */
0189: public CmsXmlContentDefinition(String innerName,
0190: String schemaLocation) {
0191:
0192: this (innerName + "s", innerName, schemaLocation);
0193: }
0194:
0195: /**
0196: * Creates a new XML content definition.<p>
0197: *
0198: * @param outerName the outer element name to use for the content definiton
0199: * @param innerName the inner element name to use for the content definiton
0200: * @param schemaLocation the location from which the XML schema was read (system id)
0201: */
0202: public CmsXmlContentDefinition(String outerName, String innerName,
0203: String schemaLocation) {
0204:
0205: m_outerName = outerName;
0206: m_innerName = innerName;
0207: setInnerName(innerName);
0208: m_typeSequence = new ArrayList();
0209: m_types = new HashMap();
0210: m_includes = new HashSet();
0211: m_schemaLocation = schemaLocation;
0212: m_contentHandler = new CmsDefaultXmlContentHandler();
0213: }
0214:
0215: /**
0216: * Required empty constructor for clone operation.<p>
0217: */
0218: protected CmsXmlContentDefinition() {
0219:
0220: // noop, required for clone operation
0221: }
0222:
0223: /**
0224: * Factory method to unmarshal (read) a XML content definition instance from a byte array
0225: * that contains XML data.<p>
0226: *
0227: * @param xmlData the XML data in a byte array
0228: * @param schemaLocation the location from which the XML schema was read (system id)
0229: * @param resolver the XML entitiy resolver to use
0230: *
0231: * @return a XML content definition instance unmarshalled from the byte array
0232: *
0233: * @throws CmsXmlException if something goes wrong
0234: */
0235: public static CmsXmlContentDefinition unmarshal(byte[] xmlData,
0236: String schemaLocation, EntityResolver resolver)
0237: throws CmsXmlException {
0238:
0239: CmsXmlContentDefinition result = getCachedContentDefinition(
0240: schemaLocation, resolver);
0241: if (result == null) {
0242: // content definition was not found in the cache, unmarshal the XML document
0243: result = unmarshalInternal(CmsXmlUtils.unmarshalHelper(
0244: xmlData, resolver), schemaLocation, resolver);
0245: }
0246: return result;
0247: }
0248:
0249: /**
0250: * Factory method to unmarshal (read) a XML content definition instance from the OpenCms VFS resource name.<p>
0251: *
0252: * @param cms the current users CmsObject
0253: * @param resourcename the resource name to unmarshal the XML content definition from
0254: *
0255: * @return a XML content definition instance unmarshalled from the VFS resource
0256: *
0257: * @throws CmsXmlException if something goes wrong
0258: */
0259: public static CmsXmlContentDefinition unmarshal(CmsObject cms,
0260: String resourcename) throws CmsXmlException {
0261:
0262: CmsXmlEntityResolver resolver = new CmsXmlEntityResolver(cms);
0263: String schemaLocation = CmsXmlEntityResolver.OPENCMS_SCHEME
0264: .concat(resourcename.substring(1));
0265: CmsXmlContentDefinition result = getCachedContentDefinition(
0266: schemaLocation, resolver);
0267: if (result == null) {
0268: // content definition was not found in the cache, unmarshal the XML document
0269: InputSource source = resolver.resolveEntity(null,
0270: schemaLocation);
0271: result = unmarshalInternal(CmsXmlUtils.unmarshalHelper(
0272: source, resolver), schemaLocation, resolver);
0273: }
0274: return result;
0275: }
0276:
0277: /**
0278: * Factory method to unmarshal (read) a XML content definition instance from a XML document.<p>
0279: *
0280: * This method does additional validation to ensure the document has the required
0281: * XML structure for a OpenCms content definition schema.<p>
0282: *
0283: * @param document the XML document to generate a XML content definition from
0284: * @param schemaLocation the location from which the XML schema was read (system id)
0285: *
0286: * @return a XML content definition instance unmarshalled from the XML document
0287: *
0288: * @throws CmsXmlException if something goes wrong
0289: */
0290: public static CmsXmlContentDefinition unmarshal(Document document,
0291: String schemaLocation) throws CmsXmlException {
0292:
0293: EntityResolver resolver = document.getEntityResolver();
0294: CmsXmlContentDefinition result = getCachedContentDefinition(
0295: schemaLocation, resolver);
0296: if (result == null) {
0297: // content definition was not found in the cache, unmarshal the XML document
0298: result = unmarshalInternal(document, schemaLocation,
0299: resolver);
0300: }
0301: return result;
0302: }
0303:
0304: /**
0305: * Factory method to unmarshal (read) a XML content definition instance from a XML InputSource.<p>
0306: *
0307: * @param source the XML InputSource to use
0308: * @param schemaLocation the location from which the XML schema was read (system id)
0309: * @param resolver the XML entitiy resolver to use
0310: *
0311: * @return a XML content definition instance unmarshalled from the InputSource
0312: *
0313: * @throws CmsXmlException if something goes wrong
0314: */
0315: public static CmsXmlContentDefinition unmarshal(InputSource source,
0316: String schemaLocation, EntityResolver resolver)
0317: throws CmsXmlException {
0318:
0319: CmsXmlContentDefinition result = getCachedContentDefinition(
0320: schemaLocation, resolver);
0321: if (result == null) {
0322: // content definition was not found in the cache, unmarshal the XML document
0323: result = unmarshalInternal(CmsXmlUtils.unmarshalHelper(
0324: source, resolver), schemaLocation, resolver);
0325: }
0326: return result;
0327: }
0328:
0329: /**
0330: * Factory method to unmarshal (read) a XML content definition instance from a given XML schema location.<p>
0331: *
0332: * The XML content definiton data to unmarshal will be read from the provided schema location using
0333: * an XML InputSource.<p>
0334: *
0335: * @param schemaLocation the location from which to read the XML schema (system id)
0336: * @param resolver the XML entitiy resolver to use
0337: *
0338: * @return a XML content definition instance unmarshalled from the InputSource
0339: *
0340: * @throws CmsXmlException if something goes wrong
0341: * @throws SAXException if the XML schema location could not be converted to an XML InputSource
0342: * @throws IOException if the XML schema location could not be converted to an XML InputSource
0343: */
0344: public static CmsXmlContentDefinition unmarshal(
0345: String schemaLocation, EntityResolver resolver)
0346: throws CmsXmlException, SAXException, IOException {
0347:
0348: CmsXmlContentDefinition result = getCachedContentDefinition(
0349: schemaLocation, resolver);
0350: if (result == null) {
0351: // content definition was not found in the cache, unmarshal the XML document
0352: InputSource source = resolver.resolveEntity(null,
0353: schemaLocation);
0354: result = unmarshalInternal(CmsXmlUtils.unmarshalHelper(
0355: source, resolver), schemaLocation, resolver);
0356: }
0357: return result;
0358: }
0359:
0360: /**
0361: * Factory method to unmarshal (read) a XML content definition instance from a String
0362: * that contains XML data.<p>
0363: *
0364: * @param xmlData the XML data in a String
0365: * @param schemaLocation the location from which the XML schema was read (system id)
0366: * @param resolver the XML entitiy resolver to use
0367: *
0368: * @return a XML content definition instance unmarshalled from the byte array
0369: *
0370: * @throws CmsXmlException if something goes wrong
0371: */
0372: public static CmsXmlContentDefinition unmarshal(String xmlData,
0373: String schemaLocation, EntityResolver resolver)
0374: throws CmsXmlException {
0375:
0376: CmsXmlContentDefinition result = getCachedContentDefinition(
0377: schemaLocation, resolver);
0378: if (result == null) {
0379: // content definition was not found in the cache, unmarshal the XML document
0380: result = unmarshalInternal(CmsXmlUtils.unmarshalHelper(
0381: xmlData, resolver), schemaLocation, resolver);
0382: }
0383: return result;
0384: }
0385:
0386: /**
0387: * Creates the name of the type attribute from the given content name.<p>
0388: *
0389: * @param name the name to use
0390: *
0391: * @return the name of the type attribute
0392: */
0393: protected static String createTypeName(String name) {
0394:
0395: StringBuffer result = new StringBuffer(32);
0396: result.append("OpenCms");
0397: result.append(name.substring(0, 1).toUpperCase());
0398: if (name.length() > 1) {
0399: result.append(name.substring(1));
0400: }
0401: return result.toString();
0402: }
0403:
0404: /**
0405: * Validates if a given attribute exists at the given element with an (optional) specified value.<p>
0406: *
0407: * If the required value is not <code>null</code>, the attribute must have excatly this
0408: * value set.<p>
0409: *
0410: * If no value is required, some simple validation is performed on the attribute value,
0411: * like a check that the value does not have leading or trainling white spaces.<p>
0412: *
0413: * @param element the element to validate
0414: * @param attributeName the attribute to check for
0415: * @param requiredValue the required value of the attribute, or <code>null</code> if any value is allowed
0416: *
0417: * @return the value of the attribute
0418: *
0419: * @throws CmsXmlException if the element does not have the required attribute set, or if the validation fails
0420: */
0421: protected static String validateAttribute(Element element,
0422: String attributeName, String requiredValue)
0423: throws CmsXmlException {
0424:
0425: Attribute attribute = element.attribute(attributeName);
0426: if (attribute == null) {
0427: throw new CmsXmlException(Messages.get().container(
0428: Messages.ERR_EL_MISSING_ATTRIBUTE_2,
0429: element.getUniquePath(), attributeName));
0430: }
0431: String value = attribute.getValue();
0432:
0433: if (requiredValue == null) {
0434: if (CmsStringUtil.isEmptyOrWhitespaceOnly(value)
0435: || !value.equals(value.trim())) {
0436: throw new CmsXmlException(Messages.get().container(
0437: Messages.ERR_EL_BAD_ATTRIBUTE_WS_3,
0438: element.getUniquePath(), attributeName, value));
0439: }
0440: } else {
0441: if (!requiredValue.equals(value)) {
0442: throw new CmsXmlException(Messages.get().container(
0443: Messages.ERR_EL_BAD_ATTRIBUTE_VALUE_4,
0444: new Object[] { element.getUniquePath(),
0445: attributeName, requiredValue, value }));
0446: }
0447: }
0448: return value;
0449: }
0450:
0451: /**
0452: * Validates if a gicen element has exactly the required attributes set.<p>
0453: *
0454: * @param element the element to validate
0455: * @param requiredAttributes the list of required attributes
0456: * @param optionalAttributes the list of optional attributes
0457: *
0458: * @throws CmsXmlException if the validation fails
0459: */
0460: protected static void validateAttributesExists(Element element,
0461: String[] requiredAttributes, String[] optionalAttributes)
0462: throws CmsXmlException {
0463:
0464: if (element.attributeCount() < requiredAttributes.length) {
0465: throw new CmsXmlException(Messages.get().container(
0466: Messages.ERR_EL_ATTRIBUTE_TOOFEW_3,
0467: element.getUniquePath(),
0468: new Integer(requiredAttributes.length),
0469: new Integer(element.attributeCount())));
0470: }
0471:
0472: if (element.attributeCount() > (requiredAttributes.length + optionalAttributes.length)) {
0473: throw new CmsXmlException(Messages.get().container(
0474: Messages.ERR_EL_ATTRIBUTE_TOOMANY_3,
0475: element.getUniquePath(),
0476: new Integer(requiredAttributes.length
0477: + optionalAttributes.length),
0478: new Integer(element.attributeCount())));
0479: }
0480:
0481: List attributes = element.attributes();
0482:
0483: for (int i = 0; i < requiredAttributes.length; i++) {
0484: String attributeName = requiredAttributes[i];
0485: if (element.attribute(attributeName) == null) {
0486: throw new CmsXmlException(Messages.get().container(
0487: Messages.ERR_EL_MISSING_ATTRIBUTE_2,
0488: element.getUniquePath(), attributeName));
0489: }
0490: }
0491:
0492: List rA = Arrays.asList(requiredAttributes);
0493: List oA = Arrays.asList(optionalAttributes);
0494:
0495: for (int i = 0; i < attributes.size(); i++) {
0496: String attributeName = element.attribute(i).getName();
0497: if (!rA.contains(attributeName)
0498: && !oA.contains(attributeName)) {
0499: throw new CmsXmlException(Messages.get().container(
0500: Messages.ERR_EL_INVALID_ATTRIBUTE_2,
0501: element.getUniquePath(), attributeName));
0502: }
0503: }
0504: }
0505:
0506: /**
0507: * Validates the given element as a complex type sequence.<p>
0508: *
0509: * @param element the element to validate
0510: * @param includes the XML schema includes
0511: *
0512: * @return a data structure containing the validated complex type sequence data
0513: *
0514: * @throws CmsXmlException if the validation fails
0515: */
0516: protected static CmsXmlComplexTypeSequence validateComplexTypeSequence(
0517: Element element, Set includes) throws CmsXmlException {
0518:
0519: validateAttributesExists(element,
0520: new String[] { XSD_ATTRIBUTE_NAME }, new String[0]);
0521:
0522: String name = validateAttribute(element, XSD_ATTRIBUTE_NAME,
0523: null);
0524:
0525: // now check the type definition list
0526: List mainElements = element.elements();
0527: if ((mainElements.size() != 1) && (mainElements.size() != 2)) {
0528: throw new CmsXmlException(Messages.get().container(
0529: Messages.ERR_TS_SUBELEMENT_COUNT_2,
0530: element.getUniquePath(),
0531: new Integer(mainElements.size())));
0532: }
0533:
0534: boolean hasLanguageAttribute = false;
0535: if (mainElements.size() == 2) {
0536: // two elements in the master list: the second must be the "language" attribute definition
0537:
0538: Element typeAttribute = (Element) mainElements.get(1);
0539: if (!XSD_NODE_ATTRIBUTE.equals(typeAttribute.getQName())) {
0540: throw new CmsXmlException(Messages.get().container(
0541: Messages.ERR_CD_ELEMENT_NAME_3,
0542: typeAttribute.getUniquePath(),
0543: XSD_NODE_ATTRIBUTE.getQualifiedName(),
0544: typeAttribute.getQName().getQualifiedName()));
0545: }
0546: validateAttribute(typeAttribute, XSD_ATTRIBUTE_NAME,
0547: XSD_ATTRIBUTE_VALUE_LANGUAGE);
0548: validateAttribute(typeAttribute, XSD_ATTRIBUTE_TYPE,
0549: CmsXmlLocaleValue.TYPE_NAME);
0550: try {
0551: validateAttribute(typeAttribute, XSD_ATTRIBUTE_USE,
0552: XSD_ATTRIBUTE_VALUE_REQUIRED);
0553: } catch (CmsXmlException e) {
0554: validateAttribute(typeAttribute, XSD_ATTRIBUTE_USE,
0555: XSD_ATTRIBUTE_VALUE_OPTIONAL);
0556: }
0557: // no error: then the language attribute is valid
0558: hasLanguageAttribute = true;
0559: }
0560:
0561: // check the main element type sequence
0562: Element typeSequence = (Element) mainElements.get(0);
0563: if (!XSD_NODE_SEQUENCE.equals(typeSequence.getQName())) {
0564: throw new CmsXmlException(Messages.get().container(
0565: Messages.ERR_CD_ELEMENT_NAME_3,
0566: typeSequence.getUniquePath(),
0567: XSD_NODE_SEQUENCE.getQualifiedName(),
0568: typeSequence.getQName().getQualifiedName()));
0569: }
0570:
0571: // check the type definition sequence
0572: List typeSequenceElements = typeSequence.elements();
0573: if (typeSequenceElements.size() < 1) {
0574: throw new CmsXmlException(Messages.get().container(
0575: Messages.ERR_TS_SUBELEMENT_TOOFEW_3,
0576: typeSequence.getUniquePath(), new Integer(1),
0577: new Integer(typeSequenceElements.size())));
0578: }
0579:
0580: // now add all type definitions from the schema
0581: List sequence = new ArrayList();
0582:
0583: if (hasLanguageAttribute) {
0584: // only generate types for sequence node with language attribute
0585:
0586: CmsXmlContentTypeManager typeManager = OpenCms
0587: .getXmlContentTypeManager();
0588: Iterator i = typeSequenceElements.iterator();
0589: while (i.hasNext()) {
0590: sequence.add(typeManager.getContentType((Element) i
0591: .next(), includes));
0592: }
0593: } else {
0594: // generate a nested content definition for the main type sequence
0595:
0596: Element e = (Element) typeSequenceElements.get(0);
0597: String typeName = validateAttribute(e, XSD_ATTRIBUTE_NAME,
0598: null);
0599: String minOccurs = validateAttribute(e,
0600: XSD_ATTRIBUTE_MIN_OCCURS, XSD_ATTRIBUTE_VALUE_ZERO);
0601: String maxOccurs = validateAttribute(e,
0602: XSD_ATTRIBUTE_MAX_OCCURS,
0603: XSD_ATTRIBUTE_VALUE_UNBOUNDED);
0604: validateAttribute(e, XSD_ATTRIBUTE_TYPE,
0605: createTypeName(typeName));
0606:
0607: CmsXmlNestedContentDefinition cd = new CmsXmlNestedContentDefinition(
0608: null, typeName, minOccurs, maxOccurs);
0609: sequence.add(cd);
0610: }
0611:
0612: // return a data structure with the collected values
0613: return new CmsXmlComplexTypeSequence(name, sequence,
0614: hasLanguageAttribute);
0615: }
0616:
0617: /**
0618: * Looks up the given XML content definition system id in the internal content definition cache.<p>
0619: *
0620: * @param schemaLocation the system id of the XML content definition to look up
0621: * @param resolver the XML entitiy resolver to use (contains the cache)
0622: *
0623: * @return the XML content definition found, or null if no definition is cached for the given system id
0624: */
0625: private static CmsXmlContentDefinition getCachedContentDefinition(
0626: String schemaLocation, EntityResolver resolver) {
0627:
0628: if (resolver instanceof CmsXmlEntityResolver) {
0629: // check for a cached version of this content definition
0630: CmsXmlEntityResolver cmsResolver = (CmsXmlEntityResolver) resolver;
0631: return cmsResolver
0632: .getCachedContentDefinition(schemaLocation);
0633: }
0634: return null;
0635: }
0636:
0637: /**
0638: * Internal method to unmarshal (read) a XML content definition instance from a XML document.<p>
0639: *
0640: * It is assumed that the XML content definition cache has already been tested and the document
0641: * has not been found in the cache. After the XML content definition has been successfully created,
0642: * it is placed in the cache.<p>
0643: *
0644: * @param document the XML document to generate a XML content definition from
0645: * @param schemaLocation the location from which the XML schema was read (system id)
0646: * @param resolver the XML entitiy resolver used by the given XML document
0647: *
0648: * @return a XML content definition instance unmarshalled from the XML document
0649: *
0650: * @throws CmsXmlException if something goes wrong
0651: */
0652: private static CmsXmlContentDefinition unmarshalInternal(
0653: Document document, String schemaLocation,
0654: EntityResolver resolver) throws CmsXmlException {
0655:
0656: // analyze the document and generate the XML content type definition
0657: Element root = document.getRootElement();
0658: if (!XSD_NODE_SCHEMA.equals(root.getQName())) {
0659: // schema node is required
0660: throw new CmsXmlException(Messages.get().container(
0661: Messages.ERR_CD_NO_SCHEMA_NODE_0));
0662: }
0663:
0664: List includes = root.elements(XSD_NODE_INCLUDE);
0665: if (includes.size() < 1) {
0666: // one include is required
0667: throw new CmsXmlException(Messages.get().container(
0668: Messages.ERR_CD_ONE_INCLUDE_REQUIRED_0));
0669: }
0670:
0671: Element include = (Element) includes.get(0);
0672: String target = validateAttribute(include,
0673: XSD_ATTRIBUTE_SCHEMA_LOCATION, null);
0674: if (!XSD_INCLUDE_OPENCMS.equals(target)) {
0675: // the first include must point to the default OpenCms standard schema include
0676: throw new CmsXmlException(Messages.get().container(
0677: Messages.ERR_CD_FIRST_INCLUDE_2,
0678: XSD_INCLUDE_OPENCMS, target));
0679: }
0680:
0681: Set nestedDefinitions = new HashSet();
0682: if (includes.size() > 1) {
0683: // resolve additional, nested include calls
0684: for (int i = 1; i < includes.size(); i++) {
0685:
0686: Element inc = (Element) includes.get(i);
0687: String schemaLoc = validateAttribute(inc,
0688: XSD_ATTRIBUTE_SCHEMA_LOCATION, null);
0689: InputSource source = null;
0690: try {
0691: source = resolver.resolveEntity(null, schemaLoc);
0692: } catch (Exception e) {
0693: throw new CmsXmlException(Messages.get().container(
0694: Messages.ERR_CD_BAD_INCLUDE_1, schemaLoc));
0695: }
0696: CmsXmlContentDefinition xmlContentDefinition = unmarshal(
0697: source, schemaLoc, resolver);
0698: nestedDefinitions.add(xmlContentDefinition);
0699: }
0700: }
0701:
0702: List elements = root.elements(XSD_NODE_ELEMENT);
0703: if (elements.size() != 1) {
0704: // only one root element is allowed
0705: throw new CmsXmlException(Messages.get().container(
0706: Messages.ERR_CD_ROOT_ELEMENT_COUNT_1,
0707: XSD_INCLUDE_OPENCMS, new Integer(elements.size())));
0708: }
0709:
0710: // collect the data from the root element node
0711: Element main = (Element) elements.get(0);
0712: String name = validateAttribute(main, XSD_ATTRIBUTE_NAME, null);
0713:
0714: // now process the complex types
0715: List complexTypes = root.elements(XSD_NODE_COMPLEXTYPE);
0716: if (complexTypes.size() != 2) {
0717: // exactly two complex types are required
0718: throw new CmsXmlException(Messages.get().container(
0719: Messages.ERR_CD_COMPLEX_TYPE_COUNT_1,
0720: new Integer(complexTypes.size())));
0721: }
0722:
0723: // generate the result XML content definition
0724: CmsXmlContentDefinition result = new CmsXmlContentDefinition(
0725: name, null, schemaLocation);
0726:
0727: // set the nested definitions
0728: result.m_includes = nestedDefinitions;
0729:
0730: List complexTypeData = new ArrayList();
0731: Iterator ct = complexTypes.iterator();
0732: while (ct.hasNext()) {
0733: Element e = (Element) ct.next();
0734: CmsXmlComplexTypeSequence sequence = validateComplexTypeSequence(
0735: e, nestedDefinitions);
0736: complexTypeData.add(sequence);
0737: }
0738:
0739: // get the outer element sequence, this must be the first element
0740: CmsXmlComplexTypeSequence outerSequence = (CmsXmlComplexTypeSequence) complexTypeData
0741: .get(0);
0742: CmsXmlNestedContentDefinition outer = (CmsXmlNestedContentDefinition) outerSequence.m_sequence
0743: .get(0);
0744:
0745: // make sure the inner and outer element names are as required
0746: String outerTypeName = createTypeName(name);
0747: String innerTypeName = createTypeName(outer.getName());
0748: validateAttribute((Element) complexTypes.get(0),
0749: XSD_ATTRIBUTE_NAME, outerTypeName);
0750: validateAttribute((Element) complexTypes.get(1),
0751: XSD_ATTRIBUTE_NAME, innerTypeName);
0752: validateAttribute(main, XSD_ATTRIBUTE_TYPE, outerTypeName);
0753:
0754: // the inner name is the element name set in the outer sequence
0755: result.setInnerName(outer.getName());
0756:
0757: // get the inner element sequence, this must be the second element
0758: CmsXmlComplexTypeSequence innerSequence = (CmsXmlComplexTypeSequence) complexTypeData
0759: .get(1);
0760:
0761: // add the types from the main sequence node
0762: Iterator it = innerSequence.m_sequence.iterator();
0763: while (it.hasNext()) {
0764: result.addType((I_CmsXmlSchemaType) it.next());
0765: }
0766:
0767: // resolve the XML content handler information
0768: List annotations = root.elements(XSD_NODE_ANNOTATION);
0769: I_CmsXmlContentHandler contentHandler = null;
0770: Element appInfoElement = null;
0771:
0772: if (annotations.size() > 0) {
0773: List appinfos = ((Element) annotations.get(0))
0774: .elements(XSD_NODE_APPINFO);
0775:
0776: if (appinfos.size() > 0) {
0777: // the first appinfo node contains the specific XML content data
0778: appInfoElement = (Element) appinfos.get(0);
0779:
0780: // check for a special content handler in the appinfo node
0781: Element handlerElement = appInfoElement
0782: .element("handler");
0783: if (handlerElement != null) {
0784: String className = handlerElement
0785: .attributeValue("class");
0786: if (className != null) {
0787: contentHandler = OpenCms
0788: .getXmlContentTypeManager()
0789: .getContentHandler(className,
0790: schemaLocation);
0791: }
0792: }
0793: }
0794: }
0795:
0796: if (contentHandler == null) {
0797: // if no content handler is defined, the default handler is used
0798: contentHandler = OpenCms
0799: .getXmlContentTypeManager()
0800: .getContentHandler(
0801: CmsDefaultXmlContentHandler.class.getName(),
0802: name);
0803: }
0804:
0805: // analyze the app info node with the selected XML content handler
0806: contentHandler.initialize(appInfoElement, result);
0807: result.m_contentHandler = contentHandler;
0808:
0809: result.freeze();
0810:
0811: if (resolver instanceof CmsXmlEntityResolver) {
0812: // put the generated content definition in the cache
0813: ((CmsXmlEntityResolver) resolver).cacheContentDefinition(
0814: schemaLocation, result);
0815: }
0816:
0817: return result;
0818: }
0819:
0820: /**
0821: * Adds the missing default XML according to this content definition to the given document element.<p>
0822: *
0823: * In case the root element already contains subnodes, only missing subnodes are added.<p>
0824: *
0825: * @param cms the current users OpenCms context
0826: * @param document the document where the XML is added in (required for default XML generation)
0827: * @param root the root node to add the missing XML for
0828: * @param locale the locale to add the XML for
0829: *
0830: * @return the given root element with the missing content added
0831: */
0832: public Element addDefaultXml(CmsObject cms,
0833: I_CmsXmlDocument document, Element root, Locale locale) {
0834:
0835: Iterator i = m_typeSequence.iterator();
0836: int currentPos = 0;
0837: List allElements = root.elements();
0838:
0839: while (i.hasNext()) {
0840: I_CmsXmlSchemaType type = (I_CmsXmlSchemaType) i.next();
0841:
0842: // check how many elements of this type already exist in the XML
0843: String elementName = type.getName();
0844: List elements = root.elements(elementName);
0845:
0846: currentPos += elements.size();
0847: for (int j = elements.size(); j < type.getMinOccurs(); j++) {
0848: // append the missing elements
0849: Element typeElement = type.generateXml(cms, document,
0850: root, locale);
0851: // need to check for default value again because the of appinfo "mappings" node
0852: I_CmsXmlContentValue value = type.createValue(document,
0853: typeElement, locale);
0854: String defaultValue = document.getContentDefinition()
0855: .getContentHandler().getDefault(cms, value,
0856: locale);
0857: if (defaultValue != null) {
0858: // only if there is a default value available use it to overwrite the initial default
0859: value.setStringValue(cms, defaultValue);
0860: }
0861:
0862: // re-sort elements as they have been appended to the end of the XML root, not at the correct position
0863: typeElement.detach();
0864: allElements.add(currentPos, typeElement);
0865: currentPos++;
0866: }
0867: }
0868:
0869: return root;
0870: }
0871:
0872: /**
0873: * Adds a nested (included) XML content definition.<p>
0874: *
0875: * @param nestedSchema the nested (included) XML content definition to add
0876: */
0877: public void addInclude(CmsXmlContentDefinition nestedSchema) {
0878:
0879: m_includes.add(nestedSchema);
0880: }
0881:
0882: /**
0883: * Adds the given content type.<p>
0884: *
0885: * @param type the content type to add
0886: *
0887: * @throws CmsXmlException in case an unregistered type is added
0888: */
0889: public void addType(I_CmsXmlSchemaType type) throws CmsXmlException {
0890:
0891: // check if the type to add actually exists in the type manager
0892: CmsXmlContentTypeManager typeManager = OpenCms
0893: .getXmlContentTypeManager();
0894: if (type.isSimpleType()
0895: && (typeManager.getContentType(type.getTypeName()) == null)) {
0896: throw new CmsXmlException(Messages.get().container(
0897: Messages.ERR_UNREGISTERED_TYPE_1,
0898: type.getTypeName()));
0899: }
0900:
0901: // add the type to the internal type sequence and lookup table
0902: m_typeSequence.add(type);
0903: m_types.put(type.getName(), type);
0904:
0905: // store reference to the content definition in the type
0906: type.setContentDefinition(this );
0907: }
0908:
0909: /**
0910: * Creates a clone of this XML content definition.<p>
0911: *
0912: * @return a clone of this XML content definition
0913: */
0914: public Object clone() {
0915:
0916: CmsXmlContentDefinition result = new CmsXmlContentDefinition();
0917: result.m_innerName = m_innerName;
0918: result.m_schemaLocation = m_schemaLocation;
0919: result.m_typeSequence = m_typeSequence;
0920: result.m_types = m_types;
0921: result.m_contentHandler = m_contentHandler;
0922: result.m_typeName = m_typeName;
0923: result.m_includes = m_includes;
0924: return result;
0925: }
0926:
0927: /**
0928: * Generates the default XML content for this content definition, and append it to the given root element.<p>
0929: *
0930: * Please note: The default values for the annotations are read from the content definition of the given
0931: * document. For a nested content definitions, this means that all defaults are set in the annotations of the
0932: * "outer" or "main" content defintition.<p>
0933: *
0934: * @param cms the current users OpenCms context
0935: * @param document the OpenCms XML document the XML is created for
0936: * @param root the node of the document where to append the generated XML to
0937: * @param locale the locale to create the default element in the document with
0938: *
0939: * @return the default XML content for this content definition, and append it to the given root element
0940: */
0941: public Element createDefaultXml(CmsObject cms,
0942: I_CmsXmlDocument document, Element root, Locale locale) {
0943:
0944: Iterator i = m_typeSequence.iterator();
0945: while (i.hasNext()) {
0946: I_CmsXmlSchemaType type = (I_CmsXmlSchemaType) i.next();
0947: for (int j = 0; j < type.getMinOccurs(); j++) {
0948: Element typeElement = type.generateXml(cms, document,
0949: root, locale);
0950: // need to check for default value again because of the appinfo "mappings" node
0951: I_CmsXmlContentValue value = type.createValue(document,
0952: typeElement, locale);
0953: String defaultValue = document.getContentDefinition()
0954: .getContentHandler().getDefault(cms, value,
0955: locale);
0956: if (defaultValue != null) {
0957: // only if there is a default value available use it to overwrite the initial default
0958: value.setStringValue(cms, defaultValue);
0959: }
0960: }
0961: }
0962:
0963: return root;
0964: }
0965:
0966: /**
0967: * Generates a valid XML document according to the XML schema of this content definition.<p>
0968: *
0969: * @param cms the current users OpenCms context
0970: * @param document the OpenCms XML document the XML is created for
0971: * @param locale the locale to create the default element in the document with
0972: *
0973: * @return a valid XML document according to the XML schema of this content definition
0974: */
0975: public Document createDocument(CmsObject cms,
0976: I_CmsXmlDocument document, Locale locale) {
0977:
0978: Document doc = DocumentHelper.createDocument();
0979:
0980: Element root = doc.addElement(getOuterName());
0981:
0982: root.add(I_CmsXmlSchemaType.XSI_NAMESPACE);
0983: root
0984: .addAttribute(
0985: I_CmsXmlSchemaType.XSI_NAMESPACE_ATTRIBUTE_NO_SCHEMA_LOCATION,
0986: getSchemaLocation());
0987:
0988: createLocale(cms, document, root, locale);
0989: return doc;
0990: }
0991:
0992: /**
0993: * Generates a valid locale (language) element for the XML schema of this content definition.<p>
0994: *
0995: * @param cms the current users OpenCms context
0996: * @param document the OpenCms XML document the XML is created for
0997: * @param root the root node of the document where to append the locale to
0998: * @param locale the locale to create the default element in the document with
0999: *
1000: * @return a valid XML element for the locale according to the XML schema of this content definition
1001: */
1002: public Element createLocale(CmsObject cms,
1003: I_CmsXmlDocument document, Element root, Locale locale) {
1004:
1005: // add an element with a "locale" attribute to the given root node
1006: Element element = root.addElement(getInnerName());
1007: element.addAttribute(XSD_ATTRIBUTE_VALUE_LANGUAGE, locale
1008: .toString());
1009:
1010: // now generate the default XML for the element
1011: return createDefaultXml(cms, document, element, locale);
1012: }
1013:
1014: /**
1015: * @see java.lang.Object#equals(java.lang.Object)
1016: */
1017: public boolean equals(Object obj) {
1018:
1019: if (obj == this ) {
1020: return true;
1021: }
1022: if (!(obj instanceof CmsXmlContentDefinition)) {
1023: return false;
1024: }
1025: CmsXmlContentDefinition other = (CmsXmlContentDefinition) obj;
1026: if (!getInnerName().equals(other.getInnerName())) {
1027: return false;
1028: }
1029: if (!getOuterName().equals(other.getOuterName())) {
1030: return false;
1031: }
1032: return m_typeSequence.equals(other.m_typeSequence);
1033: }
1034:
1035: /**
1036: * Freezes this content definition, making all internal data structures
1037: * unmodifiable.<p>
1038: *
1039: * This is required to prevent modification of a cached content definition.<p>
1040: */
1041: public void freeze() {
1042:
1043: m_types = Collections.unmodifiableMap(m_types);
1044: m_typeSequence = Collections.unmodifiableList(m_typeSequence);
1045: }
1046:
1047: /**
1048: * Returns the selected XML content handler for this XML content definition.<p>
1049: *
1050: * If no specific XML content handler was provided in the "appinfo" node of the
1051: * XML schema, the default XML content handler <code>{@link CmsDefaultXmlContentHandler}</code> is used.<p>
1052: *
1053: * @return the contentHandler
1054: */
1055: public I_CmsXmlContentHandler getContentHandler() {
1056:
1057: return m_contentHandler;
1058: }
1059:
1060: /**
1061: * Returns the set of nested (included) XML content definitions.<p>
1062: *
1063: * @return the set of nested (included) XML content definitions
1064: */
1065: public Set getIncludes() {
1066:
1067: return m_includes;
1068: }
1069:
1070: /**
1071: * Returns the inner element name of this content definition.<p>
1072: *
1073: * @return the inner element name of this content definition
1074: */
1075: public String getInnerName() {
1076:
1077: return m_innerName;
1078: }
1079:
1080: /**
1081: * Returns the outer element name of this content definition.<p>
1082: *
1083: * @return the outer element name of this content definition
1084: */
1085: public String getOuterName() {
1086:
1087: return m_outerName;
1088: }
1089:
1090: /**
1091: * Generates an XML schema for the content definition.<p>
1092: *
1093: * @return the generated XML schema
1094: */
1095: public Document getSchema() {
1096:
1097: Document schema = DocumentHelper.createDocument();
1098:
1099: Element root = schema.addElement(XSD_NODE_SCHEMA);
1100: root.addAttribute(XSD_ATTRIBUTE_ELEMENT_FORM_DEFAULT,
1101: XSD_ATTRIBUTE_VALUE_QUALIFIED);
1102:
1103: Element include = root.addElement(XSD_NODE_INCLUDE);
1104: include.addAttribute(XSD_ATTRIBUTE_SCHEMA_LOCATION,
1105: XSD_INCLUDE_OPENCMS);
1106:
1107: if (m_includes.size() > 0) {
1108: Iterator i = m_includes.iterator();
1109: while (i.hasNext()) {
1110: CmsXmlContentDefinition definition = (CmsXmlContentDefinition) i
1111: .next();
1112: root.addElement(XSD_NODE_INCLUDE).addAttribute(
1113: XSD_ATTRIBUTE_SCHEMA_LOCATION,
1114: definition.m_schemaLocation);
1115: }
1116: }
1117:
1118: String outerTypeName = createTypeName(getOuterName());
1119: String innerTypeName = createTypeName(getInnerName());
1120:
1121: Element content = root.addElement(XSD_NODE_ELEMENT);
1122: content.addAttribute(XSD_ATTRIBUTE_NAME, getOuterName());
1123: content.addAttribute(XSD_ATTRIBUTE_TYPE, outerTypeName);
1124:
1125: Element list = root.addElement(XSD_NODE_COMPLEXTYPE);
1126: list.addAttribute(XSD_ATTRIBUTE_NAME, outerTypeName);
1127:
1128: Element listSequence = list.addElement(XSD_NODE_SEQUENCE);
1129: Element listElement = listSequence.addElement(XSD_NODE_ELEMENT);
1130: listElement.addAttribute(XSD_ATTRIBUTE_NAME, getInnerName());
1131: listElement.addAttribute(XSD_ATTRIBUTE_TYPE, innerTypeName);
1132: listElement.addAttribute(XSD_ATTRIBUTE_MIN_OCCURS,
1133: XSD_ATTRIBUTE_VALUE_ZERO);
1134: listElement.addAttribute(XSD_ATTRIBUTE_MAX_OCCURS,
1135: XSD_ATTRIBUTE_VALUE_UNBOUNDED);
1136:
1137: Element main = root.addElement(XSD_NODE_COMPLEXTYPE);
1138: main.addAttribute(XSD_ATTRIBUTE_NAME, innerTypeName);
1139:
1140: Element mainSequence = main.addElement(XSD_NODE_SEQUENCE);
1141:
1142: Iterator i = m_typeSequence.iterator();
1143: while (i.hasNext()) {
1144: I_CmsXmlSchemaType schemaType = (I_CmsXmlSchemaType) i
1145: .next();
1146: schemaType.appendXmlSchema(mainSequence);
1147: }
1148:
1149: Element language = main.addElement(XSD_NODE_ATTRIBUTE);
1150: language.addAttribute(XSD_ATTRIBUTE_NAME,
1151: XSD_ATTRIBUTE_VALUE_LANGUAGE);
1152: language.addAttribute(XSD_ATTRIBUTE_TYPE,
1153: CmsXmlLocaleValue.TYPE_NAME);
1154: language.addAttribute(XSD_ATTRIBUTE_USE,
1155: XSD_ATTRIBUTE_VALUE_REQUIRED);
1156:
1157: return schema;
1158: }
1159:
1160: /**
1161: * Returns the location from which the XML schema was read (XML system id).<p>
1162: *
1163: * @return the location from which the XML schema was read (XML system id)
1164: */
1165: public String getSchemaLocation() {
1166:
1167: return m_schemaLocation;
1168: }
1169:
1170: /**
1171: * Returns the scheme type for the given element name, or <code>null</code> if no
1172: * node is defined with this name.<p>
1173: *
1174: * @param elementPath the element path to look up the type for
1175: * @return the type for the given element name, or <code>null</code> if no
1176: * node is defined with this name
1177: */
1178: public I_CmsXmlSchemaType getSchemaType(String elementPath) {
1179:
1180: String path = CmsXmlUtils.getFirstXpathElement(elementPath);
1181:
1182: I_CmsXmlSchemaType type = (I_CmsXmlSchemaType) m_types
1183: .get(path);
1184: if (type == null) {
1185: // no node with the given path defined in schema
1186: return null;
1187: }
1188:
1189: // check if recursion is required to get value from a nested schema
1190: if (type.isSimpleType()
1191: || !CmsXmlUtils.isDeepXpath(elementPath)) {
1192: // no recursion required
1193: return type;
1194: }
1195:
1196: // recursion required since the path is an xpath and the type must be a nested content definition
1197: CmsXmlNestedContentDefinition nestedDefinition = (CmsXmlNestedContentDefinition) type;
1198: path = CmsXmlUtils.removeFirstXpathElement(elementPath);
1199: return nestedDefinition.getNestedContentDefinition()
1200: .getSchemaType(path);
1201: }
1202:
1203: /**
1204: * Returns the internal set of schema type names.<p>
1205: *
1206: * @return the internal set of schema type names
1207: */
1208: public Set getSchemaTypes() {
1209:
1210: return m_types.keySet();
1211: }
1212:
1213: /**
1214: * Returns the main type name of this XML content definition.<p>
1215: *
1216: * @return the main type name of this XML content definition
1217: */
1218: public String getTypeName() {
1219:
1220: return m_typeName;
1221: }
1222:
1223: /**
1224: * Returns the type sequence, contains instances of {@link I_CmsXmlSchemaType}.<p>
1225: *
1226: * @return the type sequence, contains instances of {@link I_CmsXmlSchemaType}
1227: */
1228: public List getTypeSequence() {
1229:
1230: return m_typeSequence;
1231: }
1232:
1233: /**
1234: * @see java.lang.Object#hashCode()
1235: */
1236: public int hashCode() {
1237:
1238: return getInnerName().hashCode();
1239: }
1240:
1241: /**
1242: * Sets the inner element name to use for the content definition.<p>
1243: *
1244: * @param innerName the inner element name to set
1245: */
1246: protected void setInnerName(String innerName) {
1247:
1248: m_innerName = innerName;
1249: if (m_innerName != null) {
1250: m_typeName = createTypeName(innerName);
1251: }
1252: }
1253:
1254: /**
1255: * Sets the outer element name to use for the content definition.<p>
1256: *
1257: * @param outerName the outer element name to set
1258: */
1259: protected void setOuterName(String outerName) {
1260:
1261: m_outerName = outerName;
1262: }
1263: }
|