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 }