001: package gnu.expr;
002:
003: import java.net.*;
004: import gnu.bytecode.ClassType;
005: import gnu.text.*;
006:
007: /** A database of known modules as represented by {@link ModuleInfo}..
008: * Current there is only a single global instanceof {@code ModuleManager};
009: * in the future each different "applications" may have their own.
010: */
011:
012: public class ModuleManager {
013: private String compilationDirectory = "";
014:
015: public void setCompilationDirectory(String path) {
016: if (path == null)
017: path = "";
018: int plen = path.length();
019: if (plen > 0) {
020: char sep = java.io.File.separatorChar; // Or '/' if path is a URL??
021: if (path.charAt(plen - 1) != sep)
022: path = path + sep;
023: }
024: compilationDirectory = path;
025: }
026:
027: public String getCompilationDirectory() {
028: return compilationDirectory;
029: }
030:
031: static ModuleManager instance = new ModuleManager();
032:
033: /** For now assumes a single global ModuleManager.
034: * Later, might have multiple managers. */
035: public static ModuleManager getInstance() {
036: return instance;
037: }
038:
039: public static final long LAST_MODIFIED_CACHE_TIME = 1000;
040: /** Number millseconds before we re-check file's modified time. */
041: public long lastModifiedCacheTime = LAST_MODIFIED_CACHE_TIME;
042:
043: /** Chain of all modules managed by this ModuleManager.
044: * Linked together by ModuleInfo's next field. */
045: ModuleInfo modules;
046:
047: public ModuleInfo firstModule() {
048: return modules;
049: }
050:
051: public ModuleInfo find(Compilation comp) {
052: ModuleExp mexp = comp.getModule();
053: ClassType ctype = mexp.classFor(comp);
054: ModuleInfo info = findWithClassName(ctype.getName());
055: info.setCompilation(comp);
056: return info;
057: }
058:
059: public void add(ModuleInfo info) {
060: info.next = modules;
061: modules = info;
062: }
063:
064: public ModuleInfo searchWithClassName(String className) {
065: for (ModuleInfo info = modules; info != null; info = info.next)
066: if (className.equals(info.className))
067: return info;
068: return null;
069: }
070:
071: public synchronized ModuleInfo findWithClassName(String className) {
072: ModuleInfo info = searchWithClassName(className);
073: if (info == null) {
074: info = new ModuleInfo();
075: info.className = className;
076: add(info);
077: }
078: return info;
079: }
080:
081: private ModuleInfo searchWithAbsSourcePath(String sourcePath) {
082: for (ModuleInfo info = modules; info != null; info = info.next)
083: if (sourcePath.equals(info.getSourceAbsPathname()))
084: return info;
085: return null;
086: }
087:
088: public synchronized ModuleInfo findWithSourcePath(
089: Path sourceAbsPath, String sourcePath) {
090: String sourceAbsPathname = sourceAbsPath.toString();
091: ModuleInfo info = searchWithAbsSourcePath(sourceAbsPathname);
092: if (info == null) {
093: info = new ModuleInfo();
094: info.sourcePath = sourcePath;
095: info.sourceAbsPath = sourceAbsPath;
096: info.sourceAbsPathname = sourceAbsPathname;
097: add(info);
098: }
099: return info;
100: }
101:
102: public ModuleInfo findWithSourcePath(String sourcePath) {
103: return findWithSourcePath(ModuleInfo.absPath(sourcePath),
104: sourcePath);
105: }
106:
107: public ModuleInfo findWithURL(URL url) {
108: Path sourceAbsPath = URLPath.valueOf(url);
109: String sourcePath = url.toExternalForm();
110: return findWithSourcePath(sourceAbsPath, sourcePath);
111: }
112:
113: /** Called by compiler-generated code.
114: * The compiler generates in each package a class that extends
115: * {@link ModuleSet}, and that contains a
116: * {@link ModuleSet#register(ModuleManager)} method that calls
117: * back to this method. This method then registers the specified module.
118: */
119: public void register(String moduleClass, String moduleSource,
120: String moduleUri) {
121: // Unclear what is the right thing to do if we have an existing module
122: // with the same source or class name. One case is when we're explicitly
123: // compiling a source file and (in XQuery) importing (other) modules in the
124: // same namespace. In that case the file we're compiling should take
125: // precedence over old data in the packages's existing $ModulesMap$ class.
126: if (searchWithClassName(moduleClass) != null)
127: return;
128: Path sourcePath = Path.valueOf(moduleSource);
129: Path sourceAbsPath = sourcePath.getCanonical();
130: String sourceAbsPathname = sourceAbsPath.toString();
131: if (searchWithAbsSourcePath(sourceAbsPathname) != null)
132: return;
133: ModuleInfo info = new ModuleInfo();
134: if (sourcePath.isAbsolute()) {
135: info.sourceAbsPath = sourcePath;
136: info.sourceAbsPathname = sourceAbsPathname;
137: } else {
138: // Resolve moduleSource against moduleClass path.
139: try {
140: // See comment in loadPackageInfo.
141: Class setClass = this .packageInfoChain.getClass();
142: String setClassName = setClass.getName().replace('.',
143: '/')
144: + ".class";
145: java.net.URL setClassURL = setClass.getClassLoader()
146: .getResource(setClassName);
147: sourceAbsPath = URLPath.valueOf(setClassURL).resolve(
148: moduleSource);
149: info.sourceAbsPath = sourceAbsPath;
150: info.sourceAbsPathname = sourceAbsPath.toString();
151: } catch (Throwable ex) {
152: return;
153: }
154: }
155: info.className = moduleClass;
156: info.sourcePath = moduleSource;
157: info.uri = moduleUri;
158: add(info);
159: }
160:
161: /** List of {@link ModuleSet}s registered with this {@code ModuleManager}. */
162: ModuleSet packageInfoChain;
163:
164: /** Search for and if needed load the {@link ModuleSet} for a package.
165: */
166: public synchronized void loadPackageInfo(String packageName)
167: throws ClassNotFoundException, InstantiationException,
168: IllegalAccessException {
169: String moduleSetClassName = packageName + "."
170: + ModuleSet.MODULES_MAP;
171:
172: for (ModuleSet set = packageInfoChain; set != null; set = set.next) {
173: String setName = set.getClass().getName();
174: if (setName.equals(moduleSetClassName))
175: continue;
176: }
177: Class setClass = Class.forName(moduleSetClassName);
178: ModuleSet instance = (ModuleSet) setClass.newInstance();
179:
180: instance.next = this .packageInfoChain;
181: // The ModuleInfo.register method depends on packageInfoChain being
182: // the current ModuleSet. Bit of a kludge.
183: this .packageInfoChain = instance;
184: instance.register(this );
185: }
186:
187: /** Reset the set of known modules. */
188: public void clear() {
189: // Clear modules and packageIndoChain lists.
190: // We also clean the 'next' fields, to avoid leaks if
191: // somethings happens to be pointing at a ModuleInto or ModuleSet.
192: // This may be overkill.
193: ModuleSet set = packageInfoChain;
194: while (set != null) {
195: ModuleSet next = set.next;
196: set.next = null;
197: set = next;
198: }
199: packageInfoChain = null;
200:
201: ModuleInfo module = modules;
202: while (module != null) {
203: ModuleInfo next = module.next;
204: module.next = null;
205: module = next;
206: }
207: modules = null;
208: }
209: }
|