org.python.pydev.core.REF.java Source code

Java tutorial

Introduction

Here is the source code for org.python.pydev.core.REF.java

Source

/**
 * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
 * Licensed under the terms of the Eclipse Public License (EPL).
 * Please see the license.txt included with this distribution for details.
 * Any modifications to this file must keep this entire header intact.
 */
/*
 * Created on Nov 12, 2004
 *
 * @author Fabio Zadrozny
 */
package org.python.pydev.core;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
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.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipFile;

import org.apache.commons.codec.binary.Base64;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.filebuffers.ITextFileBufferManager;
import org.eclipse.core.filebuffers.LocationKind;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.osgi.service.environment.Constants;
import org.python.pydev.core.callbacks.ICallback;
import org.python.pydev.core.docutils.StringUtils;
import org.python.pydev.core.log.Log;
import org.python.pydev.core.structure.FastStringBuffer;

/**
 * @author Fabio Zadrozny
 */
public class REF {

    /**
     * Regular expression for finding the encoding in a python file.
     */
    private static final Pattern ENCODING_PATTERN = Pattern.compile("coding[:=][\\s]*([-\\w.]+)");

    /**
     * @return true if the passed object has a field with the name passed.
     */
    public static boolean hasAttr(Object o, String attr) {
        try {
            o.getClass().getDeclaredField(attr);
        } catch (SecurityException e) {
            return false;
        } catch (NoSuchFieldException e) {
            return false;
        }
        return true;
    }

    /**
     * @return the field from a class that matches the passed attr name (or null if it couldn't be found)
     */
    public static Field getAttrFromClass(Class<? extends Object> c, String attr) {
        try {
            return c.getDeclaredField(attr);
        } catch (SecurityException e) {
        } catch (NoSuchFieldException e) {
        }
        return null;
    }

    /**
     * @return the field from a class that matches the passed attr name (or null if it couldn't be found)
     * @see #getAttrObj(Object, String) to get the actual value of the field.
     */
    public static Field getAttr(Object o, String attr) {
        try {
            return o.getClass().getDeclaredField(attr);
        } catch (SecurityException e) {
        } catch (NoSuchFieldException e) {
        }
        return null;
    }

    public static Object getAttrObj(Object o, String attr) {
        return getAttrObj(o, attr, false);
    }

    public static Object getAttrObj(Object o, String attr, boolean raiseExceptionIfNotAvailable) {
        return getAttrObj(o.getClass(), o, attr, raiseExceptionIfNotAvailable);
    }

    /**
     * @return the value of some attribute in the given object
     */
    public static Object getAttrObj(Class<? extends Object> c, Object o, String attr,
            boolean raiseExceptionIfNotAvailable) {
        try {
            Field field = REF.getAttrFromClass(c, attr);
            if (field != null) {
                //get it even if it's not public!
                if ((field.getModifiers() & Modifier.PUBLIC) == 0) {
                    field.setAccessible(true);
                }
                Object obj = field.get(o);
                return obj;
            }
        } catch (Exception e) {
            //ignore
            if (raiseExceptionIfNotAvailable) {
                throw new RuntimeException(e);
            }
        }
        return null;
    }

    /**
     * @param file the file we want to read
     * @return the contents of the file as a string
     */
    public static String getFileContents(File file) {
        return (String) getFileContentsCustom(file, String.class);
    }

