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.insane.impl;
043:
044: import java.lang.reflect.Field;
045: import java.util.*;
046: import javax.swing.BoundedRangeModel;
047:
048: import org.netbeans.insane.scanner.ObjectMap;
049: import org.netbeans.insane.scanner.ScannerUtils;
050: import org.netbeans.insane.scanner.Visitor;
051: import org.netbeans.insane.live.*;
052: import org.netbeans.insane.scanner.Filter;
053:
054: /**
055: * Implementation class, don't use directly.
056: *
057: * @author nenik
058: */
059: public class LiveEngine implements ObjectMap, Visitor {
060:
061: private IdentityHashMap<Object, Object> objects = new IdentityHashMap<Object, Object>();
062: private Map<Object, String> rest = new IdentityHashMap<Object, String>();
063:
064: private BoundedRangeModel progress;
065: private int objCount;
066: private int objExpected;
067: private int objStep;
068: private Filter filter = ScannerUtils
069: .skipNonStrongReferencesFilter();
070:
071: public LiveEngine() {
072: }
073:
074: public LiveEngine(BoundedRangeModel progress) {
075: this .progress = progress;
076: }
077:
078: public LiveEngine(BoundedRangeModel progress, Filter filter) {
079: this .progress = progress;
080: if (filter != null) {
081: this .filter = ScannerUtils.compoundFilter(new Filter[] {
082: filter, this .filter });
083: }
084: }
085:
086: //--------------------------------------------
087: // ObjectMap-like interface. We don't provide IDs though (returns null)
088: public boolean isKnown(Object o) {
089: return objects.containsKey(o);
090: }
091:
092: public String getID(Object o) {
093: objects.put(o, null);
094: ; // mark as known
095: return null; // null - if somebody really uses it, fails quickly
096: }
097:
098: //--------------------------------------------
099: // Visitor interface
100: public void visitClass(Class cls) {
101: }
102:
103: public void visitObject(ObjectMap map, Object object) {
104: if (progress != null) {
105: objCount++;
106: if ((((objCount % objStep) == 0)) && objCount < objExpected)
107: progress.setValue(objCount);
108: }
109: }
110:
111: public void visitArrayReference(ObjectMap map, Object from,
112: Object to, int index) {
113: visitRef(from, to, null);
114: }
115:
116: public void visitObjectReference(ObjectMap map, Object from,
117: Object to, java.lang.reflect.Field ref) {
118: visitRef(from, to, ref);
119: }
120:
121: public void visitStaticReference(ObjectMap map, Object to,
122: java.lang.reflect.Field ref) {
123: visitRef(null, to, ref);
124: }
125:
126: //---------------------------------------------
127: // Implementation
128: private void visitRef(Object from, Object to, Field field) {
129: addIncommingRef(to, from, field);
130: if (rest.containsKey(to)) {
131: rest.remove(to);
132: if (rest.size() == 0)
133: throw new ObjectFoundException();
134: }
135: }
136:
137: private void addIncommingRef(Object to, Object from, Field f) {
138: // wrap statics with special (private) object
139: Object save = from != null ? from : Root.createStatic(f, to);
140:
141: Object entry = objects.get(to);
142: if (entry == null) {
143: if (save instanceof Object[]) {
144: entry = new Object[] { save };
145: } else {
146: entry = save;
147: }
148: } else if (!(entry instanceof Object[])) {
149: entry = new Object[] { entry, save };
150: } else {
151: int origLen = ((Object[]) entry).length;
152: Object[] ret = new Object[origLen + 1];
153: System.arraycopy(entry, 0, ret, 0, origLen);
154: ret[origLen] = save;
155: entry = ret;
156: }
157: objects.put(to, entry);
158: }
159:
160: private Iterator/*<Object>*/getIncomingRefs(Object to) {
161: Object oo = objects.get(to);
162: if (oo instanceof Object[]) {
163: return Arrays.asList((Object[]) oo).iterator();
164: } else {
165: return Collections.singleton(oo).iterator();
166: }
167: }
168:
169: public Map<Object, Path> trace(Collection<Object> objs,
170: Set<Object> roots) {
171: if (progress != null) {
172: long usedMemory = Utils.getUsedMemory();
173: objExpected = (int) (usedMemory / 50);
174: objStep = objExpected / 200; // plan for 200 updates
175: // cover only 90%
176: progress.setRangeProperties(0, 0, 0, 10 * objExpected / 9,
177: false);
178: }
179:
180: for (Object o : objs)
181: rest.put(o, "");
182:
183: Map<Object, Boolean> s = new IdentityHashMap<Object, Boolean>();
184: for (Object o : ScannerUtils.interestingRoots())
185: s.put(o, true);
186: if (roots != null)
187: for (Object o : roots)
188: s.put(roots, true);
189: try {
190: InsaneEngine iEngine = new InsaneEngine(this , filter, this ,
191: true);
192: iEngine.traverse(s.keySet());
193: } catch (ObjectFoundException ex) {
194: } catch (Exception e) {
195: e.printStackTrace();
196: }
197:
198: if (progress != null) {
199: progress.setValue(objExpected); // should move the mark to 90%
200: }
201:
202: Map<Object, Path> result = new IdentityHashMap<Object, Path>();
203:
204: // split last 10% of progress equally among found objects
205: int found = objs.size() - rest.size();
206: int base = objExpected;
207: int step = found > 0 ? objExpected / 9 / found : 0;
208:
209: for (Iterator it = objs.iterator(); it.hasNext();) {
210: Object obj = it.next();
211: if (rest.containsKey(obj))
212: continue; // not found
213: Path toObj = findRoots(obj, s.keySet());
214: if (toObj != null)
215: result.put(obj, toObj);
216: if (progress != null) {
217: base += step;
218: progress.setValue(base);
219: }
220: }
221:
222: return result;
223: }
224:
225: private Path findRoots(Object obj, Set roots) {
226: Set<Path> visited = new HashSet<Path>();
227: Path last = Utils.createPath(obj, null);
228:
229: List<Path> queue = new LinkedList<Path>();
230: queue.add(last);
231: visited.add(last);
232:
233: while (!queue.isEmpty()) {
234: Path act = queue.remove(0);
235: Object item = act.getObject();
236:
237: if (roots.contains(item)) {
238: return act; // XXX provide RootPath wrapper
239: }
240:
241: // follow incomming
242: Iterator it = getIncomingRefs(item);
243: while (it.hasNext()) {
244: Object o = it.next();
245: Path prev = Utils.createPath(o, act);
246: if (o instanceof Root)
247: return prev;
248:
249: if (!visited.contains(prev)) { // add to the queue if not new
250: visited.add(prev);
251: queue.add(prev);
252: }
253: }
254: }
255: return null; // Not found
256: }
257:
258: }
|