001: /*
002: * This file is part of the Echo Web Application Framework (hereinafter "Echo").
003: * Copyright (C) 2002-2005 NextApp, Inc.
004: *
005: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
006: *
007: * The contents of this file are subject to the Mozilla Public License Version
008: * 1.1 (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: * http://www.mozilla.org/MPL/
011: *
012: * Software distributed under the License is distributed on an "AS IS" basis,
013: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
014: * for the specific language governing rights and limitations under the
015: * License.
016: *
017: * Alternatively, the contents of this file may be used under the terms of
018: * either the GNU General Public License Version 2 or later (the "GPL"), or
019: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
020: * in which case the provisions of the GPL or the LGPL are applicable instead
021: * of those above. If you wish to allow use of your version of this file only
022: * under the terms of either the GPL or the LGPL, and not to allow others to
023: * use your version of this file under the terms of the MPL, indicate your
024: * decision by deleting the provisions above and replace them with the notice
025: * and other provisions required by the GPL or the LGPL. If you do not delete
026: * the provisions above, a recipient may use your version of this file under
027: * the terms of any one of the MPL, the GPL or the LGPL.
028: */
029:
030: package nextapp.echo2.app.componentxml;
031:
032: import java.util.HashMap;
033: import java.util.Map;
034:
035: import nextapp.echo2.app.MutableStyle;
036: import nextapp.echo2.app.Style;
037: import nextapp.echo2.app.util.DomUtil;
038: import nextapp.echo2.app.util.PeerFactory;
039:
040: import org.w3c.dom.Element;
041:
042: /**
043: * Parses "properties" <code>Element</code>s into maps associating property
044: * names with instantiated property values.
045: */
046: public class PropertyLoader {
047:
048: private static final String PROPERTY_XML_PEERS_PATH = "META-INF/nextapp/echo2/PropertyXmlPeers.properties";
049:
050: /**
051: * Map of <code>ClassLoader</code>s to <code>PropertyLoader</code>s.
052: */
053: private static final Map classLoaderToPropertyLoaderMap = new HashMap();
054:
055: /**
056: * Creates or retrieves a <code>PropertyLoader</code>.
057: *
058: * @param classLoader the <code>ClassLoader</code> to use for
059: * dynamically loading property classes
060: * @return the <code>PropertyLoader</code>
061: */
062: public static PropertyLoader forClassLoader(ClassLoader classLoader) {
063: synchronized (classLoaderToPropertyLoaderMap) {
064: PropertyLoader propertyLoader = (PropertyLoader) classLoaderToPropertyLoaderMap
065: .get(classLoader);
066: if (propertyLoader == null) {
067: propertyLoader = new PropertyLoader(classLoader);
068: classLoaderToPropertyLoaderMap.put(classLoader,
069: propertyLoader);
070: }
071: return propertyLoader;
072: }
073: }
074:
075: private ClassLoader classLoader;
076: private PeerFactory propertyXmlPeerFactory;
077:
078: /**
079: * Creates a new <code>PropertyLoader</code>.
080: *
081: * @param classLoader the <code>ClassLoader</code> to use for
082: * dynamically loading property classes
083: */
084: private PropertyLoader(ClassLoader classLoader) {
085: super ();
086: this .classLoader = classLoader;
087: propertyXmlPeerFactory = new PeerFactory(
088: PROPERTY_XML_PEERS_PATH, classLoader);
089: }
090:
091: /**
092: * Parses a "properties" <code>Element</code> and returns a
093: * <code>Style</code> mapping between property names and values.
094: *
095: * @param propertiesElement the properties <code>Element</code> to be
096: * parsed
097: * @param type the fully-qualified component type name
098: * @return a style representing the retrieved property names and values
099: * @throws ComponentXmlException
100: */
101: public Style createStyle(Element propertiesElement, String type)
102: throws ComponentXmlException {
103: MutableStyle propertyStyle = new MutableStyle();
104:
105: if (propertiesElement == null) {
106: // No properties.
107: return new MutableStyle();
108: }
109:
110: ComponentIntrospector ci;
111: try {
112: ci = ComponentIntrospector.forName(type, classLoader);
113: } catch (ClassNotFoundException ex) {
114: throw new ComponentXmlException(
115: "Unable to introspect component: " + type, ex);
116: }
117:
118: Element[] propertyElements = DomUtil.getChildElementsByTagName(
119: propertiesElement, "property");
120: for (int i = 0; i < propertyElements.length; ++i) {
121: String propertyName = propertyElements[i]
122: .getAttribute("name");
123: Class propertyClass;
124: if (propertyElements[i].hasAttribute("type")) {
125: try {
126: propertyClass = Class.forName(propertyElements[i]
127: .getAttribute("type"));
128: } catch (ClassNotFoundException ex) {
129: throw new ComponentXmlException(
130: "Custom property class not found: "
131: + propertyElements[i]
132: .getAttribute("type"), ex);
133: }
134: } else {
135: propertyClass = ci.getPropertyClass(propertyName);
136: }
137:
138: if (propertyClass == null) {
139: throw new ComponentXmlException(
140: "Property does not exist: " + propertyName,
141: null);
142: }
143:
144: Object propertyValue = getPropertyValue(
145: ci.getObjectClass(), propertyClass,
146: propertyElements[i]);
147:
148: if (ci.isIndexedProperty(propertyName)) {
149: try {
150: int index = Integer.parseInt(propertyElements[i]
151: .getAttribute("index"));
152: propertyStyle.setIndexedProperty(propertyName,
153: index, propertyValue);
154: } catch (NumberFormatException ex) {
155: throw new ComponentXmlException("Index not set.",
156: ex);
157: }
158: } else {
159: propertyStyle.setProperty(propertyName, propertyValue);
160: }
161: }
162:
163: return propertyStyle;
164: }
165:
166: /**
167: * Retrieves a property value from an property element.
168: *
169: * @param objectClass the object containing the property
170: * @param propertyClass the class of the property
171: * @param propertyElement the property element to analyze
172: * @return the property value
173: * @throws InvalidPropertyException
174: */
175: public Object getPropertyValue(Class objectClass,
176: Class propertyClass, Element propertyElement)
177: throws InvalidPropertyException {
178: PropertyXmlPeer propertyXmlPeer = (PropertyXmlPeer) propertyXmlPeerFactory
179: .getPeerForObject(propertyClass, false);
180: if (propertyXmlPeer == null) {
181: throw new InvalidPropertyException(
182: "Peer not found for property class: "
183: + propertyClass, null);
184: }
185: Object propertyValue = propertyXmlPeer.getValue(classLoader,
186: objectClass, propertyElement);
187: return propertyValue;
188: }
189:
190: /**
191: * Returns the <code>PropertyXmlPeer</code> for the given property class.
192: *
193: * @param propertyClass the property class
194: * @return the XML parsing peer
195: */
196: public PropertyXmlPeer getPropertyXmlPeer(Class propertyClass) {
197: return (PropertyXmlPeer) propertyXmlPeerFactory
198: .getPeerForObject(propertyClass, false);
199: }
200: }
|