1 /*
2  * Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 /*
27  * This source code is provided to illustrate the usage of a given feature
28  * or technique and has been deliberately simplified. Additional steps
29  * required for a production-quality application, such as security checks,
30  * input validation and proper error handling, might not be present in
31  * this sample code.
32  */
33 
34 
35 package com.sun.tools.example.debug.gui;
36 
37 import java.io.*;
38 import java.util.*;
39 
40 import com.sun.jdi.*;
41 
42 import com.sun.tools.example.debug.event.*;
43 
44 /**
45  * Manage the list of source files.
46  * Origin of SourceListener events.
47  */
48 public class SourceManager {
49 
50     //### TODO: The source cache should be aged, and some cap
51     //### put on memory consumption by source files loaded into core.
52 
53     private List<SourceModel> sourceList;
54     private SearchPath sourcePath;
55 
56     private ArrayList<SourceListener> sourceListeners = new ArrayList<SourceListener>();
57 
58     private Map<ReferenceType, SourceModel> classToSource = new HashMap<ReferenceType, SourceModel>();
59 
60     private Environment env;
61 
62     /**
63      * Hold on to it so it can be removed.
64      */
65     private SMClassListener classListener = new SMClassListener();
66 
SourceManager(Environment env)67     public SourceManager(Environment env) {
68         this(env, new SearchPath(""));
69     }
70 
SourceManager(Environment env, SearchPath sourcePath)71     public SourceManager(Environment env, SearchPath sourcePath) {
72         this.env = env;
73         this.sourceList = new LinkedList<SourceModel>();
74         this.sourcePath = sourcePath;
75         env.getExecutionManager().addJDIListener(classListener);
76     }
77 
78     /**
79      * Set path for access to source code.
80      */
setSourcePath(SearchPath sp)81     public void setSourcePath(SearchPath sp) {
82         sourcePath = sp;
83         // Old cached sources are now invalid.
84         sourceList = new LinkedList<SourceModel>();
85         notifySourcepathChanged();
86         classToSource = new HashMap<ReferenceType, SourceModel>();
87     }
88 
addSourceListener(SourceListener l)89     public void addSourceListener(SourceListener l) {
90         sourceListeners.add(l);
91     }
92 
removeSourceListener(SourceListener l)93     public void removeSourceListener(SourceListener l) {
94         sourceListeners.remove(l);
95     }
96 
notifySourcepathChanged()97     private void notifySourcepathChanged() {
98         ArrayList<SourceListener> l = new ArrayList<SourceListener>(sourceListeners);
99         SourcepathChangedEvent evt = new SourcepathChangedEvent(this);
100         for (int i = 0; i < l.size(); i++) {
101             l.get(i).sourcepathChanged(evt);
102         }
103     }
104 
105     /**
106      * Get path for access to source code.
107      */
getSourcePath()108     public SearchPath getSourcePath() {
109         return sourcePath;
110     }
111 
112     /**
113      * Get source object associated with a Location.
114      */
sourceForLocation(Location loc)115     public SourceModel sourceForLocation(Location loc) {
116         return sourceForClass(loc.declaringType());
117     }
118 
119     /**
120      * Get source object associated with a class or interface.
121      * Returns null if not available.
122      */
sourceForClass(ReferenceType refType)123     public SourceModel sourceForClass(ReferenceType refType) {
124         SourceModel sm = classToSource.get(refType);
125         if (sm != null) {
126             return sm;
127         }
128         try {
129             String filename = refType.sourceName();
130             String refName = refType.name();
131             int iDot = refName.lastIndexOf('.');
132             String pkgName = (iDot >= 0)? refName.substring(0, iDot+1) : "";
133             String full = pkgName.replace('.', File.separatorChar) + filename;
134             File path = sourcePath.resolve(full);
135             if (path != null) {
136                 sm = sourceForFile(path);
137                 classToSource.put(refType, sm);
138                 return sm;
139             }
140             return null;
141         } catch (AbsentInformationException e) {
142             return null;
143         }
144     }
145 
146     /**
147      * Get source object associated with an absolute file path.
148      */
149     //### Use hash table for this?
sourceForFile(File path)150     public SourceModel sourceForFile(File path) {
151         Iterator<SourceModel> iter = sourceList.iterator();
152         SourceModel sm = null;
153         while (iter.hasNext()) {
154             SourceModel candidate = iter.next();
155             if (candidate.fileName().equals(path)) {
156                 sm = candidate;
157                 iter.remove();    // Will move to start of list.
158                 break;
159             }
160         }
161         if (sm == null && path.exists()) {
162             sm = new SourceModel(env, path);
163         }
164         if (sm != null) {
165             // At start of list for faster access
166             sourceList.add(0, sm);
167         }
168         return sm;
169     }
170 
171     private class SMClassListener extends JDIAdapter
172                                    implements JDIListener {
173 
174         @Override
classPrepare(ClassPrepareEventSet e)175         public void classPrepare(ClassPrepareEventSet e) {
176             ReferenceType refType = e.getReferenceType();
177             SourceModel sm = sourceForClass(refType);
178             if (sm != null) {
179                 sm.addClass(refType);
180             }
181         }
182 
183         @Override
classUnload(ClassUnloadEventSet e)184         public void classUnload(ClassUnloadEventSet e) {
185             //### iterate through looking for (e.getTypeName()).
186             //### then remove it.
187         }
188     }
189 }
190