package org.apache.ode.bpel.obj.migrate; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.ode.bpel.obj.ExtensibleImpl; /** * Migrate from old Omodel classes to new ones. * @author fangzhen * @see ObjectTraverser */ public class OmOld2new extends AbstractObjectVisitor{ private static final Logger __log = LoggerFactory.getLogger(OmOld2new.class); private static Map<String, String> beanPkgMap = new HashMap<String, String>(); static{ beanPkgMap.put("org.apache.ode.bpel.o", "org.apache.ode.bpel.obj"); beanPkgMap.put("org.apache.ode.bpel.elang.xpath10.o", "org.apache.ode.bpel.elang.xpath10.obj"); beanPkgMap.put("org.apache.ode.bpel.elang.xpath20.o", "org.apache.ode.bpel.elang.xpath20.obj"); beanPkgMap.put("org.apache.ode.bpel.elang.xquery10.o", "org.apache.ode.bpel.elang.xquery10.obj"); } public Object visit(Object obj){ __log.debug("migrating object: " + obj.getClass() + "@" + System.identityHashCode(obj)); Object n; /* * we use two category of visitXXX methods here. The first visitXXX(Object) * return corresponding new object instance without fulfilling its contents, * which avoids recursively call. And then assign the new object. then fill contents. * other wise, on cyclic reference case, the object re-visited but hasn't prepared yet. * However, this workaround assumes that the new object is mutable, which is true in our case. */ if (isMap(obj)){ n = visitMap(obj); }else if (isCollection(obj)){ n = visitCollection(obj); }else if (isArray(obj)){ n = visitArray(obj); }else{ n = visitPojo(obj); } rtab.assign(obj, n); if (isMap(obj)){ visitMap(obj, n); }else if (isCollection(obj)){ visitCollection(obj, n); }else if (isArray(obj)){ visitArray(obj, n); }else{ visitPojo(obj, n); } return n; } @Override protected boolean isCollection(Object old) { return (old instanceof Collection); } private boolean isOmodelBean(Object old){ Class<?> cls = old.getClass(); if (beanPkgMap.containsKey(cls.getPackage().getName()) && !cls.getSimpleName().equals("Serializer")){ return true; } return false; } @Override public Object visitArray(Object old) { throw new UnsupportedOperationException("Create new Array is unsupported"); } private void visitArray(Object obj, Object n) { throw new UnsupportedOperationException("We don't need the method here"); } @Override @SuppressWarnings({ "rawtypes"}) public Object visitCollection(Object old) { Collection o = (Collection) old; try { Collection n = o.getClass().newInstance(); return n; } catch (Exception e){ //should not get here e.printStackTrace(); } return null; } @SuppressWarnings({ "rawtypes", "unchecked" }) private void visitCollection(Object old, Object nu) { Collection o = (Collection) old; Collection n = (Collection) nu; for (Object obj : o){ n.add(traverse.traverseObject(obj)); } } @Override @SuppressWarnings({ "rawtypes", "unchecked" }) public Object visitMap(Object old) { Map o = (Map) old; try{ Map n = o.getClass().newInstance(); return n; }catch (Exception e){ //should not get here e.printStackTrace(); } return null; } @SuppressWarnings({ "unchecked", "rawtypes" }) private void visitMap(Object obj, Object nu) { Set<Entry> entries = ((Map)obj).entrySet(); Map n = (Map)nu; for (Entry e : entries){ n.put(traverse.traverseObject(e.getKey()), traverse.traverseObject(e.getValue())); } } @Override public Object visitPojo(Object old) { if (!isOmodelBean(old)){ return old; }else{ return initiateNew(old); } } private void visitPojo(Object old, Object n) { if (isOmodelBean(old)){ constructNewOm(old, n); } } /** * construct new omodel instances from old ones. Assume <code>old</code> is an old OmodelBean * @param old * @return */ private Object constructNewOm(Object old, Object tn) { assert tn instanceof ExtensibleImpl; ExtensibleImpl n = (ExtensibleImpl) tn; List<Field> fields = getAllFields(old.getClass()); Map<String, Object> fieldMap = n.getFieldContainer(); for (Field f : fields){ if ((f.getModifiers() & Modifier.STATIC) != 0){ continue; //skip static fields } f.setAccessible(true); try{ String fname = f.getName(); Object fvalue = f.get(old); if (fvalue != null){ fieldMap.put(fname, traverse.traverseObject(fvalue)); }else{ fieldMap.put(fname, null); } } catch (Exception e) { RuntimeException rte = new RuntimeException( "Error when try to construct corresponding new Omodel class from old one:" +old.getClass() + "; Failed on field:" + f.getName()); rte.initCause(e); throw rte; } } n.setClassVersion(1); n.setOriginalVersion(0); return n; } private List<Field> getAllFields(Class cls) { return getAllFieldsRec(cls, new ArrayList<Field>()); } private List<Field> getAllFieldsRec(Class cls, ArrayList<Field> fields) { Class par = cls.getSuperclass(); if (par != null){ getAllFieldsRec(par, fields); } fields.addAll(Arrays.asList(cls.getDeclaredFields())); return fields; } private Object initiateNew(Object old) { String clsName = old.getClass().getName(); String qcls = clsName.replace(".o.", ".obj."); try { Constructor cons = Class.forName(qcls).getConstructor(); cons.setAccessible(true); return cons.newInstance(); } catch (Exception e) { RuntimeException rte = new RuntimeException( "Error when try to initiate corresponding new Omodel class of old one:" + old.getClass()); rte.initCause(e); throw rte; } } @Override public Object visitSet(Object obj) { throw new UnsupportedOperationException("We don't really need this operatiion here"); } }