package jadex.xml.bean; import jadex.commons.SReflect; import jadex.commons.SUtil; import jadex.xml.AccessInfo; import jadex.xml.AttributeInfo; import jadex.xml.BasicTypeConverter; import jadex.xml.IPostProcessor; import jadex.xml.IReturnValueCommand; import jadex.xml.IStringObjectConverter; import jadex.xml.ISubObjectConverter; import jadex.xml.ObjectInfo; import jadex.xml.SXML; import jadex.xml.SubobjectInfo; import jadex.xml.TypeInfo; import jadex.xml.TypeInfoPathManager; import jadex.xml.TypeInfoTypeManager; import jadex.xml.reader.IObjectReaderHandler; import jadex.xml.reader.LinkData; import jadex.xml.reader.ReadContext; import jadex.xml.reader.Reader; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.StringTokenizer; import javax.xml.namespace.QName; /** * Handler for reading XML into Java beans. */ // Todo: report warnings when method invocations fail? public class BeanObjectReaderHandler implements IObjectReaderHandler { //-------- constants -------- /** The null object. */ public static final Object NULL = new Object(); //-------- attributes -------- /** The type info path manager. */ protected TypeInfoPathManager tipmanager; /** The type info manager. */ // For special case that an object is created via the built-in // tag mechanism and there is a type info for that kind of created // object. Allows specifying generic type infos with interfaces. protected TypeInfoTypeManager titmanager; /** No type infos. */ protected Set no_typeinfos; /** The bean introspector. */ // protected IBeanIntrospector introspector = new BeanReflectionIntrospector(); protected IBeanIntrospector introspector = new BeanInfoIntrospector(); //-------- constructors -------- /** * Create a new handler. */ public BeanObjectReaderHandler(Set typeinfos) { this.tipmanager = new TypeInfoPathManager(typeinfos); this.titmanager = new TypeInfoTypeManager(typeinfos); } //-------- methods -------- /** * Get the most specific mapping info. * @param tag The tag. * @param fullpath The full path. * @return The most specific mapping info. */ public TypeInfo getTypeInfo(QName tag, QName[] fullpath, Map rawattributes) { return tipmanager.getTypeInfo(tag, fullpath, rawattributes); } /** * Get the most specific mapping info. * @param tag The tag. * @param fullpath The full path. * @return The most specific mapping info. */ public synchronized TypeInfo getTypeInfo(Object object, QName[] fullpath, ReadContext context) { Object type = getObjectType(object, context); if(no_typeinfos!=null && no_typeinfos.contains(type)) return null; TypeInfo ret = titmanager.getTypeInfo(type, fullpath); // Hack! due to HashMap.Entry is not visible as class if(ret==null) { if(type instanceof Class) { // Try if interface or supertype is registered List tocheck = new ArrayList(); tocheck.add(type); for(int i=0; i<tocheck.size() && ret==null; i++) { Class clazz = (Class)tocheck.get(i); // Set tis = titmanager.getTypeInfosByType(clazz); // ret = titmanager.findTypeInfo(tis, fullpath); ret = titmanager.getTypeInfo(clazz, fullpath); if(ret==null) { Class[] interfaces = clazz.getInterfaces(); for(int j=0; j<interfaces.length; j++) tocheck.add(interfaces[j]); clazz = clazz.getSuperclass(); if(clazz!=null) tocheck.add(clazz); } } // Special case array // Requires Object[].class being registered if(ret==null && ((Class)type).isArray()) { // ret = titmanager.findTypeInfo(titmanager.getTypeInfosByType(Object[].class), fullpath); ret = titmanager.getTypeInfo(Object[].class, fullpath); } // Add concrete class for same info if it is used if(ret!=null) { ObjectInfo cri =ret.getObjectInfo(); ObjectInfo cricpy = cri!=null? new ObjectInfo(type, cri.getPostProcessor()): new ObjectInfo(type); TypeInfo ti = new TypeInfo(ret.getXMLInfo(), cricpy, ret.getMappingInfo(), ret.getLinkInfo()); // TypeInfo ti = new TypeInfo(ret.getSupertype(), ret.getXMLPath(), // type, ret.getCommentInfo(), ret.getContentInfo(), // ret.getDeclaredAttributeInfos(), ret.getPostProcessor(), ret.getFilter(), // ret.getDeclaredSubobjectInfos()); titmanager.addTypeInfo(ti); } else { if(no_typeinfos==null) no_typeinfos = new HashSet(); no_typeinfos.add(type); } } } return ret; } /** * Create an object for the current tag. * @param type The object type to create. * @param root Flag, if object should be root object. * @param context The context. * @return The created object (or null for none). */ public Object createObject(Object type, boolean root, ReadContext context, Map rawattributes) throws Exception { Object ret = null; if(type instanceof QName) { QName tag = (QName)type; if(tag.equals(SXML.NULL)) { ret = NULL; } else { // System.out.println("here: "+typeinfo); String pck = tag.getNamespaceURI().substring(SXML.PROTOCOL_TYPEINFO.length()); String clazzname = pck+"."+tag.getLocalPart().replace("-", "$"); // System.out.println("Clazzname: "+clazzname); // Special case array int idx = clazzname.indexOf("__"); int[] lens = null; if(idx!=-1) { String strlens = clazzname.substring(idx+2); clazzname = clazzname.substring(0, idx); StringTokenizer stok = new StringTokenizer(strlens, "_"); lens = new int[stok.countTokens()]; for(int i=0; stok.hasMoreTokens(); i++) { lens[i] = Integer.parseInt(stok.nextToken()); } } Class clazz = SReflect.classForName0(clazzname, context.getClassLoader()); if(clazz!=null) { if(lens!=null) { ret = Array.newInstance(clazz, lens); } else if(!BasicTypeConverter.isBuiltInType(clazz)) { if(SReflect.isAnonymousInnerClass(clazz)) { // Create anonymous class object by supplying null values // System.out.println("Anonymous: "+clazz); Constructor c = clazz.getDeclaredConstructors()[0]; c.setAccessible(true); ret = c.newInstance(new Object[c.getParameterTypes().length]); } else { // Must have empty constructor. ret = clazz.newInstance(); } } else if(String.class.equals(clazz)) { ret = Reader.STRING_MARKER; } } } } else if(type instanceof TypeInfo) { Object ti = ((TypeInfo)type).getTypeInfo(); if(ti instanceof Class && ((Class)ti).isInterface()) { type = ((TypeInfo)type).getXMLTag(); } else { type = ti; } } if(type instanceof Class) { Class clazz = (Class)type; if(!BasicTypeConverter.isBuiltInType(clazz)) { // Must have empty constructor. ret = clazz.newInstance(); } } else if(type instanceof IBeanObjectCreator) { ret = ((IBeanObjectCreator)type).createObject(context, rawattributes); } return ret; } /** * Get the object type * @param object The object. * @return The object type. */ public Object getObjectType(Object object, ReadContext context) { return object.getClass(); } /** * Convert an object to another type of object. */ public Object convertContentObject(String value, QName tag, ReadContext context) throws Exception { Object ret = value; if(tag.getNamespaceURI().startsWith(SXML.PROTOCOL_TYPEINFO)) { String clazzname = tag.getNamespaceURI().substring(SXML.PROTOCOL_TYPEINFO.length())+"."+tag.getLocalPart(); Class clazz = SReflect.classForName0(clazzname, context.getClassLoader()); if(clazz!=null) { if(BasicTypeConverter.isBuiltInType(clazz)) { ret = BasicTypeConverter.getBasicStringConverter(clazz).convertString(value, context); } else { ret = null; context.getReporter().report("No converter known for: "+clazz, "content error", context, context.getParser().getLocation()); } } } return ret; } /** * Handle the attribute of an object. * @param object The object. * @param attrname The attribute name. * @param attrval The attribute value. * @param attrinfo The attribute info. * @param context The context. */ public void handleAttributeValue(Object object, QName xmlattrname, List attrpath, String attrval, Object attrinfo, ReadContext context) throws Exception { // Hack! Object converter = attrinfo instanceof AttributeInfo? ((AttributeInfo)attrinfo).getConverter(): null; String id = attrinfo instanceof AttributeInfo? ((AttributeInfo)attrinfo).getId(): null; Object accessinfo = attrinfo instanceof AttributeInfo? ((AttributeInfo)attrinfo).getAccessInfo(): attrinfo; if(attrval!=null) { setElementValue(accessinfo, xmlattrname, object, attrval, converter, id, context); } else if(accessinfo instanceof AccessInfo && ((AccessInfo)accessinfo).getDefaultValue()!=null) { setElementValue(accessinfo, xmlattrname, object, ((AccessInfo)accessinfo).getDefaultValue(), converter, id, context); } } /** * Link an object to its parent. * @param object The object. * @param parent The parent object. * @param linkinfo The link info. * @param tagname The current tagname (for name guessing). * @param context The context. */ public void linkObject(Object object, Object parent, Object linkinfo, QName[] pathname, ReadContext context) throws Exception { QName tag = pathname[pathname.length-1]; // Add object to its parent. boolean linked = false; if(linkinfo instanceof SubobjectInfo) { SubobjectInfo sinfo = (SubobjectInfo)linkinfo; setElementValue(sinfo.getAccessInfo(), tag, parent, object, sinfo.getConverter(), null, context); linked = true; } // Special case array else if(parent.getClass().isArray()) { int cnt = context.getArrayCount(parent); if(!NULL.equals(object)) Array.set(parent, cnt, object); linked = true; } else { List classes = new LinkedList(); classes.add(object.getClass()); String[] plunames = new String[pathname.length]; String[] sinnames = new String[pathname.length]; String[] fieldnames = new String[pathname.length]; for(int i=0; i<pathname.length; i++) { String name = pathname[i].getLocalPart().substring(0, 1).toUpperCase()+pathname[i].getLocalPart().substring(1); plunames[i] = name; sinnames[i] = SUtil.getSingular(name); fieldnames[i] = pathname[i].getLocalPart(); } // Try via fieldname for(int i=0; i<fieldnames.length && !linked; i++) { linked = setField(fieldnames[i], parent, object, null, context, null); } // Try name guessing via class/superclass/interface names of object to add while(!linked && !classes.isEmpty()) { Class clazz = (Class)classes.remove(0); for(int i=0; i<plunames.length && !linked; i++) { linked = internalLinkObjects(clazz, "set"+plunames[i], object, parent, context); if(!linked) { linked = internalLinkObjects(clazz, "add"+sinnames[i], object, parent, context); if(!linked && !sinnames[i].equals(plunames[i])) { linked = internalLinkObjects(clazz, "add"+plunames[i], object, parent, context); } } } // Try classname of object to add if(!linked && !BasicTypeConverter.isBuiltInType(clazz)) { String name = SReflect.getInnerClassName(clazz); linked = internalLinkObjects(clazz, "set"+name, object, parent, context); if(!linked) { linked = internalLinkObjects(clazz, "add"+name, object, parent, context); if(!linked) { String sinname = SUtil.getSingular(name); if(!name.equals(sinname)) linked = internalLinkObjects(clazz, "add"+sinname, object, parent, context); } } } if(!linked) { if(clazz.getSuperclass()!=null) classes.add(clazz.getSuperclass()); Class[] ifs = clazz.getInterfaces(); for(int i=0; i<ifs.length; i++) { classes.add(ifs[i]); } } } } if(!linked) { context.getReporter().report("Could not link: "+object+" "+parent, "link error", context, context.getParser().getLocation()); } } /** * Link an object to its parent. * @param object The object. * @param parent The parent object. * @param linkinfo The link info. * @param tagname The current tagname (for name guessing). * @param context The context. */ public void bulkLinkObjects(List childs, Object parent, Object linkinfo, QName[] pathname, ReadContext context) throws Exception { QName tag = pathname[pathname.length-1]; // Add object to its parent. boolean linked = false; if(linkinfo!=null) { // converter and id null?! setBulkAttributeValues(linkinfo, tag, parent, childs, null, null, context); linked = true; } // Special case array else if(parent.getClass().isArray()) { for(int i=0; i<childs.size(); i++) { int cnt = context.getArrayCount(parent); Object object = childs.get(i); if(!NULL.equals(object)) Array.set(parent, cnt, object); } linked = true; } // Try linking via field/method searching by name guessing. else { List classes = new LinkedList(); classes.add(childs.get(0).getClass()); String[] orignames = new String[pathname.length]; String[] plunames = new String[pathname.length]; String[] origfieldnames = new String[pathname.length]; String[] plufieldnames = new String[pathname.length]; for(int i=0; i<pathname.length; i++) { String origname = pathname[i].getLocalPart(); String pluname = SUtil.getPlural(pathname[i].getLocalPart()); plunames[i] = pluname.substring(0, 1).toUpperCase()+pluname.substring(1); orignames[i] = origname.substring(0, 1).toUpperCase()+origname.substring(1); plufieldnames[i] = pluname; origfieldnames[i] = origname; } // Try via fieldname for(int i=0; i<plufieldnames.length && !linked; i++) { linked = setBulkField(plufieldnames[i], parent, childs, null, context, null); if(!linked && !origfieldnames[i].equals(plufieldnames[i])) { linked = setBulkField(origfieldnames[i], parent, childs, null, context, null); } } // Try name guessing via class/superclass/interface names of object to add while(!linked && !classes.isEmpty()) { Class clazz = (Class)classes.remove(0); for(int i=0; i<plunames.length && !linked; i++) { linked = internalBulkLinkObjects(clazz, "set"+plunames[i], childs, parent, context); if(!linked && !orignames[i].equals(plunames[i])) { linked = internalBulkLinkObjects(clazz, "set"+orignames[i], childs, parent, context); } } // Try classname of object to add if(!linked && !BasicTypeConverter.isBuiltInType(clazz)) { String name = SReflect.getInnerClassName(clazz); linked = internalBulkLinkObjects(clazz, "set"+name, childs, parent, context); } if(!linked) { if(clazz.getSuperclass()!=null) classes.add(clazz.getSuperclass()); Class[] ifs = clazz.getInterfaces(); for(int i=0; i<ifs.length; i++) { classes.add(ifs[i]); } } } } if(!linked) { context.getReporter().report("Could not bulk link: "+childs+" "+parent, "link error", context, context.getParser().getLocation()); } } /** * Bulk link an object to its parent. * @param parent The parent object. * @param children The children objects (link datas). * @param context The context. * @param classloader The classloader. * @param root The root object. * / public void bulkLinkObjects(Object parent, List children, Object context, ClassLoader classloader, Object root) throws Exception { // System.out.println("bulk link for: "+parent+" "+children); for(int i=0; i<children.size(); i++) { LinkData linkdata = (LinkData)children.get(i); linkObject(linkdata.getChild(), parent, linkdata.getLinkinfo(), linkdata.getPathname(), context, classloader, root); } }*/ /** * Bulk link chilren to its parent. * @param parent The parent object. * @param children The children objects (link datas). * @param context The context. * @param classloader The classloader. * @param root The root object. */ public void bulkLinkObjects(Object parent, List children, ReadContext context) throws Exception { // System.out.println("bulk link for: "+parent+" "+children); // The default bulk strategy is as follows: // Linear scan the subpaths(tags) of the parent // As long as the path is the same remember as bulk // Whenever a new path/tag is used the last bulk is considered finished LinkData linkdata = (LinkData)children.get(0); List childs = new ArrayList(); childs.add(linkdata.getChild()); QName[] pathname = linkdata.getPathname(); int startidx = 0; for(int i=1; i<children.size(); i++) { LinkData ld = (LinkData)children.get(i); QName[] pn = ld.getPathname(); if(!Arrays.equals(pathname, pn)) { handleBulkLinking(childs, parent, context, pathname, children, startidx); pathname = pn; linkdata = ld; childs.clear(); startidx = i; } childs.add(ld.getChild()); } handleBulkLinking(childs, parent, context, pathname, children, startidx); } /** * Initiate the bulk link calls. */ protected void handleBulkLinking(List childs, Object parent, ReadContext context, QName[] pathname, List linkdatas, int startidx) throws Exception { if(childs.size()>1) { try { bulkLinkObjects(childs, parent, ((LinkData)linkdatas.get(startidx)).getLinkinfo(), pathname, context); } catch(Exception e) { context.getReporter().report("Warning. Bulk link initiated but not successful: "+childs+" "+parent+" "+e, "warning", context, context.getParser().getLocation()); for(int i=0; i<childs.size(); i++) { linkObject(childs.get(i), parent, ((LinkData)linkdatas.get(startidx+i)).getLinkinfo(), pathname, context); } } } else { linkObject(childs.get(0), parent, ((LinkData)linkdatas.get(startidx)).getLinkinfo(), pathname, context); } } //-------- helper methods -------- /** * Set an attribute value. * Similar to handleAttributValue but allows objects as attribute values (for linking). * @param attrinfo The attribute info. * @param xmlattrname The xml attribute name. * @param object The object. * @param val The attribute value. * @param root The root object. * @param classloader The classloader. */ protected void setElementValue(Object accessinfo, QName xmlname, Object object, Object val, Object converter, String id, ReadContext context) throws Exception { if(NULL.equals(val)) return; boolean set = false; // Write to a map. if(accessinfo instanceof AccessInfo && ((AccessInfo)accessinfo).getExtraInfo() instanceof BeanAccessInfo) { AccessInfo ai = (AccessInfo)accessinfo; BeanAccessInfo bai = (BeanAccessInfo)ai.getExtraInfo(); // Put value in map 1) fetch key 2) set value in map if(bai.getMapName()!=null) { // fetch the key value Object key = null; if(bai.getKeyHelp()!=null) { Object kh = bai.getKeyHelp(); Object targetobj = bai.isKeyFromParent()? object: val; if(kh instanceof Method) { try { key = ((Method)kh).invoke(targetobj, new Object[0]); } catch(InvocationTargetException e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking key getter method: "+e.getTargetException(), // "attribute error", context, context.getParser().getLocation()); } catch(Exception e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking key getter method: "+e, // "attribute error", context, context.getParser().getLocation()); } } else if(kh instanceof Field) { try { key = ((Field)kh).get(targetobj); } catch(Exception e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure getting key field: "+e, // "attribute error", context, context.getParser().getLocation()); } } else if(kh instanceof IReturnValueCommand) { key = ((IReturnValueCommand)kh).execute(targetobj); } else { context.getReporter().report("Unknown key help: "+kh, "attribute error", context, context.getParser().getLocation()); } } else { key = ai.getObjectIdentifier()!=null? ai.getObjectIdentifier(): xmlname; } // Set map value with predefined read method or field. if(bai.getStoreHelp()!=null) { Object sh = bai.getStoreHelp(); if(sh instanceof Method) { try { Method m = (Method)sh; Class[] ps = m.getParameterTypes(); Object arg = convertValue(val, ps[1], converter, context, id); m.invoke(object, new Object[]{key, arg}); set = true; } catch(InvocationTargetException e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking setter method: "+e.getTargetException(), // "attribute error", context, context.getParser().getLocation()); } catch(Exception e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking setter method: "+e, // "attribute error", context, context.getParser().getLocation()); } } else if(sh instanceof Field) { try { Field f = (Field)sh; Object map = f.get(object); // Hack?! create on demand, should be customizable. if(map==null) { map = new HashMap(); f.set(object, map); } Object arg = convertValue(val, null, converter, context, id); ((Map)map).put(key, arg); set = true; } catch(Exception e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure setting field: "+e, // "attribute error", context, context.getParser().getLocation()); } } else if(sh instanceof IReturnValueCommand) { Object arg = convertValue(val, null, converter, context, id); ((IReturnValueCommand)sh).execute(new Object[]{object, arg}); } else { context.getReporter().report("Unknown map store help: "+sh, "attribute error", context, context.getParser().getLocation()); } } // Set map value with guessing method name. else { String mapname = bai.getMapName().length()==0 || AccessInfo.THIS.equals(bai.getMapName())? "" : bai.getMapName().substring(0,1).toUpperCase()+bai.getMapName().substring(1); String[] prefixes = new String[]{"put", "set", "add"}; for(int i=0; i<prefixes.length && !set; i++) { Method[] ms = SReflect.getMethods(object.getClass(), prefixes[i]+mapname); for(int j=0; j<ms.length && !set; j++) { Class[] ps = ms[j].getParameterTypes(); if(ps.length==2) { Object arg = convertValue(val, ps[1], converter, context, id); try { ms[j].invoke(object, new Object[]{key, arg}); set = true; } catch(InvocationTargetException e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking setter method: "+e.getTargetException(), // "attribute error", context, context.getParser().getLocation()); } catch(Exception e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking setter method: "+e, // "attribute error", context, context.getParser().getLocation()); } } } } } } // Fetch value using predefined read method. else if(bai.getStoreHelp()!=null) { Object sh = bai.getStoreHelp(); if(sh instanceof Method) { Method m = (Method)sh; Class[] ps = m.getParameterTypes(); if(ps.length==1) { Object arg = convertValue(val, ps[0], converter, context, id); try { m.invoke(object, new Object[]{arg}); set = true; } catch(InvocationTargetException e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking setter method: "+e.getTargetException(), // "attribute error", context, context.getParser().getLocation()); } catch(Exception e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking setter method: "+e, // "attribute error", context, context.getParser().getLocation()); } } else { context.getReporter().report("Read method should have one parameter: "+bai+" "+m, "attribute error", context, context.getParser().getLocation()); } } else if(sh instanceof Field) { try { Field f = (Field)sh; Object arg = convertValue(val, f.getType(), converter, context, id); f.set(object, arg); set = true; } catch(Exception e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure setting field: "+e, // "attribute error", context, context.getParser().getLocation()); } } else if(sh instanceof IReturnValueCommand) { Object arg = convertValue(val, null, converter, context, id); ((IReturnValueCommand)sh).execute(new Object[]{object, arg}); } else { context.getReporter().report("Unknown store help: "+sh, "attribute error", context, context.getParser().getLocation()); } } } // Try using object identifier from access info if(!set && accessinfo instanceof AccessInfo) { AccessInfo ai = (AccessInfo)accessinfo; String fieldname = ai.getObjectIdentifier()!=null? ((String)ai.getObjectIdentifier()): xmlname.getLocalPart(); set = setField(fieldname, object, val, converter, context, id); if(!set) { String postfix = ai.getObjectIdentifier()!=null? ((String)ai.getObjectIdentifier()) .substring(0,1).toUpperCase()+((String)ai.getObjectIdentifier()).substring(1) : xmlname.getLocalPart().substring(0,1).toUpperCase()+xmlname.getLocalPart().substring(1); set = invokeSetMethod(new String[]{"set", "add"}, postfix, val, object, context, converter, id); if(!set) { String oldpostfix = postfix; postfix = SUtil.getSingular(postfix); if(!postfix.equals(oldpostfix)) { // First try add, as set might also be there and used for a non-multi attribute. set = invokeSetMethod(new String[]{"set", "add"}, postfix, val, object, context, converter, id); } } } } else if(!set) // attribute info is null or string { // Write as normal bean attribute. // Try to find bean class information Map props = introspector.getBeanProperties(object.getClass(), true); BeanProperty prop = (BeanProperty)props.get(accessinfo instanceof String? accessinfo: xmlname.getLocalPart()); if(prop!=null) { Object arg = convertValue(val, prop.getSetterType(), converter, context, id); try { if(prop.getSetter()!=null) { prop.getSetter().invoke(object, new Object[]{arg}); } else { if((prop.getField().getModifiers()&Field.PUBLIC)==0) prop.getField().setAccessible(true); prop.getField().set(object, arg); } set = true; } catch(InvocationTargetException e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking setter method: "+e.getTargetException(), // "attribute error", context, context.getParser().getLocation()); } catch(Exception e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure setting attribute: "+e, // "attribute error", context, context.getParser().getLocation()); } } if(!set) { String postfix = accessinfo instanceof String? ((String)accessinfo).substring(0,1).toUpperCase()+((String)accessinfo).substring(1) : xmlname.getLocalPart().substring(0,1).toUpperCase()+xmlname.getLocalPart().substring(1); set = invokeSetMethod(new String[]{"set", "add"}, postfix, val, object, context, null, null); if(!set) { String oldpostfix = postfix; postfix = SUtil.getSingular(postfix); if(!postfix.equals(oldpostfix)) { set = invokeSetMethod(new String[]{"set", "add"}, postfix, val, object, context, null, null); } } } } if(!set) { context.getReporter().report("Failure in setting attribute: "+xmlname+" on object: "+object+" (unknown attribute?)", "attribute error", context, context.getParser().getLocation()); } } /** * Set an attribute value. * Similar to handleAttributValue but allows objects as attribute values (for linking). * @param attrinfo The attribute info. * @param xmlattrname The xml attribute name. * @param object The object. * @param attrval The attribute value. * @param root The root object. * @param classloader The classloader. */ protected void setBulkAttributeValues(Object accessinfo, QName xmlattrname, Object object, List vals, Object converter, String id, ReadContext context) throws Exception { boolean set = false; // Write to a map. if(accessinfo instanceof AccessInfo && ((AccessInfo)accessinfo).getExtraInfo() instanceof BeanAccessInfo) { AccessInfo ai = (AccessInfo)accessinfo; BeanAccessInfo bai = (BeanAccessInfo)ai.getExtraInfo(); // todo: support map? // Fetch value using predefined read method. if(bai.getStoreHelp()!=null) { Object sh = bai.getStoreHelp(); if(sh instanceof Method) { Method m = (Method)sh; Class[] ps = m.getParameterTypes(); if(ps.length==1) { Object arg = convertBulkValues(vals, ps[0], converter, context, id); try { m.invoke(object, new Object[]{arg}); set = true; } catch(InvocationTargetException e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking setter method: "+e.getTargetException(), // "attribute error", context, context.getParser().getLocation()); } catch(Exception e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking setter method: "+e, // "attribute error", context, context.getParser().getLocation()); } } else { context.getReporter().report("Read method should have one parameter: "+bai+" "+m, "attribute error", context, context.getParser().getLocation()); } } else if(sh instanceof Field) { try { Field f = (Field)sh; Object arg = convertBulkValues(vals, f.getType(), converter, context, id); f.set(object, arg); set = true; } catch(Exception e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure setting field: "+e, // "attribute error", context, context.getParser().getLocation()); } } else { context.getReporter().report("Unknown store help: "+sh, "attribute error", context, context.getParser().getLocation()); } } } // Try if(!set && accessinfo instanceof AccessInfo) { AccessInfo ai = (AccessInfo)accessinfo; String fieldname = ai.getObjectIdentifier()!=null? ((String)ai.getObjectIdentifier()): xmlattrname.getLocalPart(); set = setBulkField(fieldname, object, vals, converter, context, id); if(!set) { String postfix = ai.getObjectIdentifier()!=null? ((String)ai.getObjectIdentifier()) .substring(0,1).toUpperCase()+((String)ai.getObjectIdentifier()).substring(1) : xmlattrname.getLocalPart().substring(0,1).toUpperCase()+xmlattrname.getLocalPart().substring(1); set = invokeBulkSetMethod(new String[]{"set"}, postfix, vals, object, context, converter, id); } } else if(!set) // attribute info is null or string { // Write as normal bean attribute. // Try to find bean class information Map props = introspector.getBeanProperties(object.getClass(), true); BeanProperty prop = (BeanProperty)props.get(accessinfo instanceof String? accessinfo: xmlattrname.getLocalPart()); if(prop!=null) { Object arg = convertBulkValues(vals, prop.getSetterType(), null, context, null); try { if(prop.getSetter()!=null) prop.getSetter().invoke(object, new Object[]{arg}); else prop.getField().set(object, arg); set = true; } catch(InvocationTargetException e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking setter method: "+e.getTargetException(), // "attribute error", context, context.getParser().getLocation()); } catch(Exception e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure setting attribute: "+e, // "attribute error", context, context.getParser().getLocation()); } } if(!set) { String postfix = accessinfo instanceof String? ((String)accessinfo).substring(0,1).toUpperCase()+((String)accessinfo).substring(1) : xmlattrname.getLocalPart().substring(0,1).toUpperCase()+xmlattrname.getLocalPart().substring(1); set = invokeBulkSetMethod(new String[]{"set"}, postfix, vals, object, context, null, null); } } if(!set) { context.getReporter().report("Failure in setting bulk values: "+xmlattrname+" on object: "+object+" (unknown attribute?)", "attribute error", context, context.getParser().getLocation()); } } /** * Set a value directly on a Java bean. * @param prefixes The method prefixes. * @param postfix The method postfix. * @param value The attribute value. * @param object The object. * @param root The root. * @param classloader The classloader. * @param converter The converter. */ protected boolean invokeSetMethod(String[] prefixes, String postfix, Object value, Object object, ReadContext context, Object converter, String idref) throws Exception { boolean set = false; for(int i=0; i<prefixes.length && !set; i++) { try { Method[] ms = SReflect.getMethods(object.getClass(), prefixes[i]+postfix); for(int j=0; j<ms.length && !set; j++) { Class[] ps = ms[j].getParameterTypes(); if(ps.length==1) { Object arg = convertValue(value, ps[0], converter, context, idref); ms[j].invoke(object, new Object[]{arg}); set = true; } } } catch(InvocationTargetException e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking setter method: "+e.getTargetException(), // "attribute error", context, context.getParser().getLocation()); } catch(Exception e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking setter method: "+e, // "attribute error", context, context.getParser().getLocation()); } } return set; } /** * Set a value directly on a Java bean. * @param prefixes The method prefixes. * @param postfix The mothod postfix. * @param attrval The attribute value. * @param object The object. * @param root The root. * @param classloader The classloader. * @param converter The converter. */ protected boolean invokeBulkSetMethod(String[] prefixes, String postfix, List vals, Object object, ReadContext context, Object converter, String idref) throws Exception { boolean set = false; for(int i=0; i<prefixes.length && !set; i++) { try { Method[] ms = SReflect.getMethods(object.getClass(), prefixes[i]+postfix); for(int j=0; j<ms.length && !set; j++) { Class[] ps = ms[j].getParameterTypes(); if(ps.length==1) { Object arg = convertBulkValues(vals, ps[0], converter, context, idref); ms[j].invoke(object, new Object[]{arg}); set = true; } } } catch(InvocationTargetException e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking setter method: "+e.getTargetException(), // "attribute error", context, context.getParser().getLocation()); } catch(Exception e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking setter method: "+e, // "attribute error", context, context.getParser().getLocation()); } } return set; } /** * Directly access a field for setting/(adding) the object. */ protected boolean setField(String fieldname, Object parent, Object object, Object converter, ReadContext context, String idref) throws Exception { boolean set = false; try { Field field = parent.getClass().getField(fieldname); Class type = field.getType(); Object val = object; val = convertValue(object, type, converter, context, idref); if(SReflect.isSupertype(type, val.getClass())) { field.set(parent, val); set = true; } } catch(Exception e) { // Ignore -> try other way of setting attribute } return set; } /** * Directly access a field for setting the objects. */ protected boolean setBulkField(String fieldname, Object parent, List objects, Object converter, ReadContext context, String idref) throws Exception { boolean set = false; try { Field field = parent.getClass().getField(fieldname); Class type = field.getType(); // object = convertAttributeValue(object, type, converter, root, classloader, idref, readobjects); Object arg = convertBulkValues(objects, type, converter, context, idref); field.set(parent, arg); set = true; } catch(Exception e) { // Ignore -> try other way of setting attribute } return set; } /** * Internal link objects method. * @param clazz The clazz. * @param name The name. * @param object The object. * @param parent The parent. * @param root The root. * @param classloader classloader. */ protected boolean internalLinkObjects(Class clazz, String name, Object object, Object parent, ReadContext context) throws Exception { boolean ret = false; Method[] ms = SReflect.getMethods(parent.getClass(), name); for(int i=0; !ret && i<ms.length; i++) { Class[] ps = ms[i].getParameterTypes(); if(ps.length==1) { if(SReflect.getWrappedType(ps[0]).isAssignableFrom(clazz)) { try { ms[i].invoke(parent, new Object[]{object}); ret = true; } catch(InvocationTargetException e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking link method: "+e.getTargetException(), // "link error", context, context.getParser().getLocation()); } catch(Exception e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking link method: "+e, // "link error", context, context.getParser().getLocation()); } } else if(object instanceof String) { IStringObjectConverter converter = BasicTypeConverter.getBasicStringConverter(ps[0]); if(converter != null) { try { object = converter.convertString((String)object, context); ms[i].invoke(parent, new Object[]{object}); ret = true; } catch(InvocationTargetException e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking link method: "+e.getTargetException(), // "link error", context, context.getParser().getLocation()); } catch(Exception e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking link method: "+e, // "link error", context, context.getParser().getLocation()); } } } } } return ret; } /** * Internal bulk link objects method. * @param clazz The clazz. * @param name The name. * @param object The object. * @param parent The parent. * @param root The root. * @param classloader classloader. */ protected boolean internalBulkLinkObjects(Class clazz, String name, List childs, Object parent, ReadContext context) throws Exception { boolean ret = false; Method[] ms = SReflect.getMethods(parent.getClass(), name); for(int i=0; !ret && i<ms.length; i++) { Class[] ps = ms[i].getParameterTypes(); if(ps.length==1) { try { Object arg = convertBulkValues(childs, ps[0], null, context, null); ms[i].invoke(parent, new Object[]{arg}); ret = true; } catch(InvocationTargetException e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking link method: "+e.getTargetException(), // "link error", context, context.getParser().getLocation()); } catch(Exception e) { // Ignore -> try other way of setting attribute // context.getReporter().report("Failure invoking link method: "+e, // "link error", context, context.getParser().getLocation()); } } } return ret; } /** * Convert a value by using a converter. * @param val The attribute value. * @param targetcalss The target class. * @param converter The converter. * @param root The root. * @param classloader The classloader. */ protected Object convertValue(Object val, Class targetclass, Object converter, ReadContext context, String id) throws Exception { Object ret = val; // When 'id' is idref interpret value as key for stored object. if(AttributeInfo.IDREF.equals(id)) { ret = context.getReadObjects().get(val); } else if(converter instanceof ISubObjectConverter) { ret = ((ISubObjectConverter)converter).convertObjectForRead(val, context); } // If a string converter is available else if(val instanceof String) { if(converter instanceof IStringObjectConverter) { ret = ((IStringObjectConverter)converter).convertString((String)val, context); } else if(!String.class.isAssignableFrom(targetclass)) { IStringObjectConverter conv = BasicTypeConverter.getBasicStringConverter(targetclass); if(conv!=null) ret = conv.convertString((String)val, context); } } return ret; } /** * Convert a list of values into the target format (list, set, collection, array). */ protected Object convertBulkValues(List vals, Class targetclass, Object converter, ReadContext context, String id) throws Exception { // todo: use converter?! Object ret = vals; // object = convertAttributeValue(object, type, converter, root, classloader, idref, readobjects); if(SReflect.isSupertype(Set.class, targetclass)) { ret = new HashSet(vals); } else if(targetclass.isArray()) { // if(SReflect.isSupertype(type.getComponentType(), clazz)) { ret = Array.newInstance(targetclass.getComponentType(), vals.size()); for(int j=0; j<vals.size(); j++) { Array.set(ret, j, vals.get(j)); } } } else if(SReflect.isSupertype(Collection.class, targetclass)) { // When collection list is ok. } else { context.getReporter().report("Conversion to target no possible: "+targetclass+" "+vals, "convert error", context, context.getParser().getLocation()); } return ret; } /** * Get the post-processor. * @return The post-processor */ public IPostProcessor getPostProcessor(Object object, Object typeinfo) { return typeinfo instanceof TypeInfo? ((TypeInfo)typeinfo).getPostProcessor(): null; } }