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 }