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.io.IOException;
033: import java.io.InputStream;
034: import java.util.HashMap;
035: import java.util.Map;
036:
037: import javax.xml.parsers.DocumentBuilder;
038: import javax.xml.parsers.DocumentBuilderFactory;
039: import javax.xml.parsers.ParserConfigurationException;
040:
041: import nextapp.echo2.app.DerivedMutableStyle;
042: import nextapp.echo2.app.MutableStyleSheet;
043: import nextapp.echo2.app.Style;
044: import nextapp.echo2.app.StyleSheet;
045: import nextapp.echo2.app.util.DomUtil;
046: import org.w3c.dom.Document;
047: import org.w3c.dom.Element;
048: import org.xml.sax.SAXException;
049:
050: /**
051: * Loads XML style sheets.
052: */
053: public class StyleSheetLoader {
054:
055: /**
056: * Parses an XML style sheet and returns a <code>StyleSheet</code>
057: * instance.
058: * <p>
059: * Styles for components that cannot be loaded by the specified
060: * <code>ClassLoader</code> will be ignored.
061: *
062: * @param resourceName the name of the resource on the
063: * <code>CLASSPATH</code> containing the XML data
064: * @param classLoader the <code>ClassLoader</code> with which to
065: * instantiate property objects
066: * @return the created <code>StyleSheet</code> or null if the resource
067: * does not exist
068: * @throws ComponentXmlException if parsing/instantiation errors occur
069: */
070: public static StyleSheet load(String resourceName,
071: ClassLoader classLoader) throws ComponentXmlException {
072: InputStream in = null;
073: try {
074: in = classLoader.getResourceAsStream(resourceName);
075: if (in == null) {
076: return null;
077: }
078: return load(in, classLoader);
079: } finally {
080: if (in != null) {
081: try {
082: in.close();
083: } catch (IOException ex) {
084: }
085: }
086: }
087: }
088:
089: /**
090: * Parses an XML style sheet and returns a <code>StyleSheet</code>
091: * instance.
092: * <p>
093: * Styles for components that cannot be loaded by the specified
094: * <code>ClassLoader</code> will be ignored.
095: *
096: * @param in the <code>InputStream</code> containing the XML data
097: * @param classLoader the <code>ClassLoader</code> with which to
098: * instantiate property objects
099: * @return the created <code>StyleSheet</code>
100: * @throws ComponentXmlException if parsing/instantiation errors occur
101: */
102: public static StyleSheet load(InputStream in,
103: ClassLoader classLoader) throws ComponentXmlException {
104: Document document;
105: try {
106: DocumentBuilderFactory factory = DocumentBuilderFactory
107: .newInstance();
108: factory.setNamespaceAware(true);
109: DocumentBuilder builder = factory.newDocumentBuilder();
110: document = builder.parse(in);
111: } catch (IOException ex) {
112: throw new ComponentXmlException(
113: "Failed to parse InputStream.", ex);
114: } catch (ParserConfigurationException ex) {
115: throw new ComponentXmlException(
116: "Failed to parse InputStream.", ex);
117: } catch (SAXException ex) {
118: throw new ComponentXmlException(
119: "Failed to parse InputStream.", ex);
120: }
121:
122: PropertyLoader propertyLoader = PropertyLoader
123: .forClassLoader(classLoader);
124:
125: Map namedStyleMap = new HashMap();
126:
127: MutableStyleSheet styleSheet = new MutableStyleSheet();
128: Element styleSheetElement = document.getDocumentElement();
129: Element[] styleElements = DomUtil.getChildElementsByTagName(
130: styleSheetElement, "style");
131:
132: // First pass, load style information.
133: for (int i = 0; i < styleElements.length; ++i) {
134: String name = styleElements[i].getAttribute("name");
135: if (!styleElements[i].hasAttribute("type")) {
136: throw new ComponentXmlException(
137: "Component type not specified in style: "
138: + name, null);
139: }
140: String type = styleElements[i].getAttribute("type");
141:
142: Class componentClass;
143: try {
144: componentClass = Class.forName(type, true, classLoader);
145: } catch (ClassNotFoundException ex) {
146: // StyleSheet contains reference to Component which does not exist in this ClassLoader,
147: // and thus should be ignored.
148: continue;
149: }
150:
151: DerivedMutableStyle style = new DerivedMutableStyle();
152:
153: Element propertiesElement = DomUtil
154: .getChildElementByTagName(styleElements[i],
155: "properties");
156: Style propertyStyle = propertyLoader.createStyle(
157: propertiesElement, type);
158: style.addStyleContent(propertyStyle);
159:
160: Map classToStyleMap = (Map) namedStyleMap.get(name);
161: if (classToStyleMap == null) {
162: classToStyleMap = new HashMap();
163: namedStyleMap.put(name, classToStyleMap);
164: }
165: classToStyleMap.put(componentClass, style);
166:
167: styleSheet.addStyle(componentClass, name, style);
168: }
169:
170: // Second pass, bind derived styles to base styles where applicable.
171: for (int i = 0; i < styleElements.length; ++i) {
172: if (styleElements[i].hasAttribute("base-name")) {
173: String name = styleElements[i].getAttribute("name");
174: String type = styleElements[i].getAttribute("type");
175: Class componentClass;
176: try {
177: componentClass = Class.forName(type, true,
178: classLoader);
179: } catch (ClassNotFoundException ex) {
180: // StyleSheet contains reference to Component which does not exist in this ClassLoader,
181: // and thus should be ignored.
182: continue;
183: }
184:
185: Map classToStyleMap = (Map) namedStyleMap.get(name);
186: DerivedMutableStyle style = (DerivedMutableStyle) classToStyleMap
187: .get(componentClass);
188:
189: String baseName = styleElements[i]
190: .getAttribute("base-name");
191:
192: classToStyleMap = (Map) namedStyleMap.get(baseName);
193: if (classToStyleMap == null) {
194: throw new ComponentXmlException(
195: "Invalid base style name for style name "
196: + name + ".", null);
197: }
198: Style baseStyle = (Style) classToStyleMap
199: .get(componentClass);
200: while (baseStyle == null
201: && componentClass != Object.class) {
202: componentClass = componentClass.getSuperclass();
203: baseStyle = (Style) classToStyleMap
204: .get(componentClass);
205: }
206: if (baseStyle == null) {
207: throw new ComponentXmlException(
208: "Invalid base style name for style name "
209: + name + ".", null);
210: }
211:
212: style.setParentStyle(baseStyle);
213: }
214: }
215:
216: return styleSheet;
217: }
218: }
|