package com.javaworld.JavaBeans.XMLBeans; import java.beans.*; import java.io.*; import org.w3c.dom.*; //import com.ibm.xml.parser.*; import java.lang.reflect.*; import java.util.*; import org.xml.sax.helpers.ParserFactory; import com.ibm.xml.parsers.*; import org.w3c.dom.Document; import org.xml.sax.*; import com.javaworld.JavaBeans.XMLBeans.*; public class XMLBeanReader { /** Default constructor */ public XMLBeanReader() { } /** * If pd is null, return the object's setAsDOM() method, or null if there isn't one. * Otherwise, if the pd is an XMLPropertyDescriptor, return its DOMWriteMethod. * Otherwise, if there's a getPROPERTYNAMEAsDOM() method in the object, * return that method. If all of these possibilities fail, return null. * @return java.lang.reflect.Method * @param pd java.beans.PropertyDescriptor */ protected static Method getDOMSetter(Object o, PropertyDescriptor pd) { // If no property descriptor, it's an object. So, look for the // setAsDOM() method. if (pd == null) { return getDOMSetterMethod(o); } // Does the PropertyDescriptor know how to set as DOM? if (pd instanceof XMLPropertyDescriptor) { XMLPropertyDescriptor xpd = (XMLPropertyDescriptor)pd; return xpd.getDOMWriteMethod(); } // Does the bean to which this property belongs have a // method called getAsDOM(), which returns a DocumentFragment? Class objectType = o.getClass(); String propName = pd.getName(); String getterName = "set" + Util.capitalize(propName) + "AsDOM"; Method mResult = null; try { mResult = objectType.getMethod(getterName, new Class[] { org.w3c.dom.Element.class } ); if (mResult.getReturnType() == Void.TYPE) { return mResult; } } catch (Exception ex) { ; // It either exists or it doesn't } // Couldn't find anything. return null; } /* * Given a JavaBean, return a method that will initialize the * bean from a given DOM tree. That method will be * void setAsDOM(Node n) * @return java.lang.reflect.Method * @param bean java.lang.Object */ public static Method getDOMSetterMethod(Object bean) { Method mResult = null; Class classOfBean = bean.getClass(); // See if the bean has a custom name for its DOM setter String nameOfSetter = "setAsDOM"; try { Method mNameGetter = classOfBean.getMethod("getDOMSetterName", new Class[] {}); nameOfSetter = (String) (mNameGetter.invoke(bean, new Object[] {})); } catch (Exception ex) { ; // Ignore exceptions -- this either works or it doesn't } Class[] classParams = {org.w3c.dom.Node.class}; try { mResult = classOfBean.getMethod(nameOfSetter, classParams); } catch (NoSuchMethodException ex) { ; // Ignore } return mResult; } /** * Return an object of type objectType, initialized from the contents of element. * Return null if the objectType is not primitive, or if the element isn't just text. * @return java.lang.Object * @param objectType java.lang.Class * @param element org.w3c.dom.Element */ protected static Object getPrimitive(Class objectType, Element element) { // If it's not primitive, we have nothing to do. if (!objectType.isPrimitive()) { return null; } // Find first nonempty text node. Return false if there's anything here // but comments or Text nodes. int i = 0; String stringValue = null; NodeList nl = element.getChildNodes(); for (i = 0; i < nl.getLength(); i++) { Node n = nl.item(i); // If it's a comment, skip if (n instanceof Comment) { continue; } // If it's Text, but contains only white space, skip if (n instanceof Text) { String text = ((Text) n).getData(); if (text.trim().equals("")) { continue; } else { stringValue = text; break; } } // If it's anything but Text or a comment, there's a problem, because // there's no sensible way to map a tree to a primitive type. So we // complain and return false. Pe("Tried to create a " + objectType.getName() + " with a non-string"); return null; } // If empty, we use empty string. Notice there's a problem here: how // do we represent property values that are null? (Exercise for the reader) if (stringValue == null) { stringValue = ""; } // Get the argument list for the setter Object setterArg = getPrimitiveSetterArg(objectType, stringValue); return setterArg; } /** * Return an array of one object, which can be used to call a property setter, * for a property that is of a primitive type. * @return java.lang.Object[] * @param propType java.lang.Class * @param sValue java.lang.String */ protected static Object getPrimitiveSetterArg(Class propType, String sValue) { Object setterArg = null; // All primitive types except for Character have a constructor // that uses "String" as an argument. We encode single characters as // a number, so we decode them as such. if (propType == char.class) { char c = (char) (Integer.decode(sValue).intValue()); setterArg = new Character(c); } else { // Select wrapper class Class wrapper; if (propType == boolean.class) wrapper = Boolean.class; else if (propType == byte.class) wrapper = Byte.class; else if (propType == int.class) wrapper = Integer.class; else if (propType == long.class) wrapper = Long.class; else if (propType == short.class) wrapper = Short.class; else if (propType == float.class) wrapper = Float.class; else if (propType == double.class) wrapper = Double.class; else { return null; } // Get the constructor for the type that takes a // String as an argument try { P("Getting ctor"); Class[] argTypes = {String.class}; Constructor ctor = wrapper.getConstructor(argTypes); P("ctor is " + isNull(ctor)); // Create an instance of the object the setter is expecting Object[] ctorArgs = {sValue}; setterArg = ctor.newInstance(ctorArgs); } catch (Exception exc) { ; // Ignore -- return null } } return setterArg; } /** Use the Introspector to return an array of property descriptors for * bean class c. If there aren't any, return an empty list. * @param class The class to analyze. * @return java.beans.PropertyDescriptor * @exception java.beans.IntrospectionException */ protected static PropertyDescriptor[] getPropertyDescriptors(Class c) { try { BeanInfo beaninfo = Introspector.getBeanInfo(c); return beaninfo.getPropertyDescriptors(); } catch (IntrospectionException ex) { Pe("Can't find property descriptors for " + c.getName()); } return new PropertyDescriptor[0]; } /** * If this node is an Element, return its tag name, UNLESS * the tag name of the element is "Property"; in which case, * return the value of the "NAME" attribute. * @return java.lang.String * @param n org.w3c.dom.Node */ protected static String getPropertyName(Node n) { if (n instanceof Element) { Element e = (Element) n; if (e.getTagName().equals("Property")) return e.getAttribute("NAME"); return e.getTagName(); } return null; } /** * Return a Class object indicating the type of the property. This is * a bit more complex than asking the PropertyDescriptor for it, because * the Element may indicate using another type; for example, a subtype of * the PropertyDescriptor type. This method enforces the rule that the * "declared type" in the element must be assignable to the type indicated * by the property descriptor, if pd is not null. * @return java.lang.Class * @param e org.w3c.dom.Element * @param pd java.beans.PropertyDescriptor */ protected static Class getPropertyType(Element element, PropertyDescriptor pd) { Class cResult = null; String className = null; // Is the Element of the form ? if (element.getTagName().equals("Property")) { // Does the Element have a subnode with tagname "JavaBean", which // carries a class attribute? int i; NodeList nl = element.getChildNodes(); for (i = 0; i < nl.getLength() && className == null; i++) { if (nl.item(i) instanceof Element) { Element e = (Element) (nl.item(i)); if (e.getTagName().equals("JavaBean")) { className = e.getAttribute("CLASS"); } } } } // If there's no JavaBean element, maybe this element has a CLASS attribute. // This is along the lines of // , where Histogram is the // property name, and CLASS gives the type of histogram (Histogram // may be abstract). if (className == null) { className = element.getAttribute("CLASS"); } // If there's still no class name, try to get the class from // the property descriptor if (className == null || className.equals("")) { if (pd != null) { cResult = pd.getPropertyType(); } else { className = element.getTagName(); } } // If there isn't a result class yet, try to load the class called ClassName if (cResult == null && className != null && !className.equals("")) { try { cResult = Class.forName(className); } catch (ClassNotFoundException ex) { Pe("Couldn't load class " + className); ex.printStackTrace(); } } // Check to be sure that property type and declared type are // assignment compatible, if possible if (cResult != null && pd != null) { Class pdType = pd.getPropertyType(); if (!pdType.isAssignableFrom(cResult)) { Pe("Declared class " + cResult.getName() + " is not " + "assignable to property type " + pdType.getName()); return null; } } // If we still don't have a result type, use whatever the PropertyDescriptor says if (cResult == null && pd != null) { cResult = pd.getPropertyType(); } return cResult; } /** * Return an argument list for a "write" accessor ("setter") for a JavaBean. * See comments for descriptions. * @return java.lang.Object[] - The argument list for the setter. * @param setter java.lang.Method - The setter method. * @param jb java.lang.Object - The JavaBean to receive the set operation * @param e org.w3c.dom.Element - The element containing the data. * @param pd java.beans.PropertyDescriptor - A descriptor describing the property. */ protected static Object[] getSetterArgs(Method setter, Object jb, Element e, PropertyDescriptor pd, String sValue, Element eJavaBean) throws IntrospectionException, InvalidPropertyException { String sPropname = pd.getName(); Object[] setterArgs = null; Class propType = pd.getPropertyType(); // The easiest solution is to use the property's property editor set the // value using setText(), and then get the value as an object using // getValue(). This is especially cool because it works with custom // property editors. Possible problems here: property editor can't // be instantiated for some reason, the property can't be // represented as text (setText() simply throws an exception in this // case), etc. try { PropertyEditor ped = null; Class classPed = pd.getPropertyEditorClass(); // Either get the property editor from the descriptor, or // ask the PropertyEditorManager for a default one. if (classPed != null) { ped = (PropertyEditor) (classPed.newInstance()); } else { ped = PropertyEditorManager.findEditor(propType); } if (ped != null) { ped.setAsText(sValue); setterArgs = new Object[] {ped.getValue()}; } } catch (Exception ex) { ; // If anything fails here, it didn't work } // Well, the property editor approach didn't work. Let's try // something else. if (setterArgs == null) { // Is this property of a primitive type? If so, convert the String // to that type as appropriate, and call "set" function. Simple. if (propType == null) { throw new IntrospectionException("Property " + pd.getName() + " has no type"); } P("Property type of " + sPropname + " is " + propType.toString()); // If the argument is primitive, create an instance of that primitive // type, and set it using its constructor from String. if (propType.isPrimitive()) { setterArgs = null; // FIXME getPrimitiveSetterArgs(sPropname, propType, sValue); } else { // Handle non-primitive types. // 1. If it's a JavaBean, instantiate the JavaBean it // represents, use the new Bean to set the property. // 2. If the setter() method for the property takes // a single string as its argument, call that setter // method. // 3. If we can construct a sValue of the type corresponding // to the property's type, do so, and then call the // property's setter method. // Otherwise, we just can't set this property. // So, set the property based on one of these strategies: // [1] Is this property's type represented as a JavaBean // in the XML document? The "CLASS" attribute of any // JavaBean that appears here must be assignable to the propType // in order for this to make sense. Note that the "CLASS" // of the bean in the XML tree may be either of the property // type OR a subclass of that property type. if (eJavaBean != null) { setterArgs = getSetterArgsAsJavaBean(sPropname, propType, eJavaBean); } // [2] Does the "setter" function take a single string // as its argument? If so, use "sValue" to set it. if (setterArgs == null) { Class[] setterParameterTypes = setter.getParameterTypes(); P("Setter has " + setterParameterTypes.length + " args"); if (setterParameterTypes.length == 1 && setterParameterTypes[0] == java.lang.String.class) { setterArgs = new Object[] {sValue}; } } // [3] Can we create one of these by passing a String to // a ctor? if (setterArgs == null) { Constructor ctor = null; try { ctor = propType.getConstructor(new Class[] {String.class}); } catch (Exception exc) { // Ignore exceptions } // If we got a constructor, use it to create the argument // to the setter, then call the setter if (ctor != null) { try { Object arg = ctor.newInstance(new Object[] {sValue}); setterArgs = new Object[] {arg}; } catch (Exception exc) { throw new InvalidPropertyException("Exception while trying to create " + "argument for " + sPropname + " setter:\n" + exc.toString()); } } else { // We can't construct the object with a string throw new InvalidPropertyException("Can't create " + propType.getName() + " for property '" + sPropname + "'"); } } } } return setterArgs; } /** * Load setter args from an Element, and return as an object to be sent to a setter. * @return java.lang.Object[] * @param sPropname java.lang.String * @param eJavaBean org.w3c.dom.Element * @exception com.javaworld.JavaBeans.XMLBeans.InvalidPropertyException The exception description. */ protected static Object[] getSetterArgsAsJavaBean(String sPropname, Class propType, Element eJavaBean) throws com.javaworld.JavaBeans.XMLBeans.InvalidPropertyException { Object[] setterArgs = null; String jbClassName = JBClassName(eJavaBean); P("jbClassName = " + jbClassName); // Check to see if it's possible to assign the JavaBean // class in the document to the class of the property // If so, instantiate the JavaBean from the document node // return as an argument list. if (jbClassName != null) { P("loading JB"); try { Class docBeanClass = Class.forName(jbClassName); if (propType.isAssignableFrom(docBeanClass)) { Object argBean = instantiateBean(eJavaBean); // Invoke the setter with that argument setterArgs = new Object[] {argBean}; } } catch (Exception exc) { throw new InvalidPropertyException("Exception occurred while trying " + "to instantiate JavaBean '" + jbClassName + "':\n" + exc.toString()); } } return setterArgs; } /** Instantiate the JavaBean, and set all of its properties * The element must be of type "JavaBean" * @param eJavaBean The DOM Element object representing the JavaBean. * It is the root of a DOM document tree that contains the information * used to instantiate and initalize the JavaBean. The element's * tag must be JavaBean. * @return java.lang.Object * @exception IOException * @exception ClassNotFoundException * @exception IntrospectionException */ protected static Object instantiateBean(Element eJavaBean) throws IOException, ClassNotFoundException, IntrospectionException { // Load the value from the node Class classOfBean = getPropertyType(eJavaBean, null); Object jb = loadValue(classOfBean, eJavaBean, null); // Return the newly-instantiated JavaBean. return jb; } /** Convenience function that prints "null" or "not null" * for an object */ public static String isNull(Object o) { // Added a comment return ((o == null) ? "null" : "not null"); } /** Given an XML document element whose type is "JavaBean", * return the value of its "CLASS" attribute. Return null * if anything goes wrong * @param eJavaBean The Element object containing a CLASS attribute * @return The value of the Element's CLASS attribute. */ protected static String JBClassName(Element eJavaBean) { /// Get bean class name and create bean based on name Attr jbClassAttr = eJavaBean.getAttributeNode("CLASS"); if (jbClassAttr == null) return null; String jbClassName = jbClassAttr.getValue(); return jbClassName; } /** * Use a string to set the value of "object". "object" must either have a property * editor or be settable/creatable as a string. * @return boolean * @param object java.lang.Object * @param value java.lang.String */ protected final static Object loadValue(Class classOfValue, String value, PropertyDescriptor pd) { // Begin by getting the setter method Method setter = pd.getWriteMethod(); // The easiest way to handle this conversion is to use the // property editor for the type. This is especially convenient // because it handles all primitive types. Class peClass = pd.getPropertyEditorClass(); PropertyEditor pe = null; // Create a property editor for this type, set it up to edit the // object we're loading, then use setAsText() to set the value. // This actually makes invoking the setter unnecessary. if (peClass != null) { try { pe = (PropertyEditor) (peClass.newInstance()); } catch (Exception ex) { ; // Ignore if we can't find it } } // Look for default if we haven't yet found one if (pe == null) { pe = PropertyEditorManager.findEditor(classOfValue); } if (pe != null) { try { pe.setAsText(value); return pe.getValue(); } catch (Exception ex) { Pe("loadValue(): error setting property " + pd.getName()); } } // Nope, couldn't do it. return null; } /** * Load a value from an element node. The resulting value will either be a * JavaBean or a primitive wrapper object; in either case, the result is almost * always passed to a setter method. If the PropertyDescriptor is not null, * then this object is a property of another object, and the descriptor describes it. * @return boolean * @param bean java.lang.Object * @param node org.w3c.dom.Node */ public static Object loadValue(Class objectType, Element element, PropertyDescriptor pd) { Object value = null; Pe("Loading element " + element.getTagName() + " for object " + objectType.getName()); // If this is a primitive property, we want to return an object // of the appropriate type for the property descriptor's setter method if (pd != null) { if (objectType.isPrimitive()) { value = getPrimitive(objectType, element); return value; } } // Create an instance of this bean. This will be our return value. try { value = Beans.instantiate(null, objectType.getName()); } catch (Exception ex) { return null; } // First, find out if this object knows how to read itself // from a DOM tree. If it does, we simply defer to the object // itself, and we have no further work to do. Method domSetter = getDOMSetter(value, null); if (domSetter != null) { try { domSetter.invoke(value, new Object[] {element}); } catch (Exception ex) { Pe("loadValue:"); Pe(ex.getClass().getName()); ex.printStackTrace(); } return value; } // Get the first child of the element that is either nonwhite text (in // which case we try to initialize the value from that); or a noncomment // nonterminal node, in which case the value must be a bean. Node n = Util.getFirstInterestingChild(element); // If the syntax is ..., // then we want to be looking under the node for properties, // not under the node. Element topElement = element; if (n instanceof Element && ((Element)n).getTagName().equals("JavaBean")) { topElement = (Element)n; } // If n is null, initialize value with a blank string. // If n is Text, initialize value with the value of the text. String scalarValue = null; if (n == null) { scalarValue = ""; } else { if (n instanceof Text) { scalarValue = ((Text) n).getData(); } } // If scalarValue is not null, then we want to set the value from // a string. This makes it easy for objects to simply serialize // themselves to flat strings, and then deserialize themselves // back from those strings. Essentially, the PropertyEditor works // as a tiny parser for the string value. if (scalarValue != null && pd != null) { // Use the property editor to initialize the value. if ((value = loadValue(objectType, scalarValue, pd)) != null) { return value; } Pe("loadValue() failed for property " + pd.getName() + ", value = '" + scalarValue + "'"); } // Since the object didn't know how to set itself from a DOM, // and the DOM that represents it contains multiple nodes, we're // going to have to enumerate all of the properties. Assume that // each subnode is a property, creating and initializing a value for // each one, and then using the property setter to set that value. // If a node exists in my subnodes, that's the list of // my properties, instead of all of my subnodes. Node propertyList = topElement; NodeList nl = topElement.getChildNodes(); int i; for (i = 0; i < nl.getLength(); i++) { if (nl.item(i) instanceof Element) { Element e = (Element) (nl.item(i)); if (e.getTagName().equals("Properties")) { propertyList = e; break; } } } // Introspect object to get properties, then create a hash table // with property names as keys and descriptors as contents // Use the introspector to get the property descriptors // for this bean. PropertyDescriptor[] pds = getPropertyDescriptors(value.getClass()); // Create a hash table of property names and property descriptors Hashtable htpd = new Hashtable(); for (i = 0; i < pds.length; i++) { htpd.put(pds[i].getName().toLowerCase(), pds[i]); } // Now iterate through all of the properties in the propertyList, // loading each one recursively, and passing the returned value // to the setter method. nl = propertyList.getChildNodes(); for (i = 0; i < nl.getLength(); i++) { // Get the name of the property, ignoring rubbish String propertyName = getPropertyName(nl.item(i)); if (propertyName == null) continue; p("Property name = '" + propertyName + "'"); // Must be an element, since it wouldn't have a propertyName if it didn't Element e = (Element) (nl.item(i)); // Get the property descriptor for the property, ignoring // if there's no descriptor for it PropertyDescriptor thispd = (PropertyDescriptor) (htpd.get(propertyName.toLowerCase())); if (thispd == null) { Pe("Couldn't get PropertyDescriptor for property " + propertyName); continue; } // See if either the property descriptor or the bean knows how // to set the value as a DOM tree Method mDOMSetter = getDOMSetter(value, thispd); if (mDOMSetter != null) { P(" DOM Setter = " + mDOMSetter.getName()); try { mDOMSetter.invoke(value, new Object[] {e}); continue; } catch (Exception ee) { Pe("Exception occurred while setting " + propertyName + " as DOM: " + ee.getMessage()); ee.printStackTrace(); return null; } } else { p(", no DOM setter"); } // Call the setter directly. To do this, we have to get the setter method for // the property, create an instance of its property type, and // try to recursively loadValue() a value for it. // That should handle both properties that are JavaBeans // and properties that can somehow be constructed from a string. Method mSetter = thispd.getWriteMethod(); if (mSetter != null) { P(", Setter " + mSetter.getName()); try { // Get the class of the property Class cValue = getPropertyType(e, thispd); Object setterArg = null; // The setter argument is the object referred to by the // current element e. Calling loadValue() recursively here // returns the argument for the setter. if ((setterArg = loadValue(cValue, e, thispd)) != null) { mSetter.invoke(value, new Object[] {setterArg}); } else { Pe("Couldn't set " + cValue.getName() + " " + thispd.getName()); } } catch (Exception ex) { Pe("Couldn't create or set " + thispd.getPropertyType().getName() + " for property " + pd.getName()); ex.printStackTrace(); } } else { P(", Setter null"); } } // Return the value we've just created. return value; } /** Reads XML from a file, creates the corresponding JavaBean, * and then prints the bean by calling its "print()" method (if * it has one.) */ public static void main(String args[]) { try { Object b = readXMLBean(args[0]); try { Method printer = b.getClass().getMethod("print", null); P("--- Read a JavaBean of type " + b.getClass().getName()); P("==================================================================="); printer.invoke(b, null); } catch (Exception ee) { P("Unable to print JavaBean"); } } catch (Exception ee) { P("Threw exception " + ee.getClass().getName()); P("Couldn't read JavaBean from file " + args[0]); ee.printStackTrace(); } } /** Prints a string to stdout. Convenience function to save typing. */ public static void p(String s) { System.out.print(s); } /** Prints a string and newline to stdout. Convenience function to save typing. */ public static void P(String s) { System.out.println(s); } /** Prints a string to stderr. Convenience function to save typing. */ public static void pe(String s) { System.err.print(s); } /** Prints a string and newline to stderr. Convenience function to save typing. */ public static void Pe(String s) { System.err.println(s); } /** Read a Bean's state from an XML file * @param f The file from which to read the JavaBean's state. * @return Object The newly-created, initialized JavaBean. * @exception IOException * @exception ClassNotFoundException * @exception IntrospectionException */ public static Object readXMLBean(File f) throws IOException, ClassNotFoundException, IntrospectionException { FileReader fr = new FileReader(f); P("Created file reader"); Object o = readXMLBean(fr); return o; } /** Read a Bean's state from a character stream * @param r The Reader from which to read the JavaBean's state. * @return Object The newly-created, initialized JavaBean. * @exception IOException * @exception ClassNotFoundException * @exception IntrospectionException */ public static Object readXMLBean(Reader r) throws IOException, ClassNotFoundException, IntrospectionException { // Read document from XML file String sParserClassname = ""; Parser parser = null; // Create a SAX parser try { parser = ParserFactory.makeParser("com.ibm.xml.parsers.NonValidatingDOMParser"); } catch (Exception exc) { System.err.println("Exception"); exc.printStackTrace(); } P("Created parser"); // Run the SAX parser against the input stream try { parser.parse(new org.xml.sax.InputSource(r)); } catch (SAXException sx) { System.err.println("Exception: " + sx.toString()); sx.printStackTrace(); } catch (Exception ex) { System.err.println("Threw " + ex.getClass().getName()); ex.printStackTrace(); } // Since we know the SAX parser is also a DOM parser, // we can ask it for its resulting document. Document d = ((NonValidatingDOMParser)parser).getDocument(); P("Got document " + isNull(d)); Element eJavaBean = d.getDocumentElement(); P("eJavaBean is " + isNull(eJavaBean)); Object o = instantiateBean(eJavaBean); return o; } /** Read a Bean's state from an XML file * @param s The name of the file from which to read the JavaBean's state. * @return Object The newly-created, initialized JavaBean. * @exception IOException * @exception ClassNotFoundException * @exception IntrospectionException */ public static Object readXMLBean(String s) throws IOException, ClassNotFoundException, IntrospectionException { return readXMLBean(new File(s)); } /** * Set the primitive property of object , indicated by , to the contents * of the first nonempty, noncomment subnode of * @return boolean * @param value java.lang.Object * @param node org.w3c.dom.Node * @param pd java.beans.PropertyDescriptor */ protected static boolean setPrimitive(Object value, Node node, PropertyDescriptor pd) { Class propertyType = pd.getPropertyType(); // If it's not primitive, we have nothing to do. if (!propertyType.isPrimitive()) { return false; } // Likewise if there's no setter Method mSetter = pd.getWriteMethod(); if (mSetter == null) { return false; } // Find first nonempty text node. Return false if there's anything here // but comments or Text nodes. int i = 0; String stringValue = null; NodeList nl = node.getChildNodes(); for (i = 0; i < nl.getLength(); i++) { Node n = nl.item(i); // If it's a comment, skip if (n instanceof Comment) { continue; } // If it's Text, but contains only white space, skip if (n instanceof Text) { String text = ((Text) n).getData(); if (text.trim().equals("")) { continue; } else { stringValue = text; break; } } // If it's anything but Text or a comment, there's a problem, because // there's no sensible way to map a tree to a primitive type. So we // complain and return false. Pe("Tried to set property " + value.getClass().getName() + "." + pd.getName() + " to non-string"); return false; } // If empty, we use empty string. Notice there's a problem here: how // do we represent property values that are null? (Exercise for the reader) if (stringValue == null) { stringValue = ""; } try { // Get the argument list for the setter Object setterArg = getPrimitiveSetterArg(propertyType, stringValue); // Call the setter with these arguments mSetter.invoke(value, new Object[] { setterArg } ); return true; } catch (Exception ex) { Pe("Calling property setter " + mSetter.getName() + " threw " + ex.getClass().getName()); ex.printStackTrace(); } // Failed if we got this far return false; } /** * Try to set a property using a custom method that knows how * to initialize a bean from a DOM tree. * @return boolean * @param jb java.lang.Object * @param e org.w3c.dom.Element * @param pd java.beans.PropertyDescriptor */ protected static boolean setPropertyAsDOM(Object jb, Element e, PropertyDescriptor pd) throws IllegalAccessException, InvocationTargetException { String sName = pd.getName(); // Let's define a "DOM property accessor" as a property accessor // that sets or returns a JavaBean property as a DOM Element, instead // of as its native type. The "DOM setter" will then have this signature: // void setter(Element e) // and the "DOM getter" will have this signature: // Element getter() // If a JavaBean specifies a DOM setter for a property, that method // is used to set the property. // // There are two ways to specify a DOM setter // 1. define a method // void setAsDOM(Element e) // 2. return a XMLPropertyDescriptor whose getDOMWriteMethod() // returns a Method object that has the same signature as // the method in [1]. // The first option is easy (naming pattern), the second option is // more flexible (since any method with the appropriate signature may // be used.) Of getDOMWriteMethod() returns null, that means that // there is no way to write that property. Method domSetter = null; // If pd is an XMLPropertyDescriptor, get the DOMSetter // method from pd, and call them method (assuming // the method reference returned isn't null). Otherwise, // check if a setPROPAsDom method exists if (pd instanceof XMLPropertyDescriptor) { XMLPropertyDescriptor xpd = (XMLPropertyDescriptor) pd; domSetter = xpd.getDOMWriteMethod(); } else { Class[] domSetterParams = {org.w3c.dom.Node.class}; String sDOMSetterName = "set" + Util.capitalize(sName) + "AsDOM"; try { domSetter = jb.getClass().getMethod(sDOMSetterName, domSetterParams); } catch (NoSuchMethodException ex) { domSetter = null; } } // If we found a DOM setter, invoke it, and we're done! if (domSetter != null) { Object[] domSetterArgs = { e }; domSetter.invoke(jb, domSetterArgs); return true; } return false; } }