001: /**************************************************************************/
002: /* N I C E */
003: /* A high-level object-oriented research language */
004: /* (c) Daniel Bonniot 2001 */
005: /* */
006: /* This program is free software; you can redistribute it and/or modify */
007: /* it under the terms of the GNU General Public License as published by */
008: /* the Free Software Foundation; either version 2 of the License, or */
009: /* (at your option) any later version. */
010: /* */
011: /**************************************************************************/package bossa.modules;
012:
013: import bossa.util.*;
014: import java.util.*;
015: import java.io.*;
016: import nice.tools.util.System;
017: import bossa.syntax.Definition;
018: import bossa.syntax.LocatedString;
019:
020: /**
021: An abstract package source, where source or interface files
022: and compiled code if applicable can be found.
023:
024: @version $Date: 2005/06/18 13:26:55 $
025: @author Daniel Bonniot
026: */
027:
028: class Content {
029: Content(Package pkg, SourceContent source, CompiledContent compiled) {
030: this .pkg = pkg;
031: this .source = source;
032: this .compiled = compiled;
033:
034: if (source == null && compiled == null)
035: User.error(pkg, "Package " + pkg.getName()
036: + " is not available." + "\nThe source path is: "
037: + str(pkg.compilation.sourcePath)
038: + "\nThe package path is: "
039: + str(pkg.compilation.packagePath));
040:
041: // By default, use the last compilation time also as last modification time
042: // This is a safe bet, and will be overwritten if source is also available.
043: if (compiled != null)
044: lastModification = lastCompilation = compiled.lastCompilation;
045:
046: if (source != null)
047: lastModification = source.lastModification;
048: }
049:
050: private String str(String s) {
051: return s == null ? "<empty>" : s;
052: }
053:
054: List getImports(Set opens, boolean shouldReload) {
055: Content.Unit[] readers;
056: /*
057: If the package is up to date, preferably load imports from
058: the compiled package, since that involves touching only one file
059: instead of possiby several.
060: */
061: if (source != null
062: && (shouldReload || compiled == null || lastModification > lastCompilation))
063: readers = source.getDefinitions();
064: else {
065: pkg.compiledModule = new bossa.syntax.Module(pkg, pkg
066: .getName(), pkg.packageScope);
067: readers = compiled.getDefinitions();
068: }
069:
070: LinkedList imports = new LinkedList();
071:
072: for (int i = 0; i < readers.length; i++)
073: read(readers[i], imports, opens);
074:
075: return imports;
076: }
077:
078: void getDefinitions(List definitions, boolean shouldReload) {
079: Content.Unit[] readers = getReaders(shouldReload);
080:
081: for (int i = 0; i < readers.length; i++)
082: try {
083: read(readers[i], definitions);
084: } catch (UserError ex) {
085: pkg.compilation.error(ex);
086: }
087:
088: pkg.compilation.exitIfErrors();
089: }
090:
091: private void read(Content.Unit unit, List imports, Set opens) {
092: bossa.util.Location.setCurrentFile(unit.file);
093:
094: bossa.syntax.LocatedString pkgName = pkg.compilation.parser
095: .readImports(unit.reader, imports, opens);
096:
097: if (pkgName != null && !pkgName.equals(pkg.name))
098: User.error(pkgName, source
099: + " declares it belongs to package " + pkgName
100: + ", but it was found in package " + pkg.name);
101: }
102:
103: private void read(Content.Unit unit, List definitions) {
104: bossa.util.Location.setCurrentFile(unit.file);
105:
106: bossa.syntax.Module module;
107:
108: if (pkg.compiledModule != null)
109: module = pkg.compiledModule;
110: else {
111: nice.tools.visibility.Scope scope = new nice.tools.visibility.Scope(
112: unit.getName(), pkg.packageScope);
113:
114: module = new bossa.syntax.Module(pkg, unit.name, scope);
115: }
116:
117: Definition.currentModule = module;
118:
119: pkg.compilation.parser.read(unit.reader, module, definitions);
120:
121: Definition.currentModule = null;
122: }
123:
124: /**
125: @param shouldReload reload if the source if available.
126: **/
127: Content.Unit[] getReaders(boolean shouldReload) {
128: // If no compiled version is available then reload
129: shouldReload |= (compiled == null);
130:
131: // If the package is out-of-date, then we must recompile
132: if (lastModification > lastCompilation) {
133: if (Debug.modules && !shouldReload) {
134: Debug.println(pkg + " compiled: "
135: + System.date(lastCompilation));
136: Debug.println(pkg + " modified: "
137: + System.date(lastModification));
138: Debug.println("Recompiling " + pkg);
139: }
140: shouldReload = true;
141: }
142:
143: // Reload if we should and can
144: if (shouldReload && source != null) {
145: sourceRead = true;
146: return source.getDefinitions();
147: }
148:
149: // We're not reloading. Did we have to?
150: if (lastModification > lastCompilation)
151: User
152: .error(
153: pkg,
154: pkg.getName()
155: + " must be compiled, but no source code is available.\nThe source path is: "
156: + pkg.compilation.sourcePath);
157:
158: if (Debug.modules)
159: Debug.println("Loading compiled version of " + pkg);
160:
161: return compiled.getDefinitions();
162: }
163:
164: /** return a short name to display this package source
165: (typically a file name, an URL, ...) */
166: public String getName() {
167: if (sourceRead)
168: return source.getName();
169: else if (compiled != null)
170: return compiled.getName();
171: else
172: return "";
173: }
174:
175: /** return a longer string that identifies the type of package source too. */
176: public String toString() {
177: return "Source : "
178: + (source == null ? "none" : source.getName())
179: + "\nCompiled: "
180: + (compiled == null ? "none" : compiled.getName());
181: }
182:
183: boolean sourceRead;
184:
185: /** Date of the last modification of the source of this package. */
186: long lastModification;
187:
188: /** Date of the last succesful compilation of this package. */
189: long lastCompilation;
190:
191: void someImportModified(long date) {
192: if (lastModification < date)
193: lastModification = date;
194: }
195:
196: gnu.bytecode.ClassType getBytecode() {
197: if (sourceRead)
198: return null;
199: else
200: return compiled.bytecode;
201: }
202:
203: gnu.bytecode.ClassType getDispatch() {
204: if (sourceRead)
205: return null;
206: else
207: return compiled.dispatch;
208: }
209:
210: gnu.bytecode.ClassType readClass(String name) {
211: if (sourceRead)
212: return null;
213: else
214: return compiled.readClass(name);
215: }
216:
217: /** @return the directory in which to place generated files
218: of this package.
219:
220: This is used even if the package was not recompiled,
221: e.g. to know where to place the 'dispatch' class.
222: */
223: File getOutputDirectory() {
224: String destDir = pkg.compilation.destinationDir;
225:
226: if (destDir == null)
227: // Default: write in the source directory if it exists
228: if (source != null)
229: return source.getOutputDirectory();
230: else
231: // Otherwise do as if the destnation is the current directory
232: destDir = ".";
233:
234: // Return the appropriate subdirectory of the destination dir
235: File res = new File(destDir, pkg.getName().replace('.',
236: File.separatorChar));
237: // Make sure that we can write there
238: res.mkdirs();
239: return res;
240: }
241:
242: /****************************************************************
243: * Private
244: ****************************************************************/
245:
246: private Package pkg;
247: private SourceContent source;
248: private CompiledContent compiled;
249:
250: static protected class Unit {
251: protected Unit(BufferedReader reader, File file) {
252: this .reader = reader;
253: this .file = file;
254: }
255:
256: protected Unit(BufferedReader reader, String name) {
257: this .reader = reader;
258: this .name = name;
259: }
260:
261: public String getName() {
262: if (file != null)
263: return file.toString();
264: else
265: return name;
266: }
267:
268: BufferedReader reader;
269: String name;
270: File file;
271: }
272:
273: static protected class Stream {
274: Stream(InputStream stream, String name) {
275: this .stream = stream;
276: this .name = name;
277: }
278:
279: public boolean equals(Object o) {
280: return o instanceof Stream
281: && name.equals(((Stream) o).name);
282: }
283:
284: public int hashCode() {
285: return name.hashCode();
286: }
287:
288: InputStream stream;
289: String name;
290: }
291:
292: /**
293: Returns the compiled classes of this source.
294: */
295: Stream[] getClasses(boolean linkPerformed) {
296: /* We first add the generated classes, then all those compiled classes
297: that were not regenerated. The Set ensures that the previous version of
298: regenerated classes is not included.
299: */
300: Set res = new HashSet();
301:
302: DirectoryCompiledContent.addClasses(res, getOutputDirectory());
303:
304: if (!sourceRead)
305: compiled.addClasses(res);
306:
307: return (Stream[]) res.toArray(new Stream[res.size()]);
308: }
309: }
|