/okapi/core/src/main/java/net/sf/okapi/common/Util.java
Java | 1545 lines | 1233 code | 43 blank | 269 comment | 94 complexity | eb0b2b93dfda65b198cfbbab84ba4166 MD5 | raw file
Possible License(s): LGPL-2.1, LGPL-3.0
- /*===========================================================================
- Copyright (C) 2008-2011 by the Okapi Framework contributors
- -----------------------------------------------------------------------------
- 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
-
- See also the full LGPL text here: http://www.gnu.org/copyleft/lesser.html
- ============================================================================*/
-
- package net.sf.okapi.common;
-
- import net.sf.okapi.common.exceptions.OkapiIOException;
- import net.sf.okapi.common.exceptions.OkapiUnsupportedEncodingException;
- import net.sf.okapi.common.LocaleId;
-
- import org.w3c.dom.Node;
-
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.FilenameFilter;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.UnsupportedEncodingException;
- import java.io.Writer;
- import java.net.MalformedURLException;
- import java.net.URI;
- import java.net.URISyntaxException;
- import java.net.URL;
- import java.net.URLDecoder;
- import java.net.URLEncoder;
- import java.nio.ByteBuffer;
- import java.nio.CharBuffer;
- import java.nio.channels.Channels;
- import java.nio.channels.FileChannel;
- import java.nio.channels.ReadableByteChannel;
- import java.nio.channels.WritableByteChannel;
- import java.nio.charset.CharacterCodingException;
- import java.nio.charset.CharsetEncoder;
- import java.util.List;
- import java.util.Map;
- import java.util.regex.Pattern;
-
- /**
- * Collection of various all-purpose helper functions.
- */
- public final class Util {
-
- /**
- * Name of the root directory variable.
- */
- public static final String ROOT_DIRECTORY_VAR = "${rootDir}";
-
- /**
- * Name of the input root directory variable.
- */
- public static final String INPUT_ROOT_DIRECTORY_VAR = "${inputRootDir}";
-
- /**
- * Enumeration of supported OSes
- */
- public static enum SUPPORTED_OS {
- WINDOWS,
- MAC,
- LINUX
- }
-
- /**
- * Line-break string for DOS/Windows.
- */
- public static final String LINEBREAK_DOS = "\r\n";
- /**
- * Line-break string for Unix/Linux
- */
- public static final String LINEBREAK_UNIX = "\n";
- /**
- * Line-break string for Macintosh
- */
- public static final String LINEBREAK_MAC = "\r";
-
- /**
- * Default RTF style for starting an external code.
- */
- public static final String RTF_STARTCODE = "{\\cs5\\f1\\cf15\\lang1024 ";
- /**
- * Default RTF style for ending an external code.
- */
- public static final String RTF_ENDCODE = "}";
- /**
- * Default RTF style for starting an internal code.
- */
- public static final String RTF_STARTINLINE = "{\\cs6\\f1\\cf6\\lang1024 ";
- /**
- * Default RTF style for ending an internal code.
- */
- public static final String RTF_ENDINLINE = "}";
- /**
- * Default RTF style for starting an in-between source/target marker.
- */
- public static final String RTF_STARTMARKER = "{\\cs15\\v\\cf12\\sub\\f2 \\{0>}{\\v\\f1 ";
- /**
- * Default RTF style for the first half of a middle part of an in-between source/target marker.
- */
- public static final String RTF_MIDMARKER1 = "}{\\cs15\\v\\cf12\\sub\\f2 <\\}";
- /**
- * Default RTF style for the second half of a middle part of an in-between source/target marker.
- */
- public static final String RTF_MIDMARKER2 = "\\{>}";
- /**
- * Default RTF style for ending an in-between source/target marker.
- */
- public static final String RTF_ENDMARKER = "{\\cs15\\v\\cf12\\sub\\f2 <0\\}}";
-
- private static final String NEWLINES_REGEX = "\r(\n)?";
- private static final Pattern NEWLINES_REGEX_PATTERN = Pattern.compile(NEWLINES_REGEX);
-
- /**
- * Shared flag indicating a translation that was generated using machine translation.
- */
- public static final String MTFLAG = "MT!";
-
-
- // Used by openURL()
- private static final String[] browsers = { "firefox", "opera", "konqueror", "epiphany",
- "seamonkey", "galeon", "kazehakase", "mozilla", "netscape" };
-
- /**
- * Converts all \r\n and \r to linefeed (\n)
- * @param text
- * the text to convert
- * @return converted string
- */
- static public String normalizeNewlines(String text) {
- return NEWLINES_REGEX_PATTERN.matcher(text).replaceAll("\n");
- }
-
- /**
- * Removes from the start of a string any of the specified characters.
- *
- * @param text
- * string to trim.
- * @param chars
- * list of the characters to trim.
- * @return The trimmed string.
- */
- static public String trimStart (String text,
- String chars)
- {
- if ( text == null ) return text;
- int n = 0;
- while ( n < text.length() ) {
- if ( chars.indexOf(text.charAt(n)) == -1 ) {
- break;
- }
- n++;
- }
- if ( n >= text.length() ) return "";
- if ( n > 0 ) return text.substring(n);
- return text;
- }
-
- /**
- * Removes from the end of a string any of the specified characters.
- * @param text
- * string to trim.
- * @param chars
- * list of the characters to trim.
- * @return the trimmed string.
- */
- static public String trimEnd (String text,
- String chars)
- {
- if ( text == null ) return text;
- int n = text.length() - 1;
- while ( n >= 0 ) {
- if (chars.indexOf(text.charAt(n)) == -1)
- break;
- n--;
- }
- if ( n < 0 ) return "";
- if ( n > 0 ) return text.substring(0, n + 1);
- return text;
- }
-
- /**
- * Gets the directory name of a full path.
- *
- * @param path
- * full path from where to extract the directory name. The path
- * can be a URL path (e.g. "/C:/test/file.ext").
- * @return The directory name (without the final separator), or an empty
- * string if path is a filename.
- */
- static public String getDirectoryName (String path) {
- String tmp = path.replace('\\', '/'); // Normalize separators (some path are mixed)
- int n = tmp.lastIndexOf('/');
- if ( n > 0 ) {
- return path.substring(0, n);
- }
- else {
- return "";
- }
- }
-
- /**
- * Determines if a given path ends with a file name separator for the current platform.
- * If not, the file separator is appended to the path.
- * @param path the given path.
- * @param forceForwardSlash true if the ending separator must be a forward slash.
- * @return the given path ending with the file name separator.
- */
- static public String ensureSeparator (String path,
- boolean forceForwardSlash)
- {
- if ( isEmpty(path) ) return path;
- if ( path.endsWith("/") ) return path;
- if ( path.endsWith(File.separator) ) {
- if ( forceForwardSlash ) {
- path = path.substring(0, path.length()-1);
- }
- else {
- return path;
- }
- }
- if ( forceForwardSlash ) {
- return path + "/";
- }
- else {
- return path + File.separator;
- }
- }
-
- /**
- * Replaces unsupported characters in a given short file name (no directory path) with a given replacer.
- * @param fileName the given short file name
- * @param replacer the given replacer
- * @return the given file name with fixed unsupported characters
- */
- public static String fixFilename(String fileName, String replacer) {
- if (Util.isEmpty(fileName)) return "";
- if (Util.isEmpty(replacer)) return fileName;
- String regex = "[*:<>?\\\\/|]"; // TODO This regex is for Windows, add for Linux/Mac OS
- replacer = replacer.replaceAll(regex, "_"); // In case replacer contains unsupported chars
- return fileName.replaceAll(regex, replacer);
- }
-
- /**
- * Replaces exceeding path separators with a slash.
- * @param path the given path
- * @return the fixed given path
- */
- public static String fixPath(String path) {
- String res = path;
- String saveRes;
- do {
- saveRes = res;
- res = res.replaceAll("\\\\/", "/");
- res = res.replaceAll("\\\\\\\\", "/");
- res = res.replaceAll("//", "/");
- res = res.replaceAll("/\\\\", "/");
- } while (saveRes != res);
- return res;
- }
-
- /**
- * Replaces unsupported characters in a given short file name (no directory path) with underscore.
- * @param fileName the given short file name
- * @return the given file name with fixed unsupported characters
- */
- public static String fixFilename(String fileName) {
- return fixFilename(fileName, "_");
- }
-
- /**
- * Return a list of files based on suffix (i.e, .xml, .html etc.)
- * @param directory - directory where files are located
- * @param suffix - the sufix used to filter files
- * @return - list of files matching the suffix
- * @throws URISyntaxException
- */
- public static String[] getFilteredFiles(final String directory, final String suffix)
- throws URISyntaxException {
- File dir = new File(directory);
- FilenameFilter filter = new FilenameFilter() {
- public boolean accept(File dir, String name) {
- return name.endsWith(suffix);
- }
- };
- return dir.list(filter);
- }
-
- /**
- * Creates the directory tree for the give full path (dir+filename)
- *
- * @param path
- * directory to create and filename. If you want to pass only a directory
- * name make sure it has a trailing separator (e.g. "c:\project\tmp\").
- * The path can be a URL path (e.g. "/C:/test/file.ext").
- */
- static public void createDirectories (String path) {
- String tmp = path.replace('\\', '/'); // Normalize separators (some path are mixed)
- int n = tmp.lastIndexOf('/');
- if ( n == -1 ) return; // Nothing to do
- // Else, use the directory part and create the tree
- String dir = path.substring(0, n);
- File F = new File(dir);
- F.mkdirs();
- }
-
- /**
- * Escapes a string for XML.
- *
- * @param text
- * string to escape.
- * @param quoteMode
- * 0=no quote escaped, 1=apos and quot, 2=#39 and quot, and
- * 3=quot only.
- * @param escapeGT
- * true to always escape '>' to gt
- * @param encoder
- * the character set encoder to use to detect un-supported
- * character, or null to never escape normal characters.
- * @return the escaped string.
- */
- static public String escapeToXML (String text,
- int quoteMode,
- boolean escapeGT,
- CharsetEncoder encoder)
- {
- if ( text == null ) return "";
- StringBuffer sbTmp = new StringBuffer(text.length());
- char ch;
- for ( int i = 0; i < text.length(); i++ ) {
- ch = text.charAt(i);
- switch (ch) {
- case '<':
- sbTmp.append("<");
- continue;
- case '>':
- if (escapeGT)
- sbTmp.append(">");
- else {
- if (( i > 0 ) && ( text.charAt(i - 1) == ']' )) {
- sbTmp.append(">");
- }
- else {
- sbTmp.append('>');
- }
- }
- continue;
- case '&':
- sbTmp.append("&");
- continue;
- case '"':
- if ( quoteMode > 0 ) {
- sbTmp.append(""");
- }
- else {
- sbTmp.append('"');
- }
- continue;
- case '\'':
- switch ( quoteMode ) {
- case 1:
- sbTmp.append("'");
- break;
- case 2:
- sbTmp.append("'");
- break;
- default:
- sbTmp.append(text.charAt(i));
- break;
- }
- continue;
- default:
- if ( text.charAt(i) > 127 ) { // Extended chars
- if ( Character.isHighSurrogate(ch) ) {
- int cp = text.codePointAt(i++);
- String tmp = new String(Character.toChars(cp));
- if (( encoder != null ) && !encoder.canEncode(tmp) ) {
- sbTmp.append(String.format("&#x%x;", cp));
- } else {
- sbTmp.append(tmp);
- }
- }
- else {
- if (( encoder != null ) && !encoder.canEncode(text.charAt(i)) ) {
- sbTmp.append(String.format("&#x%04x;", text.codePointAt(i)));
- }
- else { // No encoder or char is supported
- sbTmp.append(text.charAt(i));
- }
- }
- }
- else { // ASCII chars
- sbTmp.append(text.charAt(i));
- }
- continue;
- }
- }
- return sbTmp.toString();
- }
-
- /**
- * Escapes a given string into RTF format.
- *
- * @param text
- * the string to convert.
- * @param convertLineBreaks
- * Indicates if the line-breaks should be converted.
- * @param lineBreakStyle
- * Type of line-break conversion. 0=do nothing special, 1=close
- * then re-open as external, 2=close then re-open as internal.
- * @param encoder
- * Encoder to use for the extended characters.
- * @return The input string escaped to RTF.
- */
- static public String escapeToRTF (String text,
- boolean convertLineBreaks,
- int lineBreakStyle,
- CharsetEncoder encoder)
- {
- try {
- if ( text == null ) return "";
- StringBuffer tmp = new StringBuffer(text.length());
- CharBuffer tmpBuf = CharBuffer.allocate(1);
- ByteBuffer encBuf;
-
- for ( int i=0; i<text.length(); i++ ) {
- switch ( text.charAt(i) ) {
- case '{':
- case '}':
- case '\\':
- tmp.append("\\").append(text.charAt(i));
- break;
- case '\r': // Skip
- break;
- case '\n':
- if ( convertLineBreaks ) {
- switch ( lineBreakStyle ) {
- case 1: // Outside external
- tmp.append(RTF_ENDCODE);
- tmp.append("\r\n\\par ");
- tmp.append(RTF_STARTCODE);
- continue;
- case 2:
- tmp.append(RTF_ENDINLINE);
- tmp.append("\r\n\\par ");
- tmp.append(RTF_STARTINLINE);
- continue;
- case 0: // Just convert
- default:
- tmp.append("\r\n\\par ");
- continue;
- }
- }
- else {
- tmp.append("\n");
- }
- break;
- case '\u00a0': // Non-breaking space
- tmp.append("\\~"); // No extra space (it's a control word)
- break;
- case '\t':
- tmp.append("\\tab ");
- break;
- case '\u2022':
- tmp.append("\\bullet ");
- break;
- case '\u2018':
- tmp.append("\\lquote ");
- break;
- case '\u2019':
- tmp.append("\\rquote ");
- break;
- case '\u201c':
- tmp.append("\\ldblquote ");
- break;
- case '\u201d':
- tmp.append("\\rdblquote ");
- break;
- case '\u2013':
- tmp.append("\\endash ");
- break;
- case '\u2014':
- tmp.append("\\emdash ");
- break;
- case '\u200d':
- tmp.append("\\zwj ");
- break;
- case '\u200c':
- tmp.append("\\zwnj ");
- break;
- case '\u200e':
- tmp.append("\\ltrmark ");
- break;
- case '\u200f':
- tmp.append("\\rtlmark ");
- break;
-
- default:
- if ( text.charAt(i) > 127 ) {
- if ( encoder.canEncode(text.charAt(i)) ) {
- tmpBuf.put(0, text.charAt(i));
- tmpBuf.position(0);
- encBuf = encoder.encode(tmpBuf);
- if ( encBuf.limit() > 1 ) {
- tmp.append(String.format("{\\uc%d", encBuf.limit()));
- tmp.append(String.format("\\u%d", (int) text.charAt(i)));
- for (int j = 0; j < encBuf.limit(); j++) {
- tmp.append(String.format("\\'%x", (encBuf.get(j) < 0 ? (0xFF ^ ~encBuf.get(j))
- : encBuf.get(j))));
- }
- tmp.append("}");
- }
- else {
- tmp.append(String.format("\\u%d", (int) text.charAt(i)));
- tmp.append(String.format("\\'%x", (encBuf.get(0) < 0 ? (0xFF ^ ~encBuf.get(0))
- : encBuf.get(0))));
- }
- }
- else { // Cannot encode in the RTF encoding, so use
- // Just Unicode
- tmp.append(String.format("\\u%d ?", (int) text.charAt(i)));
- }
- }
- else {
- tmp.append(text.charAt(i));
- }
- break;
- }
- }
- return tmp.toString();
- }
- catch ( CharacterCodingException e ) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Copies a {@link ReadableByteChannel} to a {@link WritableByteChannel}.
- * @param inChannel the input Channel.
- * @param outChannel the output Channel.
- * @throws OkapiIOException if an error occurs.
- */
- public static void copy (ReadableByteChannel inChannel,
- WritableByteChannel outChannel)
- {
- final ByteBuffer buffer = ByteBuffer.allocateDirect(16 * 1024);
- try {
- while ( inChannel.read(buffer) != -1 ) {
- // prepare the buffer to be drained
- buffer.flip();
- // write to the channel, may block
- outChannel.write(buffer);
- // If partial transfer, shift remainder down
- // If buffer is empty, same as doing clear()
- buffer.compact();
- }
-
- // EOF will leave buffer in fill state
- buffer.flip();
- // make sure the buffer is fully drained.
- while (buffer.hasRemaining()) {
- outChannel.write(buffer);
- }
- }
- catch ( IOException e ) {
- throw new OkapiIOException(e);
- }
- }
-
- /**
- * Copies an {@link InputStream} to a File.
- * @param is the input stream.
- * @param outputFile the output {@link File}.
- * @throws OkapiIOException if an error occurs.
- */
- public static void copy (InputStream is,
- File outputFile)
- {
- copy(is, outputFile.getAbsolutePath());
- }
-
- /**
- * Copies an {@link InputStream} to a File.
- * @param is the input stream.
- * @param outputPath the output path.
- * @throws OkapiIOException if an error occurs.
- */
- public static void copy (InputStream is,
- String outputPath)
- {
- ReadableByteChannel inChannel = null;
- WritableByteChannel outChannel = null;
- try {
- inChannel = Channels.newChannel(is);
- outChannel = new FileOutputStream(new File(outputPath)).getChannel();
- copy(inChannel, outChannel);
- }
- catch ( FileNotFoundException e ) {
- throw new OkapiIOException(e);
- }
- finally {
- try {
- if (inChannel != null) inChannel.close();
- if (outChannel != null) outChannel.close();
- }
- catch ( IOException e ) {
- throw new OkapiIOException(e);
- }
- }
- }
-
- /**
- * Copies one file to another.
- * @param in the input file.
- * @param out the output file.
- * @throws OkapiIOException if an error occurs.
- */
- public static void copyFile (File in,
- File out)
- {
- FileChannel inChannel = null;
- FileChannel outChannel = null;
- try {
- inChannel = new FileInputStream(in).getChannel();
- outChannel = new FileOutputStream(out).getChannel();
- copy(inChannel, outChannel);
- }
- catch ( IOException e ) {
- throw new OkapiIOException(e);
- }
- finally {
- try {
- if ( inChannel != null ) inChannel.close();
- if ( outChannel != null ) outChannel.close();
- }
- catch ( IOException e ) {
- throw new OkapiIOException(e);
- }
- }
- }
-
- /**
- * Copies a file from one location to another.
- * @param fromPath the path of the file to copy.
- * @param toPath the path of the copy to make.
- * @param move true to move the file, false to copy it.
- * @throws OkapiIOException if an error occurs.
- */
- public static void copyFile (String fromPath,
- String toPath,
- boolean move)
- {
- FileChannel ic = null;
- FileChannel oc = null;
- try {
- createDirectories(toPath);
- ic = new FileInputStream(fromPath).getChannel();
- oc = new FileOutputStream(toPath).getChannel();
- copy(ic, oc);
- if ( move ) {
- ic.close();
- ic = null;
- File file = new File(fromPath);
- file.delete();
- }
- }
- catch ( IOException e ) {
- throw new OkapiIOException(e);
- }
- finally {
- try {
- if ( ic != null ) ic.close();
- if ( oc != null ) oc.close();
- }
- catch ( IOException e ) {
- throw new OkapiIOException(e);
- }
- }
- }
-
- /**
- * Recursive function to delete the content of a given directory (including
- * all its sub-directories. This does not delete the original parent
- * directory.
- *
- * @param directory
- * directory of the content to delete.
- */
- private static void deleteDirectory (File directory) {
- for ( File f : directory.listFiles() ) {
- if ( f.isDirectory() ) {
- deleteDirectory(f);
- }
- f.delete();
- }
- }
-
- /**
- * Deletes the content of a given directory, and if requested, the directory
- * itself. Sub-directories and their content are part of the deleted
- * content.
- *
- * @param directory
- * the path of the directory to delete
- * @param contentOnly
- * indicates if the directory itself should be removed. If this
- * flag is true, only the content is deleted.
- */
- public static void deleteDirectory (String directory,
- boolean contentOnly)
- {
- File f = new File(directory);
- // Make sure this is a directory
- if ( !f.isDirectory() ) {
- return;
- }
- deleteDirectory(f);
- if ( !contentOnly ) {
- f.delete();
- }
- }
-
- /**
- * Gets the filename of a path.
- *
- * @param path
- * the path from where to get the filename. The path can be a URL
- * path (e.g. "/C:/test/file.ext").
- * @param keepExtension
- * true to keep the existing extension, false to remove it.
- * @return the filename with or without extension.
- */
- static public String getFilename (String path,
- boolean keepExtension)
- {
- // Get the filename (allow path with mixed separators)
- int n = path.lastIndexOf('/');
- int n2 = path.lastIndexOf('\\');
- if ( n2 > n ) n = n2;
- if ( n == -1 ) { // Then try Windows
- n = path.lastIndexOf('\\');
- }
- if ( n > -1 ) {
- path = path.substring(n + 1);
- }
- // Stop here if we keep the extension
- if ( keepExtension ) {
- return path;
- }
- // Else: remove the extension if there is one
- n = path.lastIndexOf('.');
- if ( n > -1 ) {
- return path.substring(0, n);
- }
- // Else:
- return path;
- }
-
- /**
- * Gets the extension of a given path or filename.
- *
- * @param path
- * the original path or filename.
- * @return the last extension of the filename (including the period), or
- * empty if there is no period in the filename. If the filename ends
- * with a period, the return is a period.
- * Never returns null.
- */
- static public String getExtension (String path) {
- // Get the extension
- int n = path.lastIndexOf('.');
- if (n == -1) return ""; // Empty
- return path.substring(n);
- }
-
- /**
- * Makes a URI string from a path. If the path itself can be recognized as a
- * string URI already, it is passed unchanged. For example "C:\test" and
- * "file:///C:/test" will both return "file:///C:/test" encoded as URI.
- *
- * @param pathOrUri
- * the path to change to URI string.
- * @return the URI string.
- * @throws OkapiUnsupportedEncodingException
- */
- static public String makeURIFromPath (String pathOrUri) {
- if (isEmpty(pathOrUri)) {
- throw new IllegalArgumentException();
- }
- // This should catch most of the URI forms
- pathOrUri = pathOrUri.replace('\\', '/');
- if (pathOrUri.indexOf("://") != -1)
- return pathOrUri;
- // If not that, then assume it's a file
- try {
- if (pathOrUri.startsWith("file:/"))
- pathOrUri = pathOrUri.substring(6);
- if (pathOrUri.startsWith("/"))
- pathOrUri = pathOrUri.substring(1);
- String tmp = URLEncoder.encode(pathOrUri, "UTF-8");
- // Use '%20' instead of '+': '+ not working with File(uri) it seems
- return "file:///" + tmp.replace("+", "%20");
- }
- catch (UnsupportedEncodingException e) {
- throw new OkapiUnsupportedEncodingException(e); // UTF-8 should be
- // always supported
- // anyway
- }
- }
-
- /**
- * Creates a new URI object from a path or a URI string.
- *
- * @param pathOrUri
- * the path or URI string to use.
- * @return the new URI object for the given path or URI string.
- */
- static public URI toURI (String pathOrUri) {
- try {
- return new URI(makeURIFromPath(pathOrUri));
- }
- catch ( URISyntaxException e ) {
- throw new RuntimeException(e);
- }
- }
-
- /**
- * Gets the longest common path between an existing current directory and a
- * new one.
- *
- * @param currentDir
- * the current longest common path.
- * @param newDir
- * the new directory to compare with.
- * @param ignoreCase
- * true if the method should ignore cases differences.
- * @return the longest sub-directory that is common to both directories.
- * This can be a null if the current directory is null,
- * or empty if there is no common path.
- */
- static public String longestCommonDir (String currentDir,
- String newDir,
- boolean ignoreCase)
- {
- if ( currentDir == null ) {
- return newDir;
- }
- if ( currentDir.length() == 0 ) {
- return currentDir;
- }
-
- // Get temporary copies
- String currentLow = currentDir;
- String newLow = newDir;
- if ( ignoreCase ) {
- currentLow = currentDir.toLowerCase();
- newLow = newDir.toLowerCase();
- }
-
- // The new path equals, or include the existing root: no change
- if ( newLow.indexOf(currentLow) == 0 ) {
- return currentDir;
- }
-
- // Search the common path
- String tmp = currentLow;
- int i = 0;
- while ( newLow.indexOf(tmp) != 0 ) {
- tmp = Util.getDirectoryName(tmp);
- i++;
- if ( tmp.length() == 0 ) {
- return ""; // No common path at all
- }
- }
-
- // Do not return currentDir.substring(0, tmp.length());
- // because the lower-case string maybe of a different length than cased one
- // (e.g. German Sz). Instead re-do the splitting as many time as needed.
- tmp = currentDir;
- for ( int j = 0; j < i; j++ ) {
- tmp = Util.getDirectoryName(tmp);
- }
- return tmp;
- }
-
- /**
- * Gets the longest common path between directories on a given list.
- *
- * @param ignoreCase
- * if the method should ignore cases differences.
- * @param directories
- * the given list of directories.
- * @return the longest sub-directory that is common to all directories.
- * This can be a null or empty string.
- */
- static public String longestCommonDir (boolean ignoreCase, String... directories) {
- if (directories == null) return "";
- if (directories.length == 1) return directories[0]; // Can be null
-
- String res = directories[0];
- for (int i = 1; i < directories.length; i++) {
- res = longestCommonDir(res, directories[i], ignoreCase);
- }
- return res;
- }
-
- /**
- * Indicates if the current OS is case-sensitive.
- *
- * @return true if the current OS is case-sensitive, false if otherwise.
- */
- static public boolean isOSCaseSensitive() {
- // May not work on all platforms,
- // But should on basic Windows, Mac and Linux
- // (Use Windows file separator-type to guess the OS)
- return !File.separator.equals("\\");
- }
-
- /**
- * Writes a Byte-Order-Mark if the encoding indicates it is needed. This
- * methods must be the first call after opening the writer.
- *
- * @param writer
- * writer where to output the BOM.
- * @param bomOnUTF8
- * indicates if we should use a BOM on UTF-8 files.
- * @param encoding
- * encoding of the output.
- * @throws OkapiIOException
- */
- static public void writeBOMIfNeeded (Writer writer,
- boolean bomOnUTF8,
- String encoding)
- {
- try {
- String tmp = encoding.toLowerCase();
-
- // Check UTF-8 first (most cases)
- if ((bomOnUTF8) && (tmp.equalsIgnoreCase("utf-8"))) {
- writer.write("\ufeff");
- return;
- }
-
- /*
- * It seems writers do the following: For "UTF-16" they output
- * UTF-16BE with a BOM For "UTF-16LE" they output UTF-16LE without
- * BOM For "UTF-16BE" they output UTF-16BE without BOM So we force a
- * BOM for UTF-16LE and UTF-16BE
- */
- if (tmp.equals("utf-16be") || tmp.equals("utf-16le")) {
- writer.write("\ufeff");
- return;
- }
- // TODO: Is this an issue? Does *reading* UTF-16LE/BE does not check
- // for BOM?
- } catch (IOException e) {
- throw new OkapiIOException(e);
- }
- }
-
- /**
- * Gets the default system temporary directory to use for the current user.
- * The directory path returned has never a trailing separator.
- *
- * @return The directory path of the temporary directory to use, without
- * trailing separator.
- */
- public static String getTempDirectory() {
- String tmp = System.getProperty("java.io.tmpdir");
- // Normalize for all platforms: no trailing separator
- if (tmp.endsWith(File.separator)) // This separator is always
- // platform-specific
- tmp = tmp.substring(0, tmp.length() - 1);
- return tmp;
- }
-
- /**
- * Gets the text content of the first TEXT child of an element node. This is
- * to use instead of node.getTextContent() which does not work with some
- * Macintosh Java VMs. Note this work-around get <b>only the first TEXT
- * node</b>.
- *
- * @param node
- * the container element.
- * @return the text of the first TEXT child node.
- */
- public static String getTextContent (Node node) {
- Node tmp = node.getFirstChild();
- while (true) {
- if (tmp == null)
- return "";
- if (tmp.getNodeType() == Node.TEXT_NODE) {
- return tmp.getNodeValue();
- }
- tmp = tmp.getNextSibling();
- }
- }
-
- /**
- * Calculates safely a percentage. If the total is 0, the methods return 1.
- *
- * @param part
- * the part of the total.
- * @param total
- * the total.
- * @return the percentage of part in total.
- */
- public static int getPercentage (int part,
- int total)
- {
- return (total == 0 ? 1 : Math.round((float) part / (float) total * 100));
- }
-
- /**
- * Creates a string Identifier based on the hash code of the given text.
- *
- * @param text
- * the text to make an ID for.
- * @return The string identifier for the given text.
- */
- public static String makeId (String text) {
- int n = text.hashCode();
- return String.format("%s%X", ((n > 0) ? 'P' : 'N'), n);
- }
-
- /**
- * Indicates if two language codes are 'the same'. The comparison ignores
- * case differences, and if the parameter ignoreRegion is true, any part
- * after the first '-' is also ignored. Note that the character '_' is
- * treated like a character '-'.
- *
- * @param lang1
- * first language code to compare.
- * @param lang2
- * second language code to compare.
- * @param ignoreRegion
- * True to ignore any part after the first separator, false to
- * take it into account.
- * @return true if, according the given options, the two language codes are
- * the same. False otherwise.
- */
- static public boolean isSameLanguage (String lang1,
- String lang2,
- boolean ignoreRegion)
- {
- lang1 = lang1.replace('_', '-');
- lang2 = lang2.replace('_', '-');
- if (ignoreRegion) { // Do not take the region part into account
- int n = lang1.indexOf('-');
- if (n > -1) {
- lang1 = lang1.substring(0, n);
- }
- n = lang2.indexOf('-');
- if (n > -1) {
- lang2 = lang2.substring(0, n);
- }
- }
- return lang1.equalsIgnoreCase(lang2);
- }
-
- /**
- * Indicates if a given string is null or empty.
- *
- * @param string
- * the string to check.
- * @return true if the given string is null or empty.
- */
- static public boolean isEmpty (String string) {
- return (( string == null ) || ( string.length() == 0 ));
- }
-
- /**
- * Indicates if a locale id is null or empty.
- * @param locale the locale id to examine.
- * @return true if the given locale id is null or empty, false otherwise.
- */
- static public boolean isNullOrEmpty (LocaleId locale) {
- return (( locale == null ) || ( locale.equals(LocaleId.EMPTY) ));
- }
-
- /**
- * Indicates if a string is null or empty, optionally ignoring the white spaces.
- * @param string the string to examine.
- * @param ignoreWS true to ignore white spaces.
- * @return true if the given string is null, or empty. The argument ignoreWS is true a string
- * with only white spaces is concidered empty.
- */
- static public boolean isEmpty (String string,
- boolean ignoreWS)
- {
- if ( ignoreWS && ( string != null )) {
- string = string.trim();
- }
- return isEmpty(string);
- }
-
- /**
- * Indicates if a StringBuilder object is null or empty.
- * @param sb the object to examine.
- * @return true if the given object is null or empty.
- */
- static public boolean isEmpty (StringBuilder sb) {
- return (( sb == null ) || ( sb.length() == 0 ));
- }
-
- /**
- * Indicates if a given list is null or empty.
- * @param e the list to examine.
- * @return true if the list is null or empty.
- */
- static public <E> boolean isEmpty (List <E> e) {
- return (( e == null ) || e.isEmpty() );
- }
-
- /**
- * Indicates if a given map is null or empty.
- * @param map the map to examine.
- * @return true if the map is null or empty,
- */
- public static <K, V> boolean isEmpty (Map<K, V> map) {
- return (( map == null ) || map.isEmpty() );
- }
-
- /**
- * Indicates if an array is null or empty.
- * @param e the array to examine.
- * @return true if the given array is null or empty.
- */
- static public boolean isEmpty (Object[] e) {
- return (e == null ||(e != null && e.length == 0));
- }
-
- /**
- * Gets the length of a string, even a null one.
- * @param string the string to examine.
- * @return the length of the given string, 0 if the string is null.
- */
- static public int getLength (String string) {
- return (isEmpty(string)) ? 0 : string.length();
- }
-
- /**
- * Gets a character at a given position in a string.
- * The string can be null and the position can be beyond the last character.
- * @param string the string to examine.
- * @param pos the position of the character to retrieve.
- * @return the character at the given position,
- * or '\0' if the string is null or if the position is beyond the length of the string.
- */
- static public char getCharAt (String string,
- int pos)
- {
- if ( isEmpty(string) ) {
- return '\0';
- }
- return (string.length() > pos) ? string.charAt(pos) : '\0';
- }
-
- /**
- * Gets the last character of a given string.
- * The string can be null or empty.
- * @param string the string to examine.
- * @return the last character of the given string,
- * or '\0' if the string is null or empty.
- */
- static public char getLastChar (String string) {
- if ( isEmpty(string) ) {
- return '\0';
- }
- return string.charAt(string.length() - 1);
- }
-
- /**
- * Deletes the last character of a given string.
- * The string can be null or empty.
- * @param string the string where to remove the character.
- * @return a new string where the last character has been removed,
- * or an empty string if the given string was null or empty.
- */
- static public String deleteLastChar (String string) {
- if ( isEmpty(string) ) {
- return "";
- }
- return string.substring(0, string.length() - 1);
- }
-
- /**
- * Gets the last character of a given StringBuilder object.
- * The object can be null or empty.
- * @param sb the StringBuilder object to examine.
- * @return the last character of the given StringBuilder object,
- * or '\0' if the object is null or empty.
- */
- static public char getLastChar (StringBuilder sb) {
- if ( isEmpty(sb) ) {
- return '\0';
- }
- return sb.charAt(sb.length() - 1);
- }
-
- /**
- * Deletes the last character of a given StringBuilder object.
- * If the object is null or empty no character are removed.
- * @param sb the StringBuilder object where to remove the character.
- */
- static public void deleteLastChar (StringBuilder sb) {
- if ( isEmpty(sb) ) {
- return;
- }
- sb.deleteCharAt(sb.length() - 1);
- }
-
- /**
- * Indicates if a given index is within the list bounds.
- * @param index the given index.
- * @param list the given list.
- * @return true if a given index is within the list bounds.
- */
- public static <E> boolean checkIndex (int index,
- List<E> list)
- {
- return (list != null) && (index >= 0) && (index < list.size());
- }
-
- /**
- * Converts an integer value to a string.
- * This method simply calls <code>String.valueOf(intValue);</code>.
- * @param value the value to convert.
- * @return the string representation of the given value.
- */
- public static String intToStr (int value) {
- return String.valueOf(value);
- }
-
- /**
- * Converts a string to an integer. If the conversion fails the method
- * returns the given default value.
- * @param value the string to convert.
- * @param intDefault the default value to use if the conversion fails.
- * @return the integer value of the string, or the provided default
- * value if the conversion failed.
- */
- public static int strToInt (String value,
- int intDefault)
- {
- if ( Util.isEmpty(value) ) {
- return intDefault;
- }
- try {
- return Integer.valueOf(value);
- }
- catch (NumberFormatException e) {
- return intDefault;
- }
- }
-
- /**
- * Converts a string to a long. If the conversion fails the method
- * returns the given default value.
- * @param value the string to convert.
- * @param longDefault the default value to use if the conversion fails.
- * @return the long value of the string, or the provided default
- * value if the conversion failed.
- */
- public static long strToLong (String value,
- long longDefault)
- {
- if ( Util.isEmpty(value) ) {
- return longDefault;
- }
- try {
- return Long.valueOf(value);
- }
- catch (NumberFormatException e) {
- return longDefault;
- }
- }
-
- /**
- * Converts a string to a double. If the conversion fails the method
- * returns the given default value.
- * @param value the string to convert.
- * @param doubleDefault the default value to use if the conversion fails.
- * @return the double value of the string, or the provided default
- * value if the conversion failed.
- */
- public static double strToDouble (String value,
- double doubleDefault)
- {
- if ( Util.isEmpty(value) ) {
- return doubleDefault;
- }
- try {
- return Double.valueOf(value);
- }
- catch (NumberFormatException e) {
- return doubleDefault;
- }
- }
-
- /**
- * Gets the element of an array for a given index.
- * the method returns null if the index is out of bounds.
- * @param array the array where to lookup the element.
- * @param index the index.
- * @return the element of the array for the given index, or null if the
- * index is out of bounds, or if the element is null.
- */
- public static <T>T get(T[] array,
- int index)
- {
- if (( index >= 0 ) && ( index < array.length )) {
- return array[index];
- }
- else {
- return null;
- }
- }
-
- /**
- * Returns true if a given index is within the array bounds.
- * @param index the given index.
- * @param array the given list.
- * @return true if a given index is within the array bounds.
- */
- public static <T> boolean checkIndex (int index,
- T[] array)
- {
- return (array != null) && (index >= 0) && (index < array.length);
- }
-
- /**
- * Indicates whether a byte-flag is set or not in a given value.
- * @param value the value to check.
- * @param flag the flag to look for.
- * @return true if the flag is set, false if it is not.
- */
- public static boolean checkFlag (int value,
- int flag)
- {
- return (value & flag) == flag;
- }
-
- /**
- * Get the operating system
- * @return one of WINDOWS, MAC or LINUX
- */
- public static SUPPORTED_OS getOS () {
- String osName = System.getProperty("os.name");
- if (osName.startsWith("Windows")) { // Windows case
- return SUPPORTED_OS.WINDOWS;
- }
- else if (osName.startsWith("Mac OS")) { // Macintosh case
- return SUPPORTED_OS.MAC;
- }
- return SUPPORTED_OS.LINUX;
- }
-
- /**
- * Opens the specified page in a web browser (Java 1.5 compatible).
- * <p>This is based on the public domain class BareBonesBrowserLaunch from Dem Pilafian at
- * (<a href="http://www.centerkey.com/java/browser/">www.centerkey.com/java/browser</a>)
- * @param url the URL of the page to open.
- */
- public static void openURL (String url) {
- String osName = System.getProperty("os.name");
- try {
- if (osName.startsWith("Mac OS")) { // Macintosh case
- /* One possible way. But this causes warning when run with -XstartOnFirstThread option
- Class<?> fileMgr = Class.forName("com.apple.eio.FileManager");
- Method openURL = fileMgr.getDeclaredMethod("openURL", new Class[]{String.class});
- openURL.invoke(null, new Object[] {url}); */
- // So, use open, and seems to work on the Macintosh (not Linux)
- Runtime.getRuntime().exec("open " + url);
- }
- else if (osName.startsWith("Windows")) { // Windows case
- Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
- }
- else { // Assumes Unix or Linux
- boolean found = false;
- for ( String browser : browsers ) {
- if ( !found ) { // Search for the first browser available
- found = Runtime.getRuntime().exec(new String[] {"which", browser}).waitFor() == 0;
- if ( found ) { // Start it if we find one
- Runtime.getRuntime().exec(new String[] {browser, url});
- }
- }
- }
- if ( !found ) {
- throw new Exception("No browser found.");
- }
- }
- }
- catch ( Throwable e ) {
- throw new RuntimeException("Error attempting to launch web browser.", e);
- }
- }
-
- /**
- * Opens a given topic of the OkapiWiki.
- * @param topic the title of the topic/page.
- */
- public static void openWikiTopic (String topic) {
- try {
- // Resolve spaces
- topic = topic.replace(' ', '_');
- //TODO: get the base URL from a properties file
- Util.openURL(new URL(String.format("http://www.opentag.com/okapi/wiki/index.php?title=%s", topic)).toString());
- }
- catch ( MalformedURLException e ) {
- e.printStackTrace();
- }
- }
-
- /**
- * Gets the directory location of a given class. The value returned can be the directory
- * where the .class file is located, or, if the class in a JAR file, the directory
- * where the .jar file is located.
- * @param theClass the class to query.
- * @return the directory location of the given class, or null if an error occurs.
- */
- public static String getClassLocation (Class<?> theClass) {
- String res = null;
- try {
- File file = new File(theClass.getProtectionDomain().getCodeSource().getLocation().getFile());
- res = URLDecoder.decode(file.getAbsolutePath(), "UTF-8");
- // Remove the JAR file if necessary
- if ( res.endsWith(".jar") ) {
- res = getDirectoryName(res);
- }
- }
- catch ( Throwable e ) {
- throw new RuntimeException("Error trying to get the location of a class", e);
- }
- return res;
- }
-
-
- // Unused
- // /**
- // * Generate a random string consisting only of numbers
- // * @param length - specifies length of the string
- // * @return a random String
- // */
- // public static String generateRandomId(int length) {
- // Random rnd = new Random();
- //
- // StringBuilder sb = new StringBuilder( length );
- // for( int i = 0; i < length; i++ ) {
- // sb.append(rnd.nextInt(9));
- // }
- // return sb.toString();
- //
- // }
-
- /**
- * Replaces in a given original string, a potential variable ROOT_DIRECTORY_VAR by a given root directory.
- * @param original the original string where to perform the replacement.
- * @param rootDir the root directory. If null it will be automatically set to the
- * user home directory.
- * @return the original string with ROOT_DIRECTORY_VAR replaced if it was there.
- */
- public static String fillRootDirectoryVariable (String original,
- String rootDir)
- {
- if ( rootDir == null ) {
- rootDir = System.getProperty("user.dir");
- }
- return original.replace(ROOT_DIRECTORY_VAR, rootDir);
- }
-
- /**
- * Replaces in a given original string, a potential variable INPUT_ROOT_DIRECTORY_VAR by a given root directory.
- * @param original the original string where to perform the replacement.
- * @param inputRootDir the input root directory. If null it will be automatically set to
- * an empty string.
- * @return the original string with INPUT_ROOT_DIRECTORY_VAR replaced if it was there.
- */
- public static String fillInputRootDirectoryVariable (String original,
- String inputRootDir)
- {
- if ( inputRootDir == null ) {
- inputRootDir = "";
- }
- return original.replace(INPUT_ROOT_DIRECTORY_VAR, inputRootDir);
- }
-
- /**
- * Returns the smallest value in a given array of values.
- * @param values the given array
- * @return the smallest value in the array
- */
- public static int min (int... values) {
- int res = Integer.MAX_VALUE;
- for (int value : values) {
- res = Math.min(res, value);
- }
- return (values.length > 0) ? res : 0;
- }
-
- /**
- * Safely returns a value for a given key in a given map.
- * @param <V> the type of keys maintained by the given map
- * @param <K> the type of mapped values of the given map
- * @param map the given map
- * @param key the key whose associated value is to be returned
- * @param defaultValue the default value to be returned if the key is not found
- * @return the value to which the specified key is mapped, or
- * defaultValue if the given map contains no mapping for the given key
- */
- public static <K, V> V getValue(Map<K, V> map, K key, V defaultValue) {
- if (map == null) return defaultValue;
- if (!map.containsKey(key)) return defaultValue;
-
- return map.get(key);
- }
-
- /**
- * Given an integer range and a value normalize that value on a scale between 0 and 100.
- * @param low - lowest value of the range
- * @param high - highest value of the range
- * @param value - the value that needs to be mapped to 0-100
- * @return mapped value, a number between 0 and 100
- */
- public static int normalizeRange(float low, float high, float value) {
- float m = 0.0f; // low value of map to range
- float n = 100.0f; // high value of map to range
- return (int) (m +((value-low)/(high-low) * (n-m)));
- }
- }