001 /* 002 @license.text@ 003 */ 004 package biz.hammurapi.xml.dom; 005 006 import java.io.IOException; 007 import java.lang.reflect.InvocationTargetException; 008 import java.lang.reflect.Method; 009 import java.util.ArrayList; 010 import java.util.Collection; 011 import java.util.HashMap; 012 import java.util.Iterator; 013 import java.util.Map; 014 import java.util.Stack; 015 016 import biz.hammurapi.RuntimeException; 017 import biz.hammurapi.config.ConfigurationException; 018 import biz.hammurapi.config.DomConfigFactory; 019 import biz.hammurapi.util.ClassHierarchyVisitable; 020 import biz.hammurapi.util.Visitor; 021 022 023 024 /** 025 * @author Pavel Vlasov 026 * 027 * @version $Revision: 1.8 $ 028 */ 029 public class CompositeDomSerializer { 030 public interface Member { 031 /** 032 * Callback method 033 * @param owner 034 */ 035 void setOwner(CompositeDomSerializer owner); 036 } 037 038 private Collection serializers=new ArrayList(); 039 040 /** 041 * Serializer which to delegate serialization to 042 * if this serializer doesn't containt appropriate 043 * means to serialize given object. 044 */ 045 protected CompositeDomSerializer upInStack; 046 047 /** 048 * Serializer to delegate requests for serialization from members. 049 */ 050 protected CompositeDomSerializer downInStack; 051 052 /** 053 * For use by members. 054 * @return CompositeDomSerializer at stack's head to delegate requests 055 * from members to. 056 */ 057 public CompositeDomSerializer getStackHead() { 058 return (downInStack==null || downInStack==this) ? this : downInStack.getStackHead(); 059 } 060 061 /** 062 * Creates serializer prepopulated with standard serializers 063 * loaded from com/pavelvlasov/xml/dom/CompositeDomSerializer.xml resource. 064 */ 065 public CompositeDomSerializer() { 066 synchronized (standardSerializers) { 067 Iterator it=standardSerializers.iterator(); 068 while (it.hasNext()) { 069 addDomSerializer(it.next()); 070 } 071 } 072 } 073 074 /** 075 * Creates serializer populated with given collection of serializers 076 * @param serializers 077 */ 078 public CompositeDomSerializer(Collection serializers) { 079 Iterator it=serializers.iterator(); 080 while (it.hasNext()) { 081 addDomSerializer(it.next()); 082 } 083 } 084 085 private interface DomSerializer { 086 Class getSourceClass(); 087 DomSerializable toDomSerializable(Object o); 088 } 089 090 public void addDomSerializer(final Object ds) { 091 if (ds instanceof Member) { 092 ((Member) ds).setOwner(this); 093 } 094 095 final Class clazz=ds.getClass(); 096 Method[] ma=clazz.getMethods(); 097 098 for (int i=0; i<ma.length; i++) { 099 if ("toDomSerializable".equals(ma[i].getName()) 100 && DomSerializable.class.isAssignableFrom(ma[i].getReturnType()) 101 && ma[i].getParameterTypes().length==1) { 102 103 final Method m=ma[i]; 104 serializers.add(new DomSerializer() { 105 106 public Class getSourceClass() { 107 return m.getParameterTypes()[0]; 108 } 109 110 public DomSerializable toDomSerializable(Object o) { 111 try { 112 return (DomSerializable) m.invoke(ds, new Object[] {o}); 113 } catch (IllegalAccessException e) { 114 throw new RuntimeException(e); 115 } catch (InvocationTargetException e) { 116 throw new RuntimeException(e); 117 } 118 } 119 }); 120 121 } 122 } 123 } 124 125 private Map serializerMap=new HashMap(); 126 127 public DomSerializable toDomSerializable(Object object) { 128 if (object==null) { 129 return null; 130 } 131 132 if (object instanceof DomSerializable) { 133 return (DomSerializable) object; 134 } 135 136 DomSerializer serializer; 137 synchronized (serializerMap) { 138 serializer=(DomSerializer) serializerMap.get(object.getClass().getName()); 139 if (serializer==null) { 140 int affinity=Integer.MAX_VALUE; 141 Iterator it=serializers.iterator(); 142 while (it.hasNext()) { 143 final DomSerializer ds=(DomSerializer) it.next(); 144 if (ds.getSourceClass().isInstance(object) && ds.getSourceClass().isArray()==object.getClass().isArray()) { 145 final int[] caffinity={0}; 146 new ClassHierarchyVisitable(object.getClass()).accept(new Visitor() { 147 148 public boolean visit(Object target) { 149 if (target.equals(ds.getSourceClass())) { 150 return false; 151 } 152 153 caffinity[0]++; 154 return true; 155 } 156 157 }); 158 159 // System.out.println(object.getClass()+" - "+ds.getSourceClass()+" : "+caffinity[0]); 160 161 if (serializer==null || caffinity[0]<affinity) { 162 serializer=ds; 163 affinity=caffinity[0]; 164 } 165 } 166 } 167 168 if (serializer!=null) { 169 serializerMap.put(object.getClass().getName(), serializer); 170 } 171 } 172 } 173 174 if (serializer==null) { 175 return upInStack==null ? null : upInStack.toDomSerializable(object); 176 } 177 178 return serializer.toDomSerializable(object); 179 } 180 181 private static CompositeDomSerializer defaultInstance; 182 183 private static Collection standardSerializers; 184 185 static { 186 try { 187 DomConfigFactory factory=new DomConfigFactory(); 188 standardSerializers=(Collection) factory.create(CompositeDomSerializer.class.getResourceAsStream("CompositeDomSerializer.xml"), null); 189 } catch (ConfigurationException e) { 190 throw new ExceptionInInitializerError(e); 191 } catch (IOException e) { 192 throw new ExceptionInInitializerError(e); 193 } 194 defaultInstance=new CompositeDomSerializer(); 195 if (defaultInstance.serializers.isEmpty()) { 196 throw new IllegalStateException("Default composite serializer doesn't contain any serializers."); 197 } 198 } 199 200 private static ThreadLocal threadSerializerTL=new ThreadLocal() { 201 protected Object initialValue() { 202 return new Stack(); 203 } 204 }; 205 206 public static CompositeDomSerializer getThreadInstance() { 207 Stack stack=(Stack) threadSerializerTL.get(); 208 209 if (stack.isEmpty()) { 210 return defaultInstance; 211 } 212 213 return (CompositeDomSerializer) stack.peek(); 214 } 215 216 public static void pushThreadSerializer(CompositeDomSerializer threadSerializer) { 217 if (threadSerializer!=null) { 218 CompositeDomSerializer cti = getThreadInstance(); 219 if (cti!=threadSerializer) { 220 threadSerializer.upInStack=cti; 221 cti.downInStack=threadSerializer; 222 ((Stack) threadSerializerTL.get()).push(threadSerializer); 223 } 224 } 225 } 226 227 public static CompositeDomSerializer popThreadSerializer() { 228 Stack stack=(Stack) threadSerializerTL.get(); 229 if (stack.isEmpty()) { 230 return null; 231 } 232 233 CompositeDomSerializer ret = (CompositeDomSerializer) stack.pop(); 234 if (ret.upInStack!=null) { 235 ret.upInStack.downInStack=null; 236 ret.upInStack=null; 237 } 238 return ret; 239 } 240 241 }