1 package gnu.expr;
2 import java.net.*;
3 import gnu.kawa.io.Path;
4 import gnu.kawa.io.URLPath;
5 import gnu.mapping.WrappedException;
6 import gnu.bytecode.ClassType;
7 
8 /** A database of known modules as represented by {@link ModuleInfo}.
9  * Currently there is only a single global instance of {@code ModuleManager};
10  * in the future each different "application" may have their own.
11  */
12 
13 public class ModuleManager
14 {
15   private String compilationDirectory = "";
setCompilationDirectory(String path)16   public void setCompilationDirectory (String path)
17   {
18     if (path == null)
19       path = "";
20     int plen = path.length();
21     if (plen > 0)
22       {
23         char sep = java.io.File.separatorChar; // Or '/' if path is a URL??
24         if (path.charAt(plen - 1) != sep)
25           path = path + sep;
26       }
27     compilationDirectory = path;
28   }
getCompilationDirectory()29   public String getCompilationDirectory () { return compilationDirectory; }
30 
31   static ModuleManager instance = new ModuleManager();
32 
33   /** For now assumes a single global ModuleManager.
34    * Later, might have multiple managers. */
getInstance()35   public static ModuleManager getInstance() { return instance; }
36 
37     int interactiveCounter;
38 
39     int evalCounter;
40 
41     public static final String interactiveClassPrefix = "atInteractiveLevel-";
42     public String evalClassPrefix = "atEvalLevel-";
43 
44     /** Used to generate unique class names for interactive REPLs and loads.
45      * This is incremented from Shell.run.
46      * Unique class names are essential for {@link Compilation#usedClass}.
47      * They're also desirable for debugging.
48      */
getNewInteractiveName()49     public synchronized String getNewInteractiveName() {
50         return interactiveClassPrefix + (++interactiveCounter);
51     }
52 
53     /** Used to generate unique class names for other evals.
54      * Equivalent in functionality to getNewInteractiveName, but with
55      * a different prefix, for better user-friendliness.
56      */
getNewEvalName()57     public synchronized String getNewEvalName() {
58         return evalClassPrefix + (++evalCounter);
59     }
60 
61   public static final long LAST_MODIFIED_CACHE_TIME = 1000;
62   /** Number of milliseconds before we re-check file's modified time. */
63   public long lastModifiedCacheTime = LAST_MODIFIED_CACHE_TIME;
64 
65   /** List of all modules managed by this ModuleManager. */
66   ModuleInfo[] modules;
67   int numModules;
68 
getModule(int index)69   public synchronized ModuleInfo getModule (int index)
70   {
71     return index >= numModules ? null : modules[index];
72   }
73 
find(Compilation comp)74   public synchronized ModuleInfo find (Compilation comp)
75   {
76     ModuleExp mexp = comp.getModule();
77     ClassType ctype = mexp.classFor(comp);
78     String fileName = mexp.getFileName();
79     Path sourceAbsPath = ModuleInfo.absPath(fileName);
80     ModuleInfo info = findWithSourcePath(sourceAbsPath, fileName);
81     info.setClassName(ctype.getName());
82     info.setCompilation(comp);
83     return info;
84   }
85 
add(ModuleInfo info)86   private synchronized void add (ModuleInfo info)
87   {
88     if (modules == null)
89       modules = new ModuleInfo[10];
90     else if (numModules == modules.length)
91       {
92         ModuleInfo[] tmp = new ModuleInfo[2 * numModules];
93         System.arraycopy(modules, 0, tmp, 0, numModules);
94         modules = tmp;
95       }
96     modules[numModules++] = info;
97   }
98 
createWithClassName(String className)99     public ModuleInfo createWithClassName(String className) {
100         ModuleInfo info = new ModuleInfo();
101         info.setClassName(className);
102         add(info);
103         return info;
104     }
105 
searchWithClassName(String className)106   public synchronized ModuleInfo searchWithClassName (String className)
107   {
108     for (int i = numModules;  --i >= 0; )
109       {
110         ModuleInfo info = modules[i];
111         if (className.equals(info.getClassName()))
112           return info;
113       }
114     return null;
115   }
116 
findWithClass(Class clas)117   public static synchronized ModuleInfo findWithClass (Class clas)
118   {
119     ModuleInfo info = ModuleInfo.mapClassToInfo.get(clas);
120     if (info == null)
121       {
122         info = new ModuleInfo();
123         info.setModuleClass(clas);
124       }
125     return info;
126   }
127 
findWithClassName(String className)128   public ModuleInfo findWithClassName (String className)
129   {
130     ModuleInfo info = searchWithClassName(className);
131     if (info != null)
132       return info;
133     try
134       {
135         return findWithClass(ClassType.getContextClass(className));
136       }
137     catch (Exception ex)
138       {
139         throw WrappedException.wrapIfNeeded(ex);
140       }
141   }
142 
searchWithAbsSourcePath(String sourcePath)143   private synchronized ModuleInfo searchWithAbsSourcePath (String sourcePath)
144   {
145     for (int i = numModules;  --i >= 0; )
146       {
147         ModuleInfo info = modules[i];
148         if (sourcePath.equals(info.getSourceAbsPathname()))
149           return info;
150       }
151     return null;
152   }
153 
154   public synchronized ModuleInfo
findWithSourcePath(Path sourceAbsPath, String sourcePath)155   findWithSourcePath (Path sourceAbsPath, String sourcePath)
156   {
157     String sourceAbsPathname = sourceAbsPath.toString();
158     ModuleInfo info = searchWithAbsSourcePath(sourceAbsPathname);
159     if (info == null)
160       {
161         info = new ModuleInfo();
162         info.sourcePath = sourcePath;
163         info.sourceAbsPath = sourceAbsPath;
164         info.sourceAbsPathname = sourceAbsPathname;
165         add(info);
166       }
167     return info;
168   }
169 
findWithSourcePath(String sourcePath)170   public synchronized ModuleInfo findWithSourcePath (String sourcePath)
171   {
172     return findWithSourcePath(ModuleInfo.absPath(sourcePath), sourcePath);
173   }
174 
findWithURL(URL url)175   public synchronized ModuleInfo findWithURL (URL url)
176   {
177     Path sourceAbsPath = URLPath.valueOf(url);
178     String sourcePath = url.toExternalForm();
179     return findWithSourcePath(sourceAbsPath, sourcePath);
180   }
181 
182   /** Called by compiler-generated code.
183    * The compiler generates in each package a class that extends
184    * {@link ModuleSet}, and that contains a
185    * {@link ModuleSet#register(ModuleManager)} method that calls
186    * back to this method.  This method then registers the specified module.
187    */
register(String moduleClass, String moduleSource, String moduleUri)188   public synchronized void register (String moduleClass, String moduleSource, String moduleUri)
189   {
190     // Unclear what is the right thing to do if we have an existing module
191     // with the same source or class name.  One case is when we're explicitly
192     // compiling a source file and (in XQuery) importing (other) modules in the
193     // same namespace.  In that case the file we're compiling should take
194     // precedence over old data in the packages's existing $ModulesMap$ class.
195     if (searchWithClassName(moduleClass) != null)
196       return;
197     Path sourcePath = Path.valueOf(moduleSource);
198     Path sourceAbsPath = sourcePath.getCanonical();
199     String sourceAbsPathname = sourceAbsPath.toString();
200     if (searchWithAbsSourcePath(sourceAbsPathname) != null)
201       return;
202     ModuleInfo info = new ModuleInfo();
203     if (sourcePath.isAbsolute())
204       {
205         info.sourceAbsPath = sourcePath;
206         info.sourceAbsPathname = sourceAbsPathname;
207       }
208     else
209       {
210         // Resolve moduleSource against moduleClass path.
211         try
212           {
213             // See comment in loadPackageInfo.
214             Class setClass = this.packageInfoChain.getClass();
215             String setClassName = setClass.getName().replace('.', '/')+".class";
216             java.net.URL setClassURL
217               = setClass.getClassLoader().getResource(setClassName);
218             sourceAbsPath = URLPath.valueOf(setClassURL).resolve(moduleSource);
219             info.sourceAbsPath = sourceAbsPath;
220             info.sourceAbsPathname = sourceAbsPath.toString();
221           }
222         catch (Exception ex)
223           {
224             return;
225           }
226       }
227     info.setClassName(moduleClass);
228     info.sourcePath = moduleSource;
229     info.uri = moduleUri;
230     add(info);
231   }
232 
233   /** List of {@link ModuleSet}s registered with this {@code ModuleManager}. */
234   ModuleSet packageInfoChain;
235 
236   /** Search for and if needed load the {@link ModuleSet} for a package.
237    */
loadPackageInfo(String packageName)238   public synchronized void loadPackageInfo (String packageName)
239     throws ClassNotFoundException, InstantiationException, IllegalAccessException
240   {
241     String moduleSetClassName = packageName + "." + ModuleSet.MODULES_MAP;
242 
243     for (ModuleSet set = packageInfoChain;  set != null;  set = set.next)
244       {
245         String setName = set.getClass().getName();
246         if (setName.equals(moduleSetClassName))
247           continue;
248       }
249     Class setClass = Class.forName(moduleSetClassName);
250     ModuleSet instance = (ModuleSet) setClass.newInstance();
251 
252     instance.next = this.packageInfoChain;
253     // The ModuleInfo.register method depends on packageInfoChain being
254     // the current ModuleSet.  Bit of a kludge.
255     this.packageInfoChain = instance;
256     instance.register(this);
257   }
258 
259   /** Reset the set of known modules. */
clear()260   public synchronized void clear ()
261   {
262     // Clear modules and packageIndoChain lists.
263     // We also clean the 'next' fields, to avoid leaks if
264     // somethings happens to be pointing at a ModuleInto or ModuleSet.
265     // This may be overkill.
266     ModuleSet set = packageInfoChain;
267     while (set != null)
268       {
269         ModuleSet next = set.next;
270         set.next = null;
271         set = next;
272       }
273     packageInfoChain = null;
274 
275     modules = null;
276     numModules = 0;
277   }
278 }
279