001: /*
002: * Copyright 2007 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.user.tools;
017:
018: import com.google.gwt.user.tools.util.ArgHandlerEclipse;
019: import com.google.gwt.user.tools.util.ArgHandlerIgnore;
020: import com.google.gwt.user.tools.util.ArgHandlerOverwrite;
021: import com.google.gwt.util.tools.ArgHandlerExtra;
022: import com.google.gwt.util.tools.ArgHandlerOutDir;
023: import com.google.gwt.util.tools.ToolBase;
024: import com.google.gwt.util.tools.Utility;
025:
026: import java.io.File;
027: import java.io.IOException;
028: import java.util.HashMap;
029: import java.util.Map;
030:
031: /**
032: * Creates a GWT application.
033: *
034: */
035: public final class ApplicationCreator extends ToolBase {
036:
037: /**
038: * Arguments for the application creator.
039: *
040: */
041: protected class ArgHandlerAppClass extends ArgHandlerExtra {
042:
043: @Override
044: public boolean addExtraArg(String arg) {
045: if (fullClassName != null) {
046: System.err.println("Too many arguments");
047: return false;
048: }
049:
050: // Check className for certain properties
051: if (!arg.matches("[\\w\\$]+(\\.[\\w\\$]+)+")) {
052: System.err
053: .println("'"
054: + arg
055: + "' does not appear to be a valid fully-qualified Java class name");
056: return false;
057: }
058:
059: // Check out the class name.
060: //
061: if (arg.indexOf('$') != -1) {
062: System.err
063: .println("'"
064: + arg
065: + "': This version of the tool does not support nested classes");
066: return false;
067: }
068:
069: String[] parts = arg.split("\\.");
070: if (parts.length < 2
071: || !parts[parts.length - 2].equals("client")) {
072: System.err
073: .println("'"
074: + arg
075: + "': Please use 'client' as the final package, as in 'com.example.foo.client.MyApp'.\n"
076: + "It isn't technically necessary, but this tool enforces the best practice.");
077: return false;
078: }
079:
080: fullClassName = arg;
081: return true;
082: }
083:
084: @Override
085: public String getPurpose() {
086: return "The fully-qualified name of the application class to create";
087: }
088:
089: @Override
090: public String[] getTagArgs() {
091: return new String[] { "className" };
092: }
093:
094: @Override
095: public boolean isRequired() {
096: return true;
097: }
098: }
099:
100: private static final String PACKAGE_PATH;
101:
102: static {
103: String path = ApplicationCreator.class.getName();
104: path = path.substring(0, path.lastIndexOf('.') + 1);
105: PACKAGE_PATH = path.replace('.', '/');
106: }
107:
108: public static void main(String[] args) {
109: ApplicationCreator creator = new ApplicationCreator();
110: if (creator.processArgs(args)) {
111: if (creator.run()) {
112: return;
113: }
114: }
115: System.exit(1);
116: }
117:
118: /**
119: * @param fullClassName Name of the fully-qualified Java class to create as an
120: * Application.
121: * @param outDir Where to put the output files
122: * @param eclipse The name of a project to attach a .launch config to
123: * @param overwrite Overwrite an existing files if they exist.
124: * @param ignore Ignore existing files if they exist.
125: * @throws IOException
126: */
127: static void createApplication(String fullClassName, File outDir,
128: String eclipse, boolean overwrite, boolean ignore)
129: throws IOException {
130:
131: // Figure out the installation directory
132: String installPath = Utility.getInstallPath();
133: String gwtUserPath = installPath + '/' + "gwt-user.jar";
134: String gwtDevPath = installPath + '/' + Utility.getDevJarName();
135:
136: // Figure out what platform we're on
137: //
138: boolean isWindows = gwtDevPath.substring(
139: gwtDevPath.lastIndexOf('/') + 1).indexOf("windows") >= 0;
140: boolean isMacOsX = gwtDevPath.substring(
141: gwtDevPath.lastIndexOf('/') + 1).indexOf("mac") >= 0;
142:
143: // If the path from here to the install directory is relative, we need to
144: // set specific "base" directory tags; this is for sample generation during
145: // the build.
146: String basePathEnv;
147: if (!new File(installPath).isAbsolute()) {
148: if (isWindows) {
149: basePathEnv = "%~dp0\\";
150: } else {
151: basePathEnv = "$APPDIR/";
152: }
153: } else {
154: basePathEnv = "";
155: }
156:
157: // Check out the class and package names.
158: //
159: int pos = fullClassName.lastIndexOf('.');
160: String clientPackageName = fullClassName.substring(0, pos);
161: String className = fullClassName.substring(pos + 1);
162:
163: // Compute module name and directories
164: //
165: pos = clientPackageName.lastIndexOf('.');
166: File basePackageDir;
167: String moduleName;
168: File javaDir = Utility.getDirectory(outDir, "src", true);
169: if (pos >= 0) {
170: String basePackage = clientPackageName.substring(0, pos);
171: moduleName = basePackage + "." + className;
172: basePackage = basePackage.replace('.', '/');
173: basePackageDir = Utility.getDirectory(javaDir, basePackage,
174: true);
175: } else {
176: moduleName = className;
177: basePackageDir = javaDir;
178: }
179: File clientDir = Utility.getDirectory(basePackageDir, "client",
180: true);
181: File publicDir = Utility.getDirectory(basePackageDir, "public",
182: true);
183: String startupUrl = moduleName + "/" + className + ".html";
184:
185: // Create a map of replacements
186: //
187: Map<String, String> replacements = new HashMap<String, String>();
188: replacements.put("@className", className);
189: replacements.put("@moduleName", moduleName);
190: replacements.put("@clientPackage", clientPackageName);
191: replacements.put("@gwtUserPath", basePathEnv + gwtUserPath);
192: replacements.put("@gwtDevPath", basePathEnv + gwtDevPath);
193: replacements.put("@shellClass", "com.google.gwt.dev.GWTShell");
194: replacements.put("@compileClass",
195: "com.google.gwt.dev.GWTCompiler");
196: replacements.put("@startupUrl", startupUrl);
197: replacements.put("@vmargs", isMacOsX ? "-XstartOnFirstThread"
198: : "");
199:
200: {
201: // Create the module
202: File moduleXML = Utility.createNormalFile(basePackageDir,
203: className + ".gwt.xml", overwrite, ignore);
204: if (moduleXML != null) {
205: String out = Utility.getFileFromClassPath(PACKAGE_PATH
206: + "Module.gwt.xmlsrc");
207: Utility.writeTemplateFile(moduleXML, out, replacements);
208: }
209: }
210:
211: {
212: // Create a skeleton html file
213: File publicHTML = Utility.createNormalFile(publicDir,
214: className + ".html", overwrite, ignore);
215: if (publicHTML != null) {
216: String out = Utility.getFileFromClassPath(PACKAGE_PATH
217: + "AppHtml.htmlsrc");
218: Utility
219: .writeTemplateFile(publicHTML, out,
220: replacements);
221: }
222: }
223:
224: {
225: // Create a skeleton Application class
226: File javaClass = Utility.createNormalFile(clientDir,
227: className + ".java", overwrite, ignore);
228: if (javaClass != null) {
229: String out = Utility.getFileFromClassPath(PACKAGE_PATH
230: + "AppClassTemplate.javasrc");
231: Utility.writeTemplateFile(javaClass, out, replacements);
232: }
233: }
234:
235: if (eclipse != null) {
236: // Create an eclipse launch config
237: replacements.put("@projectName", eclipse);
238: File launchConfig = Utility.createNormalFile(outDir,
239: className + ".launch", overwrite, ignore);
240: if (launchConfig != null) {
241: String out = Utility.getFileFromClassPath(PACKAGE_PATH
242: + "App.launchsrc");
243: Utility.writeTemplateFile(launchConfig, out,
244: replacements);
245: }
246: }
247:
248: // create startup files
249: String extension;
250: if (isWindows) {
251: extension = ".cmd";
252: } else {
253: extension = "";
254: }
255:
256: File gwtshell = Utility.createNormalFile(outDir, className
257: + "-shell" + extension, overwrite, ignore);
258: if (gwtshell != null) {
259: String out = Utility.getFileFromClassPath(PACKAGE_PATH
260: + "gwtshell" + extension + "src");
261: Utility.writeTemplateFile(gwtshell, out, replacements);
262: if (extension.length() == 0) {
263: chmodExecutable(gwtshell);
264: }
265: }
266:
267: File gwtcompile = Utility.createNormalFile(outDir, className
268: + "-compile" + extension, overwrite, ignore);
269: if (gwtcompile != null) {
270: String out = Utility.getFileFromClassPath(PACKAGE_PATH
271: + "gwtcompile" + extension + "src");
272: Utility.writeTemplateFile(gwtcompile, out, replacements);
273: if (extension.length() == 0) {
274: chmodExecutable(gwtcompile);
275: }
276: }
277: }
278:
279: /**
280: * Try to make the given file executable. Implementation tries to exec chmod,
281: * which may fail if the platform doesn't support it. Prints a warning to
282: * stderr if the call fails.
283: *
284: * @param file the file to make executable
285: */
286: private static void chmodExecutable(File file) {
287: try {
288: Runtime.getRuntime().exec(
289: "chmod u+x " + file.getAbsolutePath());
290: } catch (Throwable e) {
291: System.err
292: .println(("Warning: cannot exec chmod to set permission on generated file."));
293: }
294: }
295:
296: private String eclipse = null;
297: private String fullClassName = null;
298: private boolean ignore = false;
299: private File outDir;
300: private boolean overwrite = false;
301:
302: protected ApplicationCreator() {
303:
304: registerHandler(new ArgHandlerEclipse() {
305: @Override
306: public String getPurpose() {
307: return "Creates a debug launch config for the named eclipse project";
308: }
309:
310: @Override
311: public boolean setString(String str) {
312: eclipse = str;
313: return true;
314: }
315: });
316:
317: registerHandler(new ArgHandlerOutDir() {
318: @Override
319: public void setDir(File dir) {
320: outDir = dir;
321: }
322: });
323:
324: registerHandler(new ArgHandlerOverwrite() {
325: @Override
326: public boolean setFlag() {
327: if (ignore) {
328: System.err
329: .println("-overwrite cannot be used with -ignore");
330: return false;
331: }
332: overwrite = true;
333: return true;
334: }
335: });
336:
337: registerHandler(new ArgHandlerIgnore() {
338: @Override
339: public boolean setFlag() {
340: if (overwrite) {
341: System.err
342: .println("-ignore cannot be used with -overwrite");
343: return false;
344: }
345: ignore = true;
346: return true;
347: }
348: });
349:
350: registerHandler(new ArgHandlerAppClass());
351: }
352:
353: protected boolean run() {
354: try {
355: createApplication(fullClassName, outDir, eclipse,
356: overwrite, ignore);
357: return true;
358: } catch (IOException e) {
359: System.err.println(e.getClass().getName() + ": "
360: + e.getMessage());
361: return false;
362: }
363: }
364:
365: }
|