001: /*
002: * Copyright 2006 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.dev.cfg;
017:
018: import com.google.gwt.core.ext.TreeLogger;
019: import com.google.gwt.core.ext.UnableToCompleteException;
020: import com.google.gwt.dev.util.Util;
021: import com.google.gwt.dev.util.xml.ReflectiveParser;
022: import com.google.gwt.util.tools.Utility;
023:
024: import java.io.File;
025: import java.io.Reader;
026: import java.net.URI;
027: import java.net.URISyntaxException;
028: import java.net.URL;
029: import java.util.HashMap;
030: import java.util.HashSet;
031: import java.util.Iterator;
032: import java.util.Map;
033: import java.util.Set;
034:
035: /**
036: * The top-level API for loading module XML.
037: */
038: public class ModuleDefLoader {
039:
040: // Should always be true. If it is false complete type oracle analysis and
041: // bytecode cache reload will occur on each reload.
042: private static boolean enableCachingModules = true;
043: private static final Set forceInherits = new HashSet();
044: private static final Map loadedModules = new HashMap();
045:
046: /**
047: * Forces all modules loaded via subsequent calls to
048: * {@link #loadFromClassPath(TreeLogger, String)} to automatically inherit the
049: * specified module. If this method is called multiple times, the order of
050: * inclusion is unspecified.
051: *
052: * @param moduleName The module all subsequently loaded modules should inherit
053: * from.
054: */
055: public static void forceInherit(String moduleName) {
056: forceInherits.add(moduleName);
057: }
058:
059: /**
060: * Gets whether module caching is enabled.
061: *
062: * @return <code>true</code> if module cachine is enabled, otherwise
063: * <code>false</code>.
064: */
065: public static boolean getEnableCachingModules() {
066: return enableCachingModules;
067: }
068:
069: /**
070: * Loads a new module from the class path.
071: *
072: * @param logger Logs the process.
073: * @param moduleName The module to load.
074: * @return The loaded module.
075: * @throws UnableToCompleteException
076: */
077: public static ModuleDef loadFromClassPath(TreeLogger logger,
078: String moduleName) throws UnableToCompleteException {
079: ModuleDef moduleDef = (ModuleDef) loadedModules.get(moduleName);
080: if (moduleDef == null || moduleDef.isGwtXmlFileStale()) {
081: moduleDef = new ModuleDefLoader().load(logger, moduleName);
082: if (enableCachingModules) {
083: loadedModules.put(moduleName, moduleDef);
084: }
085: } else {
086: moduleDef.refresh(logger);
087: }
088: return moduleDef;
089: }
090:
091: /**
092: * Enables or disables caching loaded modules for subsequent requests.
093: *
094: * @param enableCachingModules If <code>true</code> subsequent calls to
095: * {@link #loadFromClassPath(TreeLogger, String)} will cause the
096: * resulting module to be cached. If <code>false</code> such
097: * modules will not be cached.
098: */
099: public static void setEnableCachingModules(
100: boolean enableCachingModules) {
101: ModuleDefLoader.enableCachingModules = enableCachingModules;
102: }
103:
104: private final Set alreadyLoadedModules = new HashSet();
105:
106: private final ClassLoader classLoader;
107:
108: private ModuleDefLoader() {
109: this .classLoader = Thread.currentThread()
110: .getContextClassLoader();
111: }
112:
113: /**
114: * Loads a new module into <code>moduleDef</code> as an included module.
115: *
116: * @param logger Logs the process.
117: * @param moduleName The module to load.
118: * @param moduleDef The module to add the new module to.
119: * @throws UnableToCompleteException
120: */
121: void nestedLoad(TreeLogger logger, String moduleName,
122: ModuleDef moduleDef) throws UnableToCompleteException {
123:
124: if (alreadyLoadedModules.contains(moduleName)) {
125: logger.log(TreeLogger.TRACE, "Module '" + moduleName
126: + "' has already been loaded and will be skipped",
127: null);
128: return;
129: } else {
130: alreadyLoadedModules.add(moduleName);
131: }
132:
133: // Find the specified module using the classpath.
134: //
135: String slashedModuleName = moduleName.replace('.', '/');
136: String resName = slashedModuleName + ".gwt.xml";
137: URL moduleURL = classLoader.getResource(resName);
138:
139: if (moduleURL != null) {
140: String externalForm = moduleURL.toExternalForm();
141: logger.log(TreeLogger.TRACE, "Module location: "
142: + externalForm, null);
143: try {
144: if ((!(externalForm.startsWith("jar:file")))
145: && (!(externalForm.startsWith("zip:file")))
146: && (!(externalForm.startsWith("http://")))
147: && (!(externalForm.startsWith("ftp://")))) {
148: File gwtXmlFile = new File(new URI(externalForm));
149: moduleDef.addGwtXmlFile(gwtXmlFile);
150: }
151: } catch (URISyntaxException e) {
152: logger.log(TreeLogger.ERROR, "Error parsing URI", e);
153: throw new UnableToCompleteException();
154: }
155: }
156: if (moduleURL == null) {
157: String msg = "Unable to find '"
158: + resName
159: + "' on your classpath; could be a typo, or maybe you forgot to include a classpath entry for source?";
160: logger.log(TreeLogger.ERROR, msg, null);
161: throw new UnableToCompleteException();
162: }
163:
164: // Extract just the directory containing the module.
165: //
166: String moduleDir = "";
167: int i = slashedModuleName.lastIndexOf('/');
168: if (i != -1) {
169: moduleDir = slashedModuleName.substring(0, i) + "/";
170: }
171:
172: // Parse it.
173: //
174: Reader r = null;
175: try {
176: r = Util.createReader(logger, moduleURL);
177: ModuleDefSchema schema = new ModuleDefSchema(logger, this ,
178: moduleURL, moduleDir, moduleDef);
179: ReflectiveParser.parse(logger, schema, r);
180: } finally {
181: Utility.close(r);
182: }
183: }
184:
185: /**
186: *
187: * This method loads a module.
188: *
189: * @param logger used to log the loading process
190: * @param moduleName the name of the module
191: * @return the module returned -- cannot be null
192: * @throws UnableToCompleteException if module loading failed.
193: */
194: private ModuleDef load(TreeLogger logger, String moduleName)
195: throws UnableToCompleteException {
196: logger = logger.branch(TreeLogger.TRACE, "Loading module '"
197: + moduleName + "'", null);
198:
199: if (!ModuleDef.isValidModuleName(moduleName)) {
200: logger.log(TreeLogger.ERROR, "Invalid module name: '"
201: + moduleName + "'", null);
202: throw new UnableToCompleteException();
203: }
204:
205: ModuleDef moduleDef = new ModuleDef(moduleName);
206: for (Iterator it = forceInherits.iterator(); it.hasNext();) {
207: String forceInherit = (String) it.next();
208: TreeLogger branch = logger.branch(TreeLogger.TRACE,
209: "Loading forceably inherited module '"
210: + forceInherit + "'", null);
211: nestedLoad(branch, forceInherit, moduleDef);
212: }
213: nestedLoad(logger, moduleName, moduleDef);
214:
215: // Do any final setup.
216: //
217: moduleDef.normalize(logger);
218:
219: return moduleDef;
220: }
221: }
|