/*
* Jajuk
* Copyright (C) The Jajuk Team
* http://jajuk.info
*
* This program 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 2
* of the License, or any later version.
*
* This program 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
package org.jajuk.util;
import com.google.common.io.Files;
import ext.MersenneTwister;
import java.awt.Desktop;
import java.awt.GraphicsEnvironment;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.net.InetAddress;
import java.net.URL;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import javax.swing.ImageIcon;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.jajuk.Main;
import org.jajuk.base.Device;
import org.jajuk.base.DeviceManager;
import org.jajuk.services.core.SessionService;
import org.jajuk.util.error.JajukException;
import org.jajuk.util.error.JajukRuntimeException;
import org.jajuk.util.filters.DirectoryFilter;
import org.jajuk.util.filters.KnownTypeFilter;
import org.jajuk.util.log.Log;
/**
* Set of convenient methods for system and IO.
*/
public final class UtilSystem {
/** The Constant LOCAL_IP. */
private static final String LOCAL_IP = "127.0.0.1";
/** Is browser supported ?. */
private static Boolean browserSupported;
/** Size of the short names converter in bytes. */
private static final int CONVERTER_FILE_SIZE = 23;
/**
* MPlayer status possible values *.
*/
public static enum MPlayerStatus {
MPLAYER_STATUS_OK, MPLAYER_STATUS_NOT_FOUND, MPLAYER_STATUS_WRONG_VERSION, MPLAYER_STATUS_JNLP_DOWNLOAD_PBM
}
/** Current date cached (for performances) *. */
public static final Date TODAY = new Date();
/** Central random object for all Jajuk *. */
private static final Random RANDOM = MersenneTwister.getInstance();
/** Cached user home directory *. */
private static String cachedUserHomeDir;
/** Are we under Linux ? *. */
private static final boolean UNDER_LINUX;
/** Are we under MAC OS Intel ? *. */
private static final boolean UNDER_OSX;
/** Are we under Windows ? *. */
private static final boolean UNDER_WINDOWS;
/** Are we under Windows 32 bits ? *. */
private static final boolean UNDER_WINDOWS_32BIT;
/** Are we under Windows 64 bits ? *. */
private static final boolean UNDER_WINDOWS_64BIT;
/** Are we in JNLP mode ? *. */
private static final boolean UNDER_JNLP;
/** Are we under KDE ? *. */
private static final boolean UNDER_KDE;
/** Directory filter used in refresh. */
private static JajukFileFilter dirFilter;
/** File filter used in refresh. */
private static JajukFileFilter fileFilter;
// Computes OS detection operations for perf reasons (can be called in loop
// in refresh method for ie)
static {
final String sOS = (String) System.getProperties().get("os.name");
// os.name can be null with JWS under MacOS
UNDER_WINDOWS = ((sOS != null) && (sOS.trim().toLowerCase(Locale.getDefault())
.lastIndexOf("windows") != -1));
}
static {
UNDER_WINDOWS_32BIT = UtilSystem.isUnderWindows()
&& System.getProperties().get("sun.arch.data.model").equals("32");
}
static {
UNDER_WINDOWS_64BIT = UtilSystem.isUnderWindows()
&& !System.getProperties().get("sun.arch.data.model").equals("32");
}
static {
final String sOS = (String) System.getProperties().get("os.name");
// os.name can be null with JWS under MacOS
UNDER_LINUX = ((sOS != null) && (sOS.trim().toLowerCase(Locale.getDefault())
.lastIndexOf("linux") != -1));
}
static {
final String sArch = System.getProperty("os.arch");
UNDER_OSX = org.jdesktop.swingx.util.OS.isMacOSX()
// We only support Intel OSX
&& ((sArch != null) && sArch.matches(".*86.*"));
}
static {
UNDER_JNLP = (System.getProperty("jnlpx.jvm") != null);
}
/**
* Are we running in a KDE environment ?
*
* We check it by using ps command + a grep searching 'ksmserver' process*/
static {
boolean underKDE = false;
if (isUnderLinux()) {
BufferedReader stdInput = null;
try {
ProcessBuilder pb = new ProcessBuilder("ps", "-eaf");
Process proc = pb.start();
stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
proc.waitFor();
String s;
while ((s = stdInput.readLine()) != null) {
if (s.matches(".*ksmserver.*")) {
underKDE = true;
break;
}
}
} catch (Throwable e) {
Log.error(e);
} finally {
if (stdInput != null) {
try {
stdInput.close();
} catch (IOException e) {
Log.error(e);
}
}
}
}
UNDER_KDE = underKDE;
}
/** Icons cache. */
static Map<String, ImageIcon> iconCache = new HashMap<String, ImageIcon>(200);
/** Mplayer exe path. */
private static File mplayerPath = null;
/** current class loader. */
private static ClassLoader classLoader = null;
/**
* private constructor to avoid instantiating utility class.
*/
private UtilSystem() {
}
/**
* Save a file in the same directory with name <filename>_YYYYmmddHHMM.xml and
* with a given maximum Mb size for the file and its backup files
*
* @param file The file to back up
* @param iMB
*/
public static void backupFile(final File file, final int iMB) {
try {
if (Integer.parseInt(Conf.getString(Const.CONF_BACKUP_SIZE)) <= 0) {
// 0 or less means no backup
return;
}
// calculates total size in MB for the file to backup and its
// backup files
long lUsedMB = 0;
final List<File> alFiles = new ArrayList<File>(10);
final File[] files = new File(file.getAbsolutePath()).getParentFile().listFiles();
if (files != null) {
for (final File element : files) {
if (element.getName().indexOf(UtilSystem.removeExtension(file.getName())) != -1) {
lUsedMB += element.length();
alFiles.add(element);
}
}
// sort found files
alFiles.remove(file);
Collections.sort(alFiles);
// too much backup files, delete older
if (((lUsedMB - file.length()) / 1048576 > iMB) && (alFiles.size() > 0)) {
final File fileToDelete = alFiles.get(0);
if (fileToDelete != null) { //NOSONAR
if (!fileToDelete.delete()) {
Log.warn("Could not delete file " + fileToDelete);
}
}
}
}
// backup itself using nio, file name is
// collection-backup-yyyMMdd.xml
final String sExt = new SimpleDateFormat("yyyyMMdd", Locale.getDefault()).format(new Date());
final File fileNew = new File(UtilSystem.removeExtension(file.getAbsolutePath()) + "-backup-"
+ sExt + "." + UtilSystem.getExtension(file));
final FileChannel fcSrc = new FileInputStream(file).getChannel();
try {
final FileChannel fcDest = new FileOutputStream(fileNew).getChannel();
try {
fcDest.transferFrom(fcSrc, 0, fcSrc.size());
} finally {
fcDest.close();
}
} finally {
fcSrc.close();
}
} catch (final IOException ie) {
Log.error(ie);
}
}
/**
* Copy a file to another file.
*
* @param file : file to copy
* @param fNew : destination file
*
* @throws JajukException the jajuk exception
* @throws IOException Signals that an I/O exception has occurred.
*/
public static void copy(final File file, final File fNew) throws JajukException, IOException {
if (!file.exists() || !file.canRead()) {
throw new JajukException(9, file.getAbsolutePath(), null);
}
FileUtils.copyFile(file, fNew);
// Display a warning if copied file is void as it can happen with full
// disks
if (fNew.length() == 0) {
Log.warn("Copied file is void: {{" + file.getAbsolutePath() + "}}");
}
}
/**
* Return the device containing the io file
* @param fio the IO file to test
* @return the containing device or null if the file is outside any known device
*/
public static Device getDeviceForFio(java.io.File fio) {
for (Device device : DeviceManager.getInstance().getDevices()) {
if (UtilSystem.isAncestor(device.getFIO(), fio)) {
return device;
}
}
return null;
}
/**
* Move a file to another file (directories are not supported).
*
* Note that it may be better to use this method than java.io.File.renameTo() method that
* doesn't seem to work always under windows (in special directories) and because this method
* always return an exception in case of problem.
*
* @param file : file to move
* @param fNew : destination file
*
* @throws JajukException the jajuk exception
* @throws IOException Signals that an I/O exception has occurred.
*/
public static void move(final File file, final File fNew) throws JajukException, IOException {
copy(file, fNew);
if (!file.delete()) {
throw new IOException("Cannot delete file: " + file.getAbsolutePath());
}
}
/**
* Copy a file.
*
* @param file : source file
* @param sNewName : dest file
*
* @throws JajukException the jajuk exception
* @throws IOException Signals that an I/O exception has occurred.
*/
public static void copy(final File file, final String sNewName) throws JajukException,
IOException {
Log.debug("Renaming: {{" + file.getAbsolutePath() + "}} to : " + sNewName);
final File fileNew = new File(new StringBuilder(file.getParentFile().getAbsolutePath())
.append('/').append(sNewName).toString());
if (!file.exists() || !file.canRead()) {
throw new JajukException(9, file.getAbsolutePath(), null);
}
FileUtils.copyFile(file, fileNew);
}
/**
* Copy a URL resource to a file We don't use nio but Buffered Reader / writer
* because we can only get channels from a FileInputStream that can be or not
* be in a Jar (production / test).
*
* @param src source designed by URL
* @param dest destination file full path
*
* @throws IOException If the src or dest cannot be opened/created.
*/
public static void copy(final URL src, final String dest) throws IOException {
final BufferedReader br = new BufferedReader(new InputStreamReader(src.openStream()));
try {
final BufferedWriter bw = new BufferedWriter(new FileWriter(dest));
try {
String sLine = null;
do {
sLine = br.readLine();
if (sLine != null) {
bw.write(sLine);
bw.newLine();
}
} while (sLine != null);
bw.flush();
} finally {
bw.close();
}
} finally {
br.close();
}
}
/**
* Activate recovery support for a file than has been written into the the collection so it can be gratefully recover
* in case of breakdown during the save operation. This method must be called after the file.xml.sav has been successfully written.
* <pre>The steps are the following (every step can fail, all files in the same (collection) directory, thus on the same disk) :
* 1) Write file.xml.saving (already done when calling this method)
* 2) Write the proof file file.xml.proof (void)
* 3) Delete the file.xml file if it exists (optional under POSIX systems but we do it for consistency with Windows and to avoid a OS specific behavior)
* 4) Delete the proof file
* 5) Rename file.xml.saving to file.xml</pre>
* </p>
* @param finalFile : the final file (like collection.xml)
* @throws IOException
*/
public static void saveFileWithRecoverySupport(File finalFile) throws IOException {
// Check saving file existence
File saving = new File(finalFile.getAbsoluteFile() + "." + Const.FILE_SAVING_FILE_EXTENSION);
if (!saving.exists()) {
throw new IOException("Saving file does not exist for file : " + finalFile.getAbsolutePath());
}
// Create the proof file
File proof = new File(finalFile.getAbsoluteFile() + "." + Const.FILE_SAVED_PROOF_FILE_EXTENSION);
Files.touch(proof);
if (finalFile.exists()) {
deleteFile(finalFile);
}
deleteFile(proof);
saving.renameTo(finalFile);
}
/**
* Recover a file after a breakdown (at next jajuk session) if required. Most of the time, this does nothing but
* if a file has been partially saved using the @see saveFileWithRecoverySupport() method, the previous version is revored.
* This is guarantee to work always, except if the filesystem can't be read or written.
*
* Note that this generic method doesn't handle the special collection.xml backup files.
*
* <pre>Recovery actions and files existence when failure at step :
* No failure : only file.xml file
-> no recovery action
* @1 : file.xml, partial file.xml.saving and no file.xml.proof
-> delete file.xml.sav and file.xml.proof
* @2 : file.xml, file.xml.saving but no file.xml.proof
-> delete file.sav
* @3 : file.xml, file.xml.saving and file.xml.proof
-> execute saveFileWithRecoverySupport steps 3 to 5
* @4 : file.sav and file.xml.proof
-> execute saveFileWithRecoverySupport steps 4 to 5
* @5 : file.xml.saving only
-> execute saveFileWithRecoverySupport step 5</pre>
* @throws IOException if a temporary file cannot be deleted
*/
public static void recoverFileIfRequired(File finalFile) throws IOException {
File saving = new File(finalFile.getAbsoluteFile() + "." + Const.FILE_SAVING_FILE_EXTENSION);
File proof = new File(finalFile.getAbsoluteFile() + "." + Const.FILE_SAVED_PROOF_FILE_EXTENSION);
// No recovery required
if (!saving.exists() && !proof.exists()) {
return;
}
// Recovery after crash at saving step 1 and 2
if (finalFile.exists() && saving.exists() && !proof.exists()) {
Log.warn("Recover step 1 or 2");
deleteFile(saving);
}
// Recovery after crash at saving step 3
else if (finalFile.exists() && saving.exists() && proof.exists()) {
Log.warn("Recover step 3");
deleteFile(finalFile);
deleteFile(proof);
saving.renameTo(finalFile);
}
// Recovery after crash at saving step 4
else if (!finalFile.exists() && saving.exists() && proof.exists()) {
Log.warn("Recover step 4");
deleteFile(proof);
saving.renameTo(finalFile);
}
// Recovery after crash at saving step 5
else if (!finalFile.exists() && saving.exists() && !proof.exists()) {
Log.warn("Recover step 5");
saving.renameTo(finalFile);
}
}
/**
* Copy recursively files and directories.
*
* @param src
* @param dst
* @throws JajukException the jajuk exception
* @throws IOException Signals that an I/O exception has occurred.
*/
public static void copyRecursively(final File src, final File dst) throws JajukException,
IOException {
if (src.isDirectory()) {
if (!dst.mkdirs()) {
Log.warn("Could not create directory structure " + dst.toString());
}
final String list[] = src.list();
for (final String element : list) {
final String dest1 = dst.getAbsolutePath() + '/' + element;
final String src1 = src.getAbsolutePath() + '/' + element;
UtilSystem.copyRecursively(new File(src1), new File(dest1));
}
} else {
UtilSystem.copy(src, dst);
}
}
/**
* Copy a file to given directory.
*
* @param file : file to copy
* @param directory : destination directory
*
* @return destination file
*
* @throws JajukException the jajuk exception
* @throws IOException Signals that an I/O exception has occurred.
*/
public static void copyToDir(final File file, final File directory) throws JajukException,
IOException {
Log.debug("Copying: {{" + file.getAbsolutePath() + "}} to : " + directory.getAbsolutePath());
if (!file.exists() || !file.canRead()) {
throw new JajukException(9, file.getAbsolutePath(), null);
}
FileUtils.copyFileToDirectory(file, directory);
}
/**
* Create empty file.
*
* @param file
*
* @throws IOException Signals that an I/O exception has occurred.
*/
public static void createEmptyFile(final File file) throws IOException {
final OutputStream fos = new FileOutputStream(file);
try {
fos.write(new byte[0]);
} finally {
fos.close();
}
}
/**
* Delete recursively a directory (use with caution!).
*
* @param dir : source directory
*
* @throws IOException Signals that an I/O exception has occurred.
*/
public static void deleteDir(final File dir) throws IOException {
Log.debug("Deleting: {{" + dir.getAbsolutePath() + "}}");
FileUtils.deleteDirectory(dir);
if (dir.exists()) {
throw new IOException("Directory" + dir.getAbsolutePath() + " still exists");
}
return;
}
/**
* Delete a file that exists.
*
* @param file : source file
*
* @throws IOException Signals that an I/O exception has occurred.
*/
public static void deleteFile(final File file) throws IOException {
Log.debug("Deleting: {{" + file.getAbsolutePath() + "}}");
if (!file.isFile()) {
throw new IOException("File " + file.getAbsolutePath() + " is not a file");
}
if (!file.exists()) {
throw new IOException("File " + file.getAbsolutePath() + " didn't exist");
}
if (!file.delete()) {
Log.warn("Could not delete file " + file);
}
// check that file has been really deleted (sometimes,
// we get no exception)
if (file.exists()) {
throw new IOException("File" + file.getAbsolutePath() + " still exists");
}
return;
}
/**
* Get a file extension.
*
* @param file
*
* @return the extension
*/
public static String getExtension(final File file) {
return UtilSystem.getExtension(file.getName());
}
/**
* Get a file extension (without the dot!).
*
* @param filename The file to examine.
*
* @return The actual file extension or an empty string if no extension is
* found (i.e. no dot in the filename).
*/
public static String getExtension(final String filename) {
int dotIndex = filename.lastIndexOf('.');
// File without point
if (dotIndex == -1) {
return "";
}
if (dotIndex > 0) {
return filename.substring(dotIndex + 1, filename.length());
} else {
// File beginning by a point (unix hidden file)
return filename;
}
}
/**
* Additional file checksum used to prevent bug #886098. Simply return some
* bytes read at the middle of the file
* <p>
* uses nio api for performances
*
* @param fio
*
* @return the file checksum
*
* @throws JajukException the jajuk exception
*/
public static String getFileChecksum(final File fio) throws JajukException {
try {
String sOut = "";
final FileChannel fc = new FileInputStream(fio).getChannel();
try {
final ByteBuffer bb = ByteBuffer.allocate(500);
fc.read(bb, fio.length() / 2);
sOut = new String(bb.array());
} finally {
fc.close();
}
return MD5Processor.hash(sOut);
} catch (final IOException e) {
throw new JajukException(103, e);
}
}
/**
* Gets the host name.
*
* @return This box hostname
*/
public static String getHostName() {
String sHostname = null;
// Try to get hostname using the standard way
try {
sHostname = InetAddress.getLocalHost().getHostName();
} catch (final Exception e) {
Log.debug("Cannot get Hostname using the standard way");
}
if (sHostname == null) {
// Try using IP now
try {
final java.net.InetAddress inetAdd = java.net.InetAddress.getByName(LOCAL_IP);
sHostname = inetAdd.getHostName();
} catch (final Exception e) {
Log.debug("Cannot get Hostname by IP");
}
}
// If still no hostname, return a default value
if (sHostname == null) {
sHostname = Const.DEFAULT_HOSTNAME;
}
return sHostname;
}
/**
* Return url of jar we are executing.
*
* This code no more work with last JRE 6 under JNLP (it returns only partial URL)
*
* @param cClass
*
* @return URL of jar we are executing
*/
public static URL getJarLocation(final Class<?> cClass) {
URL url = cClass.getProtectionDomain().getCodeSource().getLocation();
if (UtilSystem.isUnderJNLP()) {
Log.debug("JAR location: " + url.getFile());
}
return url;
}
/**
* Gets the m player windows path.
*
* @return MPlayer exe file
*/
public static File getMPlayerWindowsPath() {
// Use cache
if (UtilSystem.mplayerPath != null) {
return UtilSystem.mplayerPath;
}
File file = null;
// Check in ~/.jajuk directory (used by JNLP distribution
// as well). Test exe size as well to detect unfinished downloads of
// mplayer.exe in JNLP mode
file = SessionService.getConfFileByPath(Const.FILE_MPLAYER_WINDOWS_EXE);
if (file.exists() && file.length() == Const.MPLAYER_WINDOWS_EXE_SIZE) {
UtilSystem.mplayerPath = file;
return UtilSystem.mplayerPath;
} else if (!UtilSystem.isUnderJNLP()) {
// Check in the path where jajuk.jar is executed (all others
// distributions). does not work under JNLP
String sPATH = null;
try {
// Extract file name from URL. URI returns jar path, its parent
// is the bin directory and the right dir is the parent of bin
// dir
sPATH = new File(getJarLocation(Main.class).toURI()).getParentFile().getParentFile()
.getAbsolutePath();
// Add MPlayer file name
file = new File(sPATH + '/' + Const.FILE_MPLAYER_WINDOWS_EXE);
if (file.exists() && file.length() == Const.MPLAYER_WINDOWS_EXE_SIZE) {
UtilSystem.mplayerPath = file;
}
} catch (Exception e) {
Log.error(e);
}
}
return UtilSystem.mplayerPath; // can be null if none suitable file found
}
/**
* Gets the mplayer OSX path.
* It is mainly based upon Windows getWindowsPath() path method, see comments over there
*
* @return MPLayer binary MAC full path
*/
public static File getMPlayerOSXPath() {
if (UtilSystem.mplayerPath != null) {
return UtilSystem.mplayerPath;
}
// Search in /Applications first
File file = new File("/Applications/Jajuk.app/Contents/MacOS/"
+ Const.FILE_MPLAYER_OSX_EXE);
if (file.canExecute() && file.length() == Const.MPLAYER_OSX_EXE_SIZE) {
UtilSystem.mplayerPath = file;
return UtilSystem.mplayerPath;
}
// Search in collection path
file = SessionService.getConfFileByPath(Const.FILE_MPLAYER_OSX_EXE);
if (file.exists() && file.length() == Const.MPLAYER_OSX_EXE_SIZE) {
UtilSystem.mplayerPath = file;
return UtilSystem.mplayerPath;
} else if (!UtilSystem.isUnderJNLP()) {
// Search in jajuk installation directory, do not work under JNLP
String sPATH = null;
try {
if (SessionService.isIdeMode()) {
// If under dev, take mplayer exe file from /Applications (the mplayer osx binary is not
// in SCM)
sPATH = "/Applications";
} else {
sPATH = new File(getJarLocation(Main.class).toURI()).getParentFile().getParentFile()
.getAbsolutePath();
}
file = new File(sPATH + '/' + Const.FILE_MPLAYER_OSX_EXE);
if (file.exists() && file.length() == Const.MPLAYER_OSX_EXE_SIZE) {
UtilSystem.mplayerPath = file;
}
} catch (Exception e) {
Log.error(e);
}
}
return UtilSystem.mplayerPath; // can be null if none suitable file found
}
/**
* Gets the mplayer status.
*
* @param mplayerPATH
*
* @return the mplayer status
*/
public static UtilSystem.MPlayerStatus getMplayerStatus(final String mplayerPATH) {
Process proc = null;
UtilSystem.MPlayerStatus mplayerStatus = UtilSystem.MPlayerStatus.MPLAYER_STATUS_NOT_FOUND;
try {
String fullPath = null;
if ("".equals(mplayerPATH)) {
fullPath = "mplayer";
} else {
fullPath = mplayerPATH;
}
Log.debug("Testing path: " + fullPath);
// check MPlayer release : 1.0pre8 min
proc = Runtime.getRuntime().exec(new String[] { fullPath, "-input", "cmdlist" }); //$NON-NLS-2$
final BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
try {
String line = null;
mplayerStatus = UtilSystem.MPlayerStatus.MPLAYER_STATUS_WRONG_VERSION;
for (;;) {
line = in.readLine();
if (line == null) {
break;
}
if (line.matches("get_time_pos.*")) {
mplayerStatus = UtilSystem.MPlayerStatus.MPLAYER_STATUS_OK;
break;
}
}
} finally {
in.close();
}
} catch (final IOException e) {
mplayerStatus = UtilSystem.MPlayerStatus.MPLAYER_STATUS_NOT_FOUND;
}
return mplayerStatus;
}
/**
* This method intends to cleanup a future filename so it can be created on
* all operating systems. Windows forbids characters : /\"<>|:*?
*
* @param in filename
*
* @return filename with forbidden characters replaced at best
*/
public static String getNormalizedFilename(final String in) {
String out = in.trim();
// Replace / : < > and \ by -
out = in.replaceAll("[/:<>\\\\]", "-");
// Replace * and | by spaces
out = out.replaceAll("[\\*|]", " ");
// Remove " and ? characters
out = out.replaceAll("[\"\\?]", "");
return out;
}
/**
* Return only the name of a file from a complete URL.
*
* @param sPath
*
* @return the only file
*/
public static String getOnlyFile(final String sPath) {
return new File(sPath).getName();
}
/**
* Resource loading is done this way to meet the requirements for Web Start.
* http
* ://java.sun.com/j2se/1.5.0/docs/guide/javaws/developersguide/faq.html#211
*
* @param name
*
* @return the resource
*/
public static URL getResource(final String name) {
return UtilSystem.getClassLoader().getResource(name);
}
/**
* Checks if is ancestor.
*
* @param file1 potential ancestor
* @param file2 potential child
*
* @return whether file1 is a file2 ancestor
*/
public static boolean isAncestor(final File file1, final File file2) {
File fParent = file2.getParentFile();
boolean bOut = false;
while (fParent != null) {
if (fParent.equals(file1)) {
bOut = true;
break;
}
fParent = fParent.getParentFile();
}
return bOut;
}
/**
* Checks if is descendant.
*
* @param file1
* @param file2
*
* @return whether file1 is a file2 descendant
*/
public static boolean isDescendant(final File file1, final File file2) {
// a file is a descendant to another file if the other file is it's
// ancestor...
return isAncestor(file2, file1);
}
/**
* Checks if is under linux.
*
* @return whether we are under Linux
*/
public static boolean isUnderLinux() {
return UtilSystem.UNDER_LINUX;
}
/**
* Checks if we are running as jnlp app.
*
* @return whether we are running as jnlp app
*/
public static boolean isUnderJNLP() {
return UtilSystem.UNDER_JNLP;
}
/**
* Checks if is under OSX (Intel or PowerPC).
*
* @return whether we are under OS X
*/
public static boolean isUnderOSX() {
return UtilSystem.UNDER_OSX;
}
/**
* Checks if is under windows.
*
* @return whether we are under Windows
*/
public static boolean isUnderWindows() {
return UtilSystem.UNDER_WINDOWS;
}
/**
* Checks if is under windows32bits.
*
* @return whether we are under Windows 32 bits
*/
public static boolean isUnderWindows32bits() {
return UtilSystem.UNDER_WINDOWS_32BIT;
}
/**
* Checks if is under windows64bits.
*
* @return whether we are under Windows 64 bits
*/
public static boolean isUnderWindows64bits() {
return UtilSystem.UNDER_WINDOWS_64BIT;
}
/**
* Checks if is valid file name.
*
* @param parent parent directory
* @param name file name
*
* @return whether the file name is correct on the current filesystem
*/
public static boolean isValidFileName(final File parent, final String name) {
// General tests
if ((parent == null) || (name == null)) {
return false;
}
// only digits or letters, OK, no need to test
if (!UtilString.containsNonDigitOrLetters(name)) {
return true;
}
final File f = new File(parent, name);
if (!f.exists()) {
try {
// try to create the file
f.createNewFile();
// test if the file is seen into the directory
final File[] files = parent.listFiles();
boolean b = false;
for (final File element : files) {
if (element.getName().equals(name)) {
b = true;
break;
}
}
// remove test file
if (f.exists()) {
UtilSystem.deleteFile(f);
}
return b;
} catch (final IOException ioe) {
return false;
}
} else { // file already exists
return true;
}
}
/**
* Need full fc.
*
* @return whether we need a full gc or not
*/
public static boolean needFullFC() {
final float fTotal = Runtime.getRuntime().totalMemory();
final float fFree = Runtime.getRuntime().freeMemory();
final float fLevel = (fTotal - fFree) / fTotal;
return fLevel >= Const.NEED_FULL_GC_LEVEL;
}
/**
* Open a file and return a string buffer with the file content.
*
* @param path -File path
*
* @return StringBuilder - File content.
*
* @throws JajukException - Throws a JajukException if a problem occurs during the file
* access.
*/
public static StringBuilder readFile(final String path) throws JajukException {
// Read
File file = new File(path);
FileReader fileReader;
try {
fileReader = new FileReader(file);
} catch (final FileNotFoundException e) {
throw new JajukException(9, path, e);
}
try {
final BufferedReader input = new BufferedReader(fileReader);
try {
// Read
final StringBuilder strColl = new StringBuilder();
String line = null;
while ((line = input.readLine()) != null) {
strColl.append(line);
}
return strColl;
} finally {
// Close the bufferedReader
input.close();
}
} catch (final IOException e) {
throw new JajukException(9, path, e);
}
}
/**
* Open a file from current jar and return a string buffer with the file
* content.
*
* @param sURL
*
* @return StringBuilder - File content.
*
* @throws JajukException -Throws a JajukException if a problem occurs during the file
* access.
*/
public static StringBuilder readJarFile(final String sURL) throws JajukException {
// Read
InputStream is;
StringBuilder sb = null;
try {
is = Main.class.getResourceAsStream(sURL);
try {
// Read
final byte[] b = new byte[200];
sb = new StringBuilder();
int i = 0;
do {
i = is.read(b, 0, b.length);
sb.append(new String(b));
} while (i > 0);
} finally {
// Close the bufferedReader
is.close();
}
} catch (final IOException e) {
throw new JajukException(9, e);
}
return sb;
}
/**
* Remove an extension from a file name.
*
* @param sFilename
*
* @return filename without extension
*/
public static String removeExtension(final String sFilename) {
return sFilename.substring(0, sFilename.lastIndexOf('.'));
}
/**
* Gets the class loader.
*
* @return the class loader
*/
public static ClassLoader getClassLoader() {
if (UtilSystem.classLoader == null) {
UtilSystem.classLoader = Thread.currentThread().getContextClassLoader();
}
return UtilSystem.classLoader;
}
/**
* Gets the dir filter.
*
* @return the dir filter
*/
public static JajukFileFilter getDirFilter() {
if (dirFilter == null) {
dirFilter = new JajukFileFilter(DirectoryFilter.getInstance());
}
return dirFilter;
}
/**
* Gets the file filter.
*
* @return the file filter
*/
public static JajukFileFilter getFileFilter() {
if (fileFilter == null) {
fileFilter = new JajukFileFilter(KnownTypeFilter.getInstance());
}
return fileFilter;
}
/**
* Replace a string inside a given file.
*
* @param file the file
* @param oldS the string to replace
* @param newS the new string
* @param encoding the encoding of the file
*
* @return whether some replacements occurred
*/
public static boolean replaceInFile(File file, String oldS, String newS, String encoding) {
try {
String s = FileUtils.readFileToString(file);
if (s.indexOf(oldS) != -1) {
s = s.replaceAll(oldS, newS);
Writer bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), encoding));
try {
bw.write(s);
bw.flush();
} finally {
bw.close();
}
return true;
}
} catch (IOException e) {
Log.error(e);
}
return false;
}
/**
* This method returns a single random object that can be used anywhere in
* jajuk It has to be a singleton to get a good shuffling. Indeed, Random()
* object are seeded by default with current nano date but in some cases, two
* random could be created at the same exact date in different threads or the
* same.
*
* @return Jajuk singleton random object
*/
public static Random getRandom() {
return UtilSystem.RANDOM;
}
/**
* Opens a directory with the associated explorer program. <li>Start by trying
* to open the directory with any provided explorer path</li> <li>Then, try to
* use the JDIC Desktop class if supported by the platform</li>
*
* Inspired from an aTunes method
*
* @param directory
*/
public static void openInExplorer(File directory) {
final File directoryToOpen;
/*
* Needed for UNC filenames with spaces -> http://bugs.sun.com/view_bug.do?bug_id=6550588
*/
if (isUnderWindows()) {
String shortPath = getShortPathNameW(directory.getAbsolutePath());
// If shortnames conversion doesn't work, try the initial path
if (shortPath == null || !new File(shortPath).exists()) {
directoryToOpen = directory;
} else {
directoryToOpen = new File(shortPath);
}
} else {
directoryToOpen = directory;
}
// Try to open the location using the forced explorer path of provided
if (StringUtils.isNotBlank(Conf.getString(Const.CONF_EXPLORER_PATH))) {
new Thread("Explorer Open Thread 1") {
@Override
public void run() {
try {
ProcessBuilder pb = new ProcessBuilder(Conf.getString(Const.CONF_EXPLORER_PATH),
directoryToOpen.getAbsolutePath());
pb.start();
} catch (Exception e) {
Log.error(e);
Messages.showErrorMessage(179, directoryToOpen.getAbsolutePath());
}
}
}.start();
}
// Try to open the location using the JDIC/JDK Desktop.open method
// This is not supported on some platforms (Linux/XFCE for ie)
else if (Desktop.getDesktop().isSupported(Desktop.Action.OPEN)) {
new Thread("Explorer Open Thread 2") {
@Override
public void run() {
try {
Desktop.getDesktop().open(directoryToOpen);
} catch (Exception e) {
Log.error(e);
Messages.showErrorMessage(179, directoryToOpen.getAbsolutePath());
}
}
}.start();
}
// Else, display a warning message: we don't support this platform
else {
Messages.showErrorMessage(179);
}
}
/**
* Return whether a process is still running.
*
* @param process the process
*
* @return whether the process is still running
*/
public static boolean isRunning(Process process) {
try {
process.exitValue();
return false;
} catch (IllegalThreadStateException itse) {
return true;
}
}
/**
* Return a process exit value, -100 if the process is still running.
*
* @param process the process
*
* @return the process exit value, -100 if the process is still running
*/
public static int getExitValue(Process process) {
try {
return process.exitValue();
} catch (IllegalThreadStateException itse) {
return -100;
}
}
/**
* Returns current user home directory handling Windows issues on JRE.
*
* @return current user home directory
*/
public static String getUserHome() {
// User home is cached for performances
if (cachedUserHomeDir != null) {
return cachedUserHomeDir;
}
/**
* We search first in USERPROFILE env directory before than user.home.
*
* But we give priority to user.home if it already contains a suitable jajuk
* collection to maintain backward compatibility
*
* See #1473 and
* http://bugs.sun.com/view_bug.do?bug_id=4787931
**/
if (StringUtils.isNotBlank(System.getenv("USERPROFILE"))) {
cachedUserHomeDir = System.getenv("USERPROFILE");
} else {
cachedUserHomeDir = System.getProperty("user.home");
}
return cachedUserHomeDir;
}
/**
* Convert a full regular Windows path to 8.3 DOS format
*
* @param longname the regular absolute path
*
* @return the shortname absolute path
*/
public static String getShortPathNameW(String longname) {
// Find the shortname .bat converter, create it if it doesn't yet exist
String shortname = null;
try {
File fileConverter = SessionService.getConfFileByPath(Const.FILE_FILENAME_CONVERTER);
if (!fileConverter.exists()
// Test that the converter version has not been updated
// IMPORTANT ! Don't forget to update the CONVERTER_FILE_SIZE constant if you change the
// script !
|| (fileConverter.exists() && fileConverter.length() != CONVERTER_FILE_SIZE)) {
FileWriter fw = new FileWriter(fileConverter);
fw.write("@echo off\n");
fw.write("dir /x \"%~s1\"");
fw.flush();
fw.close();
}
// these two quotes are required in the case where both directory and file are non-ascii
ProcessBuilder pc = new ProcessBuilder(fileConverter.getAbsolutePath(), "\"" + longname
+ "\"");
Process process = pc.start();
/*
* dir /x parsing : Sample output (in French but should work with any language):
*
* Le volume dans le lecteur D s'appelle Données
*
* Le numéro de série du volume est C880-0321
*
* Répertoire de D:\MESDOC~1\MAMUSI~1\FILES_~1\1F19~1
*
* 07/06/2010 21:49 <REP> .
*
* 07/06/2010 21:49 <REP> ..
*
* 07/06/2010 14:41 20 108 -(_)~1.MP3 µ×ùÕ│» - µÿÄÕñ®õ╗ÑÕ¥î (µ×ùÕ│»+µ│Õà ÆÕÉêÕö▒).mp3
*/
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = "";
String lineDirectory = null;
while ((line = br.readLine()) != null) {
String ext = UtilSystem.getExtension(new File(longname));
if (StringUtils.isNotBlank(line)) {
// Index of the file extension in short name
int indexExtension = line.indexOf(longname.substring(0, 3).toUpperCase());
if (line.endsWith(ext)) {
int indexEnd = line.indexOf(ext.toUpperCase());
int indexBegin = indexEnd;
// Find the previous space
while (line.charAt(indexBegin) != ' ') {
indexBegin--;
}
shortname = line.substring(indexBegin, indexEnd + 4).trim();
break;
} else if (indexExtension != -1) {
// We get parent directory full path in shortname thanks the %~s1 in the script
lineDirectory = line.substring(indexExtension, line.length()).trim();
}
}
}
shortname = lineDirectory + "\\" + shortname;
process.destroy();
} catch (Exception e) {
throw new JajukRuntimeException("Cannot convert the filename to 8.3 format", e);
}
return shortname;
}
/**
* Return whether the Desktop BROWSE action is actually supported
* Note this bug : http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6486393
* under KDE : isSupported returns true but we get an exception when actually browsing
*
* @return true, if checks if is browser supported
*/
public static boolean isBrowserSupported() {
// value stored for perf
if (browserSupported != null) {
return browserSupported;
}
// In server UT mode, just return false
if (GraphicsEnvironment.isHeadless()) {
return false;
} else {
if (Desktop.isDesktopSupported()) {
// If under Linux, check if we're under KDE because of a sun bug,
// see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6486393 and #1612
if (isUnderLinux() && isUnderKDE()) {
return false;
} else {
return (Desktop.getDesktop().isSupported(Desktop.Action.BROWSE));
}
} else {
return false;
}
}
}
/**
* Are we running in a KDE environment ?
*
* We check it by using ps command + a grep searching 'ksmserver' process.
*
* @return whether we are running in a KDE environment
*/
public static boolean isUnderKDE() {
return UtilSystem.UNDER_KDE;
}
}