001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021 022package org.biojava.utils.xml; 023 024import java.beans.BeanInfo; 025import java.beans.IntrospectionException; 026import java.beans.Introspector; 027import java.beans.PropertyDescriptor; 028import java.util.ArrayList; 029import java.util.Collection; 030import java.util.HashMap; 031import java.util.HashSet; 032import java.util.Map; 033 034import org.biojava.bio.Annotation; 035import org.biojava.bio.SmallAnnotation; 036import org.biojava.utils.ChangeVetoException; 037import org.biojava.utils.SmallMap; 038import org.w3c.dom.Element; 039import org.w3c.dom.Node; 040 041/** 042 * Construct java beans from XML elements 043 * 044 * @author Thomas Down 045 */ 046 047public class XMLBeans { 048 public final static XMLBeans INSTANCE; 049 050 static { 051 INSTANCE = new XMLBeans(); 052 } 053 054 protected XMLBeans() { 055 } 056 057 public Object instantiateBean(Element bel) 058 throws AppException 059 { 060 return instantiateBean(bel, ClassLoader.getSystemClassLoader(), new HashMap()); 061 } 062 063 public Object instantiateBean(Element bel, ClassLoader cloader, Map beanRefs) 064 throws AppException 065 { 066 String cl = bel.getAttribute("jclass"); 067 if (cl == null) 068 throw new AppException("No jclass attribute"); 069 070 Object bean = null; 071 072 try { 073 Class clazz = cloader.loadClass(cl); 074 bean = clazz.newInstance(); 075 configureBean(bean, bel, beanRefs); 076 if (bean instanceof Initializable) 077 ((Initializable) bean).init(); // FIXME 078 } catch (ClassNotFoundException ex) { 079 throw new AppException("Couldn't load bean class " + cl); 080 } catch (ClassCastException ex) { 081 throw new AppException("Does not implement AppBean: " + cl); 082 } catch (InstantiationException ex) { 083 throw new AppException("Couldn't intantiate bean " + cl); 084 } catch (IllegalAccessException ex) { 085 throw new AppException("Couldn't access constructor for bean " + cl); 086 } 087 088 return bean; 089 } 090 091 private void configureBean(Object bean, Element el, Map refs) 092 throws AppException 093 { 094 Class clazz = bean.getClass(); 095 096 Node child = el.getFirstChild(); 097 while (child != null) { 098 if (child instanceof Element) { 099 Element echild = (Element) child; 100 String tag = echild.getTagName(); 101 String name = echild.getAttribute("name"); 102 Object valueObject = null; 103 Class valueType = null; 104 105 if (tag.equals("string")) { 106 valueObject = echild.getAttribute("value"); 107 valueType = valueObject.getClass(); 108 } else if (tag.equals("bean") || tag.equals("child")) { 109 // child supported for backwards compatibility. 110 111 String ref = echild.getAttribute("ref"); 112 Object targ = null; 113 if (! ref.equals("")) { 114 targ = refs.get(ref); 115 if(targ == null) { 116 throw new NullPointerException( 117 "Can't find target for: " + ref 118 ); 119 } 120 } else { 121 targ = instantiateBean(echild); 122 } 123 124 valueObject = targ; 125 valueType = targ.getClass(); 126 } else if (tag.equals("int")) { 127 String value = echild.getAttribute("value"); 128 try { 129 int val = Integer.parseInt(value); 130 valueObject = new Integer(val); 131 valueType = Integer.TYPE; 132 } catch (NumberFormatException ex) { 133 throw new AppException("Invalid int: " + value); 134 } 135 } else if (tag.equals("double")) { 136 String value = echild.getAttribute("value"); 137 try { 138 double val = Double.parseDouble(value); 139 valueObject = new Double(val); 140 valueType = Double.TYPE; 141 } catch (NumberFormatException ex) { 142 throw new AppException("Invalid double: " + value); 143 } 144 } else if (tag.equals("boolean")) { 145 String value = echild.getAttribute("value"); 146 valueObject = new Boolean(value); 147 valueType = Boolean.TYPE; 148 } else if (tag.equals("set")) { 149 valueObject = new HashSet(); 150 configureBean(valueObject, echild, refs); 151 valueType = valueObject.getClass(); 152 } else if (tag.equals("list")) { 153 valueObject = new ArrayList(); 154 configureBean(valueObject, echild, refs); 155 valueType = valueObject.getClass(); 156 } else if (tag.equals("map")) { 157 valueObject = new SmallMap(); 158 configureBean(valueObject, echild, refs); 159 valueType = valueObject.getClass(); 160 } else if (tag.equals("annotation")) { 161 valueObject = new SmallAnnotation(); 162 configureBean(valueObject, echild, refs); 163 valueType = valueObject.getClass(); 164 } else { 165 throw new AppException("Unknown element `" + tag + "' in XML-bean"); 166 } 167 168 169 if (name != null && name.length() > 0) { 170 setProp(clazz, bean, name, valueObject, valueType); 171 } else { 172 if (bean instanceof Collection) { 173 ((Collection) bean).add(valueObject); 174 } else { 175 throw new AppException("Anonymous beans are only allowed as children of Collections"); 176 } 177 } 178 179 } 180 child = child.getNextSibling(); 181 } 182 } 183 184 private void setProp(Class clazz, Object bean, String prop, Object value, Class ourType) 185 throws AppException 186 { 187 BeanInfo bi = null; 188 189 try { 190 bi = Introspector.getBeanInfo(clazz); 191 } catch (IntrospectionException ex) { 192 throw new AppException("Couldn't introspect class " + bean.getClass().getName()); 193 } 194 PropertyDescriptor[] descs = bi.getPropertyDescriptors(); 195 for (int i = 0; i < descs.length; ++i) { 196 if (descs[i].getName().equals(prop)) { 197 PropertyDescriptor desc = descs[i]; 198 if (! desc.getPropertyType().isAssignableFrom(ourType)) { 199 throw new AppException("Property " + prop + " is not assignable from " + ourType.getName()); 200 } 201 Object[] obj = new Object[1]; 202 obj[0] = value; 203 try { 204 desc.getWriteMethod().invoke(bean, obj); 205 } catch (Exception ex) { 206 throw new AppException("Invocation failed, could not invoke " + prop + " " + value); 207 } 208 return; 209 } 210 } 211 if (bean instanceof Map) { 212 ((Map) bean).put(prop, value); 213 } else if (bean instanceof Annotation) { 214 try { 215 ((Annotation) bean).setProperty(prop, value); 216 } catch (ChangeVetoException cve) { 217 throw new AppException("Unexpected veto updating Annotation"); 218 } 219 } else { 220 throw new AppException("Couldn't find property " + prop + " in class " + clazz.getName()); 221 } 222 } 223} 224