001: package org.drools.eclipse.debug.core;
002:
003: import java.util.ArrayList;
004: import java.util.Collections;
005: import java.util.Comparator;
006: import java.util.Iterator;
007: import java.util.List;
008:
009: import org.drools.eclipse.DroolsEclipsePlugin;
010: import org.drools.eclipse.DRLInfo.FunctionInfo;
011: import org.drools.eclipse.DRLInfo.RuleInfo;
012: import org.eclipse.core.runtime.IStatus;
013: import org.eclipse.core.runtime.Status;
014: import org.eclipse.debug.core.DebugEvent;
015: import org.eclipse.debug.core.DebugException;
016: import org.eclipse.debug.core.model.IThread;
017: import org.eclipse.debug.core.model.IVariable;
018: import org.eclipse.jdt.debug.core.IJavaStackFrame;
019: import org.eclipse.jdt.debug.core.IJavaThread;
020: import org.eclipse.jdt.debug.core.IJavaVariable;
021: import org.eclipse.jdt.internal.debug.core.JDIDebugPlugin;
022: import org.eclipse.jdt.internal.debug.core.model.JDIDebugModelMessages;
023: import org.eclipse.jdt.internal.debug.core.model.JDIDebugTarget;
024: import org.eclipse.jdt.internal.debug.core.model.JDIFieldVariable;
025: import org.eclipse.jdt.internal.debug.core.model.JDILocalVariable;
026: import org.eclipse.jdt.internal.debug.core.model.JDIReferenceType;
027: import org.eclipse.jdt.internal.debug.core.model.JDIStackFrame;
028: import org.eclipse.jdt.internal.debug.core.model.JDIThread;
029:
030: import com.ibm.icu.text.MessageFormat;
031: import com.sun.jdi.AbsentInformationException;
032: import com.sun.jdi.Field;
033: import com.sun.jdi.LocalVariable;
034: import com.sun.jdi.Location;
035: import com.sun.jdi.Method;
036: import com.sun.jdi.NativeMethodException;
037: import com.sun.jdi.ObjectReference;
038: import com.sun.jdi.ReferenceType;
039: import com.sun.jdi.StackFrame;
040:
041: public class DroolsStackFrame extends JDIStackFrame {
042:
043: private static final String CONSEQUENCE_SIGNATURE = "(Lorg/drools/spi/KnowledgeHelper";
044:
045: private DroolsThread fThread;
046: private Location fLocation;
047: private List fVariables;
048: private boolean fRefreshVariables = true;
049: private int fDepth = -2;
050: private boolean initialized = true;
051: private StackFrame fStackFrame;
052: private ObjectReference fThisObject;
053: private String fReceivingTypeName;
054: private boolean fLocalsAvailable = true;
055:
056: public DroolsStackFrame(DroolsThread thread, StackFrame frame,
057: int depth) {
058: super (thread, frame, depth);
059: bind(frame, depth);
060: }
061:
062: public boolean isExecutingRule() {
063: try {
064: if ("consequence".equals(getMethodName())
065: && getSignature().startsWith(CONSEQUENCE_SIGNATURE)) {
066: return true;
067: }
068: } catch (DebugException exc) {
069: DroolsEclipsePlugin.log(exc);
070: }
071: return false;
072: }
073:
074: public RuleInfo getExecutingRuleInfo() {
075: try {
076: String methodName = getMethodName();
077: String signature = getSignature();
078: String type = getDeclaringTypeName();
079: if ("consequence".equals(methodName)
080: && signature.startsWith(CONSEQUENCE_SIGNATURE)) {
081: return DroolsEclipsePlugin.getDefault()
082: .getRuleInfoByClass(type);
083: }
084:
085: } catch (DebugException exc) {
086: DroolsEclipsePlugin.log(exc);
087: }
088: return null;
089: }
090:
091: public FunctionInfo getExecutingFunctionInfo() {
092: try {
093: return DroolsEclipsePlugin.getDefault()
094: .getFunctionInfoByClass(getDeclaringTypeName());
095: } catch (DebugException exc) {
096: DroolsEclipsePlugin.log(exc);
097: }
098: return null;
099: }
100:
101: public int getLineNumber() throws DebugException {
102: synchronized (fThread) {
103: RuleInfo ruleInfo = getExecutingRuleInfo();
104: if (ruleInfo != null) {
105: return ruleInfo.getConsequenceDrlLineNumber()
106: + (getInternalLineNumber()
107: - ruleInfo
108: .getConsequenceJavaLineNumber() - 1);
109: }
110: FunctionInfo functionInfo = getExecutingFunctionInfo();
111: if (functionInfo != null) {
112: return functionInfo.getDrlLineNumber()
113: + (getInternalLineNumber() - functionInfo
114: .getJavaLineNumber());
115: }
116: }
117:
118: return getInternalLineNumber();
119: }
120:
121: private int getInternalLineNumber() throws DebugException {
122: try {
123: return fLocation.lineNumber();
124: } catch (RuntimeException e) {
125: if (getThread().isSuspended()) {
126: targetRequestFailed(
127: MessageFormat
128: .format(
129: JDIDebugModelMessages.JDIStackFrame_exception_retrieving_line_number,
130: new String[] { e.toString() }),
131: e);
132: }
133: }
134: return -1;
135: }
136:
137: public IVariable[] getVariables() throws DebugException {
138: IVariable[] variables = super .getVariables();
139: List result = new ArrayList((variables.length - 1) / 2);
140: for (int i = 0; i < variables.length; i++) {
141: String name = variables[i].getName();
142: if (!(name.equals("drools"))
143: && !(name.endsWith("__Handle__"))) {
144: result.add(variables[i]);
145: }
146: }
147: return (IVariable[]) result
148: .toArray(new IVariable[result.size()]);
149: }
150:
151: protected List getVariables0() throws DebugException {
152: synchronized (fThread) {
153: if (fVariables == null) {
154:
155: // throw exception if native method, so variable view will update
156: // with information message
157: if (isNative()) {
158: requestFailed(
159: JDIDebugModelMessages.JDIStackFrame_Variable_information_unavailable_for_native_methods,
160: null);
161: }
162:
163: Method method = getUnderlyingMethod();
164: fVariables = new ArrayList();
165: // #isStatic() does not claim to throw any exceptions - so it is not try/catch coded
166: if (method.isStatic()) {
167: // add statics
168: List allFields = null;
169: ReferenceType declaringType = method
170: .declaringType();
171: try {
172: allFields = declaringType.allFields();
173: } catch (RuntimeException e) {
174: targetRequestFailed(
175: MessageFormat
176: .format(
177: JDIDebugModelMessages.JDIStackFrame_exception_retrieving_fields,
178: new String[] { e
179: .toString() }),
180: e);
181: // execution will not reach this line, as
182: // #targetRequestFailed will throw an exception
183: return Collections.EMPTY_LIST;
184: }
185: if (allFields != null) {
186: Iterator fields = allFields.iterator();
187: while (fields.hasNext()) {
188: Field field = (Field) fields.next();
189: if (field.isStatic()) {
190: fVariables
191: .add(new JDIFieldVariable(
192: (JDIDebugTarget) getDebugTarget(),
193: field, declaringType));
194: }
195: }
196: Collections.sort(fVariables, new Comparator() {
197: public int compare(Object a, Object b) {
198: JDIFieldVariable v1 = (JDIFieldVariable) a;
199: JDIFieldVariable v2 = (JDIFieldVariable) b;
200: try {
201: return v1.getName()
202: .compareToIgnoreCase(
203: v2.getName());
204: } catch (DebugException de) {
205: logError(de);
206: return -1;
207: }
208: }
209: });
210: }
211: } else {
212: // add "this"
213: ObjectReference t = getUnderlyingThisObject();
214: if (t != null) {
215: fVariables.add(new DroolsThisVariable(
216: (JDIDebugTarget) getDebugTarget(), t));
217: }
218: }
219: // add locals
220: Iterator variables = getUnderlyingVisibleVariables()
221: .iterator();
222: while (variables.hasNext()) {
223: LocalVariable var = (LocalVariable) variables
224: .next();
225: fVariables.add(new DroolsLocalVariable(this , var));
226: }
227: } else if (fRefreshVariables) {
228: updateVariables();
229: }
230: fRefreshVariables = false;
231: return fVariables;
232: }
233: }
234:
235: protected JDIStackFrame bind(StackFrame frame, int depth) {
236: if (initialized) {
237: synchronized (fThread) {
238: if (fDepth == -2) {
239: // first initialization
240: fStackFrame = frame;
241: fDepth = depth;
242: fLocation = frame.location();
243: return this ;
244: } else if (depth == -1) {
245: // mark as invalid
246: fDepth = -1;
247: fStackFrame = null;
248: return null;
249: } else if (fDepth == depth) {
250: Location location = frame.location();
251: Method method = location.method();
252: if (method.equals(fLocation.method())) {
253: try {
254: if (method.declaringType().defaultStratum()
255: .equals("Java") || //$NON-NLS-1$
256: equals(getSourceName(location),
257: getSourceName(fLocation))) {
258: // TODO: what about receiving type being the same?
259: fStackFrame = frame;
260: fLocation = location;
261: clearCachedData();
262: return this ;
263: }
264: } catch (DebugException e) {
265: }
266: }
267: }
268: // invalidate this franme
269: bind(null, -1);
270: // return a new frame
271: return createNewDroolsFrame(frame, depth);
272: }
273: } else {
274: return null;
275: }
276: }
277:
278: protected DroolsStackFrame createNewDroolsFrame(StackFrame frame,
279: int depth) {
280: return DroolsThread.createCustomFrame(fThread, depth, frame);
281: }
282:
283: public IThread getThread() {
284: return fThread;
285: }
286:
287: public Method getUnderlyingMethod() {
288: synchronized (fThread) {
289: return fLocation.method();
290: }
291: }
292:
293: protected List getUnderlyingVisibleVariables()
294: throws DebugException {
295: synchronized (fThread) {
296: List variables = Collections.EMPTY_LIST;
297: try {
298: variables = getUnderlyingStackFrame()
299: .visibleVariables();
300: } catch (AbsentInformationException e) {
301: setLocalsAvailable(false);
302: } catch (NativeMethodException e) {
303: setLocalsAvailable(false);
304: } catch (RuntimeException e) {
305: targetRequestFailed(
306: MessageFormat
307: .format(
308: JDIDebugModelMessages.JDIStackFrame_exception_retrieving_visible_variables_2,
309: new String[] { e.toString() }),
310: e);
311: }
312: return variables;
313: }
314: }
315:
316: protected ObjectReference getUnderlyingThisObject()
317: throws DebugException {
318: synchronized (fThread) {
319: if ((fStackFrame == null || fThisObject == null)
320: && !isStatic()) {
321: try {
322: fThisObject = getUnderlyingStackFrame()
323: .this Object();
324: } catch (RuntimeException e) {
325: targetRequestFailed(
326: MessageFormat
327: .format(
328: JDIDebugModelMessages.JDIStackFrame_exception_retrieving_this ,
329: new String[] { e.toString() }),
330: e);
331: // execution will not reach this line, as
332: // #targetRequestFailed will throw an exception
333: return null;
334: }
335: }
336: return fThisObject;
337: }
338: }
339:
340: public String getDeclaringTypeName() throws DebugException {
341: synchronized (fThread) {
342: try {
343: if (isObsolete()) {
344: return JDIDebugModelMessages.JDIStackFrame__unknown_declaring_type__1;
345: }
346: return JDIReferenceType
347: .getGenericName(getUnderlyingMethod()
348: .declaringType());
349: } catch (RuntimeException e) {
350: if (getThread().isSuspended()) {
351: targetRequestFailed(
352: MessageFormat
353: .format(
354: JDIDebugModelMessages.JDIStackFrame_exception_retrieving_declaring_type,
355: new String[] { e.toString() }),
356: e);
357: }
358: return JDIDebugModelMessages.JDIStackFrame__unknown_declaring_type__1;
359: }
360: }
361: }
362:
363: public String getSourceName() throws DebugException {
364: synchronized (fThread) {
365: return getSourceName(fLocation);
366: }
367: }
368:
369: public boolean isObsolete() {
370: if (!JDIDebugPlugin.isJdiVersionGreaterThanOrEqual(new int[] {
371: 1, 4 })
372: || !((JDIDebugTarget) getDebugTarget())
373: .hasHCROccurred()) {
374: // If no hot code replace has occurred, this frame
375: // cannot be obsolete.
376: return false;
377: }
378: // if this frame's thread is not suspended, the obsolete status cannot
379: // change until it suspends again
380: synchronized (fThread) {
381: if (getThread().isSuspended()) {
382: return getUnderlyingMethod().isObsolete();
383: }
384: return false;
385: }
386: }
387:
388: protected boolean exists() {
389: synchronized (fThread) {
390: return fDepth != -1;
391: }
392: }
393:
394: protected StackFrame getUnderlyingStackFrame()
395: throws DebugException {
396: synchronized (fThread) {
397: if (fStackFrame == null) {
398: if (fDepth == -1) {
399: throw new DebugException(new Status(IStatus.ERROR,
400: JDIDebugPlugin.getUniqueIdentifier(),
401: IJavaStackFrame.ERR_INVALID_STACK_FRAME,
402: JDIDebugModelMessages.JDIStackFrame_25,
403: null));
404: }
405: if (fThread.isSuspended()) {
406: // re-index stack frames - See Bug 47198
407: fThread.computeStackFrames();
408: if (fDepth == -1) {
409: // If depth is -1, then this is an invalid frame
410: throw new DebugException(
411: new Status(
412: IStatus.ERROR,
413: JDIDebugPlugin
414: .getUniqueIdentifier(),
415: IJavaStackFrame.ERR_INVALID_STACK_FRAME,
416: JDIDebugModelMessages.JDIStackFrame_25,
417: null));
418: }
419: } else {
420: throw new DebugException(new Status(IStatus.ERROR,
421: JDIDebugPlugin.getUniqueIdentifier(),
422: IJavaThread.ERR_THREAD_NOT_SUSPENDED,
423: JDIDebugModelMessages.JDIStackFrame_25,
424: null));
425: }
426: }
427: return fStackFrame;
428: }
429: }
430:
431: protected void setUnderlyingStackFrame(StackFrame frame) {
432: synchronized (fThread) {
433: fStackFrame = frame;
434: if (frame == null) {
435: fRefreshVariables = true;
436: }
437: }
438: }
439:
440: protected void setThread(JDIThread thread) {
441: fThread = (DroolsThread) thread;
442: }
443:
444: public String getSourcePath(String stratum) throws DebugException {
445: synchronized (fThread) {
446: try {
447: return fLocation.sourcePath(stratum);
448: } catch (AbsentInformationException e) {
449: } catch (RuntimeException e) {
450: targetRequestFailed(
451: MessageFormat
452: .format(
453: JDIDebugModelMessages.JDIStackFrame_exception_retrieving_source_path,
454: new String[] { e.toString() }),
455: e);
456: }
457: }
458: return null;
459: }
460:
461: public String getSourcePath() throws DebugException {
462: synchronized (fThread) {
463: try {
464: return fLocation.sourcePath();
465: } catch (AbsentInformationException e) {
466: } catch (RuntimeException e) {
467: targetRequestFailed(
468: MessageFormat
469: .format(
470: JDIDebugModelMessages.JDIStackFrame_exception_retrieving_source_path,
471: new String[] { e.toString() }),
472: e);
473: }
474: }
475: return null;
476: }
477:
478: public int getLineNumber(String stratum) throws DebugException {
479: synchronized (fThread) {
480: try {
481: return fLocation.lineNumber(stratum);
482: } catch (RuntimeException e) {
483: if (getThread().isSuspended()) {
484: targetRequestFailed(
485: MessageFormat
486: .format(
487: JDIDebugModelMessages.JDIStackFrame_exception_retrieving_line_number,
488: new String[] { e.toString() }),
489: e);
490: }
491: }
492: }
493: return -1;
494: }
495:
496: public String getSourceName(String stratum) throws DebugException {
497: synchronized (fThread) {
498: try {
499: return fLocation.sourceName(stratum);
500: } catch (AbsentInformationException e) {
501: } catch (NativeMethodException e) {
502: } catch (RuntimeException e) {
503: targetRequestFailed(
504: MessageFormat
505: .format(
506: JDIDebugModelMessages.JDIStackFrame_exception_retrieving_source_name,
507: new String[] { e.toString() }),
508: e);
509: }
510: }
511: return null;
512: }
513:
514: protected void updateVariables() throws DebugException {
515: if (fVariables == null) {
516: return;
517: }
518:
519: Method method = getUnderlyingMethod();
520: int index = 0;
521: if (!method.isStatic()) {
522: // update "this"
523: ObjectReference this Object;
524: try {
525: this Object = getUnderlyingThisObject();
526: } catch (DebugException exception) {
527: if (!getThread().isSuspended()) {
528: this Object = null;
529: } else {
530: throw exception;
531: }
532: }
533: DroolsThisVariable oldThisObject = null;
534: if (!fVariables.isEmpty()
535: && fVariables.get(0) instanceof DroolsThisVariable) {
536: oldThisObject = (DroolsThisVariable) fVariables.get(0);
537: }
538: if (this Object == null && oldThisObject != null) {
539: // removal of 'this'
540: fVariables.remove(0);
541: index = 0;
542: } else {
543: if (oldThisObject == null && this Object != null) {
544: // creation of 'this'
545: oldThisObject = new DroolsThisVariable(
546: (JDIDebugTarget) getDebugTarget(),
547: this Object);
548: fVariables.add(0, oldThisObject);
549: index = 1;
550: } else {
551: if (oldThisObject != null) {
552: // 'this' still exists, replace with new 'this' if a different receiver
553: if (!oldThisObject.retrieveValue().equals(
554: this Object)) {
555: fVariables.remove(0);
556: fVariables.add(0, new DroolsThisVariable(
557: (JDIDebugTarget) getDebugTarget(),
558: this Object));
559: }
560: index = 1;
561: }
562: }
563: }
564: }
565:
566: List locals = null;
567: try {
568: locals = getUnderlyingStackFrame().visibleVariables();
569: } catch (AbsentInformationException e) {
570: locals = Collections.EMPTY_LIST;
571: } catch (NativeMethodException e) {
572: locals = Collections.EMPTY_LIST;
573: } catch (RuntimeException e) {
574: targetRequestFailed(
575: MessageFormat
576: .format(
577: JDIDebugModelMessages.JDIStackFrame_exception_retrieving_visible_variables,
578: new String[] { e.toString() }), e);
579: // execution will not reach this line, as
580: // #targetRequestFailed will throw an exception
581: return;
582: }
583: int localIndex = -1;
584: while (index < fVariables.size()) {
585: Object var = fVariables.get(index);
586: if (var instanceof JDILocalVariable) {
587: DroolsLocalVariable local = (DroolsLocalVariable) fVariables
588: .get(index);
589: localIndex = locals.indexOf(local.getLocal());
590: if (localIndex >= 0) {
591: // update variable with new underling JDI LocalVariable
592: local.setLocal((LocalVariable) locals
593: .get(localIndex));
594: locals.remove(localIndex);
595: index++;
596: } else {
597: // remove variable
598: fVariables.remove(index);
599: }
600: } else {
601: //field variable of a static frame
602: index++;
603: }
604: }
605:
606: // add any new locals
607: Iterator newOnes = locals.iterator();
608: while (newOnes.hasNext()) {
609: DroolsLocalVariable local = new DroolsLocalVariable(this ,
610: (LocalVariable) newOnes.next());
611: fVariables.add(local);
612: }
613: }
614:
615: protected void setVariables(List variables) {
616: fVariables = variables;
617: }
618:
619: public String getReceivingTypeName() throws DebugException {
620: if (fStackFrame == null || fReceivingTypeName == null) {
621: try {
622: if (isObsolete()) {
623: fReceivingTypeName = JDIDebugModelMessages.JDIStackFrame__unknown_receiving_type__2;
624: } else {
625: ObjectReference this Object = getUnderlyingThisObject();
626: if (this Object == null) {
627: fReceivingTypeName = getDeclaringTypeName();
628: } else {
629: fReceivingTypeName = JDIReferenceType
630: .getGenericName(this Object
631: .referenceType());
632: }
633: }
634: } catch (RuntimeException e) {
635: if (getThread().isSuspended()) {
636: targetRequestFailed(
637: MessageFormat
638: .format(
639: JDIDebugModelMessages.JDIStackFrame_exception_retrieving_receiving_type,
640: new String[] { e.toString() }),
641: e);
642: }
643: return JDIDebugModelMessages.JDIStackFrame__unknown_receiving_type__2;
644: }
645: }
646: return fReceivingTypeName;
647: }
648:
649: private String getSourceName(Location location)
650: throws DebugException {
651: try {
652: return location.sourceName();
653: } catch (AbsentInformationException e) {
654: return null;
655: } catch (NativeMethodException e) {
656: return null;
657: } catch (RuntimeException e) {
658: targetRequestFailed(
659: MessageFormat
660: .format(
661: JDIDebugModelMessages.JDIStackFrame_exception_retrieving_source_name,
662: new String[] { e.toString() }), e);
663: }
664: return null;
665: }
666:
667: private boolean equals(Object o1, Object o2) {
668: if (o1 == null) {
669: return o2 == null;
670: } else {
671: return o1.equals(o2);
672: }
673: }
674:
675: private void clearCachedData() {
676: fThisObject = null;
677: fReceivingTypeName = null;
678: }
679:
680: private void setLocalsAvailable(boolean available) {
681: if (available != fLocalsAvailable) {
682: fLocalsAvailable = available;
683: fireChangeEvent(DebugEvent.STATE);
684: }
685: }
686:
687: public boolean wereLocalsAvailable() {
688: return fLocalsAvailable;
689: }
690:
691: public IJavaVariable[] getLocalVariables() throws DebugException {
692: List list = getUnderlyingVisibleVariables();
693: IJavaVariable[] locals = new IJavaVariable[list.size()];
694: for (int i = 0; i < list.size(); i++) {
695: locals[i] = new DroolsLocalVariable(this ,
696: (LocalVariable) list.get(i));
697: }
698: return locals;
699: }
700:
701: }
|