001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.debugger.jpda.models;
043:
044: import com.sun.jdi.ArrayType;
045: import com.sun.jdi.ClassLoaderReference;
046: import com.sun.jdi.ObjectCollectedException;
047: import com.sun.jdi.ReferenceType;
048: import com.sun.jdi.VMDisconnectedException;
049: import com.sun.jdi.VirtualMachine;
050:
051: import java.beans.PropertyChangeEvent;
052: import java.beans.PropertyChangeListener;
053: import java.lang.ref.WeakReference;
054: import java.util.ArrayList;
055: import java.util.Comparator;
056: import java.util.HashMap;
057: import java.util.List;
058: import java.util.Set;
059: import java.util.TreeSet;
060: import java.util.Vector;
061: import org.netbeans.api.debugger.Properties;
062:
063: import org.netbeans.spi.debugger.ContextProvider;
064: import org.netbeans.api.debugger.jpda.JPDADebugger;
065: import org.netbeans.spi.viewmodel.TreeModel;
066: import org.netbeans.spi.viewmodel.ModelListener;
067: import org.netbeans.spi.viewmodel.UnknownTypeException;
068:
069: import org.netbeans.modules.debugger.jpda.JPDADebuggerImpl;
070:
071: import org.openide.util.RequestProcessor;
072:
073: /**
074: * @author Jan Jancura
075: */
076: public class ClassesTreeModel implements TreeModel {
077:
078: private static boolean verbose = (System
079: .getProperty("netbeans.debugger.viewrefresh") != null)
080: && (System.getProperty("netbeans.debugger.viewrefresh")
081: .indexOf('s') >= 0);
082:
083: private Properties classesProperties = Properties.getDefault()
084: .getProperties("debugger").getProperties("classesView"); // NOI18N
085:
086: private JPDADebuggerImpl debugger;
087: private Listener listener;
088: private Vector<ModelListener> listeners = new Vector<ModelListener>();
089:
090: public ClassesTreeModel(ContextProvider lookupProvider) {
091: debugger = (JPDADebuggerImpl) lookupProvider.lookupFirst(null,
092: JPDADebugger.class);
093: }
094:
095: public Object getRoot() {
096: return ROOT;
097: }
098:
099: public Object[] getChildren(Object o, int from, int to)
100: throws UnknownTypeException {
101: try {
102: Object[] r = null;
103: if (o.equals(ROOT)) {
104: r = getLoaders();
105: } else if (o instanceof Object[]) {
106: r = getChildren((Object[]) o);
107: } else if (o instanceof ClassLoaderReference) {
108: r = getPackages((ClassLoaderReference) o);
109: } else if (o == NULL_CLASS_LOADER) {
110: r = getPackages(null);
111: } else if (o instanceof ReferenceType) {
112: r = ((ReferenceType) o).nestedTypes().toArray();
113: } else
114: throw new UnknownTypeException(o);
115: to = Math.min(r.length, to);
116: from = Math.min(r.length, from);
117: Object[] rr = new Object[to - from];
118: System.arraycopy(r, from, rr, 0, to - from);
119: return rr;
120: } catch (VMDisconnectedException ex) {
121: return new Object[0];
122: }
123: }
124:
125: /**
126: * Returns number of children for given node.
127: *
128: * @param node the parent node
129: * @throws UnknownTypeException if this TreeModel implementation is not
130: * able to resolve children for given node type
131: *
132: * @return true if node is leaf
133: */
134: public int getChildrenCount(Object node)
135: throws UnknownTypeException {
136: try {
137: if (node.equals(ROOT))
138: // Performance, see issue #59058.
139: return Integer.MAX_VALUE;
140: //return getLoaders ().length;
141: if (node instanceof Object[])
142: // Performance, see issue #59058.
143: return Integer.MAX_VALUE;
144: //return getChildren ((Object[]) node).length;
145: if (node instanceof ClassLoaderReference)
146: // Performance, see issue #59058.
147: return Integer.MAX_VALUE;
148: //return getPackages ((ClassLoaderReference) node).length;
149: if (node == NULL_CLASS_LOADER)
150: // Performance, see issue #59058.
151: return Integer.MAX_VALUE;
152: //return getPackages (null).length;
153: if (node instanceof ReferenceType) {
154: // Performance, see issue #59058.
155: return Integer.MAX_VALUE;
156: //return ((ReferenceType) node).nestedTypes ().size ();
157: }
158: throw new UnknownTypeException(node);
159: } catch (VMDisconnectedException ex) {
160: return 0;
161: }
162: }
163:
164: public boolean isLeaf(Object o) throws UnknownTypeException {
165: if (o.equals(ROOT))
166: return false;
167: if (o instanceof Object[])
168: return false;
169: if (o instanceof ReferenceType)
170: return false;
171: if (o instanceof ClassLoaderReference)
172: return false;
173: if (o == NULL_CLASS_LOADER)
174: return false;
175: throw new UnknownTypeException(o);
176: }
177:
178: public void addModelListener(ModelListener l) {
179: listeners.add(l);
180: if (listener == null)
181: listener = new Listener(this , debugger);
182: }
183:
184: public void removeModelListener(ModelListener l) {
185: listeners.remove(l);
186: if (listeners.size() == 0) {
187: listener.destroy();
188: listener = null;
189: }
190: }
191:
192: public void fireTreeChanged() {
193: classes = null;
194: names = null;
195: cache = new HashMap();
196: Vector v = (Vector) listeners.clone();
197: int i, k = v.size();
198: for (i = 0; i < k; i++)
199: ((ModelListener) v.get(i)).modelChanged(null);
200: }
201:
202: // private methods .........................................................
203:
204: private List<ReferenceType> classes = null; // name of class -> ReferenceType
205: private List<String> names = null; // list on names of all clases
206: private HashMap cache = new HashMap();
207: // null => list of class loaders
208: // ClassLoaderReference -> list of packages & ReferenceTypes
209: // package name -> list of packages & ReferenceTypes
210: private Comparator comparator = new PackageComparator();
211: private Comparator comparator1 = new ClassLoaderComparator();
212: static final Integer NULL_CLASS_LOADER = new Integer(11);
213:
214: private List<String> getNames() {
215: if (classes == null) {
216: VirtualMachine vm = debugger.getVirtualMachine();
217: List referenceTypes = new ArrayList();
218: if (vm != null)
219: referenceTypes = vm.allClasses();
220: int i, k = referenceTypes.size();
221: names = new ArrayList<String>();
222: classes = new ArrayList<ReferenceType>();
223: for (i = 0; i < k; i++) {
224: ReferenceType rt = (ReferenceType) referenceTypes
225: .get(i);
226: if (rt instanceof ArrayType)
227: continue;
228: names.add(rt.name());
229: classes.add(rt);
230: }
231: }
232: return names;
233: }
234:
235: private Object[] getLoaders() {
236: Object[] ch = (Object[]) cache.get(null);
237: if (ch != null)
238: return ch;
239: List<String> names = getNames();
240: Set loaders = new TreeSet(comparator1);
241: int i, k = names.size();
242: for (i = 0; i < k; i++) {
243: try {
244: String name = names.get(i);
245: ReferenceType rt = classes.get(i);
246: ClassLoaderReference clr = rt.classLoader();
247: if (clr == null)
248: loaders.add(NULL_CLASS_LOADER);
249: else if (clr.referenceType().name().equals(
250: "sun.reflect.DelegatingClassLoader"))
251: continue;
252: else
253: loaders.add(clr);
254: } catch (ObjectCollectedException ex) {
255: }
256: }
257: ch = loaders.toArray();
258: cache.put(null, ch);
259: return ch;
260: }
261:
262: private Object[] getPackages(ClassLoaderReference clr) {
263: Object[] ch = clr == null ? (Object[]) cache
264: .get(NULL_CLASS_LOADER) : (Object[]) cache.get(clr);
265: if (ch != null)
266: return ch;
267: boolean flat = classesProperties.getBoolean("flat", true);
268: List<String> names = getNames();
269: Set objects = new TreeSet(comparator);
270: int i, k = names.size();
271: for (i = 0; i < k; i++) {
272: String name = names.get(i);
273: ReferenceType rt = classes.get(i);
274: if (rt.classLoader() != clr)
275: continue;
276: int end;
277: if (flat) {
278: end = name.lastIndexOf('.');
279: } else {
280: end = name.indexOf('.');
281: }
282: if (end < 0) {
283: // ReferenceType found
284: if (name.indexOf('$') < 0) {
285: ReferenceType tr = classes.get(i);
286: objects.add(tr);
287: }
288: } else
289: objects
290: .add(new Object[] { name.substring(0, end), clr });
291: }
292: ch = objects.toArray();
293: if (clr == null)
294: cache.put(NULL_CLASS_LOADER, ch);
295: else
296: cache.put(clr, ch);
297: return ch;
298: }
299:
300: private Object[] getChildren(Object[] parent) {
301: Object[] ch = (Object[]) cache.get(parent);
302: if (ch != null)
303: return ch;
304: List<String> names = getNames();
305: Set objects = new TreeSet(comparator);
306: int i, k = names.size();
307: boolean flat = classesProperties.getBoolean("flat", true);
308: for (i = 0; i < k; i++) {
309: String name = names.get(i);
310: ReferenceType rt = classes.get(i);
311: if (rt.classLoader() != parent[1])
312: continue;
313: String parentN = ((String) parent[0]) + '.';
314: if (!name.startsWith(parentN))
315: continue;
316: int start = (parentN).length();
317: int end = name.indexOf('.', start);
318: if (end < 0) {
319: // ReferenceType found
320: if (name.indexOf('$', start) < 0) {
321: objects.add(rt);
322: }
323: } else if (!flat) {
324: objects.add(new Object[] { name.substring(0, end),
325: rt.classLoader() });
326: }
327: }
328: ch = objects.toArray();
329: cache.put(parent, ch);
330: return ch;
331: }
332:
333: JPDADebuggerImpl getDebugger() {
334: return debugger;
335: }
336:
337: private static String shortName(String name) {
338: int i = name.lastIndexOf('.');
339: if (i < 0)
340: return name;
341: return name.substring(i + 1);
342: }
343:
344: // innerclasses ............................................................
345:
346: private static class Listener implements PropertyChangeListener {
347:
348: private JPDADebugger debugger;
349: private WeakReference<ClassesTreeModel> model;
350:
351: public Listener(ClassesTreeModel tm, JPDADebugger debugger) {
352: this .debugger = debugger;
353: model = new WeakReference<ClassesTreeModel>(tm);
354: debugger.addPropertyChangeListener(this );
355: }
356:
357: void destroy() {
358: debugger.removePropertyChangeListener(this );
359: if (task != null) {
360: // cancel old task
361: task.cancel();
362: if (verbose)
363: System.out.println("ClTM cancel old task " + task);
364: task = null;
365: }
366: }
367:
368: private ClassesTreeModel getModel() {
369: ClassesTreeModel tm = model.get();
370: if (tm == null) {
371: destroy();
372: }
373: return tm;
374: }
375:
376: // currently waiting / running refresh task
377: // there is at most one
378: private RequestProcessor.Task task;
379:
380: public void propertyChange(PropertyChangeEvent e) {
381: if (((e.getPropertyName() == debugger.PROP_CURRENT_CALL_STACK_FRAME) ||
382: //(e.getPropertyName () == debugger.PROP_CURRENT_THREAD) ||
383: (e.getPropertyName() == debugger.PROP_STATE))
384: && (debugger.getState() == debugger.STATE_STOPPED)) {
385: // IF state has been changed to STOPPED or
386: // IF current call stack frame has been changed & state is stoped
387: final ClassesTreeModel ltm = getModel();
388: if (ltm == null)
389: return;
390: if (task != null) {
391: // cancel old task
392: task.cancel();
393: if (verbose)
394: System.out.println("ClTM cancel old task "
395: + task);
396: task = null;
397: }
398: task = RequestProcessor.getDefault().post(
399: new Runnable() {
400: public void run() {
401: if (debugger.getState() != debugger.STATE_STOPPED) {
402: if (verbose)
403: System.out
404: .println("ClTM cancel started task "
405: + task);
406: return;
407: }
408: if (verbose)
409: System.out.println("ClTM do task "
410: + task);
411: ltm.fireTreeChanged();
412: }
413: }, 500);
414: if (verbose)
415: System.out.println("ClTM create task " + task);
416: } else if ((e.getPropertyName() == debugger.PROP_STATE)
417: && (debugger.getState() != debugger.STATE_STOPPED)
418: && (task != null)) {
419: // debugger has been resumed
420: // =>> cancel task
421: task.cancel();
422: if (verbose)
423: System.out.println("ClTM cancel task " + task);
424: task = null;
425: }
426: }
427: }
428:
429: private static class PackageComparator implements Comparator {
430: public PackageComparator() {
431: }
432:
433: public int compare(Object o1, Object o2) {
434: if (o1 instanceof Object[]) {
435: if (o2 instanceof Object[])
436: return ((String) ((Object[]) o1)[0])
437: .compareTo((String) ((Object[]) o2)[0]);
438: return -1;
439: }
440: if (o2 instanceof Object[])
441: return 1;
442: return shortName(((ReferenceType) o1).name()).compareTo(
443: shortName(((ReferenceType) o2).name()));
444: }
445: }
446:
447: private static class ClassLoaderComparator implements Comparator {
448: public ClassLoaderComparator() {
449: }
450:
451: public int compare(Object o1, Object o2) {
452: if (o1 == NULL_CLASS_LOADER) {
453: if (o2 == NULL_CLASS_LOADER)
454: return 0;
455: return -1;
456: }
457: if (o2 == NULL_CLASS_LOADER)
458: return 1;
459: return ((ClassLoaderReference) o1).toString().compareTo(
460: ((ClassLoaderReference) o2).toString());
461: }
462: }
463: }
|