001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.test.xml;
020:
021: import java.lang.reflect.Constructor;
022: import java.lang.reflect.Method;
023: import java.util.List;
024: import java.util.ArrayList;
025:
026: import org.w3c.dom.Element;
027: import org.w3c.dom.Node;
028: import org.w3c.dom.NodeList;
029:
030: /**
031: * This helper class can be used to build Java object from their
032: * XML description.
033: *
034: * @author <a href="mailto:vhardy@apache.org">Vincent Hardy</a>
035: * @version $Id: XMLReflect.java 482121 2006-12-04 10:00:39Z dvholten $
036: */
037: public class XMLReflect implements XMLReflectConstants {
038: /**
039: * An error happened while trying to construct a test. No constructor
040: * matching the list of arguments could be found
041: * {0} : The test's class name
042: * {1} : The list of argument types for which no constructor was found
043: */
044: public static final String NO_MATCHING_CONSTRUCTOR = "xml.XMLReflect.error.no.matching.constructor";
045:
046: /**
047: * Implementation helper: builds a generic object
048: */
049: public static Object buildObject(Element element) throws Exception {
050:
051: Element classDefiningElement = getClassDefiningElement(element);
052:
053: String className = classDefiningElement
054: .getAttribute(XR_CLASS_ATTRIBUTE);
055:
056: Class cl = Class.forName(className);
057: Object[] argsArray = null;
058: Class[] argsClasses = null;
059:
060: NodeList children = element.getChildNodes();
061: if (children != null && children.getLength() > 0) {
062: int n = children.getLength();
063: List args = new ArrayList();
064: for (int i = 0; i < n; i++) {
065: Node child = children.item(i);
066: if (child.getNodeType() == Node.ELEMENT_NODE) {
067: Element childElement = (Element) child;
068: String tagName = childElement.getTagName().intern();
069: if (tagName == XR_ARG_TAG) {
070: Object arg = buildArgument(childElement);
071: args.add(arg);
072: }
073: }
074: }
075:
076: if (args.size() > 0) {
077: argsArray = new Object[args.size()];
078: args.toArray(argsArray);
079:
080: argsClasses = new Class[args.size()];
081:
082: for (int i = 0; i < args.size(); i++) {
083: argsClasses[i] = argsArray[i].getClass();
084: }
085: }
086: }
087:
088: Constructor constructor = getDeclaredConstructor(cl,
089: argsClasses);
090:
091: if (constructor == null) {
092: String argsClassesStr = "null";
093: if (argsClasses != null) {
094: argsClassesStr = "";
095: for (int i = 0; i < argsClasses.length; i++) {
096: argsClassesStr += argsClasses[i].getName() + " / ";
097: }
098: }
099: throw new Exception(Messages.formatMessage(
100: NO_MATCHING_CONSTRUCTOR, new Object[] { className,
101: argsClassesStr }));
102: }
103: return configureObject(constructor.newInstance(argsArray),
104: element, classDefiningElement);
105: }
106:
107: /**
108: * Implementation helper: configures a generic object
109: */
110: public static Object configureObject(Object obj, Element element,
111: Element classDefiningElement) throws Exception {
112: // First, build a vector of elements from the child element
113: // to the classDefiningElement so that we can go from the
114: // top (classDefiningElement) to the child and apply properties
115: // as we iterate
116: List v = new ArrayList();
117: v.add(element);
118: while (element != classDefiningElement) {
119: element = (Element) element.getParentNode();
120: v.add(element);
121: }
122:
123: int ne = v.size();
124: for (int j = ne - 1; j >= 0; j--) {
125: element = (Element) v.get(j);
126: NodeList children = element.getChildNodes();
127: if (children != null && children.getLength() > 0) {
128: int n = children.getLength();
129: for (int i = 0; i < n; i++) {
130: Node child = children.item(i);
131: if (child.getNodeType() == Node.ELEMENT_NODE) {
132: Element childElement = (Element) child;
133: String tagName = childElement.getTagName()
134: .intern();
135: if (tagName == XR_PROPERTY_TAG) {
136: Object arg = buildArgument(childElement);
137: String propertyName = childElement
138: .getAttribute(XR_NAME_ATTRIBUTE);
139: setObjectProperty(obj, propertyName, arg);
140: }
141: }
142: }
143:
144: }
145: }
146:
147: return obj;
148: }
149:
150: /**
151: * Sets the property with given name on object to the input value
152: */
153: public static void setObjectProperty(Object obj,
154: String propertyName, Object propertyValue) throws Exception {
155: Class cl = obj.getClass();
156: Method m = null;
157: try {
158: m = cl.getMethod("set" + propertyName,
159: new Class[] { propertyValue.getClass() });
160:
161: } catch (NoSuchMethodException e) {
162: //
163: // Check if the type was one of the primitive types, Double,
164: // Float, Boolean or Integer
165: //
166: Class propertyClass = propertyValue.getClass();
167: try {
168: if (propertyClass == java.lang.Double.class) {
169: m = cl.getMethod("set" + propertyName,
170: new Class[] { java.lang.Double.TYPE });
171: } else if (propertyClass == java.lang.Float.class) {
172: m = cl.getMethod("set" + propertyName,
173: new Class[] { java.lang.Float.TYPE });
174: } else if (propertyClass == java.lang.Integer.class) {
175: m = cl.getMethod("set" + propertyName,
176: new Class[] { java.lang.Integer.TYPE });
177: } else if (propertyClass == java.lang.Boolean.class) {
178: m = cl.getMethod("set" + propertyName,
179: new Class[] { java.lang.Boolean.TYPE });
180: } else {
181: System.err
182: .println("Could not find a set method for property : "
183: + propertyName
184: + " with value "
185: + propertyValue
186: + " and class "
187: + propertyValue.getClass()
188: .getName());
189: throw e;
190: }
191: } catch (NoSuchMethodException nsme) {
192: throw nsme;
193: }
194: }
195: if (m != null) {
196: m.invoke(obj, new Object[] { propertyValue });
197: }
198: }
199:
200: /**
201: * Returns a constructor that has can be used for the input class
202: * types.
203: */
204: public static Constructor getDeclaredConstructor(Class cl,
205: Class[] argClasses) {
206: Constructor[] cs = cl.getDeclaredConstructors();
207: for (int i = 0; i < cs.length; i++) {
208: Class[] reqArgClasses = cs[i].getParameterTypes();
209: if (reqArgClasses != null && reqArgClasses.length > 0) {
210: if (reqArgClasses.length == argClasses.length) {
211: int j = 0;
212: for (; j < argClasses.length; j++) {
213: if (!reqArgClasses[j]
214: .isAssignableFrom(argClasses[j])) {
215: break;
216: }
217: }
218: if (j == argClasses.length) {
219: return cs[i];
220: }
221: }
222: } else {
223: if (argClasses == null || argClasses.length == 0) {
224: return cs[i];
225: }
226: }
227: }
228:
229: return null;
230: }
231:
232: /**
233: * Limitation: Arguments *must* have a String based
234: * constructor. Or be an object that takes a set of string
235: * based arguments.
236: */
237: public static Object buildArgument(Element element)
238: throws Exception {
239: if (!element.hasChildNodes()) {
240: Element classDefiningElement = getClassDefiningElement(element);
241:
242: String classAttr = classDefiningElement
243: .getAttribute(XR_CLASS_ATTRIBUTE);
244:
245: // String based argument
246: Class cl = Class.forName(classAttr);
247:
248: if (element.hasAttribute(XR_VALUE_ATTRIBUTE)) {
249: String value = element.getAttribute(XR_VALUE_ATTRIBUTE);
250:
251: Constructor constructor = cl
252: .getDeclaredConstructor(new Class[] { String.class });
253:
254: return constructor.newInstance(new Object[] { value });
255: } else {
256: // Default constructor
257: return cl.newInstance();
258: }
259: } else {
260: return buildObject(element);
261: }
262: }
263:
264: /**
265: * Gets the defining class element
266: */
267: public static Element getClassDefiningElement(Element element) {
268: if (element != null) {
269: String classAttr = element.getAttribute(XR_CLASS_ATTRIBUTE);
270:
271: if (classAttr == null || "".equals(classAttr)) {
272: Node parent = element.getParentNode();
273: if (parent != null
274: && parent.getNodeType() == Node.ELEMENT_NODE) {
275: return getClassDefiningElement((Element) parent);
276: } else {
277: return null;
278: }
279: }
280:
281: return element;
282:
283: }
284:
285: return null;
286: }
287: }
|