001package jmri.configurexml; 002 003import java.beans.BeanInfo; 004import java.beans.Introspector; 005import java.beans.PropertyDescriptor; 006import java.lang.reflect.Constructor; 007import java.lang.reflect.Method; 008import java.util.List; 009import org.jdom2.Attribute; 010import org.jdom2.Element; 011import org.slf4j.Logger; 012import org.slf4j.LoggerFactory; 013 014/** 015 * Provides services for storing Java Beans to XML using reflection. 016 * 017 * @author Bob Jacobsen Copyright: Copyright (c) 2009 018 * @since 2.3.1 019 */ 020public class DefaultJavaBeanConfigXML extends jmri.configurexml.AbstractXmlAdapter { 021 022 public DefaultJavaBeanConfigXML() { 023 } 024 025 @Override 026 public boolean load(Element shared, Element perNode) { 027 return true; 028 } 029 030 @Override 031 public void load(Element e, Object o) { 032 } 033 034 Object unpack(Element e) 035 throws ClassNotFoundException, NoSuchMethodException, InstantiationException, 036 java.beans.IntrospectionException, IllegalAccessException, 037 java.lang.reflect.InvocationTargetException 038 { 039 String classname = e.getAttributeValue("beanClass"); 040 041 Class<?> cl = Class.forName(classname); 042 Constructor<?> ctor = cl.getConstructor(new Class<?>[]{}); 043 044 Object o = ctor.newInstance(new Object[]{}); 045 046 // reflect through and add parameters 047 BeanInfo b = Introspector.getBeanInfo(o.getClass()); 048 PropertyDescriptor[] properties = b.getPropertyDescriptors(); 049 050 // add properties 051 List<Element> children = e.getChildren("property"); 052 for (int i = 0; i < children.size(); i++) { 053 // unpack XML 054 Element property = children.get(i); 055 Element eName = property.getChild("name"); 056 Element eValue = property.getChild("value"); 057 String name = eName.getText(); 058 String value = eValue.getText(); 059 String type = eName.getAttributeValue("type"); 060 061 // find matching method 062 for (int j = 0; j < properties.length; j++) { 063 if (properties[j].getName().equals(name)) { 064 // match, set this one by first finding method 065 Method m = properties[j].getWriteMethod(); 066 067 // sort by type 068 if (type.equals("class java.lang.String")) { 069 m.invoke(o, new Object[]{value}); 070 } else if (type.equals("int")) { 071 m.invoke(o, new Object[]{Integer.valueOf(value)}); 072 } else { 073 log.error("Can't handle type: {}", type); 074 } 075 break; 076 } 077 } 078 } 079 080 return o; 081 } 082 083 @Override 084 public Element store(Object o) { 085 Element e = new Element("javabean"); 086 e.setAttribute("class", this.getClass().getName()); 087 e.setAttribute("beanClass", o.getClass().getName()); 088 089 try { 090 // reflect through and add parameters 091 BeanInfo b = Introspector.getBeanInfo(o.getClass()); 092 PropertyDescriptor[] properties = b.getPropertyDescriptors(); 093 094 for (int i = 0; i < properties.length; i++) { 095 if (properties[i].getName().equals("class")) { 096 // we skip this one 097 continue; 098 } 099 if (properties[i].getPropertyType() == null) { 100 log.warn("skipping property with null type: {}", properties[i].getName()); 101 continue; 102 } 103 Element p = new Element("property"); 104 Element n = new Element("name"); 105 n.addContent(properties[i].getName()); 106 n.setAttribute("type", properties[i].getPropertyType().toString()); 107 p.addContent(n); 108 Element v = new Element("value"); 109 if (properties[i].getReadMethod() != null) { 110 Object value = properties[i].getReadMethod().invoke(o, (Object[]) null); 111 if (value != null) { 112 v.addContent(value.toString()); 113 } 114 } 115 p.addContent(v); 116 e.addContent(p); 117 } 118 } catch (java.beans.IntrospectionException ex) { 119 log.error("Partial store due to IntrospectionException", ex); 120 } catch (java.lang.reflect.InvocationTargetException ex) { 121 log.error("Partial store due to InvocationTargetException", ex); 122 } catch (IllegalAccessException ex) { 123 log.error("Partial store due to IllegalAccessException", ex); 124 } 125 126 return e; 127 } 128 129 /** 130 * Get an attribute string value from an Element defining a NamedBean 131 * 132 * @param elem The existing Element 133 * @param name name of desired Attribute 134 * @return the attribute string or null if name is not an attribute of elem 135 */ 136 String getAttributeString(Element elem, String name) { 137 Attribute a = elem.getAttribute(name); 138 if (a != null) { 139 return a.getValue(); 140 } else { 141 return null; 142 } 143 } 144 145 /** 146 * Get an attribute boolean value from an Element defining a NamedBean 147 * 148 * @param elem The existing Element 149 * @param name Name of desired Attribute 150 * @param def Default value for attribute 151 * @return value of name or def if name is not an attribute of elem 152 */ 153 boolean getAttributeBool(Element elem, String name, boolean def) { 154 String v = getAttributeString(elem, name); 155 if (v == null) { 156 return def; 157 } else if (def) { 158 return !v.equals("false"); 159 } else { 160 return v.equals("true"); 161 } 162 } 163 164 private final static Logger log = LoggerFactory.getLogger(DefaultJavaBeanConfigXML.class); 165}