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    }