/*
* 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.platform;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.JComboBox;
import org.netbeans.api.ruby.platform.RubyPlatform;
import org.netbeans.api.ruby.platform.RubyPlatformManager;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.filesystems.FileUtil;
import org.openide.util.NbBundle;
import org.openide.util.NbPreferences;
public final class Util {
/**
* Regexp for matching version number in gem packages: name-x.y.z (we need
* to pull out x,y,z such that we can do numeric comparisons on them)
*/
private static final Pattern VERSION_PATTERN = Pattern.compile("(\\d+)\\.(\\d+)(\\.(\\d+)\\.?(\\w+)?(-\\S+)?)?"); // NOI18N
private static final Logger LOGGER = Logger.getLogger(Util.class.getName());
// FIXME: get rid of those proxy constants as soon as some NB Proxy API is available
private static final String USE_PROXY_AUTHENTICATION = "useProxyAuthentication"; // NOI18N
private static final String PROXY_AUTHENTICATION_USERNAME = "proxyAuthenticationUsername"; // NOI18N
private static final String PROXY_AUTHENTICATION_PASSWORD = "proxyAuthenticationPassword"; // NOI18N
private static final String FIRST_TIME_KEY = "platform-manager-called-first-time"; // NOI18N
private static final String FETCH_ALL_VERSIONS = "gem-manager-fetch-all-versions"; // NOI18N
private static final String FETCH_GEM_DESCRIPTIONS = "gem-manager-fetch-descriptions"; // NOI18N
private static final String RVM_RUBIES_PATH = ".rvm" + File.separator + "rubies"; //NOI18N
public static final Comparator<String> VERSION_COMPARATOR = new Comparator<String>() {
public int compare(String v1, String v2) {
return Util.compareVersions(v1, v2);
}
};
private Util() {
}
/** Return true iff the given line seems to be colored using ANSI terminal escape codes */
public static boolean containsAnsiColors(String line) {
// RSpec will color output with ANSI color sequence terminal escapes
return line.indexOf("\033[") != -1; // NOI18N
}
/**
* Remove ANSI terminal escape codes from a line.
*/
public static String stripAnsiColors(String line) {
StringBuilder sb = new StringBuilder(line.length());
int index = 0;
int max = line.length();
while (index < max) {
int nextEscape = line.indexOf("\033[", index); // NOI18N
if (nextEscape == -1) {
nextEscape = line.length();
}
for (int n = (nextEscape == -1) ? max : nextEscape; index < n; index++) {
sb.append(line.charAt(index));
}
if (nextEscape != -1) {
for (; index < max; index++) {
char c = line.charAt(index);
if (c == 'm') {
index++;
break;
}
}
}
}
return sb.toString();
}
public static void adjustProxy(final ProcessBuilder pb) {
String proxy = Util.getNetBeansHttpProxy();
if (proxy != null) {
Map<String, String> env = pb.environment();
if ((env.get("HTTP_PROXY") == null) && (env.get("http_proxy") == null)) { // NOI18N
env.put("HTTP_PROXY", proxy); // NOI18N
env.put("http_proxy", proxy); // NOI18N
}
// PENDING - what if proxy was null so the user has TURNED off
// proxies while there is still an environment variable set - should
// we honor their environment, or honor their NetBeans proxy
// settings (e.g. unset HTTP_PROXY in the environment before
// launching plugin?
}
}
/**
* FIXME: get rid of the whole method as soon as some NB Proxy API is
* available.
*/
private static String getNetBeansHttpProxy() {
String host = System.getProperty("http.proxyHost"); // NOI18N
if (host == null) {
return null;
}
String portHttp = System.getProperty("http.proxyPort"); // NOI18N
int port;
try {
port = Integer.parseInt(portHttp);
} catch (NumberFormatException e) {
port = 8080;
}
Preferences prefs = NbPreferences.root().node("org/netbeans/core"); // NOI18N
boolean useAuth = prefs.getBoolean(USE_PROXY_AUTHENTICATION, false);
String auth = "";
if (useAuth) {
auth = prefs.get(PROXY_AUTHENTICATION_USERNAME, "") + ":" + prefs.get(PROXY_AUTHENTICATION_PASSWORD, "") + '@'; // NOI18N
}
// Gem requires "http://" in front of the port name if it's not already there
if (host.indexOf(':') == -1) {
host = "http://" + auth + host; // NOI18N
}
return host + ":" + port; // NOI18N
}
/**
* Returns an {@link Iterable} which will uniquely traverse all valid
* elements on the <em>PATH</em> environment variables. That means,
* duplicates and elements which are not valid, existing directories are
* skipped.
*
* @return a {@link Collection} of all valid elements on the
* <em>PATH</em> environment variables.
*/
public static Collection<String> dirsOnPath() {
String rawPath = System.getenv("PATH"); // NOI18N
if (rawPath == null) {
rawPath = System.getenv("Path"); // NOI18N
}
if (rawPath == null) {
return Collections.emptyList();
}
Set<String> candidates = new LinkedHashSet<String>(Arrays.asList(rawPath.split(File.pathSeparator)));
for (Iterator<String> it = candidates.iterator(); it.hasNext();) {
String dir = it.next();
if (!new File(dir).isDirectory()) { // remove non-existing directories (#124562)
LOGGER.fine(dir + " found in the PATH environment variable. But is not a valid directory. Ignoring...");
it.remove();
}
}
return candidates;
}
/**
* Gets the {@code bin} dirs for dirs in {@code ~/.rvm/rubies}, which typically
* contains ruby platforms.
*
* @return a collections of absolute directory paths; never {@code null}.
*/
public static Collection<String> rvmRubies() {
String homeDir = System.getProperty("user.home");
if (homeDir == null) { // can this happen??
return Collections.emptyList();
}
File rvmRubies = new File(homeDir, RVM_RUBIES_PATH);
if (!rvmRubies.exists()) {
return Collections.emptyList();
}
File[] dirs = rvmRubies.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isDirectory();
}
});
List<String> result = new ArrayList<String>(dirs.length);
for (File dir : dirs) {
File bin = new File(dir, "bin");
if (bin.exists() && bin.isDirectory()) {
result.add(bin.getAbsolutePath());
}
}
return result;
}
public static String findOnPath(final String toFind) {
for (String path : Util.dirsOnPath()) {
String result = path + File.separator + toFind;
if (new File(result).isFile()) {
return result;
}
}
return null;
}
public static void preselectPlatform(final JComboBox platforms, final String preferencePlatformIDKey) {
String lastPlatformID = RubyPreferences.getPreferences().get(preferencePlatformIDKey, null);
if (lastPlatformID != null) {
RubyPlatform platform = RubyPlatformManager.getPlatformByID(lastPlatformID);
if (platform != null) {
platforms.setSelectedItem(platform);
}
}
}
public static void notifyLocalized(Class aClass, String resName, int type, Object... params) {
String message = NbBundle.getMessage(aClass, resName, params);
if (type == NotifyDescriptor.ERROR_MESSAGE) {
LOGGER.severe(message);
}
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(message, type));
}
public static void notifyLocalizedInfo(Class aClass, String resName, Object... params) {
notifyLocalized(aClass, resName, NotifyDescriptor.INFORMATION_MESSAGE, params);
}
/** Returns whether the user confirmed the question or not. */
public static boolean confirmLocalized(Class aClass, String resName, Object... params) {
String message = NbBundle.getMessage(aClass, resName, params);
Object result = DialogDisplayer.getDefault().notify(new NotifyDescriptor.Confirmation(message, NotifyDescriptor.Confirmation.OK_CANCEL_OPTION));
return result.equals(NotifyDescriptor.OK_OPTION);
}
public static String readAsString(final InputStream is) throws IOException {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
FileUtil.copy(is, baos);
return baos.toString("UTF-8"); // NOI18N
} finally {
is.close();
}
}
/**
* Return > 1 if <code>version1</code> is greater than
* <code>version2</code>, 0 if equal and -1 otherwise.
*/
public static int compareVersions(String version1, String version2) {
if (version1.equals(version2)) {
return 0;
}
Matcher matcher1 = VERSION_PATTERN.matcher(version1);
if (matcher1.matches()) {
int major1 = Integer.parseInt(matcher1.group(1));
int minor1 = Integer.parseInt(matcher1.group(2));
int micro1 = matcher1.group(4) == null ? 0 : Integer.parseInt(matcher1.group(4));
// e.g. beta, as in rails-3.0.0.beta
String suffix1 = matcher1.group(5);
Matcher matcher2 = VERSION_PATTERN.matcher(version2);
if (matcher2.matches()) {
int major2 = Integer.parseInt(matcher2.group(1));
int minor2 = Integer.parseInt(matcher2.group(2));
int micro2 = matcher2.group(4) == null ? 0 : Integer.parseInt(matcher2.group(4));
String suffix2 = matcher2.group(5);
if (major1 != major2) {
return major1 - major2;
}
if (minor1 != minor2) {
return minor1 - minor2;
}
if (micro1 != micro2) {
return micro1 - micro2;
}
if (suffix1 == null) {
return 1;
}
if (suffix2 == null) {
return -1;
}
// do just alphabetical comparison on suffix, stupid but
// covers the most common cases, e.g. alpha < beta
return suffix1.compareTo(suffix2);
} else {
// TODO uh oh
//assert false : "no version match on " + version2;
}
} else {
// TODO assert false : "no version match on " + version1;
}
// Just do silly alphabetical comparison
return version1.compareTo(version2);
}
}