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}