    /**
     * @param file the file we want to read
     * @return the contents of the file as a string
     */
    public static Object getFileContentsCustom(File file, Class<? extends Object> returnType) {
        FileInputStream stream = null;
        try {
            stream = new FileInputStream(file);
            return getStreamContents(stream, null, null, returnType);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (stream != null)
                    stream.close();
            } catch (Exception e) {
                Log.log(e);
            }
        }
    }

    /**
     * Get the contents from a given stream.
     * @param returnType the class that specifies the return type of this method. 
     * If null, it'll return in the fastest possible way available.
     * Valid options are:
     *      String.class
     *      IDocument.class
     *      FastStringBuffer.class
     *      
     */
    private static Object getStreamContents(InputStream contentStream, String encoding, IProgressMonitor monitor,
            Class<? extends Object> returnType) throws IOException {

        Reader in = null;
        try {
            final int DEFAULT_FILE_SIZE = 15 * 1024;

            //discover how to actually read the passed input stream.
            if (encoding == null) {
                in = new BufferedReader(new InputStreamReader(contentStream), DEFAULT_FILE_SIZE);
            } else {
                try {
                    in = new BufferedReader(new InputStreamReader(contentStream, encoding), DEFAULT_FILE_SIZE);
                } catch (UnsupportedEncodingException e) {
                    Log.log(e);
                    //keep going without the encoding
                    in = new BufferedReader(new InputStreamReader(contentStream), DEFAULT_FILE_SIZE);
                }
            }

            //fill a buffer with the contents
            FastStringBuffer buffer = new FastStringBuffer(DEFAULT_FILE_SIZE);
            char[] readBuffer = new char[2048];
            int n = in.read(readBuffer);
            while (n > 0) {
                if (monitor != null && monitor.isCanceled()) {
                    return null;
                }

                buffer.append(readBuffer, 0, n);
                n = in.read(readBuffer);
            }

            //return it in the way specified by the user
            if (returnType == null || returnType == FastStringBuffer.class) {
                return buffer;

            } else if (returnType == IDocument.class) {
                Document doc = new Document(buffer.toString());
                return doc;

            } else if (returnType == String.class) {
                return buffer.toString();

            } else {
                throw new RuntimeException("Don't know how to handle return type: " + returnType);
            }

        } finally {
            try {
                if (in != null)
                    in.close();
            } catch (Exception e) {
                Log.log(e);
            }
        }
    }

    /**
     * @param o the object we want as a string
     * @return the string representing the object as base64
     */
    public static String getObjAsStr(Object o) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            ObjectOutputStream stream = new ObjectOutputStream(out);
            stream.writeObject(o);
            stream.close();
        } catch (Exception e) {
            Log.log(e);
            throw new RuntimeException(e);
        }

        return new String(encodeBase64(out));
    }

    /**
     * @return the contents of the passed ByteArrayOutputStream as a byte[] encoded with base64.
     */
    public static byte[] encodeBase64(ByteArrayOutputStream out) {
        byte[] byteArray = out.toByteArray();
        return encodeBase64(byteArray);
    }

    /**
     * @return the contents of the passed byteArray[] as a byte[] encoded with base64.
     */
    public static byte[] encodeBase64(byte[] byteArray) {
        return Base64.encodeBase64(byteArray);
    }

    /**
     * This method loads the contents of an object that was serialized.
     * 
     * @param readFromFileMethod see {@link #getStrAsObj(String, ICallback)}
     * @param input is the input stream that contains the serialized object
     * 
     * @return the object that was previously serialized in the passed input stream.
     */
    public static Object readFromInputStreamAndCloseIt(ICallback<Object, ObjectInputStream> readFromFileMethod,
            InputStream input) {

        ObjectInputStream in = null;
        Object o = null;
        try {
            try {
                in = new ObjectInputStream(input);
                o = readFromFileMethod.call(in);
            } finally {
                if (in != null) {
                    in.close();
                }
                input.close();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return o;
    }

    /**
     * Decodes some string that was encoded as base64
     */
    public static byte[] decodeBase64(String persisted) {
        return Base64.decodeBase64(persisted.getBytes());
    }

    /**
     * Appends the contents of the passed string to the given file.
     */
    public static void appendStrToFile(String str, String file) {
        try {
            FileOutputStream stream = new FileOutputStream(file, true);
            try {
                stream.write(str.getBytes());
            } finally {
                stream.close();
            }
        } catch (FileNotFoundException e) {
            Log.log(e);
        } catch (IOException e) {
            Log.log(e);
        }
    }

    /**
     * Writes the contents of the passed string to the given file.
     */
    public static void writeStrToFile(String str, String file) {
        writeStrToFile(str, new File(file));
    }

    /**
     * Writes the contents of the passed string to the given file.
     */
    public static void writeStrToFile(String str, File file) {
        try {
            FileOutputStream stream = new FileOutputStream(file);
            try {
                stream.write(str.getBytes());
            } finally {
                stream.close();
            }
        } catch (FileNotFoundException e) {
            Log.log(e);
        } catch (IOException e) {
            Log.log(e);
        }
    }

    /**
     * Writes the contents of the passed string to the given file.
     */
    public static void writeToFile(Object o, File file) {
        try {
            OutputStream out = new FileOutputStream(file);
            writeToStreamAndCloseIt(o, out);
        } catch (Exception e) {
            Log.log(e);
        }
    }

    /**
     * Serializes some object to the given stream
     * 
     * @param o the object to be written to some stream
     * @param out the output stream to be used
     */
    public static void writeToStreamAndCloseIt(Object o, OutputStream out) throws IOException {
        //change: checks if we have a buffered output stream (if we don't, one will be provided)
        OutputStream b = null;
        if (out instanceof BufferedOutputStream || out instanceof ByteArrayOutputStream) {
            b = out;
        } else {
            b = new BufferedOutputStream(out);
        }

        try {

            ObjectOutputStream stream = new ObjectOutputStream(b);
            stream.writeObject(o);
            stream.close();
        } catch (Exception e) {
            Log.log(e);
            throw new RuntimeException(e);
        } finally {
            b.close();
        }
    }

    /**
     * Reads some object from a file (an object that was previously serialized)
     * 
     * Important: can only deserialize objects that are defined in this plugin -- 
     * see {@link #getStrAsObj(String, ICallback)} if you want to deserialize objects defined in another plugin.
     * 
     * @param file the file from where we should read
     * @return the object that was read (or null if some error happened while reading)
     */
    public static Object readFromFile(File file) {
        try {
            InputStream in = new BufferedInputStream(new FileInputStream(file));
            try {
                ObjectInputStream stream = new ObjectInputStream(in);
                try {
                    Object o = stream.readObject();
                    return o;
                } finally {
                    stream.close();
                }
            } finally {
                in.close();
            }
        } catch (Exception e) {
            Log.log(e);
            return null;
        }

    }

    /**
     * Get the absolute path in the filesystem for the given file.
     * 
     * @param f the file we're interested in
     * 
     * @return the absolute (canonical) path to the file
     */
    public static String getFileAbsolutePath(String f) {
        return getFileAbsolutePath(new File(f));
    }

    /**
     * @see #getFileAbsolutePath(String)
     */
    public static String getFileAbsolutePath(File f) {
        try {
            return f.getCanonicalPath();
        } catch (IOException e) {
            return f.getAbsolutePath();
        }
    }

    /**
     * Calls a method for an object
     * 
     * @param obj the object with the method we want to call
     * @param name the method name
     * @param args the arguments received for the call
     * @return the return of the method
     * 
     * @throws RuntimeException if the object could not be invoked
     */
    public static Object invoke(Object obj, String name, Object... args) {
        //the args are not checked for the class because if a subclass is passed, the method is not correctly gotten
        //another method might do it...
        Method m = findMethod(obj, name, args);
        return invoke(obj, m, args);
    }

    /**
     * @see #invoke(Object, String, Object...)
     */
    public static Object invoke(Object obj, Method m, Object... args) {
        try {
            return m.invoke(obj, args);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @return a method that has the given name and arguments
     * @throws RuntimeException if the method could not be found
     */
    public static Method findMethod(Object obj, String name, Object... args) {
        return findMethod(obj.getClass(), name, args);
    }

    /**
     * @return a method that has the given name and arguments
     * @throws RuntimeException if the method could not be found
     */
    public static Method findMethod(Class<? extends Object> class_, String name, Object... args) {
        try {
            Method[] methods = class_.getMethods();
            for (Method method : methods) {

                Class<? extends Object>[] parameterTypes = method.getParameterTypes();
                if (method.getName().equals(name) && parameterTypes.length == args.length) {
                    //check the parameters
                    int i = 0;
                    for (Class<? extends Object> param : parameterTypes) {
                        if (!param.isInstance(args[i])) {
                            continue;
                        }
                        i++;
                    }
                    //invoke it
                    return method;
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        throw new RuntimeException("The method with name: " + name
                + " was not found (or maybe it was found but the parameters didn't match).");
    }

    /**
     * Characters that files in the filesystem cannot have.
     */
    public static char[] INVALID_FILESYSTEM_CHARS = { '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '[', ']',
            '{', '}', '=', '+', '.', ' ', '`', '~', '\'', '"', ',', ';' };

    /**
     * Determines if we're in tests: When in tests, some warnings may be supressed.
     */
    public static boolean IN_TESTS = false;

    /**
     * @return a valid name for a project so that the returned name can be used to create a file in the filesystem
     */
    public static String getValidProjectName(IProject project) {
        String name = project.getName();

        for (char c : INVALID_FILESYSTEM_CHARS) {
            name = name.replace(c, '_');
        }

        return name;
    }

    /**
     * Makes an equal comparison taking into account that one of the parameters may be null.
     */
    public static boolean nullEq(Object o1, Object o2) {
        if (o1 == null && o2 == null) {
            return true;
        }
        if (o1 == null || o2 == null) {
            return false;
        }
        return o1.equals(o2);
    }

    public static IDocument getDocFromFile(java.io.File f) throws IOException {
        return getDocFromFile(f, true);
    }

    /**
     * @return a string with the contents from a path within a zip file.
     */
    public static String getStringFromZip(File f, String pathInZip) throws Exception {
        return (String) getCustomReturnFromZip(f, pathInZip, String.class);
    }

    /**
     * @return a document with the contents from a path within a zip file.
     */
    public static IDocument getDocFromZip(File f, String pathInZip) throws Exception {
        return (IDocument) getCustomReturnFromZip(f, pathInZip, IDocument.class);
    }

    /**
     * @param f the zip file that should be opened
     * @param pathInZip the path within the zip file that should be gotten
     * @param returnType the class that specifies the return type of this method. 
     * If null, it'll return in the fastest possible way available.
     * Valid options are:
     *      String.class
     *      IDocument.class
     *      FastStringBuffer.class
     * 
     * @return an object with the contents from a path within a zip file, having the return type
     * of the object specified by the parameter returnType.
     */
    public static Object getCustomReturnFromZip(File f, String pathInZip, Class<? extends Object> returnType)
            throws Exception {

        ZipFile zipFile = new ZipFile(f, ZipFile.OPEN_READ);
        try {
            InputStream inputStream = zipFile.getInputStream(zipFile.getEntry(pathInZip));
            try {
                return REF.getStreamContents(inputStream, null, null, returnType);
            } finally {
                inputStream.close();
            }
        } finally {
            zipFile.close();
        }
    }

    /**
     * @return a string with the contents of the passed file
     */
    public static String getStringFromFile(java.io.File f, boolean loadIfNotInWorkspace) throws IOException {
        return (String) getCustomReturnFromFile(f, loadIfNotInWorkspace, String.class);
    }

    /**
     * @return the document given its 'filesystem' file
     */
    public static IDocument getDocFromFile(java.io.File f, boolean loadIfNotInWorkspace) throws IOException {
        return (IDocument) getCustomReturnFromFile(f, loadIfNotInWorkspace, IDocument.class);
    }

    /**
     * @param f the file from where we want to get the contents
     * @param returnType the class that specifies the return type of this method. 
     * If null, it'll return in the fastest possible way available.
     * Valid options are:
     *      String.class
     *      IDocument.class
     *      FastStringBuffer.class
     *      
     * 
     * @return an object with the contents from the file, having the return type
     * of the object specified by the parameter returnType.
     */
    public static Object getCustomReturnFromFile(java.io.File f, boolean loadIfNotInWorkspace,
            Class<? extends Object> returnType) throws IOException {

        IPath path = Path.fromOSString(getFileAbsolutePath(f));
        IDocument doc = getDocFromPath(path);

        if (doc != null) {
            if (returnType == null || returnType == IDocument.class) {
                return doc;

            } else if (returnType == String.class) {
                return doc.get();

            } else if (returnType == FastStringBuffer.class) {
                return new FastStringBuffer(doc.get(), 16);

            } else {
                throw new RuntimeException("Don't know how to treat requested return type: " + returnType);
            }
        }

        if (doc == null && loadIfNotInWorkspace) {
            FileInputStream stream = new FileInputStream(f);
            try {
                String encoding = getPythonFileEncoding(f);
                return getStreamContents(stream, encoding, null, returnType);
            } finally {
                try {
                    if (stream != null)
                        stream.close();
                } catch (Exception e) {
                    Log.log(e);
                }
            }
        }
        return doc;
    }

    /**
     * @param path the path we're interested in
     * @return a file buffer to be used.
     */
    @SuppressWarnings("deprecation")
    public static ITextFileBuffer getBufferFromPath(IPath path) {
        try {
            try {

                //eclipse 3.3 has a different interface
                ITextFileBufferManager textFileBufferManager = ITextFileBufferManager.DEFAULT;
                if (textFileBufferManager != null) {//we don't have it in tests
                    ITextFileBuffer textFileBuffer = textFileBufferManager.getTextFileBuffer(path,
                            LocationKind.LOCATION);

                    if (textFileBuffer != null) { //we don't have it when it is not properly refreshed
                        return textFileBuffer;
                    }
                }

            } catch (Throwable e) {//NoSuchMethod/NoClassDef exception 
                if (e instanceof ClassNotFoundException || e instanceof LinkageError
                        || e instanceof NoSuchMethodException || e instanceof NoSuchMethodError
                        || e instanceof NoClassDefFoundError) {

                    ITextFileBufferManager textFileBufferManager = FileBuffers.getTextFileBufferManager();

                    if (textFileBufferManager != null) {//we don't have it in tests
                        ITextFileBuffer textFileBuffer = textFileBufferManager.getTextFileBuffer(path);

                        if (textFileBuffer != null) { //we don't have it when it is not properly refreshed
                            return textFileBuffer;
                        }
                    }
                } else {
                    throw e;
                }

            }
            return null;

        } catch (Throwable e) {
            //private static final IWorkspaceRoot WORKSPACE_ROOT= ResourcesPlugin.getWorkspace().getRoot();
            //throws an error and we don't even have access to the FileBuffers class in tests
            if (!IN_TESTS) {
                Log.log("Unable to get doc from text file buffer");
            }
            return null;
        }
    }

    /**
     * @return null if it was unable to get the document from the path (this may happen if it was not refreshed).
     * Or the document that represents the file
     */
    public static IDocument getDocFromPath(IPath path) {
        ITextFileBuffer buffer = getBufferFromPath(path);
        if (buffer != null) {
            return buffer.getDocument();
        }
        return null;
    }

    /**
     * Returns a document, created with the contents of a resource (first tries to get from the 'FileBuffers',
     * and if that fails, it creates one reading the file.
     */
    public static IDocument getDocFromResource(IResource resource) {
        IProject project = resource.getProject();
        if (project != null && resource instanceof IFile && resource.exists()) {

            IFile file = (IFile) resource;

            try {
                if (file.exists() && !file.isSynchronized(IResource.DEPTH_ZERO)) {
                    file.refreshLocal(IResource.DEPTH_ZERO, new NullProgressMonitor());
                }
                IPath path = file.getFullPath();

                IDocument doc = getDocFromPath(path);
                if (doc == null) {
                    //can this actually happen?... yeap, it can
                    doc = (IDocument) REF.getStreamContents(file.getContents(true), null, null, IDocument.class);
                }
                return doc;
            } catch (CoreException e) {
                //it may stop existing from the initial exists check to the getContents call
                return null;
            } catch (Exception e) {
                Log.log(e);
            }
        }
        return null;
    }

    /**
     * The encoding declared in the document is returned (according to the PEP: http://www.python.org/doc/peps/pep-0263/)
     */
    public static String getPythonFileEncoding(IDocument doc, String fileLocation)
            throws IllegalCharsetNameException {
        Reader inputStreamReader = new StringReader(doc.get());
        return getPythonFileEncoding(inputStreamReader, fileLocation);
    }

    /**
     * The encoding declared in the file is returned (according to the PEP: http://www.python.org/doc/peps/pep-0263/)
     */
    public static String getPythonFileEncoding(File f) throws IllegalCharsetNameException {
        try {
            final FileInputStream fileInputStream = new FileInputStream(f);
            try {
                Reader inputStreamReader = new InputStreamReader(new BufferedInputStream(fileInputStream));
                String pythonFileEncoding = getPythonFileEncoding(inputStreamReader, f.getAbsolutePath());
                return pythonFileEncoding;
            } finally {
                //NOTE: the reader will be closed at 'getPythonFileEncoding'. 
                try {
                    fileInputStream.close();
                } catch (Exception e) {
                    Log.log(e);
                }
            }
        } catch (FileNotFoundException e) {
            return null;
        }
    }

    /**
     * This is usually what's on disk
     */
    public static String BOM_UTF8 = new String(new char[] { 0xEF, 0xBB, 0xBF });

    /**
     * When we convert a string from the disk to a java string, if it had an UTF-8 BOM, it'll have that BOM converted
     * to this BOM. See: org.python.pydev.parser.PyParser27Test.testBom()
     */
    public static String BOM_UNICODE = new String(new char[] { 0xFEFF });

    /**
     * The encoding declared in the reader is returned (according to the PEP: http://www.python.org/doc/peps/pep-0263/)
     * -- may return null
     * 
     * Will close the reader.
     * @param fileLocation the file we want to get the encoding from (just passed for giving a better message 
     * if it fails -- may be null).
     */
    public static String getPythonFileEncoding(Reader inputStreamReader, String fileLocation)
            throws IllegalCharsetNameException {

        String ret = null;
        BufferedReader reader = new BufferedReader(inputStreamReader);
        try {
            String lEnc = null;

            //pep defines that coding must be at 1st or second line: http://www.python.org/doc/peps/pep-0263/
            String l1 = reader.readLine();
            if (l1 != null) {
                //Special case -- determined from the python docs:
                //http://docs.python.org/reference/lexical_analysis.html#encoding-declarations
                //We can return promptly in this case as utf-8 should be always valid.
                if (l1.startsWith(BOM_UTF8)) {
                    return "utf-8";
                }

                if (l1.indexOf("coding") != -1) {
                    lEnc = l1;
                }
            }

            if (lEnc == null) {
                String l2 = reader.readLine();

                //encoding must be specified in first or second line...
                if (l2 != null && l2.indexOf("coding") != -1) {
                    lEnc = l2;
                } else {
                    ret = null;
                }
            }

            if (lEnc != null) {
                lEnc = lEnc.trim();
                if (lEnc.length() == 0) {
                    ret = null;

                } else if (lEnc.charAt(0) == '#') { //it must be a comment line

                    Matcher matcher = ENCODING_PATTERN.matcher(lEnc);
                    if (matcher.find()) {
                        ret = matcher.group(1).trim();
                    }
                }
            }
        } catch (IOException e) {
            Log.log(e);
        } finally {
            try {
                reader.close();
            } catch (IOException e1) {
            }
        }
        ret = getValidEncoding(ret, fileLocation);
        return ret;
    }

    /**
     * @param fileLocation may be null
     */
    /*package*/static String getValidEncoding(String ret, String fileLocation) {
        if (ret == null) {
            return ret;
        }
        final String lower = ret.trim().toLowerCase();
        if (lower.startsWith("latin")) {
            if (lower.indexOf("1") != -1) {
                return "latin1"; //latin1
            }
        }
        if (lower.equals("iso-latin-1-unix")) {
            return "latin1"; //handle case from python libraries
        }
        try {
            if (!Charset.isSupported(ret)) {
                if (LOG_ENCODING_ERROR) {
                    if (fileLocation != null) {
                        if ("uft-8".equals(ret) && fileLocation.endsWith("bad_coding.py")) {
                            return null; //this is an expected error in the python library.
                        }
                    }
                    String msg = "The encoding found: >>" + ret + "<< on " + fileLocation
                            + " is not a valid encoding.";
                    Log.log(IStatus.ERROR, msg, new UnsupportedEncodingException(msg));
                }
                return null; //ok, we've been unable to make it supported (better return null than an unsupported encoding).
            }
            return ret;
        } catch (IllegalCharsetNameException ex) {
            if (LOG_ENCODING_ERROR) {
                String msg = "The encoding found: >>" + ret + "<< on " + fileLocation + " is not a valid encoding.";
                Log.log(IStatus.ERROR, msg, ex);
            }
        }
        return null;
    }

    /**
     * Useful to silent it on tests
     */
    public static boolean LOG_ENCODING_ERROR = true;

    /**
     * Start null... filled on 1st request.
     * 
     * Currently we only care for: windows, mac os or linux (if we need some other special support,
     * this could be improved).
     */
    public static Integer platform;
    public static int WINDOWS = 1;
    public static int MACOS = 2;
    public static int LINUX = 3;

    /**
     * @return whether we are in windows or not
     */
    public static boolean isWindowsPlatform() {
        discoverPlatform();
        return platform == WINDOWS;
    }

    /**
     * @return whether we are in MacOs or not
     */
    public static boolean isMacOsPlatform() {
        discoverPlatform();
        return platform == MACOS;
    }

    private static void discoverPlatform() {
        if (platform == null) {
            try {
                String os = Platform.getOS();
                if (os.equals(Constants.OS_WIN32)) {
                    platform = WINDOWS;
                } else if (os.equals(Constants.OS_MACOSX)) {
                    platform = MACOS;
                } else {
                    platform = LINUX;
                }

            } catch (NullPointerException e) {
                String env = System.getProperty("os.name").toLowerCase();
                if (env.indexOf("win") != -1) {
                    platform = WINDOWS;
                } else if (env.startsWith("mac os")) {
                    platform = MACOS;
                } else {
                    platform = LINUX;
                }
            }
        }
    }

    /**
     * Copy a file from one place to another.
     * 
     * Example from: http://www.exampledepot.com/egs/java.nio/File2File.html
     * 
     * @param srcFilename the source file
     * @param dstFilename the destination
     */
    public static void copyFile(String srcFilename, String dstFilename) {
        FileChannel srcChannel = null;
        FileChannel dstChannel = null;
        try {
            // Create channel on the source
            srcChannel = new FileInputStream(srcFilename).getChannel();

            // Create channel on the destination
            dstChannel = new FileOutputStream(dstFilename).getChannel();

            // Copy file contents from source to destination
            dstChannel.transferFrom(srcChannel, 0, srcChannel.size());

        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            // Close the channels
            if (srcChannel != null) {
                try {
                    srcChannel.close();
                } catch (IOException e) {
                    Log.log(e);
                }
            }
            if (dstChannel != null) {
                try {
                    dstChannel.close();
                } catch (IOException e) {
                    Log.log(e);
                }
            }
        }

    }

    /**
     * Copies (recursively) the contents of one directory to another.
     * 
     * @param filter: a callback that can be used to choose files that should not be copied. 
     * If null, all files are copied, otherwise, if filter returns true, it won't be copied, and
     * if it returns false, it will be copied
     * 
     * @param changeFileContents: a callback that's called before copying any file, so that clients
     * have a change of changing the file contents to be written.
     */
    public static void copyDirectory(File srcPath, File dstPath, ICallback<Boolean, File> filter,
            ICallback<String, String> changeFileContents) throws IOException {
        if (srcPath.isDirectory()) {
            if (filter != null && filter.call(srcPath)) {
                return;
            }
            if (!dstPath.exists()) {
                dstPath.mkdir();
            }
            String files[] = srcPath.list();
            for (int i = 0; i < files.length; i++) {
                copyDirectory(new File(srcPath, files[i]), new File(dstPath, files[i]), filter, changeFileContents);
            }
        } else {
            if (!srcPath.exists()) {
                //do nothing
            } else {
                if (filter != null && filter.call(srcPath)) {
                    return;
                }
                if (changeFileContents == null) {
                    copyFile(srcPath.getAbsolutePath(), dstPath.getAbsolutePath());
                } else {
                    String fileContents = getFileContents(srcPath);
                    fileContents = changeFileContents.call(fileContents);
                    writeStrToFile(fileContents, dstPath);
                }
            }
        }
    }

    /**
     * This method will try to create a backup file of the passed file.
     * @param file this is the file we want to copy as the backup.
     * @return true if it was properly copied and false otherwise.
     */
    public static boolean createBackupFile(File file) {
        if (file != null && file.isFile()) {
            File parent = file.getParentFile();
            if (parent.isDirectory()) {
                String[] list = parent.list();
                HashSet<String> set = new HashSet<String>();
                set.addAll(Arrays.asList(list));
                String initialName = file.getName();
                initialName += ".bak";
                String name = initialName;
                int i = 0;
                while (set.contains(name)) {
                    name = initialName + i;
                    i++;
                }
                copyFile(file.getAbsolutePath(), new File(parent, name).getAbsolutePath());
                return true;
            }
        }
        return false;
    }

    /**
     * Log with base is missing in java!
     */
    public static double log(double a, double base) {
        return Math.log(a) / Math.log(base);
    }

    public static void print(Object... objects) {
        System.out.println(StringUtils.join(" ", objects));
    }

    private static final Map<File, Set<String>> alreadyReturned = new HashMap<File, Set<String>>();
    private static Object lockTempFiles = new Object();

    public static File getTempFileAt(File parentDir, String prefix) {
        return getTempFileAt(parentDir, prefix, "");
    }

    /**
     * @param extension the extension (i.e.: ".py")
     * @return
     */
    public static File getTempFileAt(File parentDir, String prefix, String extension) {
        synchronized (lockTempFiles) {
            Assert.isTrue(parentDir.isDirectory());
            Set<String> current = alreadyReturned.get(parentDir);
            if (current == null) {
                current = new HashSet<String>();
                alreadyReturned.put(parentDir, current);
            }
            current.addAll(getFilesStartingWith(parentDir, prefix));

            FastStringBuffer buf = new FastStringBuffer();

            for (long i = 0; i < Long.MAX_VALUE; i++) {
                String v = buf.clear().append(prefix).append(i).append(extension).toString();
                if (current.contains(v)) {
                    continue;
                }
                File file = new File(parentDir, v);
                if (!file.exists()) {
                    current.add(file.getName());
                    return file;
                }
            }
            return null;
        }
    }

    public static HashSet<String> getFilesStartingWith(File parentDir, String prefix) {
        String[] list = parentDir.list();
        HashSet<String> hashSet = new HashSet<String>();
        for (String string : list) {
            if (string.startsWith(prefix)) {
                hashSet.add(string);
            }
        }
        return hashSet;
    }

    public static void clearTempFilesAt(File parentDir, String prefix) {
        synchronized (lockTempFiles) {
            try {
                Assert.isTrue(parentDir.isDirectory());
                String[] list = parentDir.list();
                for (String string : list) {
                    if (string.startsWith(prefix)) {
                        String integer = string.substring(prefix.length());
                        try {
                            Integer.parseInt(integer);
                            try {
                                new File(parentDir, string).delete();
                            } catch (Exception e) {
                                //ignore
                            }
                        } catch (NumberFormatException e) {
                            //ignore (not a file we generated)
                        }
                    }
                }
                alreadyReturned.remove(parentDir);
            } catch (Throwable e) {
                Log.log(e); //never give an error here, just log it.
            }
        }
    }

    /**
     * Yes, this will delete everything under a directory. Use with care!
     */
    public static void deleteDirectoryTree(File directory) throws IOException {
        if (!directory.exists()) {
            return;
        }
        File[] files = directory.listFiles();
        if (files != null) {

            for (int i = 0; i < files.length; ++i) {
                File f = files[i];

                if (f.isDirectory()) {
                    deleteDirectoryTree(f);
                } else {
                    deleteFile(f);
                }
            }
        }
        if (!directory.delete()) {
            throw new IOException("Delete operation failed when deleting: " + directory);
        }
    }

    public static void deleteFile(File file) throws IOException {
        if (!file.exists())
            throw new FileNotFoundException(file.getAbsolutePath());

        if (!file.delete()) {
            throw new IOException("Delete operation failed when deleting: " + file);
        }
    }

    public static void openDirectory(File dir) {
        //Note: on java 6 it seems we could use java.awt.Desktop.
        String executable = getOpenDirectoryExecutable();
        if (executable != null) {
            try {
                if (executable.equals("kfmclient")) {
                    //Yes, KDE needs an exec after kfmclient.
                    Runtime.getRuntime().exec(new String[] { executable, "exec", dir.toString() }, null, dir);

                } else {
                    Runtime.getRuntime().exec(new String[] { executable, dir.toString() }, null, dir);
                }
            } catch (Throwable e) {
                Log.log(e);
            }
        }
    }

    private static String openDirExecutable = null;
    private final static String OPEN_DIR_EXEC_NOT_AVAILABLE = "NOT_AVAILABLE";

    private static String getOpenDirectoryExecutable() {
        if (openDirExecutable == null) {
            if (REF.isWindowsPlatform()) {
                openDirExecutable = "explorer";
                return openDirExecutable;

            }

            if (REF.isMacOsPlatform()) {
                openDirExecutable = "open";
                return openDirExecutable;
            }

            try {
                String env = System.getenv("DESKTOP_LAUNCH");
                if (env != null && env.trim().length() > 0) {
                    openDirExecutable = env;
                    return openDirExecutable;
                }
            } catch (Throwable e) {
                //ignore -- it seems not all java versions have System.getenv
            }

            try {
                Map<String, String> env = System.getenv();
                if (env.containsKey("KDE_FULL_SESSION") || env.containsKey("KDE_MULTIHEAD")) {
                    openDirExecutable = "kfmclient";
                    return openDirExecutable;
                }
                if (env.containsKey("GNOME_DESKTOP_SESSION_ID") || env.containsKey("GNOME_KEYRING_SOCKET")) {
                    openDirExecutable = "gnome-open";
                    return openDirExecutable;
                }
            } catch (Throwable e) {
                //ignore -- it seems not all java versions have System.getenv
            }

            //If it hasn't returned until now, we don't know about it!
            openDirExecutable = OPEN_DIR_EXEC_NOT_AVAILABLE;
        }
        //Yes, we can compare with identity since we know which string we've set.
        if (openDirExecutable == OPEN_DIR_EXEC_NOT_AVAILABLE) {
            return null;
        }
        return openDirExecutable;
    }

    public static boolean getSupportsOpenDirectory() {
        return getOpenDirectoryExecutable() != null;
    }
}