| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.netbeans.modules.java.freeform; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.net.MalformedURLException; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| import javax.swing.JButton; |
| import javax.xml.parsers.ParserConfigurationException; |
| import javax.xml.parsers.SAXParser; |
| import javax.xml.parsers.SAXParserFactory; |
| import org.netbeans.api.project.Project; |
| import org.netbeans.api.project.ProjectManager; |
| import org.netbeans.api.project.ProjectUtils; |
| import org.netbeans.modules.ant.freeform.spi.support.Util; |
| import org.netbeans.modules.java.freeform.jdkselection.JdkConfiguration; |
| import org.netbeans.modules.java.freeform.ui.ProjectModel; |
| import org.netbeans.spi.project.ActionProvider; |
| import org.netbeans.spi.project.AuxiliaryConfiguration; |
| import org.netbeans.spi.project.support.ant.AntProjectHelper; |
| import org.netbeans.spi.project.support.ant.PropertyEvaluator; |
| import org.netbeans.spi.project.support.ant.PropertyUtils; |
| import org.netbeans.spi.project.ui.CustomizerProvider; |
| import org.openide.DialogDisplayer; |
| import org.openide.ErrorManager; |
| import org.openide.NotifyDescriptor; |
| import org.openide.cookies.LineCookie; |
| import org.openide.filesystems.FileLock; |
| import org.openide.filesystems.FileObject; |
| import org.openide.filesystems.FileSystem; |
| import org.openide.filesystems.FileUtil; |
| import org.openide.loaders.DataObject; |
| import org.openide.loaders.DataObjectNotFoundException; |
| import org.openide.text.Line.ShowOpenType; |
| import org.openide.text.Line.ShowVisibilityType; |
| import org.openide.util.Exceptions; |
| import org.openide.util.Lookup; |
| import org.openide.util.NbBundle; |
| import org.openide.util.Utilities; |
| import org.openide.xml.XMLUtil; |
| import org.w3c.dom.Attr; |
| import org.w3c.dom.Comment; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.NamedNodeMap; |
| import org.w3c.dom.NodeList; |
| import org.xml.sax.Attributes; |
| import org.xml.sax.InputSource; |
| import org.xml.sax.Locator; |
| import org.xml.sax.SAXException; |
| import org.xml.sax.helpers.DefaultHandler; |
| |
| /** |
| * Handles providing implementations of some Java-oriented IDE-specific actions. |
| * @see "issue #46886" |
| */ |
| final class JavaActions implements ActionProvider { |
| |
| /* Too problematic for importing <classpath> from existing <java>, since Ant would want NS on that too (oddly): |
| private static final String NS_JPDA = "antlib:org.netbeans.modules.debugger.jpda.ant"; // NOI18N |
| */ |
| |
| static final String JAVA_FILE_PATTERN = "\\.java$"; |
| |
| private static final String[] ACTIONS = { |
| ActionProvider.COMMAND_COMPILE_SINGLE, |
| ActionProvider.COMMAND_DEBUG, |
| ActionProvider.COMMAND_PROFILE, |
| ActionProvider.COMMAND_RUN_SINGLE, |
| ActionProvider.COMMAND_DEBUG_SINGLE, |
| ActionProvider.COMMAND_PROFILE_SINGLE |
| // XXX more |
| }; |
| |
| /** |
| * Script to hold file-sensitive generated targets like compile.single. |
| * (Or for generated targets for debug which cannot reuse any existing target body.) |
| * These pick up at least project.dir from project.xml and the entire |
| * target body is fixed by the IDE, except for some strings determined |
| * by information from project.xml like the classpath. The basedir |
| * is set to the project directory so that properties match their |
| * semantics in project.xml. |
| */ |
| static final String FILE_SCRIPT_PATH = "nbproject/ide-file-targets.xml"; // NOI18N |
| /** |
| * Script to hold non-file-sensitive generated targets like debug. |
| * These import the original build script and share its basedir, so that |
| * properties match the semantics of build.xml. |
| */ |
| static final String GENERAL_SCRIPT_PATH = "nbproject/ide-targets.xml"; // NOI18N |
| |
| private final Project project; |
| private final AntProjectHelper helper; |
| private final PropertyEvaluator evaluator; |
| private final AuxiliaryConfiguration aux; |
| private boolean setOutputsNotified; |
| |
| public JavaActions(Project project, AntProjectHelper helper, PropertyEvaluator evaluator, AuxiliaryConfiguration aux) { |
| this.project = project; |
| this.helper = helper; |
| this.evaluator = evaluator; |
| this.aux = aux; |
| } |
| |
| public String[] getSupportedActions() { |
| return ACTIONS; |
| } |
| |
| public boolean isActionEnabled(String command, Lookup context) throws IllegalArgumentException { |
| if (command.equals(ActionProvider.COMMAND_COMPILE_SINGLE)) { |
| return findPackageRoot(context) != null; |
| } else if (command.equals(ActionProvider.COMMAND_DEBUG)) { |
| return true; |
| } else if (command.equals(ActionProvider.COMMAND_PROFILE)) { |
| return true; |
| } else if (command.equals(ActionProvider.COMMAND_RUN_SINGLE)) { |
| return (findPackageRoot(context) != null) && isSingleJavaFileSelected(context); |
| } else if (command.equals(ActionProvider.COMMAND_DEBUG_SINGLE)) { |
| return (findPackageRoot(context) != null) && isSingleJavaFileSelected(context); |
| } else if (command.equals(ActionProvider.COMMAND_PROFILE_SINGLE)) { |
| return (findPackageRoot(context) != null) && isSingleJavaFileSelected(context); |
| } else { |
| throw new IllegalArgumentException(command); |
| } |
| } |
| |
| public void invokeAction(final String command, final Lookup context) throws IllegalArgumentException { |
| try { |
| project.getProjectDirectory().getFileSystem().runAtomicAction(new FileSystem.AtomicAction() { |
| public void run() throws IOException { |
| try { |
| if (command.equals(ActionProvider.COMMAND_COMPILE_SINGLE)) { |
| handleCompileSingle(context); |
| } else if (command.equals(ActionProvider.COMMAND_DEBUG)) { |
| handleDebug(); |
| } else if (command.equals(ActionProvider.COMMAND_PROFILE)) { |
| handleProfile(); |
| } else if (command.equals(ActionProvider.COMMAND_RUN_SINGLE)) { |
| handleRunSingle(context); |
| } else if (command.equals(ActionProvider.COMMAND_DEBUG_SINGLE)) { |
| handleDebugSingle(context); |
| } else if (command.equals(ActionProvider.COMMAND_PROFILE_SINGLE)) { |
| handleProfileSingle(context); |
| } else { |
| throw new IllegalArgumentException(command); |
| } |
| } catch (SAXException e) { |
| throw (IOException) new IOException(e.toString()).initCause(e); |
| } |
| } |
| }); |
| } catch (IOException e) { |
| ErrorManager.getDefault().notify(e); |
| } |
| } |
| |
| /** |
| * Display an alert asking the user whether to really generate a target. |
| * @param commandDisplayName the display name of the action to be bound |
| * @param scriptPath the path that to the script that will be generated or written to |
| * @return true if IDE should proceed |
| */ |
| private boolean alert(String commandDisplayName, String scriptPath) { |
| String projectDisplayName = ProjectUtils.getInformation(project).getDisplayName(); |
| String title = NbBundle.getMessage(JavaActions.class, "TITLE_generate_target_dialog", commandDisplayName, projectDisplayName); |
| String body = NbBundle.getMessage(JavaActions.class, "TEXT_generate_target_dialog", commandDisplayName, scriptPath); |
| NotifyDescriptor d = new NotifyDescriptor.Message(body, NotifyDescriptor.QUESTION_MESSAGE); |
| d.setTitle(title); |
| d.setOptionType(NotifyDescriptor.OK_CANCEL_OPTION); |
| JButton generate = new JButton(NbBundle.getMessage(JavaActions.class, "LBL_generate")); |
| generate.setDefaultCapable(true); |
| d.setOptions(new Object[] {generate, NotifyDescriptor.CANCEL_OPTION}); |
| return DialogDisplayer.getDefault().notify(d) == generate; |
| } |
| |
| /** |
| * Warns the user about missing project outputs setting |
| * @param commandDisplayName the display name of the action to be bound |
| */ |
| private boolean alertOutputs (String commandDisplayName) { |
| JButton setOutputOption = new JButton (NbBundle.getMessage(JavaActions.class,"CTL_SetOutput")); |
| setOutputOption.getAccessibleContext().setAccessibleDescription (NbBundle.getMessage(JavaActions.class,"AD_SetOutput")); |
| setOutputOption.setDefaultCapable(true); |
| String projectDisplayName = ProjectUtils.getInformation(project).getDisplayName(); |
| String title = NbBundle.getMessage(JavaActions.class, "TITLE_set_outputs_dialog", commandDisplayName, projectDisplayName); |
| String body = NbBundle.getMessage(JavaActions.class,"TEXT_set_outputs_dialog"); |
| NotifyDescriptor d = new NotifyDescriptor.Message (body, NotifyDescriptor.QUESTION_MESSAGE); |
| d.setTitle(title); |
| d.setOptionType(NotifyDescriptor.OK_CANCEL_OPTION); |
| d.setOptions(new Object[] {setOutputOption, NotifyDescriptor.CANCEL_OPTION}); |
| if (DialogDisplayer.getDefault().notify (d) == setOutputOption) { |
| CustomizerProvider customizerProvider = project.getLookup().lookup(CustomizerProvider.class); |
| assert customizerProvider != null; |
| customizerProvider.showCustomizer(); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Implementation of Compile File. |
| */ |
| private void handleCompileSingle(Lookup context) throws IOException, SAXException { |
| // XXX could also try copy + mod from build.xml? but less likely to have <compile> in an accessible place... |
| if (!alert(NbBundle.getMessage(JavaActions.class, "ACTION_compile.single"), FILE_SCRIPT_PATH)) { |
| return; |
| } |
| Document doc = readCustomScript(FILE_SCRIPT_PATH); |
| ensurePropertiesCopied(doc.getDocumentElement()); |
| Comment comm = doc.createComment(" " + NbBundle.getMessage(JavaActions.class, "COMMENT_edit_target") + " "); |
| doc.getDocumentElement().appendChild(comm); |
| comm = doc.createComment(" " + NbBundle.getMessage(JavaActions.class, "COMMENT_more_info_x.single") + " "); |
| doc.getDocumentElement().appendChild(comm); |
| String propertyName = "files"; // NOI18N |
| AntLocation root = findPackageRoot(context); |
| assert root != null : context; |
| Element target = createCompileSingleTarget(doc, context, propertyName, root); |
| doc.getDocumentElement().appendChild(target); |
| writeCustomScript(doc, FILE_SCRIPT_PATH); |
| // XXX #53622: support also folders (i.e. just files w/o ext??): |
| String targetName = target.getAttribute("name"); |
| addBinding(ActionProvider.COMMAND_COMPILE_SINGLE, FILE_SCRIPT_PATH, targetName, propertyName, root.virtual, JAVA_FILE_PATTERN, "relative-path", ","); // NOI18N |
| jumpToBinding(ActionProvider.COMMAND_COMPILE_SINGLE); |
| jumpToBuildScript(FILE_SCRIPT_PATH, targetName); |
| } |
| |
| Element createCompileSingleTarget(Document doc, Lookup context, String propertyName, AntLocation root) { |
| String targetName = "compile-selected-files-in-" + root.physical.getNameExt(); // NOI18N |
| // XXX do a uniquification check |
| Element target = doc.createElement("target"); // NOI18N |
| addJdkInitDeps(target); |
| target.setAttribute("name", targetName); // NOI18N |
| Element fail = doc.createElement("fail"); // NOI18N |
| fail.setAttribute("unless", propertyName); // NOI18N |
| fail.appendChild(doc.createTextNode(NbBundle.getMessage(JavaActions.class, "COMMENT_must_set_property", propertyName))); |
| target.appendChild(fail); |
| String classesDir = findClassesOutputDir(root.virtual); |
| if (classesDir == null) { |
| target.appendChild(doc.createComment(" " + NbBundle.getMessage(JavaActions.class, "COMMENT_must_set_build_classes_dir") + " ")); |
| classesDir = "${build.classes.dir}"; // NOI18N |
| } |
| Element mkdir = doc.createElement("mkdir"); // NOI18N |
| mkdir.setAttribute("dir", classesDir); // NOI18N |
| target.appendChild(mkdir); |
| Element javac = doc.createElement("javac"); // NOI18N |
| javac.setAttribute("srcdir", root.virtual); // NOI18N |
| javac.setAttribute("destdir", classesDir); // NOI18N |
| javac.setAttribute("includes", "${" + propertyName + "}"); // NOI18N |
| String sourceLevel = findSourceLevel(root.virtual); |
| if (sourceLevel != null) { |
| javac.setAttribute("source", sourceLevel); // NOI18N |
| } |
| String cp = findCUClasspath(root.virtual, "compile"); |
| if (cp != null) { |
| Element classpath = doc.createElement("classpath"); // NOI18N |
| classpath.setAttribute("path", cp); // NOI18N |
| javac.appendChild(classpath); |
| } |
| target.appendChild(javac); |
| return target; |
| } |
| |
| private void handleDebug() throws IOException, SAXException { |
| if (!this.setOutputsNotified) { |
| ProjectModel pm = ProjectModel.createModel(Util.getProjectLocation(this.helper, this.evaluator), |
| FileUtil.toFile(project.getProjectDirectory()), this.evaluator, this.helper); |
| List<ProjectModel.CompilationUnitKey> cuKeys = pm.createCompilationUnitKeys(); |
| assert cuKeys != null; |
| boolean hasOutputs = false; |
| for (Iterator it = cuKeys.iterator(); it.hasNext();) { |
| ProjectModel.CompilationUnitKey ck = (ProjectModel.CompilationUnitKey) it.next(); |
| JavaProjectGenerator.JavaCompilationUnit cu = pm.getCompilationUnit(ck,false); |
| if (cu.output != null && cu.output.size()>0) { |
| hasOutputs = true; |
| break; |
| } |
| } |
| if (!hasOutputs) { |
| alertOutputs (NbBundle.getMessage(JavaActions.class, "ACTION_debug")); |
| this.setOutputsNotified = true; |
| return; |
| } |
| } |
| String[] bindings = findCommandBinding(ActionProvider.COMMAND_RUN); |
| Element task = null; |
| Element origTarget = null; |
| if (bindings != null && bindings.length <= 2) { |
| origTarget = findExistingBuildTarget(ActionProvider.COMMAND_RUN); |
| //The origTarget may be null if the user has removed it from build.xml |
| if (origTarget != null) { |
| task = targetUsesTaskExactlyOnce(origTarget, "java"); // NOI18N |
| } |
| } |
| |
| if (!alert(NbBundle.getMessage(JavaActions.class, "ACTION_debug"), task != null ? GENERAL_SCRIPT_PATH : FILE_SCRIPT_PATH)) { |
| return; |
| } |
| |
| String generatedTargetName = "debug-nb"; // NOI18N |
| String generatedScriptPath; |
| Document doc; |
| Element generatedTarget; |
| if (task != null) { |
| // We can copy the original run target with some modifications. |
| generatedScriptPath = GENERAL_SCRIPT_PATH; |
| doc = readCustomScript(GENERAL_SCRIPT_PATH); |
| ensureImports(doc.getDocumentElement(), bindings[0]); |
| generatedTarget = createDebugTargetFromTemplate(generatedTargetName, origTarget, task, doc); |
| } else { |
| // No info, need to generate a dummy debug target. |
| generatedScriptPath = FILE_SCRIPT_PATH; |
| doc = readCustomScript(FILE_SCRIPT_PATH); |
| ensurePropertiesCopied(doc.getDocumentElement()); |
| generatedTarget = createDebugTargetFromScratch(generatedTargetName, doc); |
| } |
| Comment comm = doc.createComment(" " + NbBundle.getMessage(JavaActions.class, "COMMENT_edit_target") + " "); |
| doc.getDocumentElement().appendChild(comm); |
| comm = doc.createComment(" " + NbBundle.getMessage(JavaActions.class, "COMMENT_more_info_debug") + " "); |
| doc.getDocumentElement().appendChild(comm); |
| doc.getDocumentElement().appendChild(generatedTarget); |
| writeCustomScript(doc, generatedScriptPath); |
| addBinding(ActionProvider.COMMAND_DEBUG, generatedScriptPath, generatedTargetName, null, null, null, null, null); |
| jumpToBinding(ActionProvider.COMMAND_DEBUG); |
| jumpToBuildScript(generatedScriptPath, generatedTargetName); |
| } |
| |
| private Element createNbjpdastart(Document ownerDocument) { |
| Element nbjpdastart = ownerDocument.createElement("nbjpdastart"); // NOI18N |
| nbjpdastart.setAttribute("name", ProjectUtils.getInformation(project).getDisplayName()); // NOI18N |
| nbjpdastart.setAttribute("addressproperty", "jpda.address"); // NOI18N |
| nbjpdastart.setAttribute("transport", "dt_socket"); // NOI18N |
| return nbjpdastart; |
| } |
| |
| private static final String[] DEBUG_VM_ARGS = { |
| "-agentlib:jdwp=transport=dt_socket,address=${jpda.address}", // NOI18N |
| }; |
| private void addDebugVMArgs(Element java, Document ownerDocument) { |
| //Add fork="true" if not alredy there |
| NamedNodeMap attrs = java.getAttributes(); |
| boolean found = false; |
| for (int i=0; i<attrs.getLength(); i++) { |
| Attr attr = (Attr) attrs.item(i); |
| if ("fork".equals(attr.getName())) { //NOI18N |
| String value = attr.getValue(); |
| if ("on".equalsIgnoreCase (value) || //NOI18N |
| "true".equalsIgnoreCase(value) || //NOI18N |
| "yes".equalsIgnoreCase(value)) { //NOI18N |
| found = true; |
| } |
| break; |
| } |
| } |
| if (!found) { |
| java.setAttribute("fork", "true"); //NOI18N |
| } |
| for (int i = 0; i < DEBUG_VM_ARGS.length; i++) { |
| Element jvmarg = ownerDocument.createElement("jvmarg"); // NOI18N |
| jvmarg.setAttribute("value", DEBUG_VM_ARGS[i]); // NOI18N |
| java.appendChild(jvmarg); |
| } |
| } |
| |
| Element createDebugTargetFromTemplate(String generatedTargetName, Element origTarget, Element origTask, Document ownerDocument) { |
| NodeList tasks = origTarget.getChildNodes(); |
| int taskIndex = -1; |
| for (int i = 0; i < tasks.getLength(); i++) { |
| if (tasks.item(i) == origTask) { |
| taskIndex = i; |
| break; |
| } |
| } |
| assert taskIndex != -1; |
| Element target = (Element) ownerDocument.importNode(origTarget, true); |
| addJdkInitDeps(target); |
| Element task = (Element) target.getChildNodes().item(taskIndex); |
| target.setAttribute("name", generatedTargetName); // NOI18N |
| Element nbjpdastart = createNbjpdastart(ownerDocument); |
| String textualCp = task.getAttribute("classpath"); // NOI18N |
| if (textualCp.length() > 0) { |
| Element classpath = ownerDocument.createElement("classpath"); // NOI18N |
| classpath.setAttribute("path", textualCp); // NOI18N |
| nbjpdastart.appendChild(classpath); |
| } else { |
| NodeList origClasspath = task.getElementsByTagName("classpath"); // NOI18N |
| if (origClasspath.getLength() == 1) { |
| Element classpath = (Element) ownerDocument.importNode(origClasspath.item(0), true); |
| nbjpdastart.appendChild(classpath); |
| } |
| } |
| target.insertBefore(nbjpdastart, task); |
| addDebugVMArgs(task, ownerDocument); |
| return target; |
| } |
| |
| Element createDebugTargetFromScratch(String generatedTargetName, Document ownerDocument) { |
| Element target = ownerDocument.createElement("target"); |
| addJdkInitDeps(target); |
| target.setAttribute("name", generatedTargetName); // NOI18N |
| Element path = ownerDocument.createElement("path"); // NOI18N |
| // XXX would be better to determine runtime CP from project.xml and put it here instead (if that is possible)... |
| path.setAttribute("id", "cp"); // NOI18N |
| path.appendChild(ownerDocument.createComment(" " + NbBundle.getMessage(JavaActions.class, "COMMENT_set_runtime_cp") + " ")); |
| target.appendChild(path); |
| Element nbjpdastart = createNbjpdastart(ownerDocument); |
| Element classpath = ownerDocument.createElement("classpath"); // NOI18N |
| classpath.setAttribute("refid", "cp"); // NOI18N |
| nbjpdastart.appendChild(classpath); |
| target.appendChild(nbjpdastart); |
| target.appendChild(ownerDocument.createComment(" " + NbBundle.getMessage(JavaActions.class, "COMMENT_set_main_class") + " ")); |
| Element java = ownerDocument.createElement("java"); // NOI18N |
| java.setAttribute("classname", "some.main.Class"); // NOI18N |
| classpath = ownerDocument.createElement("classpath"); // NOI18N |
| classpath.setAttribute("refid", "cp"); // NOI18N |
| java.appendChild(classpath); |
| addDebugVMArgs(java, ownerDocument); |
| target.appendChild(java); |
| return target; |
| } |
| |
| private void handleProfile() throws IOException, SAXException { |
| if (!this.setOutputsNotified) { |
| ProjectModel pm = ProjectModel.createModel(Util.getProjectLocation(this.helper, this.evaluator), |
| FileUtil.toFile(project.getProjectDirectory()), this.evaluator, this.helper); |
| List<ProjectModel.CompilationUnitKey> cuKeys = pm.createCompilationUnitKeys(); |
| assert cuKeys != null; |
| boolean hasOutputs = false; |
| for (Iterator it = cuKeys.iterator(); it.hasNext();) { |
| ProjectModel.CompilationUnitKey ck = (ProjectModel.CompilationUnitKey) it.next(); |
| JavaProjectGenerator.JavaCompilationUnit cu = pm.getCompilationUnit(ck,false); |
| if (cu.output != null && cu.output.size()>0) { |
| hasOutputs = true; |
| break; |
| } |
| } |
| if (!hasOutputs) { |
| alertOutputs (NbBundle.getMessage(JavaActions.class, "ACTION_profile")); // NOI18N |
| this.setOutputsNotified = true; |
| return; |
| } |
| } |
| String[] bindings = findCommandBinding(ActionProvider.COMMAND_RUN); |
| Element task = null; |
| Element origTarget = null; |
| if (bindings != null && bindings.length <= 2) { |
| origTarget = findExistingBuildTarget(ActionProvider.COMMAND_RUN); |
| //The origTarget may be null if the user has removed it from build.xml |
| if (origTarget != null) { |
| task = targetUsesTaskExactlyOnce(origTarget, "java"); // NOI18N |
| } |
| } |
| |
| if (!alert(NbBundle.getMessage(JavaActions.class, "ACTION_profile"), task != null ? GENERAL_SCRIPT_PATH : FILE_SCRIPT_PATH)) { // NOI18N |
| return; |
| } |
| |
| String generatedTargetName = "profile-nb"; // NOI18N |
| String generatedScriptPath; |
| Document doc; |
| Element generatedTarget; |
| if (task != null) { |
| // We can copy the original run target with some modifications. |
| generatedScriptPath = GENERAL_SCRIPT_PATH; |
| doc = readCustomScript(GENERAL_SCRIPT_PATH); |
| ensureImports(doc.getDocumentElement(), bindings[0]); |
| generatedTarget = createProfileTargetFromTemplate(generatedTargetName, origTarget, task, doc); |
| } else { |
| // No info, need to generate a dummy profile target. |
| generatedScriptPath = FILE_SCRIPT_PATH; |
| doc = readCustomScript(FILE_SCRIPT_PATH); |
| ensurePropertiesCopied(doc.getDocumentElement()); |
| generatedTarget = createProfileTargetFromScratch(generatedTargetName, doc); |
| } |
| Comment comm = doc.createComment(" " + NbBundle.getMessage(JavaActions.class, "COMMENT_edit_target") + " "); // NOI18N |
| doc.getDocumentElement().appendChild(comm); |
| comm = doc.createComment(" " + NbBundle.getMessage(JavaActions.class, "COMMENT_more_info_profile") + " "); // NOI18N |
| doc.getDocumentElement().appendChild(comm); |
| doc.getDocumentElement().appendChild(generatedTarget); |
| writeCustomScript(doc, generatedScriptPath); |
| addBinding(ActionProvider.COMMAND_PROFILE, generatedScriptPath, generatedTargetName, null, null, null, null, null); |
| jumpToBinding(ActionProvider.COMMAND_PROFILE); |
| jumpToBuildScript(generatedScriptPath, generatedTargetName); |
| } |
| |
| Element createProfileTargetFromTemplate(String generatedTargetName, Element origTarget, Element origTask, Document ownerDocument) { |
| NodeList tasks = origTarget.getChildNodes(); |
| int taskIndex = -1; |
| for (int i = 0; i < tasks.getLength(); i++) { |
| if (tasks.item(i) == origTask) { |
| taskIndex = i; |
| break; |
| } |
| } |
| assert taskIndex != -1; |
| Element target = (Element) ownerDocument.importNode(origTarget, true); |
| target.setAttribute("depends", "-profile-check"); // NOI18N |
| target.setAttribute("if", "profiler.configured"); // NOI18N |
| addJdkInitDeps(target); |
| Element task = (Element) target.getChildNodes().item(taskIndex); |
| target.setAttribute("name", generatedTargetName); // NOI18N |
| addProfileInit(ownerDocument, ownerDocument.getDocumentElement()); |
| |
| addProfileVMArgs(task, ownerDocument); |
| return target; |
| } |
| |
| Element createProfileTargetFromScratch(String generatedTargetName, Document ownerDocument) { |
| Element target = ownerDocument.createElement("target"); // NOI18N |
| target.setAttribute("depends", "-profile-check"); // NOI18N |
| target.setAttribute("if", "profiler.configured"); // NOI18N |
| addJdkInitDeps(target); |
| target.setAttribute("name", generatedTargetName); // NOI18N |
| Element path = ownerDocument.createElement("path"); // NOI18N |
| // XXX would be better to determine runtime CP from project.xml and put it here instead (if that is possible)... |
| path.setAttribute("id", "cp"); // NOI18N |
| path.appendChild(ownerDocument.createComment(" " + NbBundle.getMessage(JavaActions.class, "COMMENT_set_runtime_cp") + " ")); // NOI18N |
| target.appendChild(path); |
| addProfileInit(ownerDocument, ownerDocument.getDocumentElement()); |
| |
| target.appendChild(ownerDocument.createComment(" " + NbBundle.getMessage(JavaActions.class, "COMMENT_set_main_class") + " ")); // NOI18N |
| Element java = ownerDocument.createElement("java"); // NOI18N |
| java.setAttribute("classname", "some.main.Class"); // NOI18N |
| Element classpath = ownerDocument.createElement("classpath"); // NOI18N |
| classpath.setAttribute("refid", "cp"); // NOI18N |
| java.appendChild(classpath); |
| addProfileVMArgs(java, ownerDocument); |
| target.appendChild(java); |
| return target; |
| } |
| |
| private void addProfileInit(Document ownerDocument, Element parent) { |
| NodeList nl = ownerDocument.getElementsByTagName("target"); |
| for(int i=0;i<nl.getLength();i++) { |
| if ("-profile-check".equals(((Element)nl.item(i)).getAttribute("name"))) { |
| return; |
| } |
| } |
| Element init = ownerDocument.createElement("target"); // NOI18N |
| init.setAttribute("name", "-profile-check"); |
| Element profilerStart = ownerDocument.createElement("startprofiler"); |
| profilerStart.setAttribute("freeform", "true"); |
| init.appendChild(profilerStart); |
| |
| parent.appendChild(init); |
| } |
| |
| private void addProfileVMArgs(Element java, Document ownerDocument) { |
| //Add fork="true" if not alredy there |
| NamedNodeMap attrs = java.getAttributes(); |
| boolean found = false; |
| for (int i=0; i<attrs.getLength(); i++) { |
| Attr attr = (Attr) attrs.item(i); |
| if ("fork".equals(attr.getName())) { //NOI18N |
| String value = attr.getValue(); |
| if ("on".equalsIgnoreCase (value) || //NOI18N |
| "true".equalsIgnoreCase(value) || //NOI18N |
| "yes".equalsIgnoreCase(value)) { //NOI18N |
| found = true; |
| } |
| break; |
| } |
| } |
| if (!found) { |
| java.setAttribute("fork", "true"); //NOI18N |
| } |
| Element jvmarg = ownerDocument.createElement("jvmarg"); // NOI18N |
| jvmarg.setAttribute("line", "${agent.jvmargs}"); // NOI18N |
| java.appendChild(jvmarg); |
| } |
| |
| /** |
| * Read a generated script if it exists, else create a skeleton. |
| * Imports jdk.xml if appropriate. |
| * @param scriptPath e.g. {@link #FILE_SCRIPT_PATH} or {@link #GENERAL_SCRIPT_PATH} |
| */ |
| Document readCustomScript(String scriptPath) throws IOException, SAXException { |
| // XXX if there is TAX support for rewriting XML files, use that here... |
| FileObject script = helper.getProjectDirectory().getFileObject(scriptPath); |
| Document doc; |
| if (script != null) { |
| InputStream is = script.getInputStream(); |
| try { |
| doc = XMLUtil.parse(new InputSource(is), false, true, null, null); |
| } finally { |
| is.close(); |
| } |
| } else { |
| doc = XMLUtil.createDocument("project", /*XXX:"antlib:org.apache.tools.ant"*/null, null, null); // NOI18N |
| Element root = doc.getDocumentElement(); |
| String projname = ProjectUtils.getInformation(project).getDisplayName(); |
| root.setAttribute("name", NbBundle.getMessage(JavaActions.class, "LBL_generated_script_name", projname)); |
| } |
| if (helper.getProjectDirectory().getFileObject(JdkConfiguration.JDK_XML) != null) { |
| JdkConfiguration.insertJdkXmlImport(doc); |
| } |
| return doc; |
| } |
| |
| /** |
| * Make sure that if the project defines ${project.dir} in project.xml that |
| * a custom build script also defines this property. |
| * Generally, copy any properties defined in project.xml to Ant syntax. |
| * Used for generated targets which essentially copy Ant fragments from project.xml |
| * (rather than the user's build.xml). |
| * Also sets the basedir to the (IDE) project directory. |
| * Idempotent, takes effect only once. |
| * Use with {@link #FILE_SCRIPT_PATH}. |
| * @param antProject XML of an Ant project (document element) |
| */ |
| void ensurePropertiesCopied(Element antProject) { |
| if (antProject.getAttribute("basedir").length() > 0) { |
| // Do not do it twice to the same script. |
| return; |
| } |
| antProject.setAttribute("basedir", /* ".." times count('/', FILE_SCRIPT_PATH) */".."); // NOI18N |
| // Look for <properties> in project.xml and make corresponding definitions in the Ant script. |
| Element data = Util.getPrimaryConfigurationData(helper); |
| Element properties = XMLUtil.findElement(data, "properties", Util.NAMESPACE); |
| if (properties != null) { |
| for (Element el : XMLUtil.findSubElements(properties)) { |
| Element nue = antProject.getOwnerDocument().createElement("property"); // NOI18N |
| if (el.getLocalName().equals("property")) { // NOI18N |
| String name = el.getAttribute("name"); // NOI18N |
| assert name != null; |
| String text = XMLUtil.findText(el); |
| assert text != null; |
| nue.setAttribute("name", name); |
| nue.setAttribute("value", text); |
| } else if (el.getLocalName().equals("property-file")) { // NOI18N |
| String text = XMLUtil.findText(el); |
| assert text != null; |
| nue.setAttribute("file", text); |
| } else { |
| assert false : el; |
| } |
| antProject.appendChild(nue); |
| } |
| } |
| } |
| |
| /** |
| * Make sure that the custom build script imports the original build script |
| * and is using the same base dir. |
| * Used for generated targets which essentially copy Ant targets from build.xml. |
| * Use with {@link #GENERAL_SCRIPT_PATH}. |
| * Idempotent, takes effect only once. |
| * @param antProject XML of an Ant project (document element) |
| * @oaram origScriptPath Ant name of original build script's path |
| */ |
| void ensureImports(Element antProject, String origScriptPath) throws IOException, SAXException { |
| if (antProject.getAttribute("basedir").length() > 0) { |
| // Do not do it twice to the same script. |
| return; |
| } |
| String origScriptPathEval = evaluator.evaluate(origScriptPath); |
| if (origScriptPathEval == null) { |
| // Can't do anything, forget it. |
| return; |
| } |
| String origScriptURI = Utilities.toURI(helper.resolveFile(origScriptPathEval)).toString(); |
| Document origScriptDocument = XMLUtil.parse(new InputSource(origScriptURI), false, true, null, null); |
| String origBasedir = origScriptDocument.getDocumentElement().getAttribute("basedir"); // NOI18N |
| if (origBasedir.length() == 0) { |
| origBasedir = "."; // NOI18N |
| } |
| String basedir, importPath; |
| File origScript = new File(origScriptPathEval); |
| if (origScript.isAbsolute()) { |
| // Use full path. |
| importPath = origScriptPathEval; |
| if (new File(origBasedir).isAbsolute()) { |
| basedir = origBasedir; |
| } else { |
| basedir = PropertyUtils.resolveFile(origScript.getParentFile(), origBasedir).getAbsolutePath(); |
| } |
| } else { |
| // Import relative to that path. |
| // Note that <import>'s path is always relative to the location of the importing script, regardless of the basedir. |
| String prefix = /* ".." times count('/', FILE_SCRIPT_PATH) */"../"; // NOI18N |
| importPath = prefix + origScriptPathEval; |
| if (new File(origBasedir).isAbsolute()) { |
| basedir = origBasedir; |
| } else { |
| int slash = origScriptPathEval.replace(File.separatorChar, '/').lastIndexOf('/'); |
| if (slash == -1) { |
| basedir = prefix + origBasedir; |
| } else { |
| basedir = prefix + origScriptPathEval.substring(0, slash + 1) + origBasedir; |
| } |
| // Trim: |
| basedir = basedir.replaceAll("/\\.$", ""); // NOI18N |
| } |
| } |
| antProject.setAttribute("basedir", basedir); // NOI18N |
| Element importEl = antProject.getOwnerDocument().createElement("import"); // NOI18N |
| importEl.setAttribute("file", importPath); // NOI18N |
| antProject.appendChild(importEl); |
| } |
| |
| /** |
| * Write a script with a new or modified document. |
| * @param scriptPath e.g. {@link #FILE_SCRIPT_PATH} or {@link #GENERAL_SCRIPT_PATH} |
| */ |
| void writeCustomScript(Document doc, String scriptPath) throws IOException { |
| FileObject script = helper.getProjectDirectory().getFileObject(scriptPath); |
| if (script == null) { |
| script = FileUtil.createData(helper.getProjectDirectory(), scriptPath); |
| } |
| FileLock lock = script.lock(); |
| try { |
| OutputStream os = script.getOutputStream(lock); |
| try { |
| XMLUtil.write(doc, os, "UTF-8"); // NOI18N |
| } finally { |
| os.close(); |
| } |
| } finally { |
| lock.releaseLock(); |
| } |
| } |
| |
| private List<Element> compilationUnits() { |
| Element java = aux.getConfigurationFragment(JavaProjectNature.EL_JAVA, JavaProjectNature.NS_JAVA_LASTEST, true); |
| if (java == null) { |
| return Collections.emptyList(); |
| } |
| return XMLUtil.findSubElements(java); |
| } |
| |
| /** |
| * Find a Java package root in which the selection is contained. |
| * @param context lookup with Java source files and/or folders and/or junk |
| * @return the package root if there is one, or null if the lookup is empty, has junk, or has multiple roots |
| */ |
| AntLocation findPackageRoot(Lookup context) { |
| for (Element compilationUnitEl : compilationUnits()) { |
| assert compilationUnitEl.getLocalName().equals("compilation-unit") : compilationUnitEl; |
| List<String> packageRootNames = Classpaths.findPackageRootNames(compilationUnitEl); |
| Map<String,FileObject> packageRootsByName = Classpaths.findPackageRootsByName(helper, evaluator, packageRootNames); |
| for (Map.Entry<String,FileObject> entry : packageRootsByName.entrySet()) { |
| FileObject root = entry.getValue(); |
| if (containsSelectedJavaSources(root, context)) { |
| return new AntLocation(entry.getKey(), root); |
| } |
| } |
| } |
| // Couldn't find it. |
| return null; |
| } |
| |
| /** |
| * Check to see if a (node-like) selection contains one or more Java sources (or folders) inside the root. |
| */ |
| static boolean containsSelectedJavaSources(FileObject root, Lookup context) { |
| Set<FileObject> selection = new HashSet<FileObject>(); |
| for (DataObject dob : context.lookupAll(DataObject.class)) { |
| selection.add(dob.getPrimaryFile()); |
| } |
| if (selection.isEmpty()) { |
| return false; |
| } |
| for (FileObject f : selection) { |
| if (f.isData() && !f.hasExt("java")) { // NOI18N |
| return false; |
| } |
| if (f != root && !FileUtil.isParentOf(root, f)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Represents a location as referred to by the Ant script. |
| * Contains both its logical representation, and actual (current) location (or null). |
| */ |
| static final class AntLocation { |
| public final String virtual; |
| public final FileObject physical; |
| public AntLocation(String virtual, FileObject physical) { |
| this.virtual = virtual; |
| this.physical = physical; |
| } |
| public String toString() { |
| return "AntLocation[" + virtual + "=" + physical + "]"; // NOI18N |
| } |
| } |
| |
| /** |
| * Try to find the compilation unit containing a source root. |
| * @param sources a source root in the project (as a virtual Ant name) |
| * @return the compilation unit owning it, or null if not found |
| */ |
| private Element findCompilationUnit(String sources) { |
| for (Element compilationUnitEl : compilationUnits()) { |
| for (Element packageRoot : XMLUtil.findSubElements(compilationUnitEl)) { |
| if (packageRoot.getLocalName().equals("package-root")) { // NOI18N |
| if (XMLUtil.findText(packageRoot).equals(sources)) { |
| return compilationUnitEl; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Try to determine where classes from a given package root should be compiled to. |
| * @param sources a source root in the project (as a virtual Ant name) |
| * @return an output directory (never a JAR), as a virtual name, or null if none could be found |
| */ |
| String findClassesOutputDir(String sources) { |
| Element compilationUnitEl = findCompilationUnit(sources); |
| if (compilationUnitEl != null) { |
| return findClassesOutputDir(compilationUnitEl); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Find output classes given a compilation unit from project.xml. |
| */ |
| private String findClassesOutputDir(Element compilationUnitEl) { |
| // Look for an appropriate <built-to>. |
| for (Element builtTo : XMLUtil.findSubElements(compilationUnitEl)) { |
| if (builtTo.getLocalName().equals("built-to")) { // NOI18N |
| String rawtext = XMLUtil.findText(builtTo); |
| // Check that it is not an archive. |
| String evaltext = evaluator.evaluate(rawtext); |
| if (evaltext != null) { |
| File dest = helper.resolveFile(evaltext); |
| URL destU; |
| try { |
| destU = Utilities.toURI(dest).toURL(); |
| } catch (MalformedURLException e) { |
| throw new AssertionError(e); |
| } |
| if (!FileUtil.isArchiveFile(destU)) { |
| // OK, dir, take it. |
| return rawtext; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Try to find the source level corresponding to a source root. |
| * @param sources a source root in the project (as a virtual Ant name) |
| * @return the source level, or null if none was specified or there was no such source root |
| */ |
| String findSourceLevel(String sources) { |
| Element compilationUnitEl = findCompilationUnit(sources); |
| if (compilationUnitEl != null) { |
| Element sourceLevel = XMLUtil.findElement(compilationUnitEl, "source-level", JavaProjectNature.NS_JAVA_LASTEST); |
| if (sourceLevel != null) { |
| return XMLUtil.findText(sourceLevel); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Try to find the compile-time classpath corresponding to a source root. |
| * @param sources a source root in the project (as a virtual Ant name) |
| * @return the classpath (in Ant form), or null if none was specified or there was no such source root |
| */ |
| String findCUClasspath(String sources, String moud) { |
| Element compilationUnitEl = findCompilationUnit(sources); |
| if (compilationUnitEl != null) { |
| for (Element classpath : XMLUtil.findSubElements(compilationUnitEl)) { |
| if (classpath.getLocalName().equals("classpath")) { // NOI18N |
| String mode = classpath.getAttribute("mode"); // NOI18N |
| if (mode.equals(moud)) { |
| return XMLUtil.findText(classpath); |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Finds all build-to elements in project.xml and returns their content as array |
| * @param srcRoot source root in the project |
| * @return array with content of all <built-to> elements built by CU containing the srcRoot |
| */ |
| private String[] findCUOutputs(String srcRoot) { |
| List<String> outputs = new ArrayList<String>(); |
| Element cuElem = findCompilationUnit(srcRoot); |
| if (cuElem != null) { |
| NodeList builts = cuElem.getElementsByTagName("built-to"); // NOI18N |
| for (int i = 0; i < builts.getLength(); i++) { |
| outputs.add(builts.item(i).getTextContent()); |
| } |
| } |
| return outputs.toArray(new String[outputs.size()]); |
| } |
| |
| //The order of the root elements as specified in the schema. |
| //Used to add <ide-actions> at the correct place. |
| private static final String[] rootElementsOrder = {"name", "properties", "folders", "ide-actions", "export", "view", "subprojects"}; // NOI18N |
| |
| /** |
| * Add an action binding to project.xml. |
| * If there is no required context, the action is also added to the context menu of the project node. |
| * @param command the command name |
| * @param scriptPath the path to the generated script |
| * @param target the name of the target (in scriptPath) |
| * @param propertyName a property name to hold the selection (or null for no context, in which case remainder should be null) |
| * @param dir the raw text to use for the directory name |
| * @param pattern the regular expression to match, or null |
| * @param format the format to use |
| * @param separator the separator to use for multiple files, or null for single file only |
| */ |
| void addBinding(String command, String scriptPath, String target, String propertyName, String dir, String pattern, String format, String separator) throws IOException { |
| // XXX cannot use FreeformProjectGenerator since that is currently not a public support SPI from ant/freeform |
| // XXX should this try to find an existing binding? probably not, since it is assumed that if there was one, we would never get here to begin with |
| Element data = Util.getPrimaryConfigurationData(helper); |
| Element ideActions = XMLUtil.findElement(data, "ide-actions", Util.NAMESPACE); // NOI18N |
| if (ideActions == null) { |
| //fix for #58442: |
| ideActions = data.getOwnerDocument().createElementNS(Util.NAMESPACE, "ide-actions"); // NOI18N |
| XMLUtil.appendChildElement(data, ideActions, rootElementsOrder); |
| } |
| Document doc = data.getOwnerDocument(); |
| Element action = doc.createElementNS(Util.NAMESPACE, "action"); // NOI18N |
| action.setAttribute("name", command); // NOI18N |
| Element script = doc.createElementNS(Util.NAMESPACE, "script"); // NOI18N |
| script.appendChild(doc.createTextNode(scriptPath)); |
| action.appendChild(script); |
| Element targetEl = doc.createElementNS(Util.NAMESPACE, "target"); // NOI18N |
| targetEl.appendChild(doc.createTextNode(target)); |
| action.appendChild(targetEl); |
| if (propertyName != null) { |
| Element context = doc.createElementNS(Util.NAMESPACE, "context"); // NOI18N |
| Element property = doc.createElementNS(Util.NAMESPACE, "property"); // NOI18N |
| property.appendChild(doc.createTextNode(propertyName)); |
| context.appendChild(property); |
| Element folder = doc.createElementNS(Util.NAMESPACE, "folder"); // NOI18N |
| folder.appendChild(doc.createTextNode(dir)); |
| context.appendChild(folder); |
| if (pattern != null) { |
| Element patternEl = doc.createElementNS(Util.NAMESPACE, "pattern"); // NOI18N |
| patternEl.appendChild(doc.createTextNode(pattern)); |
| context.appendChild(patternEl); |
| } |
| Element formatEl = doc.createElementNS(Util.NAMESPACE, "format"); // NOI18N |
| formatEl.appendChild(doc.createTextNode(format)); |
| context.appendChild(formatEl); |
| Element arity = doc.createElementNS(Util.NAMESPACE, "arity"); // NOI18N |
| if (separator != null) { |
| Element separatorEl = doc.createElementNS(Util.NAMESPACE, "separated-files"); // NOI18N |
| separatorEl.appendChild(doc.createTextNode(separator)); |
| arity.appendChild(separatorEl); |
| } else { |
| arity.appendChild(doc.createElementNS(Util.NAMESPACE, "one-file-only")); // NOI18N |
| } |
| context.appendChild(arity); |
| action.appendChild(context); |
| } else { |
| // Add a context menu item, since it applies to the project as a whole. |
| // Assume there is already a <context-menu> defined, which is quite likely. |
| Element view = XMLUtil.findElement(data, "view", Util.NAMESPACE); // NOI18N |
| if (view != null) { |
| Element contextMenu = XMLUtil.findElement(view, "context-menu", Util.NAMESPACE); // NOI18N |
| if (contextMenu != null) { |
| Element ideAction = doc.createElementNS(Util.NAMESPACE, "ide-action"); // NOI18N |
| ideAction.setAttribute("name", command); // NOI18N |
| contextMenu.appendChild(ideAction); |
| } |
| } |
| } |
| ideActions.appendChild(action); |
| Util.putPrimaryConfigurationData(helper, data); |
| ProjectManager.getDefault().saveProject(project); |
| } |
| |
| /** |
| * Jump to a target in the editor. |
| * @param scriptPath the script to open |
| * @param target the name of the target (in scriptPath) |
| */ |
| private void jumpToBuildScript(String scriptPath, String target) { |
| jumpToFile(scriptPath, target, "target", "name"); // NOI18N |
| } |
| |
| /** |
| * Jump to an action binding in the editor. |
| * @param command an {@link ActionProvider} command name found in project.xml |
| */ |
| private void jumpToBinding(String command) { |
| jumpToFile(AntProjectHelper.PROJECT_XML_PATH, command, "action", "name"); // NOI18N |
| } |
| |
| /** |
| * Jump to some line in an XML file. |
| * @param path project-relative path to the file |
| * @param match {@see #findLine} |
| * @param elementLocalName {@see #findLine} |
| * @param elementAttributeName {@see #findLine} |
| */ |
| private void jumpToFile(String path, String match, String elementLocalName, String elementAttributeName) { |
| FileObject file = helper.getProjectDirectory().getFileObject(path); |
| if (file == null) { |
| return; |
| } |
| int line; |
| try { |
| line = findLine(file, match, elementLocalName, elementAttributeName); |
| } catch (Exception e) { |
| ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, e); |
| return; |
| } |
| if (line == -1) { |
| // Just open it. |
| line = 0; |
| } |
| DataObject fileDO; |
| try { |
| fileDO = DataObject.find(file); |
| } catch (DataObjectNotFoundException e) { |
| throw new AssertionError(e); |
| } |
| LineCookie lines = fileDO.getCookie(LineCookie.class); |
| if (lines != null) { |
| try { |
| lines.getLineSet().getCurrent(line).show(ShowOpenType.OPEN, ShowVisibilityType.FOCUS); |
| } catch (IndexOutOfBoundsException e) { |
| // XXX reproducibly thrown if the document was already open. Why?? (file.refresh() above does not help.) |
| ErrorManager.getDefault().getInstance(JavaActions.class.getName()).log(ErrorManager.WARNING, e + " [file=" + file + " match=" + match + " line=" + line + "]"); // NOI18N |
| lines.getLineSet().getCurrent(0).show(ShowOpenType.OPEN, ShowVisibilityType.FOCUS); |
| } |
| } |
| } |
| |
| /** |
| * Find the line number of a target in an Ant script, or some other line in an XML file. |
| * Able to find a certain element with a certain attribute matching a given value. |
| * See also AntTargetNode.TargetOpenCookie. |
| * @param file an Ant script or other XML file |
| * @param match the attribute value to match (e.g. target name) |
| * @param elementLocalName the (local) name of the element to look for |
| * @param elementAttributeName the name of the attribute to match on |
| * @return the line number (0-based), or -1 if not found |
| */ |
| static final int findLine(FileObject file, final String match, final String elementLocalName, final String elementAttributeName) throws IOException, SAXException, ParserConfigurationException { |
| InputSource in = new InputSource(file.getURL().toString()); |
| SAXParserFactory factory = SAXParserFactory.newInstance(); |
| factory.setNamespaceAware(true); |
| SAXParser parser = factory.newSAXParser(); |
| final int[] line = new int[] {-1}; |
| class Handler extends DefaultHandler { |
| private Locator locator; |
| public void setDocumentLocator(Locator l) { |
| locator = l; |
| } |
| public void startElement(String uri, String localname, String qname, Attributes attr) throws SAXException { |
| if (line[0] == -1) { |
| if (localname.equals(elementLocalName) && match.equals(attr.getValue(elementAttributeName))) { // NOI18N |
| line[0] = locator.getLineNumber() - 1; |
| } |
| } |
| } |
| } |
| parser.parse(in, new Handler()); |
| return line[0]; |
| } |
| |
| /** |
| * Attempt to find the Ant build script target bound to a given IDE command. |
| * @param command an {@link ActionProvider} command |
| * @return the XML for the target if it could be found (and there was no more than one target bound), else null |
| */ |
| Element findExistingBuildTarget(String command) throws IOException, SAXException { |
| String[] binding = findCommandBinding(command); |
| if (binding == null) { |
| return null; |
| } |
| String scriptName = binding[0]; |
| assert scriptName != null; |
| String targetName; |
| if (binding.length == 1) { |
| targetName = null; |
| } else if (binding.length == 2) { |
| targetName = binding[1]; |
| } else { |
| // Too many bindings; we do not support this. |
| return null; |
| } |
| String scriptPath = evaluator.evaluate(scriptName); |
| if (scriptPath == null) { |
| return null; |
| } |
| File scriptFile = helper.resolveFile(scriptPath); |
| String scriptURI = Utilities.toURI(scriptFile).toString(); |
| Document doc = XMLUtil.parse(new InputSource(scriptURI), false, true, null, null); |
| if (targetName == null) { |
| targetName = doc.getDocumentElement().getAttribute("default"); // NOI18N |
| if (targetName == null) { |
| return null; |
| } |
| } |
| for (Element target : XMLUtil.findSubElements(doc.getDocumentElement())) { |
| if (target.getLocalName().equals("target") && targetName.equals(target.getAttribute("name"))) { // NOI18N |
| return target; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Find the target binding for some command. |
| * @param command an {@link ActionProvider} command |
| * @return an array of a script name (Ant syntax, never null) and zero or more target names (none means default target) |
| * or null if no binding could be found for this command |
| */ |
| String[] findCommandBinding(String command) { |
| Element data = Util.getPrimaryConfigurationData(helper); |
| Element ideActions = XMLUtil.findElement(data, "ide-actions", Util.NAMESPACE); // NOI18N |
| if (ideActions == null) { |
| return null; |
| } |
| String scriptName = "build.xml"; // NOI18N |
| for (Element action : XMLUtil.findSubElements(ideActions)) { |
| assert action.getLocalName().equals("action"); |
| if (action.getAttribute("name").equals(command)) { |
| Element script = XMLUtil.findElement(action, "script", Util.NAMESPACE); // NOI18N |
| if (script != null) { |
| scriptName = XMLUtil.findText(script); |
| } |
| List<String> scriptPlusTargetNames = new ArrayList<String>(); |
| scriptPlusTargetNames.add(scriptName); |
| for (Element target : XMLUtil.findSubElements(action)) { |
| if (target.getLocalName().equals("target")) { // NOI18N |
| scriptPlusTargetNames.add(XMLUtil.findText(target)); |
| } |
| } |
| if (scriptName.equals(JdkConfiguration.NBJDK_XML) && scriptPlusTargetNames.size() > 1) { |
| // Try to find the original script instead. |
| FileObject nbjdkFO = helper.getProjectDirectory().getFileObject(JdkConfiguration.NBJDK_XML); |
| if (nbjdkFO != null) { |
| try { |
| Document nbjdk = XMLUtil.parse(new InputSource(nbjdkFO.getURL().toString()), false, false, null, null); |
| NodeList nl = nbjdk.getElementsByTagName("target"); // NOI18N |
| for (int i = 0; i < nl.getLength(); i++) { |
| if (((Element) nl.item(i)).getAttribute("name").equals(scriptPlusTargetNames.get(1))) { // NOI18N |
| NodeList nl2 = ((Element) nl.item(i)).getElementsByTagName("ant"); // NOI18N |
| if (nl2.getLength() == 1) { |
| String antfile = ((Element) nl2.item(0)).getAttribute("antfile"); // NOI18N |
| if (antfile.length() == 0) { |
| antfile = "build.xml"; // NOI18N |
| } |
| scriptPlusTargetNames.set(0, antfile); |
| break; |
| } |
| } |
| } |
| } catch (Exception x) { |
| Exceptions.printStackTrace(x); |
| } |
| } |
| } |
| return scriptPlusTargetNames.toArray(new String[scriptPlusTargetNames.size()]); |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Check to see if a given Ant target uses a given task once (and only once). |
| * @param target an Ant <code><target></code> element |
| * @param taskName the (unqualified) name of an Ant task |
| * @return a task element with that name, or null if there is none or more than one |
| */ |
| Element targetUsesTaskExactlyOnce(Element target, String taskName) { |
| // XXX should maybe also look for any other usage of the task in the same script in case there is none in the mentioned target |
| Element foundTask = null; |
| for (Element task : XMLUtil.findSubElements(target)) { |
| if (task.getLocalName().equals(taskName)) { |
| if (foundTask != null) { |
| // Duplicate. |
| return null; |
| } else { |
| foundTask = task; |
| } |
| } |
| } |
| return foundTask; |
| } |
| |
| private void handleRunSingle(Lookup context) throws IOException, SAXException { |
| if (!alert(NbBundle.getMessage(JavaActions.class, "ACTION_run.single"), FILE_SCRIPT_PATH)) { |
| return; |
| } |
| Document doc = readCustomScript(FILE_SCRIPT_PATH); |
| AntLocation root = handleInitials(doc, context); |
| assert root != null : context; |
| String propertyName = "run.class"; // NOI18N |
| String targetName = "run-selected-file-in-" + root.physical.getNameExt(); // NOI18N |
| Element target = createRunSingleTargetElem(doc, targetName, propertyName, root); |
| doc.getDocumentElement().appendChild(target); |
| writeCustomScript(doc, FILE_SCRIPT_PATH); |
| addBinding(ActionProvider.COMMAND_RUN_SINGLE, FILE_SCRIPT_PATH, targetName, |
| propertyName, root.virtual, JAVA_FILE_PATTERN, "java-name", null); // NOI18N |
| jumpToBinding(ActionProvider.COMMAND_RUN_SINGLE); |
| jumpToBuildScript(FILE_SCRIPT_PATH, targetName); |
| } |
| |
| private void handleDebugSingle(Lookup context) throws IOException, SAXException { |
| if (!alert(NbBundle.getMessage(JavaActions.class, "ACTION_debug.single"), FILE_SCRIPT_PATH)) { |
| return; |
| } |
| Document doc = readCustomScript(FILE_SCRIPT_PATH); |
| AntLocation root = handleInitials(doc, context); |
| assert root != null : context; |
| String propertyName = "debug.class"; // NOI18N |
| String targetName = "debug-selected-file-in-" + root.physical.getNameExt(); // NOI18N |
| Element targetElem = createDebugSingleTargetElem(doc, targetName, propertyName, root); |
| doc.getDocumentElement().appendChild(targetElem); |
| writeCustomScript(doc, FILE_SCRIPT_PATH); |
| addBinding(ActionProvider.COMMAND_DEBUG_SINGLE, FILE_SCRIPT_PATH, targetName, |
| propertyName, root.virtual, JAVA_FILE_PATTERN, "java-name", null); // NOI18N |
| jumpToBinding(ActionProvider.COMMAND_DEBUG_SINGLE); |
| jumpToBuildScript(FILE_SCRIPT_PATH, targetName); |
| } |
| |
| private void handleProfileSingle(Lookup context) throws IOException, SAXException { |
| if (!alert(NbBundle.getMessage(JavaActions.class, "ACTION_profile.single"), FILE_SCRIPT_PATH)) { // NOI18N |
| return; |
| } |
| Document doc = readCustomScript(FILE_SCRIPT_PATH); |
| AntLocation root = handleInitials(doc, context); |
| assert root != null : context; |
| String propertyName = "profile.class"; // NOI18N |
| String targetName = "profile-selected-file-in-" + root.physical.getNameExt(); // NOI18N |
| Element targetElem = createProfileSingleTargetElem(doc, targetName, propertyName, root); |
| doc.getDocumentElement().appendChild(targetElem); |
| writeCustomScript(doc, FILE_SCRIPT_PATH); |
| addBinding(ActionProvider.COMMAND_PROFILE_SINGLE, FILE_SCRIPT_PATH, targetName, |
| propertyName, root.virtual, JAVA_FILE_PATTERN, "java-name", null); // NOI18N |
| jumpToBinding(ActionProvider.COMMAND_PROFILE_SINGLE); |
| jumpToBuildScript(FILE_SCRIPT_PATH, targetName); |
| } |
| |
| Element createRunSingleTargetElem(Document doc, String tgName, |
| String propName, AntLocation root) throws IOException, SAXException { |
| |
| Element targetElem = doc.createElement("target"); // NOI18N |
| addJdkInitDeps(targetElem); |
| targetElem.setAttribute("name", tgName); // NOI18N |
| Element failElem = doc.createElement("fail"); // NOI18N |
| failElem.setAttribute("unless", propName); // NOI18N |
| failElem.appendChild(doc.createTextNode(NbBundle.getMessage(JavaActions.class, |
| "COMMENT_must_set_property", propName))); |
| targetElem.appendChild(failElem); |
| |
| String depends[] = getRunDepends(); |
| if (depends != null) { |
| targetElem.appendChild(createAntElem(doc, depends[0], depends[1])); |
| } |
| |
| Element javaElem = doc.createElement("java"); // NOI18N |
| javaElem.setAttribute("classname", "${" + propName + "}"); // NOI18N |
| javaElem.setAttribute("fork", "true"); // NOI18N |
| javaElem.setAttribute("failonerror", "true"); // NOI18N |
| |
| Element cpElem = getPathFromCU(doc, root.virtual, "classpath"); |
| // add comment only if there is no definition |
| if (cpElem.getChildNodes().getLength() == 0) { |
| cpElem.appendChild(doc.createComment(" " + NbBundle.getMessage(JavaActions.class, |
| "COMMENT_set_runtime_cp") + " ")); |
| } |
| javaElem.appendChild(cpElem); |
| targetElem.appendChild(javaElem); |
| return targetElem; |
| } |
| |
| Element createDebugSingleTargetElem(Document doc, String tgName, |
| String propName, AntLocation root) throws IOException, SAXException { |
| |
| Element targetElem = doc.createElement("target"); // NOI18N |
| addJdkInitDeps(targetElem); |
| targetElem.setAttribute("name", tgName); // NOI18N |
| Element failElem = doc.createElement("fail"); // NOI18N |
| failElem.setAttribute("unless", propName); // NOI18N |
| failElem.appendChild(doc.createTextNode(NbBundle.getMessage(JavaActions.class, |
| "COMMENT_must_set_property", propName))); |
| targetElem.appendChild(failElem); |
| |
| String depends[] = getRunDepends(); |
| if (depends != null) { |
| targetElem.appendChild(createAntElem(doc, depends[0], depends[1])); |
| } |
| |
| Element pElem = getPathFromCU(doc, root.virtual, "path"); // NOI18N |
| pElem.setAttribute("id", "cp"); // NOI18N |
| // add comment only if there is no definition |
| if (pElem.getChildNodes().getLength() == 0) { |
| pElem.appendChild(doc.createComment(" " + NbBundle.getMessage(JavaActions.class, |
| "COMMENT_set_runtime_cp") + " ")); |
| } |
| targetElem.appendChild(pElem); |
| |
| Element nbjpdastartElem = createNbjpdastart(doc); |
| Element cpElem = doc.createElement("classpath"); // NOI18N |
| cpElem.setAttribute("refid", "cp"); // NOI18N |
| nbjpdastartElem.appendChild(cpElem); |
| targetElem.appendChild(nbjpdastartElem); |
| |
| Element javaElem = doc.createElement("java"); // NOI18N |
| javaElem.setAttribute("classname", "${" + propName + "}"); // NOI18N |
| |
| cpElem = doc.createElement("classpath"); // NOI18N |
| cpElem.setAttribute("refid", "cp"); // NOI18N |
| javaElem.appendChild(cpElem); |
| addDebugVMArgs(javaElem, doc); |
| |
| targetElem.appendChild(javaElem); |
| return targetElem; |
| } |
| |
| Element createProfileSingleTargetElem(Document doc, String tgName, |
| String propName, AntLocation root) throws IOException, SAXException { |
| |
| Element targetElem = doc.createElement("target"); // NOI18N |
| addJdkInitDeps(targetElem); |
| targetElem.setAttribute("name", tgName); // NOI18N |
| targetElem.setAttribute("if", "profiler.configured"); // NOI18N |
| targetElem.setAttribute("depends", "-profile-check"); // NOI18N |
| Element failElem = doc.createElement("fail"); // NOI18N |
| failElem.setAttribute("unless", propName); // NOI18N |
| failElem.appendChild(doc.createTextNode(NbBundle.getMessage(JavaActions.class, |
| "COMMENT_must_set_property", propName))); |
| targetElem.appendChild(failElem); |
| |
| String depends[] = getRunDepends(); |
| if (depends != null) { |
| targetElem.appendChild(createAntElem(doc, depends[0], depends[1])); |
| } |
| |
| Element pElem = getPathFromCU(doc, root.virtual, "path"); // NOI18N |
| pElem.setAttribute("id", "cp"); // NOI18N |
| // add comment only if there is no definition |
| if (pElem.getChildNodes().getLength() == 0) { |
| pElem.appendChild(doc.createComment(" " + NbBundle.getMessage(JavaActions.class, |
| "COMMENT_set_runtime_cp") + " ")); |
| } |
| targetElem.appendChild(pElem); |
| |
| Element cpElem = doc.createElement("classpath"); // NOI18N |
| cpElem.setAttribute("refid", "cp"); // NOI18N |
| |
| Element javaElem = doc.createElement("java"); // NOI18N |
| javaElem.setAttribute("classname", "${" + propName + "}"); // NOI18N |
| |
| cpElem = doc.createElement("classpath"); // NOI18N |
| cpElem.setAttribute("refid", "cp"); // NOI18N |
| javaElem.appendChild(cpElem); |
| addProfileInit(doc, doc.getDocumentElement()); |
| addProfileVMArgs(javaElem, doc); |
| |
| targetElem.appendChild(javaElem); |
| return targetElem; |
| } |
| |
| private AntLocation handleInitials(Document doc, Lookup context) { |
| ensurePropertiesCopied(doc.getDocumentElement()); |
| Comment comm = doc.createComment(" " + NbBundle.getMessage(JavaActions.class, "COMMENT_edit_target") + " "); |
| doc.getDocumentElement().appendChild(comm); |
| comm = doc.createComment(" " + NbBundle.getMessage(JavaActions.class, "COMMENT_more_info_run.single") + " "); |
| doc.getDocumentElement().appendChild(comm); |
| return findPackageRoot(context); |
| } |
| |
| /** |
| * Get value of depends attribute of target mapped as Run target by user |
| */ |
| String[] getRunDepends() throws IOException, SAXException { |
| String depends[] = null; |
| Element targetElem = findExistingBuildTarget(ActionProvider.COMMAND_RUN); |
| if (targetElem == null) |
| return null; |
| String[] bindings = findCommandBinding(ActionProvider.COMMAND_RUN); |
| String dep = targetElem.getAttribute("depends"); // NOI18N |
| if (bindings != null && bindings.length <= 2 && !"".equals(dep)) { |
| depends = new String[2]; |
| depends[0] = bindings[0]; |
| depends[1] = dep; |
| } |
| return depends; |
| } |
| |
| /** |
| * Create Path like element (path or classpath) consisting of either execute cp |
| * if it exists or compilation cp and build products |
| */ |
| Element getPathFromCU(Document doc, String srcRoot, String type) { |
| String cp = findCUClasspath(srcRoot, "execute"); // NOI18N |
| Element pElem = null; |
| if (cp != null) { |
| // if EXECUTE cp exists use it |
| pElem = createPathLikeElem(doc, type, null, new String[] {cp}, null, null, null); |
| } else { |
| // if not try to create run cp from COMPILE cp and all output locations |
| cp = findCUClasspath(srcRoot, "compile"); // NOI18N |
| String paths[] = cp == null ? null : new String[] {cp}; |
| String outputs[] = findCUOutputs(srcRoot); |
| pElem = createPathLikeElem(doc, type, null, paths, outputs, null, null); |
| } |
| return pElem; |
| } |
| |
| /** |
| * Creates path or classpath element according to params |
| */ |
| Element createPathLikeElem(Document doc, String type, String id, |
| String[] paths, String[] locations, String refid, String comm) { |
| |
| Element pElem = doc.createElement(type); // NOI18N |
| if (id != null) |
| pElem.setAttribute("id", id); // NOI18N |
| if (refid != null) |
| pElem.setAttribute("refid", refid); // NOI18N |
| if (comm != null) |
| pElem.appendChild(doc.createComment(comm)); |
| if (paths != null && paths.length > 0) { |
| for (int i = 0; i < paths.length; i++) { |
| Element pathelElem = doc.createElement("pathelement"); // NOI18N |
| pathelElem.setAttribute("path", paths[i]); // NOI18N |
| pElem.appendChild(pathelElem); |
| } |
| } |
| if (locations != null && locations.length > 0) { |
| for (int j = 0; j < locations.length; j++) { |
| Element pathelElem = doc.createElement("pathelement"); // NOI18N |
| pathelElem.setAttribute("location", locations[j]); // NOI18N |
| pElem.appendChild(pathelElem); |
| } |
| } |
| return pElem; |
| } |
| |
| /** |
| * Creates ant element according to params |
| */ |
| Element createAntElem(Document doc, String antFile, String deps) { |
| assert antFile != null; |
| Element antElem = doc.createElement("ant"); // NOI18N |
| antElem.setAttribute("antfile", antFile); |
| antElem.setAttribute("inheritall", "false"); // NOI18N |
| StringTokenizer st = new StringTokenizer(deps, ","); // NOI18N |
| if (st.countTokens() > 1) { |
| while (st.hasMoreTokens()) { |
| String dep = st.nextToken(); |
| Element tgElem = doc.createElement("target"); // NOI18N |
| tgElem.setAttribute("name", dep.trim()); // NOI18N |
| antElem.appendChild(tgElem); |
| } |
| } else { |
| antElem.setAttribute("target", deps); // NOI18N |
| } |
| return antElem; |
| } |
| |
| private boolean isSingleJavaFileSelected(Lookup context) { |
| Collection<? extends DataObject> selectedDO = context.lookupAll(DataObject.class); |
| if (selectedDO.size() == 1 && selectedDO.iterator().next().getPrimaryFile().hasExt("java")) { |
| return true; |
| } |
| return false; |
| } |
| |
| private void addJdkInitDeps(Element target) { |
| if (helper.getProjectDirectory().getFileObject(JdkConfiguration.JDK_XML) != null) { |
| String deps = target.getAttribute("depends"); // NOI18N |
| target.setAttribute("depends", deps.length() == 0 ? "-jdk-init" : "-jdk-init," + deps); // NOI18N |
| } |
| } |
| |
| } |