001    /*
002     @license.text@
003      */
004    package biz.hammurapi.swing;
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 javax.swing.table.DefaultTableModel;
017    import javax.swing.table.TableModel;
018    import javax.swing.tree.DefaultMutableTreeNode;
019    import javax.swing.tree.MutableTreeNode;
020    
021    import biz.hammurapi.RuntimeException;
022    import biz.hammurapi.config.ConfigurationException;
023    import biz.hammurapi.config.DomConfigFactory;
024    import biz.hammurapi.util.ClassHierarchyVisitable;
025    import biz.hammurapi.util.Visitor;
026    
027    
028    
029    /**
030     * @author Pavel Vlasov
031     *
032     * @version $Revision: 1.2 $
033     */
034    public class CompositeVisualizer {
035            
036            public interface Member {
037                    /**
038                     * Callback method
039                     * @param owner
040                     */
041                    void setOwner(CompositeVisualizer owner);
042            }
043            
044            private Collection visualizers=new ArrayList();
045            
046            /**
047             * Visualizer which to delegate visualization to 
048             * if this visualizer doesn't containt appropriate 
049             * means to visualize given object.
050             */     
051            protected CompositeVisualizer upInStack;
052            
053            /**
054             * Visualizer to delegate requests for visualization from members.
055             */
056            protected CompositeVisualizer downInStack;
057            
058            /**
059             * For use by members.
060             * @return CompositeVisualizer at stack's head to delegate requests 
061             * from members to.
062             */
063            public CompositeVisualizer getStackHead() {
064                    return (downInStack==null || downInStack==this) ? this : downInStack.getStackHead();
065            }
066    
067            /**
068             * Creates visualizer prepopulated with standard visualizers
069             * loaded from com/pavelvlasov/swing/CompositeVisualizer.xml resource.
070             */
071            public CompositeVisualizer() {
072                    Iterator it=standardVisualizers.iterator();
073                    while (it.hasNext()) {
074                            addVisualizer(it.next());
075                    }
076            }
077    
078            /**
079             * Creates visualizer populated with given collection of visualizers
080             * @param visualizers
081             */
082            public CompositeVisualizer(Collection visualizers) {
083                    Iterator it=visualizers.iterator();
084                    while (it.hasNext()) {
085                            addVisualizer(it.next());
086                    }
087            }
088            
089            private interface Visualizer {
090                    Class getSourceClass();
091                    Visualizable toVisualizable(Object o);
092            }
093            
094            public void addVisualizer(final Object v) {
095                    if (v instanceof Member) {
096                            ((Member) v).setOwner(this);
097                    }
098                    
099                    final Class clazz=v.getClass();
100                    Method[] ma=clazz.getMethods();
101                    
102                    for (int i=0; i<ma.length; i++) {
103                            if ("toVisualizable".equals(ma[i].getName()) 
104                                            && Visualizable.class.isAssignableFrom(ma[i].getReturnType()) 
105                                            && ma[i].getParameterTypes().length==1) {
106                                    
107                                    final Method m=ma[i];
108                                    visualizers.add(new Visualizer() {
109    
110                                            public Class getSourceClass() {
111                                                    return m.getParameterTypes()[0];
112                                            }
113    
114                                            public Visualizable toVisualizable(Object o) {                                          
115                                                    try {
116                                                            return (Visualizable) m.invoke(v, new Object[] {o});
117                                                    } catch (IllegalAccessException e) {
118                                                            throw new RuntimeException(e);
119                                                    } catch (InvocationTargetException e) {
120                                                            throw new RuntimeException(e);
121                                                    }
122                                            }                                       
123                                    });
124                                    
125                            }
126                    }
127            }       
128    
129            private Map visualizerMap=new HashMap();
130    
131            private static final Visualizable nullVisualizible=new Visualizable() {
132    
133                    public MutableTreeNode toTree(final String title) {
134                            DefaultMutableTreeNode ret=new DefaultMutableTreeNode() {
135                                    public String toString() {
136                                            return title + " - null";
137                                    }
138                            };
139                            return ret;
140                    }
141    
142                    public TableModel toTable() {
143                            DefaultTableModel ret=new DefaultTableModel(1,2);
144                            ret.setValueAt("Value", 0, 0);
145                            ret.setValueAt("(null)", 0, 1);
146                            ret.setColumnIdentifiers(new String[] {"Property", "Value"});
147                            return null;
148                    }
149                    
150            }; 
151            
152            public Visualizable toVisualizable(Object object) {
153                    if (object==null) {
154                            return nullVisualizible;
155                    }
156                    
157                    if (object instanceof Visualizable) {
158                            return (Visualizable) object;
159                    }
160                    
161                    Visualizer visualizer;
162                    synchronized (visualizerMap) {
163                            visualizer=(Visualizer) visualizerMap.get(object.getClass().getName());
164                            if (visualizer==null) {
165                                    int affinity=Integer.MAX_VALUE;
166                                    Iterator it=visualizers.iterator();
167                                    while (it.hasNext()) {
168                                            final Visualizer v=(Visualizer) it.next();
169                                            if (v.getSourceClass().isInstance(object) && v.getSourceClass().isArray()==object.getClass().isArray()) {
170                                                    final int[] caffinity={0};
171                                                    new ClassHierarchyVisitable(object.getClass()).accept(new Visitor() {
172            
173                                                            public boolean visit(Object target) {
174                                                                    if (target.equals(v.getSourceClass())) {
175                                                                            return false;
176                                                                    } 
177                                                                    
178                                                                    caffinity[0]++;
179                                                                    return true;
180                                                            }
181                                                            
182                                                    });
183                                                                                                    
184    //                                              System.out.println(object.getClass()+" - "+ds.getSourceClass()+" : "+caffinity[0]);
185                                                    
186                                                    if (visualizer==null || caffinity[0]<affinity) {
187                                                            visualizer=v;
188                                                            affinity=caffinity[0];
189                                                    }
190                                            }
191                                    }
192                                    
193                                    if (visualizer!=null) {
194                                            visualizerMap.put(object.getClass().getName(), visualizer);
195                                    }
196                            }                       
197                    }
198                    
199                    if (visualizer==null) {
200                            return upInStack==null ? null : upInStack.toVisualizable(object);
201                    }
202                    
203                    return visualizer.toVisualizable(object);
204            }
205            
206            private static CompositeVisualizer defaultInstance;
207            
208            private static Collection standardVisualizers;
209            
210            static {
211                    try {
212                            DomConfigFactory factory=new DomConfigFactory();
213                            standardVisualizers=(Collection) factory.create(CompositeVisualizer.class.getResourceAsStream("CompositeVisualizer.xml"), null);
214                    } catch (ConfigurationException e) {
215                            throw new ExceptionInInitializerError(e);
216                    } catch (IOException e) {
217                            throw new ExceptionInInitializerError(e);
218                    }
219                    defaultInstance=new CompositeVisualizer();
220            }
221                    
222            private static ThreadLocal threadVisualizerTL=new ThreadLocal() {
223                    protected Object initialValue() {
224                            return new Stack();
225                    }
226            };
227            
228            public static CompositeVisualizer getThreadInstance() {
229                    Stack stack=(Stack) threadVisualizerTL.get();
230                    
231                    if (stack.isEmpty()) {
232                            return defaultInstance;
233                    }
234                    
235                    return (CompositeVisualizer) stack.peek();
236            }
237            
238            public static void pushThreadVisualizer(CompositeVisualizer threadVisualizer) {
239                    if (threadVisualizer!=null) {
240                            CompositeVisualizer cti = getThreadInstance();
241                            if (cti!=threadVisualizer) {
242                                    threadVisualizer.upInStack=cti;
243                                    cti.downInStack=threadVisualizer;
244                                    ((Stack) threadVisualizerTL.get()).push(threadVisualizer);
245                            }
246                    }
247            }
248            
249            public static CompositeVisualizer popThreadVisualizer() {
250                    Stack stack=(Stack) threadVisualizerTL.get();
251                    if (stack.isEmpty()) {
252                            return null;
253                    }
254                    
255                    CompositeVisualizer ret = (CompositeVisualizer) stack.pop();
256                    if (ret.upInStack!=null) {
257                            ret.upInStack.downInStack=null;
258                            ret.upInStack=null;
259                    }
260                    return ret;
261            }
262            
263    }