001: /*
002: * All content copyright (c) 2003-2007 Terracotta, Inc., except as may otherwise be noted in a separate copyright
003: * notice. All rights reserved.
004: */
005: package com.tc.object;
006:
007: import com.tc.aspectwerkz.reflect.impl.java.JavaClassInfo;
008: import com.tc.object.appevent.ApplicationEventContext;
009: import com.tc.object.appevent.NonPortableFieldSetContext;
010: import com.tc.object.appevent.NonPortableObjectState;
011: import com.tc.object.appevent.NonPortableRootContext;
012: import com.tc.object.config.DSOClientConfigHelper;
013: import com.tc.object.config.TransparencyClassSpec;
014: import com.tc.object.walker.MemberValue;
015: import com.tc.object.walker.ObjectGraphWalker;
016: import com.tc.object.walker.Visitor;
017: import com.tc.object.walker.WalkTest;
018: import com.tc.util.NonPortableReason;
019:
020: import java.lang.reflect.Field;
021:
022: import javax.swing.tree.DefaultMutableTreeNode;
023: import javax.swing.tree.DefaultTreeModel;
024: import javax.swing.tree.MutableTreeNode;
025:
026: public class WalkVisitor implements Visitor, WalkTest {
027:
028: private static final LiteralValues literals = new LiteralValues();
029:
030: private final ClientObjectManager objMgr;
031: private final DSOClientConfigHelper config;
032: private final ApplicationEventContext context;
033: private final DefaultTreeModel treeModel;
034:
035: private static final String MAX_WALK_DEPTH_PROP = "org.terracotta.non-portable.max-walk-depth";
036: private static final int DEFAULT_MAX_WALK_DEPTH = -1;
037: private static final Integer maxWalkDepth = Integer.getInteger(
038: MAX_WALK_DEPTH_PROP, DEFAULT_MAX_WALK_DEPTH);
039:
040: public WalkVisitor(ClientObjectManager objMgr,
041: DSOClientConfigHelper config, Object root,
042: ApplicationEventContext context) {
043: this .objMgr = objMgr;
044: this .config = config;
045: this .context = context;
046: this .treeModel = new DefaultTreeModel(
047: new DefaultMutableTreeNode());
048: }
049:
050: public DefaultTreeModel getTreeModel() {
051: return treeModel;
052: }
053:
054: public DefaultMutableTreeNode getRootNode() {
055: DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel
056: .getRoot();
057: return (DefaultMutableTreeNode) root.getChildAt(0);
058: }
059:
060: MutableTreeNode getParent(int depth) {
061: MutableTreeNode node = (MutableTreeNode) treeModel.getRoot();
062: while (depth-- > 0) {
063: node = (MutableTreeNode) node.getChildAt(node
064: .getChildCount() - 1);
065: }
066: return node;
067: }
068:
069: public void addRoot(MemberValue value) {
070: DefaultMutableTreeNode parent = (DefaultMutableTreeNode) getParent(0);
071: NonPortableObjectState objectState = createRootObjectState(value);
072: DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(
073: objectState);
074: addChild(parent, childNode);
075: }
076:
077: static String getTypeName(Class type) {
078: if (type.isArray()) {
079: Class cl = type;
080: int dimensions = 0;
081: while (cl.isArray()) {
082: dimensions++;
083: cl = cl.getComponentType();
084: }
085: StringBuffer sb = new StringBuffer();
086: sb.append(cl.getName());
087: for (int i = 0; i < dimensions; i++) {
088: sb.append("[]");
089: }
090: return sb.toString();
091: }
092: return type.getName();
093: }
094:
095: String createLabel(MemberValue value) {
096: Field field = value.getSourceField();
097: Object o = value.getValueObject();
098: StringBuffer sb = new StringBuffer();
099: String rep = null;
100:
101: if (value.isElement()) {
102: sb.append("[" + value.getIndex() + "] ");
103: } else if (value.isMapKey()) {
104: sb.append("key ");
105: } else if (value.isMapValue()) {
106: sb.append("value ");
107: }
108:
109: if (field != null) {
110: Class type = getType(value);
111:
112: sb.append(field.getDeclaringClass().getName());
113: sb.append(".");
114: sb.append(field.getName());
115: sb.append(" (");
116: sb.append(getTypeName(type));
117: sb.append(")");
118:
119: if (type.isArray()
120: && type.getComponentType() == Character.TYPE
121: && o != null) {
122: rep = new String((char[]) o);
123: }
124: } else {
125: sb.append(o != null ? getTypeName(o.getClass())
126: : "java.lang.Object");
127: }
128:
129: if (o == null || rep != null || isSimpleLiteralInstance(o)) {
130: sb.append("=");
131: if (rep != null) {
132: sb.append(rep);
133: } else {
134: sb.append(o != null ? o.toString() : "null");
135: }
136: }
137:
138: return sb.toString();
139: }
140:
141: public void addField(MemberValue value, int depth) {
142: Field field = value.getSourceField();
143: DefaultMutableTreeNode parent = (DefaultMutableTreeNode) getParent(depth);
144:
145: if (field != null
146: && context instanceof NonPortableFieldSetContext) {
147: NonPortableFieldSetContext cntx = (NonPortableFieldSetContext) context;
148: String fieldName = field.getDeclaringClass().getName()
149: + "." + field.getName();
150:
151: if (fieldName.equals(cntx.getFieldName())) {
152: Object fieldValue = cntx.getFieldValue();
153: WalkVisitor wv = new WalkVisitor(objMgr, config,
154: fieldValue, null);
155: ObjectGraphWalker walker = new ObjectGraphWalker(
156: fieldValue, wv, wv);
157: walker.setMaxDepth(maxWalkDepth.intValue());
158: walker.walk();
159: DefaultMutableTreeNode node = wv.getRootNode();
160: NonPortableObjectState state = (NonPortableObjectState) node
161: .getUserObject();
162: state.setFieldName(fieldName);
163: state.setLabel(createLabel(value));
164: addChild(parent, node);
165: return;
166: }
167: }
168:
169: NonPortableObjectState objectState = createObjectState(value);
170: DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(
171: objectState);
172: addChild(parent, childNode);
173: }
174:
175: private String getFieldName(MemberValue value) {
176: Field field = value.getSourceField();
177:
178: if (field != null) {
179: return field.getDeclaringClass().getName() + "."
180: + field.getName();
181: }
182:
183: return null;
184: }
185:
186: private Class getType(MemberValue value) {
187: Field field = value.getSourceField();
188: Object o = value.getValueObject();
189:
190: if (o != null) {
191: return o.getClass();
192: } else if (field != null) {
193: return field.getType();
194: }
195:
196: return null;
197: }
198:
199: private String getTypeName(MemberValue value) {
200: Class type = getType(value);
201: return type != null ? type.getName() : null;
202: }
203:
204: private NonPortableObjectState createRootObjectState(
205: MemberValue value) {
206: String typeName = getTypeName(value);
207: boolean isPortable = isPortable(value);
208: boolean isTransient = isTransient(value);
209: boolean neverPortable = isNeverAdaptable(value);
210: boolean isPreInstrumented = isPreInstrumented(value);
211: boolean isRepeated = value.isRepeated();
212: boolean isSystemType = isSystemType(value);
213: boolean isNull = value.getValueObject() == null;
214: String fieldName = null;
215: String label;
216:
217: if (context instanceof NonPortableRootContext) {
218: fieldName = ((NonPortableRootContext) context)
219: .getFieldName();
220: } else {
221: fieldName = getFieldName(value);
222: }
223: label = fieldName != null ? fieldName + " (" + typeName + ")"
224: : typeName;
225:
226: NonPortableObjectState objectState = new NonPortableObjectState(
227: label, fieldName, typeName, isPortable, isTransient,
228: neverPortable, isPreInstrumented, isRepeated,
229: isSystemType, isNull);
230:
231: if (!isPortable) {
232: NonPortableReason reason = config.getPortability()
233: .getNonPortableReason(getType(value));
234: objectState.setNonPortableReason(reason);
235: objectState.setExplaination(handleSpecialCases(value));
236: }
237:
238: return objectState;
239: }
240:
241: private NonPortableObjectState createObjectState(MemberValue value) {
242: String fieldName = getFieldName(value);
243: String typeName = getTypeName(value);
244: boolean isPortable = isPortable(value);
245: boolean isTransient = isTransient(value);
246: boolean neverPortable = isNeverAdaptable(value);
247: boolean isPreInstrumented = isPreInstrumented(value);
248: boolean isRepeated = value.isRepeated();
249: boolean isSystemType = isSystemType(value);
250: boolean isNull = value.getValueObject() == null;
251:
252: NonPortableObjectState objectState = new NonPortableObjectState(
253: createLabel(value), fieldName, typeName, isPortable,
254: isTransient, neverPortable, isPreInstrumented,
255: isRepeated, isSystemType, isNull);
256:
257: if (!isPortable) {
258: NonPortableReason reason = config.getPortability()
259: .getNonPortableReason(getType(value));
260: objectState.setNonPortableReason(reason);
261: objectState.setExplaination(handleSpecialCases(value));
262: }
263:
264: return objectState;
265: }
266:
267: private String handleSpecialCases(MemberValue value) {
268: // TODO: create specialized explainations for well-known types, such as java.util.logging.Logger.
269: return null;
270: }
271:
272: public void visitMapEntry(int index, int depth) {
273: DefaultMutableTreeNode parent = (DefaultMutableTreeNode) getParent(depth);
274: DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(
275: "mapEntry [" + index + "]");
276: addChild(parent, childNode);
277: }
278:
279: private void addChild(DefaultMutableTreeNode parent,
280: DefaultMutableTreeNode child) {
281: parent.add(child);
282: }
283:
284: public void visitRootObject(MemberValue value) {
285: addRoot(value);
286: }
287:
288: public void visitValue(MemberValue value, int depth) {
289: if (!skipVisit(value)) {
290: addField(value, depth);
291: }
292: }
293:
294: private boolean isNeverAdaptable(Class type) {
295: while (!type.equals(Object.class)) {
296: if (config.isNeverAdaptable(JavaClassInfo
297: .getClassInfo(type)))
298: return true;
299: type = type.getSuperclass();
300: }
301: return false;
302: }
303:
304: private boolean isNeverAdaptable(MemberValue value) {
305: Object o = value.getValueObject();
306: if (o != null)
307: return isNeverAdaptable(o.getClass());
308: return false;
309: }
310:
311: public boolean shouldTraverse(MemberValue value) {
312: if (skipVisit(value) || value.isRepeated()
313: || isNeverAdaptable(value) || isTransient(value)) {
314: return false;
315: }
316:
317: if (!isPortable(value) && isSystemType(value))
318: return false;
319:
320: if (context instanceof NonPortableFieldSetContext) {
321: NonPortableFieldSetContext cntx = (NonPortableFieldSetContext) context;
322: Field field = value.getSourceField();
323: if (field != null) {
324: String fieldName = field.getDeclaringClass().getName()
325: + "." + field.getName();
326: if (fieldName.equals(cntx.getFieldName())) {
327: return false;
328: }
329: }
330: }
331:
332: Field field = value.getSourceField();
333: if (field != null) {
334: Class type = field.getType();
335: if (type.isArray() && type.getComponentType().isPrimitive()) {
336: return false;
337: }
338: }
339:
340: return !isLiteralInstance(value.getValueObject());
341: }
342:
343: private boolean isTransient(MemberValue val) {
344: Field f = val.getSourceField();
345: if (f == null) {
346: return false;
347: }
348:
349: return config.isTransient(f.getModifiers(), JavaClassInfo
350: .getClassInfo(f.getDeclaringClass()), f.getName());
351: }
352:
353: public boolean includeFieldsForType(Class type) {
354: return !config.isLogical(type.getName());
355: }
356:
357: private boolean skipVisit(MemberValue value) {
358: Field field = value.getSourceField();
359: if (field != null) {
360: return (field.getType().getName().startsWith("com.tc."));
361: }
362: return false;
363: }
364:
365: private boolean isPortable(MemberValue value) {
366: Object valueObject = value.getValueObject();
367:
368: if (valueObject != null)
369: return objMgr.isPortableInstance(valueObject);
370: return true;
371: }
372:
373: private boolean isPreInstrumented(Class type) {
374: if (type != null) {
375: TransparencyClassSpec spec = config.getSpec(type.getName());
376: return spec != null && spec.isPreInstrumented();
377: }
378: return false;
379: }
380:
381: private boolean isPreInstrumented(MemberValue value) {
382: Object o = value.getValueObject();
383:
384: if (o != null) {
385: return isPreInstrumented(o.getClass());
386: } else {
387: Field field = value.getSourceField();
388: if (field != null) {
389: return isPreInstrumented(field.getType());
390: }
391: }
392: return false;
393: }
394:
395: private boolean isSystemType(MemberValue value) {
396: Object o = value.getValueObject();
397:
398: if (o != null) {
399: return (o.getClass().getClassLoader() == null);
400: } else {
401: Field field = value.getSourceField();
402: if (field != null)
403: return (field.getType().getClassLoader() == null);
404: }
405: return false;
406: }
407:
408: private boolean isLiteralInstance(Object obj) {
409: if (obj == null) {
410: return false;
411: }
412: return literals.isLiteralInstance(obj);
413: }
414:
415: private boolean isSimpleLiteralInstance(Object obj) {
416: if (obj == null) {
417: return false;
418: }
419: int i = literals.valueFor(obj);
420: return i != LiteralValues.OBJECT && i != LiteralValues.ARRAY
421: && i != LiteralValues.JAVA_LANG_CLASS
422: && i != LiteralValues.JAVA_LANG_CLASS_HOLDER
423: && i != LiteralValues.JAVA_LANG_CLASSLOADER
424: && i != LiteralValues.JAVA_LANG_CLASSLOADER_HOLDER;
425: }
426: }
|