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 
  67     public SourceManager(Environment env) {
  68         this(env, new SearchPath(""));
  69     }
  70 
  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      */
  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 
  89     public void addSourceListener(SourceListener l) {
  90         sourceListeners.add(l);
  91     }
  92 
  93     public void removeSourceListener(SourceListener l) {
  94         sourceListeners.remove(l);
  95     }
  96 
  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      */
 108     public SearchPath getSourcePath() {
 109         return sourcePath;
 110     }
 111 
 112     /**
 113      * Get source object associated with a Location.
 114      */
 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      */
 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?
 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
 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
 184         public void classUnload(ClassUnloadEventSet e) {
 185             //### iterate through looking for (e.getTypeName()).
 186             //### then remove it.
 187         }
 188     }
 189 }