/*
* Copyright [1999-2015] Wellcome Trust Sanger Institute and the EMBL-European Bioinformatics Institute
* Copyright [2016-2017] EMBL-European Bioinformatics Institute
*
* Licensed 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.
*/
/*
* Copyright (C) 2004 EBI, GRL
*
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.ensembl.healthcheck.util;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* General utilities (not database-related). For database-related utilities, see
* {@link DBUtils DBUtils}.
*/
public final class Utils {
static final Logger log = Logger.getLogger(Utils.class.getCanonicalName());
// hide constructor to prevent instantiation
private Utils() {
}
/**
* Read the <code>database.properties</code> file into the System properties
* so that it can be overridden with -D.
*
* @param propertiesFileName
* The properties file to read.
* @param skipBuildDatabaseURLs
* Don't automatically build database URLs. Should generally be
* false.
*/
public static void readPropertiesFileIntoSystem(final String propertiesFileName,
final boolean skipBuildDatabaseURLs) {
String propsFile;
// Prepend home directory if not absolute path
if (propertiesFileName.indexOf(File.separator) == -1) {
propsFile = System.getProperty("user.dir") + File.separator + propertiesFileName;
} else {
propsFile = propertiesFileName;
}
if (new File(propsFile).exists()) {
Properties dbProps = Utils.readSimplePropertiesFile(propsFile);
log.fine("\n---------------------------------------------------\n");
for (String name : dbProps.stringPropertyNames()) {
String value = dbProps.getProperty(name);
log.fine("name = " + name + " value = " + value);
// add to System
System.setProperty(name, value);
}
log.fine("\n---------------------------------------------------\n");
if (!skipBuildDatabaseURLs) {
buildDatabaseURLs();
}
} else {
log.warning("Properties file " + propsFile + " not found - skipping");
}
} // readPropertiesFile
/**
* Build database URLs from system properties.
*/
public static void buildDatabaseURLs() {
// others done in DatabaseServer
// ... and for output database URL
String outputDatabaseURL = System.getProperty("output.databaseURL");
if (outputDatabaseURL == null || outputDatabaseURL.equals("")) {
// build it
outputDatabaseURL = "jdbc:mysql://";
if (System.getProperty("output.host") != null) {
outputDatabaseURL += System.getProperty("output.host");
}
if (System.getProperty("output.port") != null) {
outputDatabaseURL += ":" + System.getProperty("output.port");
}
outputDatabaseURL += "/";
System.setProperty("output.databaseURL", outputDatabaseURL);
} else {
// validate output database URL - if it doesn't start with jdbc:
// this
// can
// cause confusion
String prefix = outputDatabaseURL.substring(0, 5);
if (!prefix.equalsIgnoreCase("jdbc:")) {
System.err.println(
"WARNING - output.databaseURL property should start with jdbc: but it does not seem to. Check this if you experience problems loading the database driver");
}
}
}
// /**
// * Check that certain properties are set.
// */
// private static void checkProperties() {
//
// // check that properties that need to be set are set
// String[] requiredProps = { "port", "host", "user" };
// for (int i = 0; i < requiredProps.length; i++) {
// if (System.getProperty(requiredProps[i]) == null) {
// String msg = "WARNING: " + requiredProps[i] + " is not set in config file
// or on command line - cannot connect to database";
// System.err.println(msg);
// throw new RuntimeException(msg);
// }
// }
// }
// -------------------------------------------------------------------------
/**
* Read a properties file.
*
* @param propertiesFileName
* The name of the properties file to use.
* @return The Properties hashtable.
*/
public static Properties readSimplePropertiesFile(String propertiesFileName) {
Properties props = new Properties();
try {
FileInputStream in = new FileInputStream(propertiesFileName);
props.load(in);
in.close();
} catch (Exception e) {
String msg = "Could not read from file " + propertiesFileName;
throw new RuntimeException(msg);
}
return props;
} // readPropertiesFile
// -------------------------------------------------------------------------
/**
* Print a list of Strings, one per line.
*
* @param l
* The List to be printed.
*/
public static void printList(List<? extends Object> l) {
for (Object o : l) {
System.out.println(String.valueOf(o));
}
} // printList
// -------------------------------------------------------------------------
/**
* Concatenate a list of Strings into a single String.
*
* @param list
* The Strings to list.
* @param delim
* The delimiter to use.
* @return A String containing the elements of list separated by delim. No
* trailing delimiter.
*/
public static String listToString(List<? extends Object> list, String delim) {
StringBuilder buf = new StringBuilder();
Iterator<?> it = list.iterator();
while (it.hasNext()) {
buf.append(String.valueOf(it.next()));
if (it.hasNext()) {
buf.append(delim);
}
}
return buf.toString();
}
// -------------------------------------------------------------------------
/**
* Concatenate an array of Strings into a single String.
*
* @param a
* The Strings to list.
* @param delim
* The delimiter to use.
* @return A String containing the elements of a separated by delim. No
* trailing delimiter.
*/
public static String arrayToString(String[] a, String delim) {
StringBuffer buf = new StringBuffer();
for (int i = 0; i < a.length; i++) {
buf.append(a[i]);
if (i + 1 < a.length) {
buf.append(delim);
}
}
return buf.toString();
}
// -------------------------------------------------------------------------
/**
* Print the keys in a HashMap.
*
* @param m
* The map to use.
*/
public static void printKeys(Map<? extends Object, ? extends Object> m) {
for (Object s : m.keySet()) {
System.out.println(s);
}
} // printKeys
// -------------------------------------------------------------------------
/**
* Print the keys and values in a HashMap.
*
* @param m
* The map to use.
*/
public static void printMap(Map<String, String> m) {
Set<Map.Entry<String, String>> set = m.entrySet();
for (Map.Entry<String, String> e : set) {
System.out.println(String.format("%s %s", e.getKey(), e.getValue()));
}
} // printMap
// -------------------------------------------------------------------------
/**
* Print an array of Strings, one per line.
*
* @param a
* The array to be printed.
*/
public static void printArray(String[] a) {
for (int i = 0; i < a.length; i++) {
System.out.println(a[i]);
}
} // printArray
// -------------------------------------------------------------------------
/**
* Print an Enumeration, one String per line.
*
* @param e
* The enumeration to be printed.
*/
public static void printEnumeration(Enumeration<?> e) {
while (e.hasMoreElements()) {
System.out.println(e.nextElement());
}
} // printEnumeration
// -------------------------------------------------------------------------
/**
* Split a classpath-like string into a list of constituent paths.
*
* @param classPath
* The String to split.
* @param delim
* FileSystem classpath delimiter.
* @return An array containing one string per path, in the order they appear
* in classPath.
*/
public static String[] splitClassPath(String classPath, String delim) {
StringTokenizer tok = new StringTokenizer(classPath, delim);
String[] paths = new String[tok.countTokens()];
int i = 0;
while (tok.hasMoreElements()) {
paths[i++] = tok.nextToken();
}
return paths;
} // splitClassPath
// -------------------------------------------------------------------------
/**
* Search an array of strings for those that contain a pattern.
*
* @param paths
* The List to search.
* @param pattern
* The pattern to look for.
* @return The matching paths, in the order that they were in the input
* array.
*/
public static String[] grepPaths(String[] paths, String pattern) {
int count = 0;
for (int i = 0; i < paths.length; i++) {
if (paths[i].indexOf(pattern) > -1) {
count++;
}
}
String[] greppedPaths = new String[count];
int j = 0;
for (int i = 0; i < paths.length; i++) {
if (paths[i].indexOf(pattern) > -1) {
greppedPaths[j++] = paths[i];
}
}
return greppedPaths;
} // grepPaths
// -------------------------------------------------------------------------
/**
* Print the contents of a jar file.
*
* @param path
* The path to the jar file.
*/
public static void printJarFileContents(String path) {
try {
JarFile f = new JarFile(path);
printEnumeration(f.entries());
} catch (IOException ioe) {
ioe.printStackTrace();
}
} // printJarFileContents
// -------------------------------------------------------------------------
/**
* Truncate a string to a certain number of characters.
*
* @param str
* The string to truncate.
* @param size
* The maximum number of characters.
* @param useEllipsis
* If true, add "..." to the truncated string to show it's been
* truncated.
* @return The truncated String, with ellipsis if specified.
*/
public static String truncate(String str, int size, boolean useEllipsis) {
String result = str;
if (str != null && str.length() > size) {
result = str.substring(0, size);
if (useEllipsis) {
result += "...";
}
}
return result;
} // truncate
// -------------------------------------------------------------------------
/**
* Pad (on the right) a string with a certain number of characters.
*
* @return The padded String.
* @param size
* The desired length of the final, padded string.
* @param str
* The String to add the padding to.
* @param pad
* The String to pad with.
*/
public static String pad(String str, String pad, int size) {
StringBuffer result = new StringBuffer(str);
int startSize = str.length();
for (int i = startSize; i < size; i++) {
result.append(pad);
}
return result.toString();
} // pad
// -------------------------------------------------------------------------
/**
* Read a text file.
*
* @param name
* The name of the file to read.
* @return An array of Strings representing the lines of the file.
*/
public static String[] readTextFile(String name) {
List<String> lines = new ArrayList<String>();
String line = null;
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(name));
while ((line = br.readLine()) != null) {
lines.add(line);
}
} catch (FileNotFoundException e) {
log.log(Level.WARNING, "Cannot find " + name, e);
} catch (IOException e) {
log.log(Level.WARNING, "Error whilst reading " + name, e);
} finally {
closeClosable(br);
}
return (String[]) lines.toArray(new String[lines.size()]);
} // readTextFile
/**
* Null safe way of closing down a Closable object
*/
public static void closeClosable(Closeable closable) {
try {
if (closable != null)
closable.close();
} catch (IOException e) {
log.log(Level.WARNING, "Found exception whilst closing handle", e);
}
}
// -------------------------------------------------------------------------
/**
* Check if a String is in an array of Strings. The whole array is searched
* (until a match is found); this is quite slow but does not require the
* array to be sorted in any way beforehand.
*
* @param str
* The String to search for.
* @param a
* The array to search through.
* @param caseSensitive
* If true, case sensitive searching is done.
* @return true if str is in a.
*/
public static boolean stringInArray(String str, String[] a, boolean caseSensitive) {
boolean result = false;
for (int i = 0; i < a.length; i++) {
if (caseSensitive) {
if (a[i].equals(str)) {
result = true;
break;
}
} else {
if (a[i].equalsIgnoreCase(str)) {
result = true;
break;
}
}
}
return result;
}
// -------------------------------------------------------------------------
/**
* Check if an object is in an array. The whole array is searched (until a
* match is found); this is quite slow but does not require the array to be
* sorted in any way beforehand.
*
* @param o
* The Object to search for.
* @param a
* The array to search through.
* @return true if o is in a.
*/
public static boolean objectInArray(Object o, Object[] a) {
for (int i = 0; i < a.length; i++) {
if (a[i].equals(o)) {
return true;
}
}
return false;
}
// -----------------------------------------------------------------
/**
* Return an array containing all of the subdirectories of a given
* directory.
*
* @param parentDir
* The directory to look in.
* @return All the subdirectories (if any) in parentDir.
*/
public static String[] getSubDirs(String parentDir) {
List<String> dirs = new ArrayList<String>();
File parentDirFile = new File(parentDir);
String[] filesAndDirs = parentDirFile.list();
if (filesAndDirs != null) {
for (int i = 0; i < filesAndDirs.length; i++) {
File f = new File(parentDir + File.separator + filesAndDirs[i]);
if (f.isDirectory()) {
dirs.add(filesAndDirs[i]);
}
}
}
return (String[]) (dirs.toArray(new String[dirs.size()]));
}
// -----------------------------------------------------------------
/**
* Remove the objects from one array that are present in another.
*
* @param source
* The array to be filtered.
* @param remove
* An array of objects to be removed from source.
* @return A new array containing all objects that are in source minus any
* that are in remove.
*/
public static Object[] filterArray(Object[] source, Object[] remove) {
List<Object> result = new ArrayList<Object>();
for (int i = 0; i < source.length; i++) {
if (!objectInArray(source[i], remove)) {
result.add(source[i]);
}
}
return result.toArray(new Object[result.size()]);
}
// ---------------------------------------------------------------------
/**
* Format a time as hours, minutes and seconds.
*
* @param time
* The time in ms, e.g. from System.currentTimeMillis()
* @return The time formatted as e.g. 4 hours 2 min 3s. Hours is largest
* unit currently supported.
*/
public static String formatTimeString(long time) {
String s = "";
Calendar cal = new GregorianCalendar();
cal.setTimeInMillis(time);
// TODO years etc
// Calendar.HOUR starts from 1
if (cal.get(Calendar.HOUR_OF_DAY) > 1) {
s += (cal.get(Calendar.HOUR_OF_DAY) - 1) + " hours ";
}
if (cal.get(Calendar.MINUTE) > 0) {
s += cal.get(Calendar.MINUTE) + " min ";
}
if (cal.get(Calendar.SECOND) > 0) {
s += cal.get(Calendar.SECOND) + "s ";
}
if (time < 1000) {
s = time + "ms";
}
return s;
}
// -------------------------------------------------------------------------
/**
* Delete a file.
*
* @param file
* The file to delete.
*/
public static void deleteFile(String file) {
File f = new File(file);
boolean retVal = f.delete();
if (!retVal) {
System.err.println(String.format("Error - could not delete %s", file));
}
}
// -------------------------------------------------------------------------
/**
* Write/append a string to a file.
*
* @param file
* The file to write to.
* @param str
* The string to write.
* @param append
* If true, append the string to the file if it already exists.
* @param newLine
* If true, add a new line character after writing
*/
public static void writeStringToFile(String file, String str, boolean append, boolean newLine) {
try {
FileWriter fw = new FileWriter(file, append);
fw.write(str);
if (newLine) {
fw.write("\n");
}
fw.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
// -------------------------------------------------------------------------
/**
* Convert a list of Longs to an array of longs.
*/
public static long[] listToArrayLong(List<? extends Number> list) {
long[] array = new long[list.size()];
int i = 0;
for (Number n : list) {
array[i++] = n.longValue();
}
return array;
}
// -------------------------------------------------------------------------
/**
* Convert the first character of a string to upper case. Ignore the rest of
* the string.
*/
public static String ucFirst(String str) {
String first = str.substring(0, 1).toUpperCase();
String last = str.substring(1);
return first + last;
}
public static String truncateDatabaseName(String db) {
if (db.length() <= 27) {
return db;
}
// change genus to single letter
int underscoreIndex = db.indexOf("_");
String genusLetter = db.substring(0, 1);
String result = genusLetter + "_" + db.substring(underscoreIndex + 1);
// if length is *still* > 27, change long database type to an
// abbreviated
// version
if (result.length() > 27) {
result = result.replaceAll("otherfeatures", "other...");
}
return result;
}
// ---------------------------------------------------------------------
public static String truncateTestName(String test) {
return (test.length() <= 27) ? test : test.substring(0, 28);
}
// ---------------------------------------------------------------------
public static String[] removeStringFromArray(String[] tables, String table) {
// test whether the item is in the array
boolean found = false;
for (int i = 0; i < tables.length; i++) {
if (tables[i].equalsIgnoreCase(table)) {
found = true;
break;
}
}
if (!found) {
return tables;
}
String[] result = new String[tables.length - 1];
int j = 0;
for (int i = 0; i < tables.length; i++) {
if (!tables[i].equalsIgnoreCase(table)) {
if (j < result.length) {
result[j++] = tables[i];
}
} // else { System.out.println("Removing table " + table); }
}
return result;
}
// ---------------------------------------------------------------------
/**
* Convert a list to a HashMap, where the key and value of each map element
* is the same as each list element.
*/
public static HashMap<Object, Object> listToMap(List<Object> list) {
HashMap<Object, Object> map = new HashMap<Object, Object>();
for (Object o : list) {
map.put(o, o);
}
return map;
}
// ---------------------------------------------------------------------
/**
* Convert an array to a HashMap, where the key and value of each map
* element is the same as each list element.
*/
public static HashMap<Object, Object> arrayToMap(Object[] array) {
return listToMap(Arrays.asList(array));
}
// ---------------------------------------------------------------------
/**
* Create a list of databases and groups from the properties file. Multiple
* properties can be used as long as they start with output.databases, e.g.
* <code>
* output.databases1 = ^[a-k].*_core_63.*:release
* output.databases2 = ^[l-z].*_core_63.*:release
* </code> Individual properties can also contain multiple comma-separated
* sets.
*/
public static List<String> getDatabasesAndGroups() {
List<String> list = new ArrayList<String>();
Properties props = System.getProperties();
for (Enumeration<?> en = props.propertyNames(); en.hasMoreElements();) {
String key = (String) en.nextElement();
if (key.startsWith("output.databases")) {
String value = props.getProperty(key);
list.addAll(Arrays.asList(value.split(",")));
}
}
return list;
}
// -------------------------------------------------------------------------
} // Utils