/* * The contents of this file are subject to the terms of the Common Development * and Distribution License (the License). You may not use this file except in * compliance with the License. * * You can obtain a copy of the License at http://www.netbeans.org/cddl.html * or http://www.netbeans.org/cddl.txt. * * When distributing Covered Code, include this CDDL Header Notice in each file * and include the License file at http://www.netbeans.org/cddl.txt. * If applicable, add the following below the CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" */ package org.netbeans.modules.gwt4nb; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.netbeans.api.java.project.JavaProjectConstants; import org.netbeans.api.project.Project; import org.netbeans.api.project.ProjectInformation; import org.netbeans.api.project.ProjectUtils; import org.netbeans.api.project.SourceGroup; import org.netbeans.api.project.ui.OpenProjects; import org.netbeans.spi.project.support.ant.EditableProperties; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileSystem; import org.openide.filesystems.FileSystem.AtomicAction; import org.openide.modules.ModuleInstall; import org.openide.util.Exceptions; /** * Module installation class for gwt4nb * * @author prem * @author see https://github.com/gwt4nb/gwt4nb/ */ public class Gwt4nbModule extends ModuleInstall { private final static long serialVersionUID = 1; private static final List<PropertyInfo> PROPERTY_INFOS = new ArrayList<PropertyInfo>(); static { PROPERTY_INFOS.add(new PropertyInfo("gwt.module", // NOI18N "org.yournamehere.Main", new String [] { // NOI18N "# The names of the modules to compile (separated by a space character)" // NOI18N })); PROPERTY_INFOS.add(new PropertyInfo("gwt.output.dir", // NOI18N "/org.yournamehere.Main", new String [] { // NOI18N "# Folder within the web app context path where the output", // NOI18N "# of the GWT module compilation will be stored.", // NOI18N "# This setting is only used for GWT 1.5. For newer versions please use", // NOI18N "# the rename-to attribute in the GWT module file (.gwt.xml).", // NOI18N })); PROPERTY_INFOS.add(new PropertyInfo("gwt.compiler.output.style", // NOI18N "OBF", new String [] { // NOI18N "# Script output style: OBF[USCATED], PRETTY, or DETAILED", // NOI18N })); PROPERTY_INFOS.add(new PropertyInfo("gwt.compiler.jvmargs", // NOI18N "-Xmx256M", new String [] { // NOI18N "# Additional JVM arguments for the GWT compiler", // NOI18N })); PROPERTY_INFOS.add(new PropertyInfo("gwt.compiler.local.workers", // NOI18N "1", new String [] { // NOI18N "# Specifies the number of local workers to use whe compiling permutations and module(s)", // NOI18N })); PROPERTY_INFOS.add(new PropertyInfo("gwt.compiler.logLevel", // NOI18N "INFO", new String [] { // NOI18N "# The level of logging detail: ERROR, WARN, INFO, TRACE, DEBUG,", // NOI18N })); PROPERTY_INFOS.add(new PropertyInfo("gwt.shell.output.style", // NOI18N "OBF", new String [] { // NOI18N "# Script output style: OBF[USCATED], PRETTY, or DETAILED", // NOI18N })); PROPERTY_INFOS.add(new PropertyInfo("gwt.shell.logLevel", // NOI18N "WARN", new String [] { // NOI18N "# The level of logging detail: ERROR, WARN, INFO, TRACE, DEBUG,", // NOI18N })); PROPERTY_INFOS.add(new PropertyInfo("gwt.shell.jvmargs", // NOI18N "-Xmx256M", new String [] { // NOI18N "# Additional JVM arguments for the GWT shell/GWT hosted mode (GWT 1.6)", // NOI18N "# Add -d32 here and use at least GWT 1.7.1 to debug on a Mac", // NOI18N "# (32-bit JVM is required by GWT for debugging before GWT 2.0)", // NOI18N })); PROPERTY_INFOS.add(new PropertyInfo("gwt.shell.java", // NOI18N "", new String [] { // NOI18N "# Java executable that should be used for the GWT Dev mode", // NOI18N "# This can be used to choose a 32-bit JVM on a 64-bit OS", // NOI18N "# (32-bit JVM is required by GWT for debugging before GWT 2.0)", // NOI18N "# Leave the value empty if the default JVM should be used.", // NOI18N })); PROPERTY_INFOS.add(new PropertyInfo("gwt.version", // NOI18N "1.7", new String [] { // NOI18N "# GWT version: 1.5, 1.6, 1.7, 2.0 or 2.1", // NOI18N })); PROPERTY_INFOS.add(new PropertyInfo("gwt.shell.code.server.port", // NOI18N "9997", new String [] { // NOI18N "# since GWT 2.0", // NOI18N "# Specifies the TCP port for the code server" // NOI18N })); PROPERTY_INFOS.add(new PropertyInfo("gwt.shell.port", // NOI18N "8888", new String [] { // NOI18N "# since GWT 2.0", // NOI18N "# Specifies the TCP port for the embedded web server" // NOI18N })); PROPERTY_INFOS.add(new PropertyInfo("gwt.compiler.args", // NOI18N "", new String [] { // NOI18N "# Additional GWT compiler arguments", // NOI18N "# GWT 2.0/2.1 compiler supports these:", // NOI18N "# -workDir The compiler's working directory for internal use (must be writeable; defaults to a system temp dir)", // NOI18N "# -gen Debugging: causes normally-transient generated types to be saved in the specified directory", // NOI18N "# -ea Debugging: causes the compiled output to check assert statements", // NOI18N "# -XdisableClassMetadata EXPERIMENTAL: Disables some java.lang.Class methods (e.g. getName())", // NOI18N "# -XdisableCastChecking EXPERIMENTAL: Disables run-time checking of cast operations", // NOI18N "# -validateOnly Validate all source code, but do not compile", // NOI18N "# -draftCompile Enable faster, but less-optimized, compilations", // NOI18N "# -compileReport Create a compile report that tells the Story of Your Compile", // NOI18N "# -localWorkers The number of local workers to use when compiling permutations", // NOI18N "# -extra The directory into which extra files, not intended for deployment, will be written", // NOI18N })); PROPERTY_INFOS.add(new PropertyInfo("gwt.test.jvmargs", // NOI18N "-Xmx256M", new String [] { // NOI18N "# Additional JVM arguments for JUnit tests" // NOI18N })); PROPERTY_INFOS.add(new PropertyInfo("gwt.shell.args", // NOI18N "", new String [] { // NOI18N "# Additional arguments for the GWT shell", // NOI18N "# e.g. -bindAddress 0.0.0.0 since GWT 2.0.1" // NOI18N })); } /** * Checks the comments for a property and adds the specified line if it is * not yet there. * * @param props properties * @param propertyName name of the property * @param commentLine a comment line (should start with a #) */ private static void ensureCommentLine(EditableProperties props, String propertyName, String commentLine) { String[] c = props.getComment(propertyName); boolean found = false; for (String s: c) { if (s.contains(commentLine)) { found = true; break; } } if (!found) { String[] newc = new String[c.length + 1]; System.arraycopy(c, 0, newc, 0, c.length); newc[newc.length - 1] = commentLine; props.setComment(propertyName, newc, true); } } /** * Information about a property in gwt.properties */ private final static class PropertyInfo { /** name of the property */ public String name; /** default property value */ public String defaultValue; /** comments */ public String[] comments; public PropertyInfo(String name, String defaultValue, String[] comments) { this.name = name; this.defaultValue = defaultValue; this.comments = comments; } } private PropertyChangeListener openProjectsListener; private List<Project> lastOpenProjects; @Override public void restored() { // update build-gwt.xml when a project is opened openProjectsListener = new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { List<Project> openProjects = new ArrayList<Project>( Arrays.asList(OpenProjects.getDefault(). getOpenProjects())); // lastOpenProjects will contain the open projects before // the event was triggered openProjects.removeAll(lastOpenProjects); updateProjects(openProjects); lastOpenProjects = new ArrayList<Project>(openProjects); } }; OpenProjects op = OpenProjects.getDefault(); lastOpenProjects = new ArrayList<Project>( Arrays.asList(op.getOpenProjects())); op.addPropertyChangeListener(openProjectsListener); //update build-gwt.xml of the open projects when the ide starts up updateProjects(lastOpenProjects); } /** * Iterate through projects and updates them. * * @param projects projects */ private void updateProjects(List<Project> projects) { for (final Project p: projects) { if (GWTProjectInfo.isGWTProject(p)) { try { FileSystem fileSystem = p.getProjectDirectory(). getFileSystem(); fileSystem.runAtomicAction(new AtomicAction() { public void run() throws IOException { updateGWTProject(p); } }); } catch (IOException ex) { Exceptions.printStackTrace(ex); } } } } /** * Creates a new gwt.properties file * * @return gwt.properties */ public static EditableProperties createGWTProperties() { EditableProperties ep = new EditableProperties(false); for (PropertyInfo propInfo: PROPERTY_INFOS) { ep.setProperty(propInfo.name, propInfo.defaultValue); ep.setComment(propInfo.name, propInfo.comments, true); } return ep; } /** * Updates a GWT project * - build-gwt.xml * - gwt.properties * * @param p a GWT project * @throws IOException */ public static void updateGWTProject(final Project p) throws IOException { ProjectInformation projectInfo = ProjectUtils.getInformation(p); String projectName = projectInfo.getName(); FileObject nbprj = p.getProjectDirectory(). getFileObject(GWTProjectInfo.PRJ_DIR); // Maven projects do not have nbproject dir if (nbprj != null) { //support for multiple Java sources for both compile and debug //strategy: find all relevant occurances of ${src.dir} and append additional source SourceGroup[] sg = ProjectUtils.getSources(p).getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA); List<String> patterns = new ArrayList<String>(); List<String> replace = new ArrayList<String>(); //escaped ${src.dir} pattern String srcDir = "\\$\\{src\\.dir\\}"; for (int i = 0; i < sg.length; i++) { String name = sg[i].getName(); if ("${src.dir}".equals(name)) continue; //ignore the primary source if (name.startsWith("${test")) continue; //ignore test sources //escape secondary src var (always starts with $) String append = "\\" + name; //find any occurance of ${src.dir} followed by ':' path separator patterns.add("(" + srcDir + ")\\:"); //append secondary source path right after the primary one replace.add("$1:" + append + ":"); //find any ${src.dir}" preceeded by ':' path separator patterns.add("\\:(" + srcDir + ")\""); //append secondary source path right after the primary one replace.add(":$1:" + append + "\""); //find any <pathelement path="${src.dir}"/> element patterns.add("(?s)(\\s*<pathelement path=\")(" + srcDir + ")(\"\\/>)"); //append the <pathelement> for the additional source on the next line replace.add("$1$2$3$1" + append + "$3"); //find any <srcfiles path="${src.dir}">...</srcfiles> patterns.add("(?s)(\\s*<srcfiles dir=\")(" + srcDir + ")(\">.*?<\\/srcfiles>)"); //append the <srcfile> for the additional source on the next line(s) replace.add("$1$2$3$1" + append + "$3"); } patterns.add("__PROJECT_NAME__"); replace.add(projectName); // COPY build-gwt.xml FileObject buildGwt = nbprj.getFileObject( GWTProjectInfo.BUILD_GWT); GWT4NBUtil.copyResource(GWTProjectInfo.RESOURCE_BASE + GWTProjectInfo.BUILD_GWT, buildGwt, patterns.toArray(new String[patterns.size()]), // NOI18N replace.toArray(new String[patterns.size()]), true); //multiline mode } GWTProjectInfo pi = GWTProjectInfo.get(p); if (pi != null && !pi.isMaven()) { EditableProperties props = pi.getProperties(); for (PropertyInfo propInfo: PROPERTY_INFOS) { String v = props.getProperty(propInfo.name); if (v == null) { props.setProperty(propInfo.name, propInfo.defaultValue); props.setComment(propInfo.name, propInfo.comments, true); } } ensureCommentLine(props, "gwt.module", // NOI18N "# The names of the modules to compile " + // NOI18N "(separated by a space character)"); // NOI18N pi.setProperties(props); } } }