/* * The MIT License * * Copyright (c) 2013 The Broad Institute * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package htsjdk.tribble.util; import java.awt.*; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.WeakHashMap; /** * @author jrobinso */ public class ParsingUtils { public static Map<Object, Color> colorCache = new WeakHashMap<Object, Color>(100); // HTML 4.1 color table, + orange and magenta static Map<String, String> colorSymbols = new HashMap(); private static final Class defaultUrlHelperClass = RemoteURLHelper.class; public static Class urlHelperClass = defaultUrlHelperClass; static { colorSymbols.put("white", "FFFFFF"); colorSymbols.put("silver", "C0C0C0"); colorSymbols.put("gray", "808080"); colorSymbols.put("black", "000000"); colorSymbols.put("red", "FF0000"); colorSymbols.put("maroon", "800000"); colorSymbols.put("yellow", "FFFF00"); colorSymbols.put("olive", "808000"); colorSymbols.put("lime", "00FF00"); colorSymbols.put("green", "008000"); colorSymbols.put("aqua", "00FFFF"); colorSymbols.put("teal", "008080"); colorSymbols.put("blue", "0000FF"); colorSymbols.put("navy", "000080"); colorSymbols.put("fuchsia", "FF00FF"); colorSymbols.put("purple", "800080"); colorSymbols.put("orange", "FFA500"); colorSymbols.put("magenta", "FF00FF"); } public static InputStream openInputStream(String path) throws IOException { InputStream inputStream; if (path.startsWith("http:") || path.startsWith("https:") || path.startsWith("ftp:")) { inputStream = getURLHelper(new URL(path)).openInputStream(); } else { File file = new File(path); inputStream = new FileInputStream(file); } return inputStream; } //public static String join(String separator, Collection<String> strings) { // return join( separator, strings.toArray(new String[0]) ); //} public static <T> String join(String separator, Collection<T> objects) { if (objects.isEmpty()) { return ""; } Iterator<T> iter = objects.iterator(); final StringBuilder ret = new StringBuilder(iter.next().toString()); while (iter.hasNext()) { ret.append(separator); ret.append(iter.next().toString()); } return ret.toString(); } /** * a small utility function for sorting a list * * @param list * @param <T> * @return */ public static <T extends Comparable> List<T> sortList(Collection<T> list) { ArrayList<T> ret = new ArrayList<T>(); ret.addAll(list); Collections.sort(ret); return ret; } public static <T extends Comparable<T>, V> String sortedString(Map<T, V> c) { List<T> t = new ArrayList<T>(c.keySet()); Collections.sort(t); List<String> pairs = new ArrayList<String>(); for (T k : t) { pairs.add(k + "=" + c.get(k)); } return "{" + ParsingUtils.join(", ", pairs.toArray(new String[pairs.size()])) + "}"; } /** * join an array of strings given a seperator * * @param separator the string to insert between each array element * @param strings the array of strings * @return a string, which is the joining of all array values with the separator */ public static String join(String separator, String[] strings) { return join(separator, strings, 0, strings.length); } /** * join a set of strings, using the separator provided, from index start to index stop * * @param separator the separator to use * @param strings the list of strings * @param start the start position (index in the list)0 * @param end the end position (index in the list) * @return a joined string, or "" if end - start == 0 */ public static String join(String separator, String[] strings, int start, int end) { if ((end - start) == 0) { return ""; } StringBuilder ret = new StringBuilder(strings[start]); for (int i = start + 1; i < end; ++i) { ret.append(separator); ret.append(strings[i]); } return ret.toString(); } /** * Split the string into tokesn separated by the given delimiter. Profiling has * revealed that the standard string.split() method typically takes > 1/2 * the total time when used for parsing ascii files. * * @param aString the string to split * @param tokens an array to hold the parsed tokens * @param delim character that delimits tokens * @return the number of tokens parsed */ public static int split(String aString, String[] tokens, char delim) { return split(aString, tokens, delim, false); } /** * Split the string into tokens separated by the given delimiter. Profiling has * revealed that the standard string.split() method typically takes > 1/2 * the total time when used for parsing ascii files. * * @param aString the string to split * @param tokens an array to hold the parsed tokens * @param delim character that delimits tokens * @param condenseTrailingTokens if true and there are more tokens than will fit in the tokens array, * condense all trailing tokens into the last token * @return the number of tokens parsed */ public static int split(String aString, String[] tokens, char delim, boolean condenseTrailingTokens) { int maxTokens = tokens.length; int nTokens = 0; int start = 0; int end = aString.indexOf(delim); if (end == 0) { if (aString.length() > 1) { start = 1; end = aString.indexOf(delim, start); } else { return 0; } } if (end < 0) { tokens[nTokens++] = aString.substring(start); return nTokens; } while ((end > 0) && (nTokens < maxTokens)) { tokens[nTokens++] = aString.substring(start, end); start = end + 1; end = aString.indexOf(delim, start); } // condense if appropriate if (condenseTrailingTokens && nTokens == maxTokens) { tokens[nTokens - 1] = tokens[nTokens - 1] + delim + aString.substring(start); } // Add the trailing string else if (nTokens < maxTokens) { String trailingString = aString.substring(start); tokens[nTokens++] = trailingString; } return nTokens; } // trim a string for the given character (i.e. not just whitespace) public static String trim(String str, char ch) { char[] array = str.toCharArray(); int start = 0; while (start < array.length && array[start] == ch) start++; int end = array.length - 1; while (end > start && array[end] == ch) end--; return str.substring(start, end + 1); } /** * Split the string into tokens separated by tab or space(s). This method * was added so support wig and bed files, which apparently accept space delimiters. * <p/> * Note: TODO REGEX expressions are not used for speed. This should be re-evaluated with JDK 1.5 or later * * @param aString the string to split * @param tokens an array to hold the parsed tokens * @return the number of tokens parsed */ public static int splitWhitespace(String aString, String[] tokens) { int maxTokens = tokens.length; int nTokens = 0; int start = 0; int tabEnd = aString.indexOf('\t'); int spaceEnd = aString.indexOf(' '); int end = tabEnd < 0 ? spaceEnd : spaceEnd < 0 ? tabEnd : Math.min(spaceEnd, tabEnd); while ((end > 0) && (nTokens < maxTokens)) { //tokens[nTokens++] = new String(aString.toCharArray(), start, end-start); // aString.substring(start, end); tokens[nTokens++] = aString.substring(start, end); start = end + 1; // Gobble up any whitespace before next token -- don't gobble tabs, consecutive tabs => empty cell while (start < aString.length() && aString.charAt(start) == ' ') { start++; } tabEnd = aString.indexOf('\t', start); spaceEnd = aString.indexOf(' ', start); end = tabEnd < 0 ? spaceEnd : spaceEnd < 0 ? tabEnd : Math.min(spaceEnd, tabEnd); } // Add the trailing string if (nTokens < maxTokens) { String trailingString = aString.substring(start); tokens[nTokens++] = trailingString; } return nTokens; } public static <T extends Comparable<? super T>> boolean isSorted(Iterable<T> iterable) { Iterator<T> iter = iterable.iterator(); if (!iter.hasNext()) return true; T t = iter.next(); while (iter.hasNext()) { T t2 = iter.next(); if (t.compareTo(t2) > 0) return false; t = t2; } return true; } /** * Convert an rgb string, hex, or symbol to a color. * * @param string * @return */ public static Color parseColor(String string) { try { Color c = colorCache.get(string); if (c == null) { if (string.contains(",")) { String[] rgb = string.split(","); int red = Integer.parseInt(rgb[0]); int green = Integer.parseInt(rgb[1]); int blue = Integer.parseInt(rgb[2]); c = new Color(red, green, blue); } else if (string.startsWith("#")) { c = hexToColor(string.substring(1)); } else { String hexString = colorSymbols.get(string.toLowerCase()); if (hexString != null) { c = hexToColor(hexString); } } if (c == null) { c = Color.black; } colorCache.put(string, c); } return c; } catch (NumberFormatException numberFormatException) { //TODO Throw this exception? return Color.black; } } private static Color hexToColor(String string) { if (string.length() == 6) { int red = Integer.parseInt(string.substring(0, 2), 16); int green = Integer.parseInt(string.substring(2, 4), 16); int blue = Integer.parseInt(string.substring(4, 6), 16); return new Color(red, green, blue); } else { return null; } } public static boolean resourceExists(String resource) throws IOException{ boolean remoteFile = resource.startsWith("http://") || resource.startsWith("https://") || resource.startsWith("ftp://"); if (remoteFile) { URL url = null; try { url = new URL(resource); } catch (MalformedURLException e) { // Malformed URLs by definition don't exist return false; } URLHelper helper = getURLHelper(url); return helper.exists(); } else { return (new File(resource)).exists(); } } /** * Return the registered URLHelper, constructed with the provided URL * @see #registerHelperClass(Class) * @param url * @return */ public static URLHelper getURLHelper(URL url) { try { return getURLHelper(urlHelperClass, url); } catch (Exception e) { return getURLHelper(defaultUrlHelperClass, url); } } private static URLHelper getURLHelper(Class helperClass, URL url) { try { Constructor constr = helperClass.getConstructor(URL.class); return (URLHelper) constr.newInstance(url); } catch (Exception e) { String errMsg = "Error instantiating url helper for class: " + helperClass; throw new IllegalStateException(errMsg, e); } } /** * Register a {@code URLHelper} class to be used for URL operations. The helper * may be used for both FTP and HTTP operations, so if any FTP URLs are used * the {@code URLHelper} must support it. * * The default helper class is {@link RemoteURLHelper}, which delegates to FTP/HTTP * helpers as appropriate. * * @see URLHelper * @param helperClass Class which implements {@link URLHelper}, and have a constructor * which takes a URL as it's only argument. */ public static void registerHelperClass(Class helperClass) { if (!URLHelper.class.isAssignableFrom(helperClass)) { throw new IllegalArgumentException("helperClass must implement URLHelper"); //TODO check that it has 1 arg constructor of proper type } urlHelperClass = helperClass; } /** * Add the {@code indexExtension} to the {@code filepath}, preserving * query string elements if present. Intended for use where {@code filepath} * is a URL. Will behave correctly on regular file paths (just add the extension * to the end) * @param filepath * @param indexExtension * @return */ public static String appendToPath(String filepath, String indexExtension) { String tabxIndex = null; URL url = null; try{ url = new URL(filepath); }catch (MalformedURLException e){ //pass } if (url != null) { String path = url.getPath(); String indexPath = path + indexExtension; tabxIndex = filepath.replace(path, indexPath); } else { tabxIndex = filepath + indexExtension; } return tabxIndex; } }