/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved. * * Oracle and Java are registered trademarks of Oracle and/or its affiliates. * Other names may be trademarks of their respective owners. * * 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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-2008 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.ruby.rubyproject; import java.io.File; import java.io.IOException; import java.util.Collection; import java.util.LinkedHashSet; import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; import org.netbeans.api.ruby.platform.RubyInstallation; import org.netbeans.api.ruby.platform.RubyPlatform; import org.netbeans.api.ruby.platform.RubyPlatformManager; import org.netbeans.api.extexecution.ExecutionService; import org.netbeans.api.extexecution.print.LineConvertor; import org.netbeans.modules.ruby.platform.execution.RubyExecutionDescriptor; import org.netbeans.modules.ruby.platform.execution.RubyProcessCreator; import org.netbeans.modules.ruby.rubyproject.ui.customizer.RubyProjectProperties; import org.netbeans.spi.project.ActionProvider; import org.openide.cookies.SaveCookie; import org.openide.filesystems.FileObject; import org.openide.filesystems.FileUtil; import org.openide.loaders.DataObject; import org.openide.loaders.DataObjectNotFoundException; import org.openide.util.Lookup; import org.openide.util.Utilities; /** * Base action provider of the Ruby projects. */ public abstract class RubyBaseActionProvider implements ActionProvider, ScriptDescProvider { private static final Logger LOGGER = Logger.getLogger(RubyBaseActionProvider.class.getName()); /** * Standard command for running rdoc on a project. * @see org.netbeans.spi.project.ActionProvider */ public static final String COMMAND_RDOC = "rdoc"; // NOI18N /** * Command for running auto test on this project (if installed) * @see org.netbeans.spi.project.ActionProvider */ public static final String COMMAND_AUTOTEST = "autotest"; // NOI18N /** * Command for running autospec on this project (if installed) * @see org.netbeans.spi.project.ActionProvider */ public static final String COMMAND_AUTOSPEC = "autospec"; // NOI18N /** * Command for running RSpec tests on this project (if installed) * @see org.netbeans.spi.project.ActionProvider */ public static final String COMMAND_RSPEC = "rspec"; //NOI18N /** * Command for running all the rspec files in the project. Specifically * does not run the spec rake task (or any other rake task); runs * TestRunner#runAllTests always. */ public static final String COMMAND_RSPEC_ALL = "rspec-all"; //NOI18N /** * The name of the test rake task. */ protected static final String TEST_TASK_NAME = "test"; //NOI18N /** * The name of the spec rake task. */ protected static final String RSPEC_TASK_NAME = "spec";//NOI18N private final RubyBaseProject project; private final UpdateHelper updateHelper; protected RubyBaseActionProvider(final RubyBaseProject project, final UpdateHelper updateHelper) { this.project = project; this.updateHelper = updateHelper; } protected abstract FileObject[] getSourceRoots(); protected abstract FileObject[] getTestSourceRoots(); protected abstract String[] getMimeTypes(); protected RubyPlatform getPlatform() { RubyPlatform platform = RubyPlatform.platformFor(project); if (platform == null) { platform = RubyPlatformManager.getDefaultPlatform(); } return platform; } protected UpdateHelper getUpdateHelper() { return updateHelper; } protected static void saveFile(final FileObject file) { // Save the file try { DataObject dobj = DataObject.find(file); if (dobj != null) { SaveCookie saveCookie = dobj.getCookie(SaveCookie.class); if (saveCookie != null) { saveCookie.save(); } } } catch (DataObjectNotFoundException donfe) { LOGGER.log(Level.SEVERE, donfe.getLocalizedMessage(), donfe); } catch (IOException ioe) { LOGGER.log(Level.SEVERE, ioe.getLocalizedMessage(), ioe); } } protected void runRubyScript(final FileObject fileObject, final String target, final String displayName, final Lookup context, final boolean debug, LineConvertor... extraConvertors) { if (!getPlatform().showWarningIfInvalid()) { return; } // bit messy here - we first get a desc preconfigured with project settings, // then modify it again with the args user may have provided in the run file dialog RubyExecutionDescriptor desc = getScriptDescriptor(null, fileObject, target, displayName, context, debug, extraConvertors); if (fileObject != null) { // null when running/debugging project, don't ask for parameters File file = FileUtil.toFile(fileObject); RunFileActionProvider.RunFileArgs args = RunFileActionProvider.getRunArgs(file); if (args == null) { String workDir = desc.getPwd() != null ? desc.getPwd().getAbsolutePath() : ""; args = new RunFileActionProvider.RunFileArgs(getPlatform(), asString(desc.getAdditionalArgs()), asString(desc.getJVMArguments()), desc.getInitialArgsPlain(), workDir, true); } if (args != null && args.displayDialog()) { args = RunFileActionProvider.showDialog(args, file, debug, false); } if (args == null) { // cancel pressed return; } if (args.getRunArgs() != null) { desc.additionalArgs(Utilities.parseParameters(args.getRunArgs())); } desc.jvmArguments(args.getJvmArgs()); desc.initialArgs(args.getRubyOpts()); desc.setPwd(new File(args.getWorkDir())); } RubyProcessCreator rpc = new RubyProcessCreator(desc, getSourceEncoding()); if (rpc.isAbleToCreateProcess()) { ExecutionService service = ExecutionService.newService(rpc, desc.toExecutionDescriptor(), displayName); service.run(); } } private static String asString(String[] array) { if (array == null) { return ""; } StringBuilder result = new StringBuilder(); for (int i = 0; i < array.length; i++) { result.append(array[i]); if (i + 1 < array.length) { result.append(" "); //NOI18N } } return result.toString(); } protected String getSourceEncoding() { return project.evaluator().getProperty(RubyProjectProperties.SOURCE_ENCODING); } /** * Find selected sources, the sources has to be under single source root, * * @param context the lookup in which files should be found * @return The file objects in the sources folder */ protected FileObject[] findSources(final Lookup context) { FileObject[] srcPath = getSourceRoots(); for (int i = 0; i < srcPath.length; i++) { for (String mimeType : getMimeTypes()) { FileObject[] files = findSelectedFiles(context, srcPath[i], mimeType, true); if (files != null) { return files; } } } return null; } /** * Find either selected tests or tests which belong to selected source * files. */ protected FileObject[] findTestSources(final Lookup context) { //XXX: Ugly, should be rewritten FileObject[] testSrcPath = getTestSourceRoots(); for (int i = 0; i < testSrcPath.length; i++) { FileObject[] files = findSelectedFiles(context, testSrcPath[i], RubyInstallation.RUBY_MIME_TYPE, true); // NOI18N if (files != null) { return files; } } return null; } protected FileObject getCurrentFile(final Lookup context) { FileObject[] fos = findSources(context); if (fos == null) { fos = findTestSources(context); } if (fos == null || fos.length == 0) { for (DataObject d : context.lookupAll(DataObject.class)) { FileObject fo = d.getPrimaryFile(); if (fo.getMIMEType().equals(RubyInstallation.RUBY_MIME_TYPE)) { return fo; } } return null; } return fos[0]; } public static FileObject[] findSelectedFiles( final Lookup context, final FileObject dir, final String mimeType, final boolean strict) { if (dir != null && !dir.isFolder()) { throw new IllegalArgumentException("Not a folder: " + dir); // NOI18N } Collection<FileObject> files = new LinkedHashSet<FileObject>(); // #50644: remove dupes // XXX this should perhaps also check for FileObject's... for (DataObject d : context.lookupAll(DataObject.class)) { FileObject f = d.getPrimaryFile(); boolean matches = FileUtil.toFile(f) != null; if (dir != null) { matches &= (FileUtil.isParentOf(dir, f) || dir == f); } if (mimeType != null) { matches &= f.getMIMEType().equals(mimeType); } // Generally only files from one project will make sense. // Currently the action UI infrastructure (PlaceHolderAction) // checks for that itself. Should there be another check here? if (matches) { files.add(f); } else if (strict) { return null; } } if (files.isEmpty()) { return null; } return files.toArray(new FileObject[files.size()]); } protected String[] getApplicationArguments() { String applicationArgs = project.evaluator().getProperty(SharedRubyProjectProperties.APPLICATION_ARGS); return (applicationArgs == null || applicationArgs.trim().length() == 0) ? null : Utilities.parseParameters(applicationArgs); } }