/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common * Development and Distribution License("CDDL") (collectively, 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-gplv2.html * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the * specific language governing permissions and limitations under the * License. When distributing the software, include this License Header * Notice in each file and include the License file at * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the GPL Version 2 section of the License file that * accompanied this code. If applicable, add the following below the * License Header, with the fields enclosed by brackets [] replaced by * your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * * Contributor(s): * * The Original Software is NetBeans. The Initial Developer of the Original * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun * Microsystems, Inc. All Rights Reserved. * * If you wish your version of this file to be governed by only the CDDL * or only the GPL Version 2, indicate your decision by adding * "[Contributor] elects to include this software in this distribution * under the [CDDL or GPL Version 2] license." If you do not indicate a * single choice of license, a recipient has the option to distribute * your version of this file under either the CDDL, the GPL Version 2 or * to extend the choice of license to its licensees as provided above. * However, if you add GPL Version 2 code and therefore, elected the GPL * Version 2 license, then the option applies only if the new code is * made subject to such option by the copyright holder. */ package org.netbeans.modules.scala.project; import java.awt.Dialog; import java.awt.event.MouseEvent; import java.io.IOException; import java.io.InputStream; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import java.util.TreeSet; import java.util.regex.Pattern; import javax.swing.JButton; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.apache.tools.ant.module.api.support.ActionUtils; import org.netbeans.api.fileinfo.NonRecursiveFolder; import org.netbeans.api.java.classpath.ClassPath; import org.netbeans.api.java.project.JavaProjectConstants; import org.netbeans.api.java.queries.UnitTestForSourceQuery; import org.netbeans.api.java.source.ui.ScanDialog; import org.netbeans.api.project.ProjectInformation; import org.netbeans.api.project.ProjectManager; import org.netbeans.api.project.ProjectUtils; import org.netbeans.api.project.SourceGroup; import org.netbeans.api.project.Sources; import org.netbeans.modules.csl.api.ElementKind; import org.netbeans.modules.java.api.common.ant.UpdateHelper; import org.netbeans.modules.java.api.common.project.ProjectProperties; import org.netbeans.modules.parsing.api.ParserManager; import org.netbeans.modules.parsing.api.ResultIterator; import org.netbeans.modules.parsing.api.Source; import org.netbeans.modules.parsing.api.UserTask; import org.netbeans.modules.parsing.spi.ParseException; import org.netbeans.modules.scala.project.classpath.ClassPathProviderImpl; import org.netbeans.modules.scala.project.ui.customizer.J2SEProjectProperties; import org.netbeans.modules.scala.project.ui.customizer.MainClassChooser; import org.netbeans.modules.scala.project.ui.customizer.MainClassWarning; import org.netbeans.spi.project.ActionProvider; import org.netbeans.spi.project.support.ant.AntProjectHelper; import org.netbeans.spi.project.support.ant.EditableProperties; import org.netbeans.spi.project.support.ant.GeneratedFilesHelper; import org.netbeans.spi.project.ui.support.DefaultProjectOperations; import org.openide.DialogDescriptor; import org.openide.DialogDisplayer; import org.openide.ErrorManager; import org.openide.NotifyDescriptor; import org.openide.awt.MouseUtils; import org.openide.execution.ExecutorTask; import org.openide.filesystems.FileChangeAdapter; import org.openide.filesystems.FileChangeListener; import org.openide.filesystems.FileEvent; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileStateInvalidException; import org.openide.filesystems.FileSystem; import org.openide.filesystems.FileUtil; import org.openide.loaders.DataObject; import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.Task; import org.openide.util.TaskListener; import org.netbeans.api.language.util.ast.AstDfn; import org.netbeans.modules.scala.console.shell.ScalaConsoleTopComponent; import org.netbeans.modules.scala.core.ScalaParserResult; import org.netbeans.modules.scala.core.ast.ScalaRootScope; /** * Action provider of the J2SE project. This is the place where to do strange * things to J2SE actions. E.g. compile-single. */ public class J2SEActionProvider implements ActionProvider { public static final String COMMAND_SCALA_CONSOLE = "scala-console"; // Commands available from J2SE project private static final String[] supportedActions = { COMMAND_BUILD, COMMAND_CLEAN, COMMAND_REBUILD, COMMAND_COMPILE_SINGLE, COMMAND_RUN, COMMAND_RUN_SINGLE, COMMAND_DEBUG, COMMAND_DEBUG_SINGLE, JavaProjectConstants.COMMAND_JAVADOC, COMMAND_TEST, COMMAND_TEST_SINGLE, COMMAND_DEBUG_TEST_SINGLE, JavaProjectConstants.COMMAND_DEBUG_FIX, COMMAND_DEBUG_STEP_INTO, COMMAND_DELETE, COMMAND_COPY, COMMAND_MOVE, COMMAND_RENAME, COMMAND_SCALA_CONSOLE }; private static final String[] platformSensitiveActions = { COMMAND_BUILD, COMMAND_REBUILD, COMMAND_COMPILE_SINGLE, COMMAND_RUN, COMMAND_RUN_SINGLE, COMMAND_DEBUG, COMMAND_DEBUG_SINGLE, JavaProjectConstants.COMMAND_JAVADOC, COMMAND_TEST, COMMAND_TEST_SINGLE, COMMAND_DEBUG_TEST_SINGLE, JavaProjectConstants.COMMAND_DEBUG_FIX, COMMAND_DEBUG_STEP_INTO,}; // Project final J2SEProject project; // Ant project helper of the project private UpdateHelper updateHelper; /** * Map from commands to ant targets */ Map<String, String[]> commands; /** * Set of commands which are affected by background scanning */ final Set<String> bkgScanSensitiveActions; /** * Set of Java source files (as relative path from source root) known to * have been modified. See issue #104508. */ private Set<String> dirty = null; private Sources src; private List<FileObject> roots; // Used only from unit tests to suppress detection of top level classes. If value // is different from null it will be returned instead. String unitTestingSupport_fixClasses; public J2SEActionProvider(J2SEProject project, UpdateHelper updateHelper) { commands = new HashMap<String, String[]>(); // treated specially: COMMAND_{,RE}BUILD commands.put(COMMAND_CLEAN, new String[]{"clean"}); // NOI18N commands.put(COMMAND_COMPILE_SINGLE, new String[]{"compile-single"}); // NOI18N commands.put(COMMAND_RUN, new String[]{"run"}); // NOI18N commands.put(COMMAND_RUN_SINGLE, new String[]{"run-single"}); // NOI18N commands.put(COMMAND_DEBUG, new String[]{"debug"}); // NOI18N commands.put(COMMAND_DEBUG_SINGLE, new String[]{"debug-single"}); // NOI18N commands.put(JavaProjectConstants.COMMAND_JAVADOC, new String[]{"javadoc"}); // NOI18N commands.put(COMMAND_TEST, new String[]{"test"}); // NOI18N commands.put(COMMAND_TEST_SINGLE, new String[]{"test-single"}); // NOI18N commands.put(COMMAND_DEBUG_TEST_SINGLE, new String[]{"debug-test"}); // NOI18N commands.put(JavaProjectConstants.COMMAND_DEBUG_FIX, new String[]{"debug-fix"}); // NOI18N commands.put(COMMAND_DEBUG_STEP_INTO, new String[]{"debug-stepinto"}); // NOI18N this.bkgScanSensitiveActions = new HashSet<String>(Arrays.asList( COMMAND_RUN, COMMAND_RUN_SINGLE, COMMAND_DEBUG, COMMAND_DEBUG_SINGLE, COMMAND_DEBUG_STEP_INTO )); this.updateHelper = updateHelper; this.project = project; } private final FileChangeListener modificationListener = new FileChangeAdapter() { public @Override void fileChanged(FileEvent fe) { modification(fe.getFile()); } public @Override void fileDataCreated(FileEvent fe) { modification(fe.getFile()); } }; private final ChangeListener sourcesChangeListener = new ChangeListener() { public void stateChanged(ChangeEvent e) { synchronized (J2SEActionProvider.this) { J2SEActionProvider.this.roots = null; } } }; void startFSListener() { //Listener has to be started when the project's lookup is initialized try { FileSystem fs = project.getProjectDirectory().getFileSystem(); // XXX would be more efficient to only listen while DO_DEPEND=false (though this is the default) fs.addFileChangeListener(FileUtil.weakFileChangeListener(modificationListener, fs)); } catch (FileStateInvalidException x) { Exceptions.printStackTrace(x); } } private void modification(FileObject f) { final Iterable<? extends FileObject> _roots = getRoots(); assert _roots != null; for (FileObject root : _roots) { String path = FileUtil.getRelativePath(root, f); if (path != null) { synchronized (this) { if (dirty != null) { dirty.add(path); } } break; } } } private Iterable<? extends FileObject> getRoots() { Sources _src = null; synchronized (this) { if (this.roots != null) { return this.roots; } if (this.src == null) { this.src = this.project.getLookup().lookup(Sources.class); this.src.addChangeListener(sourcesChangeListener); } _src = this.src; } assert _src != null; final SourceGroup[] sgs = _src.getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA); final List<FileObject> _roots = new ArrayList<FileObject>(sgs.length); for (SourceGroup sg : sgs) { final FileObject root = sg.getRootFolder(); if (UnitTestForSourceQuery.findSources(root).length == 0) { _roots.add(root); } } synchronized (this) { if (this.roots == null) { this.roots = _roots; } return this.roots; } } private FileObject findBuildXml() { return project.getProjectDirectory().getFileObject(GeneratedFilesHelper.BUILD_XML_PATH); } public String[] getSupportedActions() { return supportedActions; } public void invokeAction(final String command, final Lookup context) throws IllegalArgumentException { if (COMMAND_SCALA_CONSOLE.equals(command)) { scala.collection.immutable.List<String> nil = scala.collection.immutable.List$.MODULE$.empty(); ScalaConsoleTopComponent.openInstance(project, nil, false, null); return; } if (COMMAND_DELETE.equals(command)) { DefaultProjectOperations.performDefaultDeleteOperation(project); return; } if (COMMAND_COPY.equals(command)) { DefaultProjectOperations.performDefaultCopyOperation(project); return; } if (COMMAND_MOVE.equals(command)) { DefaultProjectOperations.performDefaultMoveOperation(project); return; } if (COMMAND_RENAME.equals(command)) { DefaultProjectOperations.performDefaultRenameOperation(project, null); return; } final Runnable action = new Runnable() { public void run() { Properties p = new Properties(); String[] targetNames; targetNames = getTargetNames(command, context, p); if (targetNames == null) { return; } if (targetNames.length == 0) { targetNames = null; } if (p.keySet().size() == 0) { p = null; } try { FileObject buildFo = findBuildXml(); if (buildFo == null || !buildFo.isValid()) { //The build.xml was deleted after the isActionEnabled was called NotifyDescriptor nd = new NotifyDescriptor.Message(NbBundle.getMessage(J2SEActionProvider.class, "LBL_No_Build_XML_Found"), NotifyDescriptor.WARNING_MESSAGE); DialogDisplayer.getDefault().notify(nd); } else { ActionUtils.runTarget(buildFo, targetNames, p).addTaskListener(new TaskListener() { public void taskFinished(Task task) { if (((ExecutorTask) task).result() != 0) { synchronized (J2SEActionProvider.this) { // #120843: if a build fails, disable dirty-list optimization. dirty = null; } } } }); } } catch (IOException e) { ErrorManager.getDefault().notify(e); } } }; if (this.bkgScanSensitiveActions.contains(command)) { ScanDialog.runWhenScanFinished(action, NbBundle.getMessage(J2SEActionProvider.class, "ACTION_" + command)); //NOI18N } else { action.run(); } } /** * @return array of targets or null to stop execution; can return empty * array */ /*private*/ String[] getTargetNames(String command, Lookup context, Properties p) throws IllegalArgumentException { if (Arrays.asList(platformSensitiveActions).contains(command)) { final String activePlatformId = this.project.evaluator().getProperty("platform.active"); //NOI18N if (J2SEProjectUtil.getJavaActivePlatform(activePlatformId) == null) { showPlatformWarning(); return null; } } String[] targetNames = new String[0]; Map<String, String[]> targetsFromConfig = loadTargetsFromConfig(); if (command.equals(COMMAND_COMPILE_SINGLE)) { FileObject[] sourceRoots = project.getSourceRoots().getRoots(); FileObject[] files = findSourcesAndPackages(context, sourceRoots); boolean recursive = (context.lookup(NonRecursiveFolder.class) == null); if (files != null) { p.setProperty("javac.includes", ActionUtils.antIncludesList(files, getRoot(sourceRoots, files[0]), recursive)); // NOI18N String[] targets = targetsFromConfig.get(command); targetNames = (targets != null) ? targets : commands.get(command); } else { FileObject[] testRoots = project.getTestSourceRoots().getRoots(); files = findSourcesAndPackages(context, testRoots); p.setProperty("javac.includes", ActionUtils.antIncludesList(files, getRoot(testRoots, files[0]), recursive)); // NOI18N targetNames = new String[]{"compile-test-single"}; // NOI18N } } else if (command.equals(COMMAND_TEST_SINGLE)) { FileObject[] files = findTestSourcesForSources(context); targetNames = setupTestSingle(p, files); } else if (command.equals(COMMAND_DEBUG_TEST_SINGLE)) { FileObject[] files = findTestSourcesForSources(context); targetNames = setupDebugTestSingle(p, files); } else if (command.equals(JavaProjectConstants.COMMAND_DEBUG_FIX)) { FileObject[] files = findSources(context); String path = null; String classes = ""; //NOI18N if (files != null) { path = FileUtil.getRelativePath(getRoot(project.getSourceRoots().getRoots(), files[0]), files[0]); targetNames = new String[]{"debug-fix"}; // NOI18N classes = getTopLevelClasses(files[0]); } else { files = findTestSources(context, false); path = FileUtil.getRelativePath(getRoot(project.getTestSourceRoots().getRoots(), files[0]), files[0]); targetNames = new String[]{"debug-fix-test"}; // NOI18N } // Convert foo/FooTest.java -> foo/FooTest if (path.endsWith(".scala")) { // NOI18N path = path.substring(0, path.length() - 5); } p.setProperty("fix.includes", path); // NOI18N p.setProperty("fix.classes", classes); // NOI18N } else if (command.equals(COMMAND_RUN) || command.equals(COMMAND_DEBUG) || command.equals(COMMAND_DEBUG_STEP_INTO)) { String config = project.evaluator().getProperty(J2SEConfigurationProvider.PROP_CONFIG); String path; if (config == null || config.length() == 0) { path = AntProjectHelper.PROJECT_PROPERTIES_PATH; } else { // Set main class for a particular config only. path = "nbproject/configs/" + config + ".properties"; // NOI18N } EditableProperties ep = updateHelper.getProperties(path); // check project's main class // Check whether main class is defined in this config. Note that we use the evaluator, // not ep.getProperty(MAIN_CLASS), since it is permissible for the default pseudoconfig // to define a main class - in this case an active config need not override it. String mainClass = project.evaluator().getProperty(J2SEProjectProperties.MAIN_CLASS); MainClassStatus result = isSetMainClass(project.getSourceRoots().getRoots(), mainClass); if (context.lookup(J2SEConfigurationProvider.Config.class) != null) { // If a specific config was selected, just skip this check for now. // XXX would ideally check that that config in fact had a main class. // But then evaluator.getProperty(MAIN_CLASS) would be inaccurate. // Solvable but punt on it for now. result = MainClassStatus.SET_AND_VALID; } if (result != MainClassStatus.SET_AND_VALID) { do { // show warning, if cancel then return if (showMainClassWarning(mainClass, ProjectUtils.getInformation(project).getDisplayName(), ep, result)) { return null; } // No longer use the evaluator: have not called putProperties yet so it would not work. mainClass = ep.get(J2SEProjectProperties.MAIN_CLASS); result = isSetMainClass(project.getSourceRoots().getRoots(), mainClass); } while (result != MainClassStatus.SET_AND_VALID); try { if (updateHelper.requestUpdate()) { updateHelper.putProperties(path, ep); ProjectManager.getDefault().saveProject(project); } else { return null; } } catch (IOException ioe) { ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL, "Error while saving project: " + ioe); } } if (!command.equals(COMMAND_RUN) && /* XXX should ideally look up proper mainClass in evaluator x config */ mainClass != null) { p.setProperty("debug.class", mainClass); // NOI18N } String[] targets = targetsFromConfig.get(command); targetNames = (targets != null) ? targets : commands.get(command); if (targetNames == null) { throw new IllegalArgumentException(command); } prepareDirtyList(p, false); } else if (command.equals(COMMAND_RUN_SINGLE) || command.equals(COMMAND_DEBUG_SINGLE)) { FileObject[] files = findTestSources(context, false); if (files != null) { if (command.equals(COMMAND_RUN_SINGLE)) { targetNames = setupTestSingle(p, files); } else { targetNames = setupDebugTestSingle(p, files); } } else { FileObject file = findSources(context)[0]; String clazz = FileUtil.getRelativePath(getRoot(project.getSourceRoots().getRoots(), file), file); p.setProperty("javac.includes", clazz); // NOI18N // Convert foo/FooTest.java -> foo.FooTest if (clazz.endsWith(".scala")) { // NOI18N clazz = clazz.substring(0, clazz.length() - 6); } clazz = clazz.replace('/', '.'); final boolean hasMainClassFromTest = MainClassChooser.unitTestingSupport_hasMainMethodResult == null ? false : MainClassChooser.unitTestingSupport_hasMainMethodResult.booleanValue(); final Collection<AstDfn> mainClasses = J2SEProjectUtil.getMainMethods(file); if (!hasMainClassFromTest && mainClasses.isEmpty()) { // if (AppletSupport.isApplet(file)) { // // EditableProperties ep = updateHelper.getProperties (AntProjectHelper.PROJECT_PROPERTIES_PATH); // String jvmargs = ep.getProperty("run.jvmargs"); // // URL url = null; // // // do this only when security policy is not set manually // if ((jvmargs == null) || !(jvmargs.indexOf("java.security.policy") > 0)) { //NOI18N // AppletSupport.generateSecurityPolicy(project.getProjectDirectory()); // if ((jvmargs == null) || (jvmargs.length() == 0)) { // ep.setProperty("run.jvmargs", "-Djava.security.policy=applet.policy"); //NOI18N // } else { // ep.setProperty("run.jvmargs", jvmargs + " -Djava.security.policy=applet.policy"); //NOI18N // } // updateHelper.putProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH, ep); // try { // ProjectManager.getDefault().saveProject(project); // } catch (Exception e) { // ErrorManager.getDefault().log(ErrorManager.INFORMATIONAL, "Error while saving project: " + e); // } // } // // if (file.existsExt("html") || file.existsExt("HTML")) { //NOI18N // url = copyAppletHTML(file, "html"); //NOI18N // } else { // url = generateAppletHTML(file); // } // if (url == null) { // return null; // } // p.setProperty("applet.url", url.toString()); // NOI18N // if (command.equals (COMMAND_RUN_SINGLE)) { // targetNames = new String[] {"run-applet"}; // NOI18N // } else { // p.setProperty("debug.class", clazz); // NOI18N // targetNames = new String[] {"debug-applet"}; // NOI18N // } // } else { NotifyDescriptor nd = new NotifyDescriptor.Message(NbBundle.getMessage(J2SEActionProvider.class, "LBL_No_Main_Classs_Found", clazz), NotifyDescriptor.INFORMATION_MESSAGE); DialogDisplayer.getDefault().notify(nd); return null; // } } else { if (!hasMainClassFromTest) { if (mainClasses.size() == 1) { final AstDfn next = mainClasses.iterator().next(); //Just one main class, resolve from the symbol clazz = next.qualifiedName(); } else { //Several main classes, let the user choose clazz = showMainClassWarning(file, mainClasses); if (clazz == null) { return null; } } } if (command.equals(COMMAND_RUN_SINGLE)) { p.setProperty("run.class", clazz); // NOI18N String[] targets = targetsFromConfig.get(command); targetNames = (targets != null) ? targets : commands.get(COMMAND_RUN_SINGLE); } else { p.setProperty("debug.class", clazz); // NOI18N String[] targets = targetsFromConfig.get(command); targetNames = (targets != null) ? targets : commands.get(COMMAND_DEBUG_SINGLE); } } } } else { String[] targets = targetsFromConfig.get(command); targetNames = (targets != null) ? targets : commands.get(command); if (targetNames == null) { String buildTarget = "false".equalsIgnoreCase(project.evaluator().getProperty(J2SEProjectProperties.DO_JAR)) ? "compile" : "jar"; // NOI18N if (command.equals(COMMAND_BUILD)) { targetNames = new String[]{buildTarget}; prepareDirtyList(p, true); } else if (command.equals(COMMAND_REBUILD)) { targetNames = new String[]{"clean", buildTarget}; // NOI18N } else { throw new IllegalArgumentException(command); } } if (COMMAND_CLEAN.equals(command)) { //After clean, rebuild all dirty = null; } } J2SEConfigurationProvider.Config c = context.lookup(J2SEConfigurationProvider.Config.class); if (c != null) { String config; if (c.name != null) { config = c.name; } else { // Invalid but overrides any valid setting in config.properties. config = ""; } p.setProperty(J2SEConfigurationProvider.PROP_CONFIG, config); } return targetNames; } private void prepareDirtyList(Properties p, boolean isExplicitBuildTarget) { String doDepend = project.evaluator().getProperty(J2SEProjectProperties.DO_DEPEND); synchronized (this) { if (dirty == null) { // #119777: the first time, build everything. dirty = new TreeSet<String>(); return; } for (DataObject d : DataObject.getRegistry().getModified()) { // Treat files modified in memory as dirty as well. // (If you make an edit and press F11, the save event happens *after* Ant is launched.) modification(d.getPrimaryFile()); } if (!"true".equalsIgnoreCase(doDepend) && !(isExplicitBuildTarget && dirty.isEmpty())) { // NOI18N // #104508: if not using <depend>, try to compile just those files known to have been touched since the last build. // (In case there are none such, yet the user invoked build anyway, probably they know what they are doing.) if (dirty.isEmpty()) { // includes="" apparently is ignored. dirty.add("nothing whatsoever"); // NOI18N } StringBuilder dirtyList = new StringBuilder(); for (String f : dirty) { if (dirtyList.length() > 0) { dirtyList.append(','); } dirtyList.append(f); } // * let scalac's dependencies feature to manage changed and transitive files //p.setProperty(ProjectProperties.INCLUDES, dirtyList.toString()); } dirty.clear(); } } // loads targets for specific commands from shared config property file // returns map; key=command name; value=array of targets for given command private HashMap<String, String[]> loadTargetsFromConfig() { HashMap<String, String[]> targets = new HashMap<String, String[]>(6); String config = project.evaluator().getProperty(J2SEConfigurationProvider.PROP_CONFIG); // load targets from shared config FileObject propFO = project.getProjectDirectory().getFileObject("nbproject/configs/" + config + ".properties"); if (propFO == null) { return targets; } Properties props = new Properties(); try { InputStream is = propFO.getInputStream(); try { props.load(is); } finally { is.close(); } } catch (IOException ex) { ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); return targets; } Enumeration propNames = props.propertyNames(); while (propNames.hasMoreElements()) { String propName = (String) propNames.nextElement(); if (propName.startsWith("$target.")) { String tNameVal = props.getProperty(propName); String cmdNameKey = null; if (tNameVal != null && !tNameVal.equals("")) { cmdNameKey = propName.substring("$target.".length()); StringTokenizer stok = new StringTokenizer(tNameVal.trim(), " "); List<String> targetNames = new ArrayList<String>(3); while (stok.hasMoreTokens()) { targetNames.add(stok.nextToken()); } targets.put(cmdNameKey, targetNames.toArray(new String[targetNames.size()])); } } } return targets; } private String[] setupTestSingle(Properties p, FileObject[] files) { FileObject[] testSrcPath = project.getTestSourceRoots().getRoots(); FileObject root = getRoot(testSrcPath, files[0]); p.setProperty("test.includes", ActionUtils.antIncludesList(files, root)); // NOI18N p.setProperty("javac.includes", ActionUtils.antIncludesList(files, root)); // NOI18N return new String[]{"test-single"}; // NOI18N } private String[] setupDebugTestSingle(Properties p, FileObject[] files) { FileObject[] testSrcPath = project.getTestSourceRoots().getRoots(); FileObject root = getRoot(testSrcPath, files[0]); String path = FileUtil.getRelativePath(root, files[0]); // Convert foo/FooTest.java -> foo.FooTest p.setProperty("test.class", path.substring(0, path.length() - 6).replace('/', '.')); // NOI18N p.setProperty("javac.includes", ActionUtils.antIncludesList(files, root)); // NOI18N return new String[]{"debug-test"}; // NOI18N } public boolean isActionEnabled(String command, Lookup context) { FileObject buildXml = findBuildXml(); if (buildXml == null || !buildXml.isValid()) { return false; } if (command.equals(COMMAND_COMPILE_SINGLE)) { return findSourcesAndPackages(context, project.getSourceRoots().getRoots()) != null || findSourcesAndPackages(context, project.getTestSourceRoots().getRoots()) != null; } else if (command.equals(COMMAND_TEST_SINGLE)) { return findTestSourcesForSources(context) != null; } else if (command.equals(COMMAND_DEBUG_TEST_SINGLE)) { FileObject[] files = findTestSourcesForSources(context); return files != null && files.length == 1; } else if (command.equals(COMMAND_RUN_SINGLE) || command.equals(COMMAND_DEBUG_SINGLE) || command.equals(JavaProjectConstants.COMMAND_DEBUG_FIX)) { FileObject fos[] = findSources(context); if (fos != null && fos.length == 1) { return true; } fos = findTestSources(context, false); return fos != null && fos.length == 1; } else { // other actions are global return true; } } // Private methods ----------------------------------------------------- private static final Pattern SRCDIRJAVA = Pattern.compile("\\.scala$"); // NOI18N private static final String SUBST = "Test.scala"; // NOI18N /** * Lists all top level classes in a String, classes are separated by space * (" ") Used by debuger fix and continue (list of files to fix) * * @param file for which the top level classes should be found * @return list of top levels */ private String getTopLevelClasses(final FileObject file) { assert file != null; if (unitTestingSupport_fixClasses != null) { return unitTestingSupport_fixClasses; } final String[] classes = new String[]{""}; //NOI18N Source source = Source.create(file); if (source != null) { try { ParserManager.parse(Collections.singleton(source), new UserTask() { @Override public void run(ResultIterator resultIterator) throws Exception { ScalaRootScope rootScope = ((ScalaParserResult) resultIterator.getParserResult()).rootScope(); if (rootScope == null) { return; } rootScope.visibleDfns(ElementKind.CLASS); scala.collection.Seq<AstDfn> tmpls = rootScope.visibleDfns(ElementKind.CLASS); if (!tmpls.isEmpty()) { scala.collection.Iterator itr = tmpls.iterator(); while (itr.hasNext()) { AstDfn tmpl = (AstDfn) itr.next(); if (classes[0].length() > 0) { classes[0] = classes[0] + " "; // NOI18N } classes[0] = classes[0] + tmpl.getName().toString().replace('.', '/') + "*.class"; // NOI18N } } } }); } catch (ParseException ex) { Exceptions.printStackTrace(ex); } } return classes[0]; } /** * Find selected sources, the sources has to be under single source root, * * @param context the lookup in which files should be found */ private FileObject[] findSources(Lookup context) { FileObject[] srcPath = project.getSourceRoots().getRoots(); for (int i = 0; i < srcPath.length; i++) { FileObject[] files = ActionUtils.findSelectedFiles(context, srcPath[i], ".scala", true); // NOI18N if (files != null) { return files; } } return null; } private FileObject[] findSourcesAndPackages(Lookup context, FileObject srcDir) { if (srcDir != null) { FileObject[] files = ActionUtils.findSelectedFiles(context, srcDir, null, true); // NOI18N //Check if files are either packages of java files if (files != null) { for (int i = 0; i < files.length; i++) { if (!files[i].isFolder() && !"scala".equals(files[i].getExt())) { return null; } } } return files; } else { return null; } } private FileObject[] findSourcesAndPackages(Lookup context, FileObject[] srcRoots) { for (int i = 0; i < srcRoots.length; i++) { FileObject[] result = findSourcesAndPackages(context, srcRoots[i]); if (result != null) { return result; } } return null; } /** * Find either selected tests or tests which belong to selected source files */ private FileObject[] findTestSources(Lookup context, boolean checkInSrcDir) { //XXX: Ugly, should be rewritten FileObject[] testSrcPath = project.getTestSourceRoots().getRoots(); for (int i = 0; i < testSrcPath.length; i++) { FileObject[] files = ActionUtils.findSelectedFiles(context, testSrcPath[i], ".scala", true); // NOI18N if (files != null) { return files; } } if (checkInSrcDir && testSrcPath.length > 0) { FileObject[] files = findSources(context); if (files != null) { //Try to find the test under the test roots FileObject srcRoot = getRoot(project.getSourceRoots().getRoots(), files[0]); for (int i = 0; i < testSrcPath.length; i++) { FileObject[] files2 = ActionUtils.regexpMapFiles(files, srcRoot, SRCDIRJAVA, testSrcPath[i], SUBST, true); if (files2 != null) { return files2; } } } } return null; } /** * Find tests corresponding to selected sources. */ private FileObject[] findTestSourcesForSources(Lookup context) { FileObject[] sourceFiles = findSources(context); if (sourceFiles == null) { return null; } FileObject[] testSrcPath = project.getTestSourceRoots().getRoots(); if (testSrcPath.length == 0) { return null; } FileObject[] srcPath = project.getSourceRoots().getRoots(); FileObject srcDir = getRoot(srcPath, sourceFiles[0]); for (int i = 0; i < testSrcPath.length; i++) { FileObject[] files2 = ActionUtils.regexpMapFiles(sourceFiles, srcDir, SRCDIRJAVA, testSrcPath[i], SUBST, true); if (files2 != null) { return files2; } } return null; } private FileObject getRoot(FileObject[] roots, FileObject file) { assert file != null : "File can't be null"; //NOI18N FileObject srcDir = null; for (int i = 0; i < roots.length; i++) { assert roots[i] != null : "Source Path Root can't be null"; //NOI18N if (FileUtil.isParentOf(roots[i], file) || roots[i].equals(file)) { srcDir = roots[i]; break; } } return srcDir; } private static enum MainClassStatus { SET_AND_VALID, SET_BUT_INVALID, UNSET } /** * Tests if the main class is set * * @param sourcesRoots source roots * @param mainClass main class name * @return status code */ private MainClassStatus isSetMainClass(FileObject[] sourcesRoots, String mainClass) { // support for unit testing if (MainClassChooser.unitTestingSupport_hasMainMethodResult != null) { return MainClassChooser.unitTestingSupport_hasMainMethodResult ? MainClassStatus.SET_AND_VALID : MainClassStatus.SET_BUT_INVALID; } if (mainClass == null || mainClass.length() == 0) { return MainClassStatus.UNSET; } if (sourcesRoots.length > 0) { ClassPath bootPath = ClassPath.getClassPath(sourcesRoots[0], ClassPath.BOOT); //Single compilation unit ClassPath compilePath = ClassPath.getClassPath(sourcesRoots[0], ClassPath.EXECUTE); ClassPath sourcePath = ClassPath.getClassPath(sourcesRoots[0], ClassPath.SOURCE); if (J2SEProjectUtil.isMainClass(mainClass, bootPath, compilePath, sourcePath)) { return MainClassStatus.SET_AND_VALID; } } else { ClassPathProviderImpl cpProvider = project.getClassPathProvider(); if (cpProvider != null) { ClassPath bootPath = cpProvider.getProjectSourcesClassPath(ClassPath.BOOT); ClassPath compilePath = cpProvider.getProjectSourcesClassPath(ClassPath.EXECUTE); ClassPath sourcePath = cpProvider.getProjectSourcesClassPath(ClassPath.SOURCE); //Empty ClassPath if (J2SEProjectUtil.isMainClass(mainClass, bootPath, compilePath, sourcePath)) { return MainClassStatus.SET_AND_VALID; } } } return MainClassStatus.SET_BUT_INVALID; } /** * Asks user for name of main class * * @param mainClass current main class * @param projectName the name of project * @param ep project.properties to possibly edit * @param messgeType type of dialog * @return true if user selected main class */ private boolean showMainClassWarning(String mainClass, String projectName, EditableProperties ep, MainClassStatus messageType) { boolean canceled; final JButton okButton = new JButton(NbBundle.getMessage(MainClassWarning.class, "LBL_MainClassWarning_ChooseMainClass_OK")); // NOI18N okButton.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(MainClassWarning.class, "AD_MainClassWarning_ChooseMainClass_OK")); // main class goes wrong => warning String message; switch (messageType) { case UNSET: message = MessageFormat.format(NbBundle.getMessage(MainClassWarning.class, "LBL_MainClassNotFound"), new Object[]{ projectName }); break; case SET_BUT_INVALID: message = MessageFormat.format(NbBundle.getMessage(MainClassWarning.class, "LBL_MainClassWrong"), new Object[]{ mainClass, projectName }); break; default: throw new IllegalArgumentException(); } final MainClassWarning panel = new MainClassWarning(message, project.getSourceRoots().getRoots()); Object[] options = new Object[]{ okButton, DialogDescriptor.CANCEL_OPTION }; panel.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { if (e.getSource() instanceof MouseEvent && MouseUtils.isDoubleClick(((MouseEvent) e.getSource()))) { // click button and the finish dialog with selected class okButton.doClick(); } else { okButton.setEnabled(panel.getSelectedMainClass() != null); } } }); okButton.setEnabled(false); DialogDescriptor desc = new DialogDescriptor(panel, NbBundle.getMessage(MainClassWarning.class, "CTL_MainClassWarning_Title", ProjectUtils.getInformation(project).getDisplayName()), // NOI18N true, options, options[0], DialogDescriptor.BOTTOM_ALIGN, null, null); desc.setMessageType(DialogDescriptor.INFORMATION_MESSAGE); Dialog dlg = DialogDisplayer.getDefault().createDialog(desc); dlg.setVisible(true); if (desc.getValue() != options[0]) { canceled = true; } else { mainClass = panel.getSelectedMainClass(); canceled = false; ep.put(J2SEProjectProperties.MAIN_CLASS, mainClass == null ? "" : mainClass); } dlg.dispose(); return canceled; } private String showMainClassWarning(final FileObject file, final Collection<AstDfn> mainClasses) { assert mainClasses != null; String mainClass = null; final JButton okButton = new JButton(NbBundle.getMessage(MainClassWarning.class, "LBL_MainClassWarning_ChooseMainClass_OK")); // NOI18N okButton.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(MainClassWarning.class, "AD_MainClassWarning_ChooseMainClass_OK")); final MainClassWarning panel = new MainClassWarning(NbBundle.getMessage(MainClassWarning.class, "CTL_FileMultipleMain", file.getNameExt()), mainClasses); Object[] options = new Object[]{ okButton, DialogDescriptor.CANCEL_OPTION }; panel.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { if (e.getSource() instanceof MouseEvent && MouseUtils.isDoubleClick(((MouseEvent) e.getSource()))) { // click button and the finish dialog with selected class okButton.doClick(); } else { okButton.setEnabled(panel.getSelectedMainClass() != null); } } }); DialogDescriptor desc = new DialogDescriptor(panel, NbBundle.getMessage(MainClassWarning.class, "CTL_FileMainClass_Title"), // NOI18N true, options, options[0], DialogDescriptor.BOTTOM_ALIGN, null, null); desc.setMessageType(DialogDescriptor.INFORMATION_MESSAGE); Dialog dlg = DialogDisplayer.getDefault().createDialog(desc); dlg.setVisible(true); if (desc.getValue() == options[0]) { mainClass = panel.getSelectedMainClass(); } dlg.dispose(); return mainClass; } private void showPlatformWarning() { final JButton closeOption = new JButton(NbBundle.getMessage(J2SEActionProvider.class, "CTL_BrokenPlatform_Close")); closeOption.getAccessibleContext().setAccessibleDescription(NbBundle.getMessage(J2SEActionProvider.class, "AD_BrokenPlatform_Close")); final ProjectInformation pi = project.getLookup().lookup(ProjectInformation.class); final String projectDisplayName = pi == null ? NbBundle.getMessage(J2SEActionProvider.class, "TEXT_BrokenPlatform_UnknownProjectName") : pi.getDisplayName(); final DialogDescriptor dd = new DialogDescriptor( NbBundle.getMessage(J2SEActionProvider.class, "TEXT_BrokenPlatform", projectDisplayName), NbBundle.getMessage(J2SEActionProvider.class, "MSG_BrokenPlatform_Title"), true, new Object[]{closeOption}, closeOption, DialogDescriptor.DEFAULT_ALIGN, null, null); dd.setMessageType(DialogDescriptor.WARNING_MESSAGE); final Dialog dlg = DialogDisplayer.getDefault().createDialog(dd); dlg.setVisible(true); } // private URL generateAppletHTML(FileObject file) { // URL url = null; // try { // String buildDirProp = project.evaluator().getProperty("build.dir"); //NOI18N // String classesDirProp = project.evaluator().getProperty("build.classes.dir"); //NOI18N // FileObject buildDir = this.updateHelper.getAntProjectHelper().resolveFileObject(buildDirProp); // FileObject classesDir = this.updateHelper.getAntProjectHelper().resolveFileObject(classesDirProp); // // if (buildDir == null) { // buildDir = FileUtil.createFolder(project.getProjectDirectory(), buildDirProp); // } // // if (classesDir == null) { // classesDir = FileUtil.createFolder(project.getProjectDirectory(), classesDirProp); // } // String activePlatformName = project.evaluator().getProperty("platform.active"); //NOI18N // url = AppletSupport.generateHtmlFileURL(file, buildDir, classesDir, activePlatformName); // } catch (FileStateInvalidException fe) { // //ingore // } catch (IOException ioe) { // ErrorManager.getDefault().notify(ioe); // return null; // } // return url; // } // private URL copyAppletHTML(FileObject file, String ext) { // URL url = null; // try { // String buildDirProp = project.evaluator().getProperty("build.dir"); //NOI18N // FileObject buildDir = updateHelper.getAntProjectHelper().resolveFileObject(buildDirProp); // // if (buildDir == null) { // buildDir = FileUtil.createFolder(project.getProjectDirectory(), buildDirProp); // } // // FileObject htmlFile = null; // htmlFile = file.getParent().getFileObject(file.getName(), "html"); //NOI18N // if (htmlFile == null) { // htmlFile = file.getParent().getFileObject(file.getName(), "HTML"); //NOI18N // } // if (htmlFile == null) { // return null; // } // // FileObject existingFile = buildDir.getFileObject(htmlFile.getName(), htmlFile.getExt()); // if (existingFile != null) { // existingFile.delete(); // } // // FileObject targetHtml = htmlFile.copy(buildDir, file.getName(), ext); // // if (targetHtml != null) { // String activePlatformName = project.evaluator().getProperty("platform.active"); //NOI18N // url = AppletSupport.getHTMLPageURL(targetHtml, activePlatformName); // } // } catch (FileStateInvalidException fe) { // //ingore // } catch (IOException ioe) { // ErrorManager.getDefault().notify(ioe); // return null; // } // return url; // } }