/************************************************************************** OmegaT - Computer Assisted Translation (CAT) tool with fuzzy matching, translation memory, keyword search, glossaries, and translation leveraging into updated projects. Copyright (C) 2000-2006 Keith Godfrey, Maxym Mykhalchuk, and Henry Pijffers 2007 Didier Briel, Zoltan Bartko, Alex Buloichik 2008-2011 Didier Briel 2012 Martin Fleurke, Didier Briel 2013 Aaron Madlon-Kay, Zoltan Bartko, Didier Briel, Alex Buloichik 2014 Aaron Madlon-Kay, Alex Buloichik 2015 Aaron Madlon-Kay Home page: http://www.omegat.org/ Support center: http://groups.yahoo.com/group/OmegaT/ This file is part of OmegaT. OmegaT is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OmegaT 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. **************************************************************************/ package org.omegat.util; import java.awt.GraphicsEnvironment; import java.awt.event.KeyEvent; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarInputStream; import java.util.regex.Pattern; import org.apache.commons.io.IOUtils; import org.omegat.util.Platform.OsType; /** * Static functions taken from CommandThread to reduce file size. * * @author Keith Godfrey * @author Maxym Mykhalchuk * @author Henry Pijffers (henry.pijffers@saxnot.com) * @author Didier Briel * @author Zoltan Bartko - bartkozoltan@bartkozoltan.com * @author Alex Buloichik * @author Martin Fleurke * @author Aaron Madlon-Kay */ public final class StaticUtils { private StaticUtils() { } /** * Configuration directory on Windows platforms */ private static final String WINDOWS_CONFIG_DIR = "\\OmegaT\\"; /** * Configuration directory on UNIX platforms */ private static final String UNIX_CONFIG_DIR = "/.omegat/"; /** * Configuration directory on Mac OS X */ private static final String OSX_CONFIG_DIR = "/Library/Preferences/OmegaT/"; /** * Script directory */ private static final String SCRIPT_DIR = "script"; /** * Char which should be used instead protected parts. It should be non-letter char, to be able to have * correct words counter. * * This char can be placed around protected text for separate words inside protected text and words * outside if there are no spaces between they. */ public static final char TAG_REPLACEMENT_CHAR = '\b'; public static final String TAG_REPLACEMENT = "\b"; /** * Contains the location of the directory containing the configuration * files. */ private static String configDir = null; /** * Contains the location of the script dir containing the exported text * files. */ private static String scriptDir = null; /** * Check if specified key pressed. * * @param e * pressed key event * @param code * required key code * @param modifiers * required modifiers * @return true if checked key pressed */ public static boolean isKey(KeyEvent e, int code, int modifiers) { return e.getKeyCode() == code && e.getModifiers() == modifiers; } /** * Returns the names of all font families available. */ public static String[] getFontNames() { GraphicsEnvironment graphics; graphics = GraphicsEnvironment.getLocalGraphicsEnvironment(); return graphics.getAvailableFontFamilyNames(); } /** Caching install dir */ private static String installDir = null; /** * Returns OmegaT installation directory. The code uses this method to look * up for OmegaT documentation. */ public static String installDir() { if (installDir == null) { File file = null; try { URI sourceUri = StaticUtils.class.getProtectionDomain().getCodeSource().getLocation().toURI(); if (sourceUri.getScheme().equals("file")) { File uriFile = Paths.get(sourceUri).toFile(); // If running from a JAR, get the enclosing folder // (the JAR is assumed to be at the installation root) if (uriFile.getName().endsWith(".jar")) { file = uriFile.getParentFile(); } else { // Running from an IDE or build tool; use CWD. } } else { // Running from Java WebStart; use CWD. } } catch (URISyntaxException e) { } if (file == null) { file = Paths.get(".").toFile(); } installDir = file.getAbsolutePath(); } return installDir; } /** * Returns the location of the configuration directory, depending on the * user's platform. Also creates the configuration directory, if necessary. * If any problems occur while the location of the configuration directory * is being determined, an empty string will be returned, resulting in the * current working directory being used. * * <ul><li>Windows XP: <Documents and Settings>\<User name>\Application Data\OmegaT * <li>Windows Vista: User\<User name>\AppData\Roaming * <li>Linux: ~/.omegat * <li>Solaris/SunOS: ~/.omegat * <li>FreeBSD: ~/.omegat * <li>Mac OS X: ~/Library/Preferences/OmegaT * <li>Other: User home directory * </ul> * * @return The full path of the directory containing the OmegaT * configuration files, including trailing path separator. */ public static String getConfigDir() { // if the configuration directory has already been determined, return it if (configDir != null) { return configDir; } String cd = RuntimePreferences.getConfigDir(); if (cd != null) { // use the forced specified directory configDir = new File(cd).getAbsolutePath() + File.separator; return configDir; } OsType os = Platform.getOsType(); // name of operating system String home; // user home directory // get os and user home properties try { // get the user's home directory home = System.getProperty("user.home"); } catch (SecurityException e) { // access to the os/user home properties is restricted, // the location of the config dir cannot be determined, // set the config dir to the current working dir configDir = new File(".").getAbsolutePath() + File.separator; // log the exception, only do this after the config dir // has been set to the current working dir, otherwise // the log method will probably fail Log.logErrorRB("SU_USERHOME_PROP_ACCESS_ERROR"); Log.log(e.toString()); return configDir; } // if os or user home is null or empty, we cannot reliably determine // the config dir, so we use the current working dir (= empty string) if (os == null || StringUtil.isEmpty(home)) { // set the config dir to the current working dir configDir = new File(".").getAbsolutePath() + File.separator; return configDir; } // check for Windows versions if (os == OsType.WIN32 || os == OsType.WIN64) { String appData = null; // We do not use %APPDATA% // Trying first Vista/7, because "Application Data" exists also as virtual folder, // so we would not be able to differentiate with 2000/XP otherwise File appDataFile = new File(home, "AppData\\Roaming"); if (appDataFile.exists()) { appData = appDataFile.getAbsolutePath(); } else { // Trying to locate "Application Data" for 2000 and XP // C:\Documents and Settings\<User>\Application Data appDataFile = new File(home, "Application Data"); if (appDataFile.exists()) { appData = appDataFile.getAbsolutePath(); } } if (!StringUtil.isEmpty(appData)) { // if a valid application data dir has been found, // append an OmegaT subdir to it configDir = appData + WINDOWS_CONFIG_DIR; } else { // otherwise set the config dir to the user's home directory, // usually // C:\Documents and Settings\<User>\OmegaT configDir = home + WINDOWS_CONFIG_DIR; } // Check for UNIX varieties // Solaris is generally detected as SunOS } else if (os == OsType.LINUX32 || os == OsType.LINUX64 || os == OsType.OTHER) { // set the config dir to the user's home dir + "/.omegat/", so it's // hidden configDir = home + UNIX_CONFIG_DIR; // check for Mac OS X } else if (Platform.isMacOSX()) { // set the config dir to the user's home dir + // "/Library/Preferences/OmegaT/" configDir = home + OSX_CONFIG_DIR; // other OSes / default } else { // use the user's home directory by default configDir = home + File.separator; } // create the path to the configuration dir, if necessary if (!configDir.isEmpty()) { try { // check if the dir exists File dir = new File(configDir); if (!dir.exists()) { // create the dir boolean created = dir.mkdirs(); // if the dir could not be created, // set the config dir to the current working dir if (!created) { Log.logErrorRB("SU_CONFIG_DIR_CREATE_ERROR"); configDir = new File(".").getAbsolutePath() + File.separator; } } } catch (SecurityException e) { // the system doesn't want us to write where we want to write // reset the config dir to the current working dir configDir = new File(".").getAbsolutePath() + File.separator; // log the exception, but only after the config dir has been // reset Log.logErrorRB("SU_CONFIG_DIR_CREATE_ERROR"); Log.log(e.toString()); } } // we should have a correct, existing config dir now return configDir; } public static String getScriptDir() { // If the script directory has already been determined, return it if (scriptDir != null) { return scriptDir; } scriptDir = getConfigDir() + SCRIPT_DIR + File.separator; try { // Check if the directory exists File dir = new File(scriptDir); if (!dir.exists()) { // Create the directory boolean created = dir.mkdirs(); // If the directory could not be created, // set the script directory to config directory if (!created) { Log.logErrorRB("SU_SCRIPT_DIR_CREATE_ERROR"); scriptDir = getConfigDir(); } } } catch (SecurityException e) { // The system doesn't want us to write where we want to write // reset the script dir to the current config dir scriptDir = getConfigDir(); // log the exception, but only after the script dir has been reset Log.logErrorRB("SU_SCRIPT_DIR_CREATE_ERROR"); Log.log(e.toString()); } return scriptDir; } /** * Encodes the array of bytes to store them in a plain text file. */ public static String uuencode(byte[] buf) { if (buf.length <= 0) { return ""; } StringBuilder res = new StringBuilder(); res.append(buf[0]); for (int i = 1; i < buf.length; i++) { res.append('#'); res.append(buf[i]); } return res.toString(); } /** * Decodes the array of bytes that was stored in a plain text file as a * string, back to array of bytes. */ public static byte[] uudecode(String buf) { String[] bytes = buf.split("#"); byte[] res = new byte[bytes.length]; for (int i = 0; i < bytes.length; i++) { try { res[i] = Byte.parseByte(bytes[i]); } catch (NumberFormatException e) { res[i] = 0; } } return res; } /** * Makes the file name relative to the given path. */ public static String makeFilenameRelative(String filename, String path) { if (filename.toLowerCase().startsWith(path.toLowerCase())) { return filename.substring(path.length()); } else { return filename; } } /** * Translates a string containing word-processing "glob"-style wildcards * (<code>?</code> matches a single non-whitespace character, <code>*</code> * matches zero or more non-whitespace characters) to standard regex. * <p> * If <code>spaceMatchesNbsp</code> is <code>true</code>, non-breaking * spaces (<code>U+00A0</code>) will also be considered whitespace. * <ul> * <li><code>?</code> is translated to <code>\S</code> (or * <code>[^\s\u00A0]</code>) * <li><code>*</code> is translated to <code>\S*</code> (or * <code>[^\s\u00A0]*</code>) * <li>If <code>spaceMatchesNbsp</code> is <code>true</code>, then * '<code> </code>' is translated to <code>( |\u00A0)</code> * <li>All other special regex characters are escaped as literals * </ul> * * @param text * The text to escape * @param spaceMatchesNbsp * Whether to consider regular spaces to also match non-breaking * spaces * @return The escaped text */ public static String globToRegex(String text, boolean spaceMatchesNbsp) { String quoted = Pattern.quote(text); StringBuilder sb = new StringBuilder(quoted); // We should blow up if the standard library implementation ever // switches to a scheme other than using \Q and \E. assert quoted.startsWith("\\Q"); assert quoted.endsWith("\\E"); if (spaceMatchesNbsp) { replaceGlobs(sb, " ", "( |\u00A0)"); replaceGlobs(sb, "*", "[^\\s\u00A0]*"); replaceGlobs(sb, "?", "[^\\s\u00A0]"); } else { replaceGlobs(sb, "*", "\\S*"); replaceGlobs(sb, "?", "\\S"); } return sb.toString(); } private static void replaceGlobs(StringBuilder haystack, String needle, String replacement) { replacement = "\\E" + replacement + "\\Q"; int current = 0; int globIndex = 0; while ((globIndex = haystack.indexOf(needle, current)) != -1) { haystack.replace(globIndex, globIndex + 1, replacement); current = globIndex + replacement.length(); } } /** * dowload a file from the internet */ public static String downloadFileToString(String urlString) throws IOException { URLConnection urlConn; InputStream in; URL url = new URL(urlString); urlConn = url.openConnection(); //don't wait forever. 10 seconds should be enough. urlConn.setConnectTimeout(10000); in = urlConn.getInputStream(); try { return IOUtils.toString(in, "UTF-8"); } finally { in.close(); } } /** * Download a file to the disk */ public static void downloadFileToDisk(String address, String filename) throws MalformedURLException { URLConnection urlConn; InputStream in = null; OutputStream out = null; try { URL url = new URL(address); urlConn = url.openConnection(); in = urlConn.getInputStream(); out = new BufferedOutputStream(new FileOutputStream(filename)); byte[] byteBuffer = new byte[1024]; int numRead; while ((numRead = in.read(byteBuffer)) != -1) { out.write(byteBuffer, 0, numRead); } } catch (IOException ex) { Log.logErrorRB("IO exception"); Log.log(ex); } finally { try { if (in != null) { in.close(); } if (out != null) { out.close(); } } catch (IOException ex) { // munch this } } } public static void extractFileFromJar(File archive, String destination, String... filenames) throws IOException { InputStream is = new FileInputStream(archive); extractFileFromJar(is, destination, filenames); is.close(); } public static void extractFileFromJar(InputStream in, String destination, String... filenames) throws IOException { if (filenames == null || filenames.length == 0) { throw new IllegalArgumentException("Caller must provide non-empty list of files to extract."); } List<String> toExtract = new ArrayList<>(Arrays.asList(filenames)); try (JarInputStream jis = new JarInputStream(in)) { // parse the entries JarEntry entry; while ((entry = jis.getNextJarEntry()) != null) { if (!toExtract.contains(entry.getName())) { continue; } // match found File f = new File(destination, entry.getName()); f.getParentFile().mkdirs(); try (FileOutputStream fos = new FileOutputStream(f); BufferedOutputStream out = new BufferedOutputStream(fos)) { byte[] byteBuffer = new byte[1024]; int numRead; while ((numRead = jis.read(byteBuffer)) != -1) { out.write(byteBuffer, 0, numRead); } } toExtract.remove(entry.getName()); } } if (!toExtract.isEmpty()) { throw new FileNotFoundException("Failed to extract all of the specified files."); } } /** * Parse a command line string into arguments, interpreting * double and single quotes as Bash does. * @param cmd Command string * @return Array of arguments */ public static String[] parseCLICommand(String cmd) { cmd = cmd.trim(); if (cmd.isEmpty()) { return new String[] { "" }; } StringBuilder arg = new StringBuilder(); List<String> result = new ArrayList<String>(); final char noQuote = '\0'; char currentQuote = noQuote; for (int cp, i = 0; i < cmd.length(); i += Character.charCount(cp)) { cp = cmd.codePointAt(i); if (cp == currentQuote) { currentQuote = noQuote; } else if (cp == '"' && currentQuote == noQuote) { currentQuote = '"'; } else if (cp == '\'' && currentQuote == noQuote) { currentQuote = '\''; } else if (cp == '\\' && i + 1 < cmd.length()) { int ncp = cmd.codePointAt(cmd.offsetByCodePoints(i, 1)); if ((currentQuote == noQuote && Character.isWhitespace(ncp)) || (currentQuote == '"' && ncp == '"')) { arg.appendCodePoint(ncp); i += Character.charCount(ncp); } else { arg.appendCodePoint(cp); } } else { if (Character.isWhitespace(cp) && currentQuote == noQuote) { if (arg.length() > 0) { result.add(arg.toString()); arg = new StringBuilder(); } else { // Discard } } else { arg.appendCodePoint(cp); } } } // Catch last arg if (arg.length() > 0) { result.add(arg.toString()); } return result.toArray(new String[result.size()]); } public static boolean isProjectDir(File f) { if (f == null || f.getName().isEmpty()) { return false; } File projFile = new File(f.getAbsolutePath(), OConsts.FILE_PROJECT); return projFile.isFile(); } } // StaticUtils