net.sf.fjep.fatjar.popup.actions.BuildFatJar.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.fjep.fatjar.popup.actions.BuildFatJar.java

Source

/*******************************************************************************
 * Copyright (c) 2004 Ferenc Hechler - ferenc_hechler@users.sourceforge.net
 * 
 * This file is part of the Fat Jar Eclipse Plug-In
 *
 * The Fat Jar Eclipse Plug-In 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 (at your option) any later version.
 * 
 * The Fat Jar Eclipse Plug-In 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 the Fat Jar Eclipse Plug-In;
 * if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *  
 *******************************************************************************/
package net.sf.fjep.fatjar.popup.actions;

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.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.Vector;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;

import net.sf.fjep.fatjar.builder.IFileSystemElement;
import net.sf.fjep.fatjar.builder.IFileSystemSource;
import net.sf.fjep.fatjar.extensionpoints.IJarUtilFactory;
import net.sf.fjep.fatjar.preferences.FatJarPreferencePage;
import net.sf.fjep.fatjar.wizards.export.FJExpWizard;
import net.sf.fjep.fatjar.wizards.export.JProjectConfiguration;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Preferences;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.ProgressMonitorDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IActionDelegate;
import org.eclipse.ui.IObjectActionDelegate;
import org.eclipse.ui.IWorkbenchPart;

import com.google.common.base.Strings;

import thahn.java.agui.ide.eclipse.project.AguiConstants;
import thahn.java.agui.ide.eclipse.project.BaseProjectHelper;
import thahn.java.agui.ide.eclipse.wizard.AguiPlugin;

public class BuildFatJar implements IObjectActionDelegate {

    private final static int PROGRESS_CLEAN = 50;

    private final static int PROGRESS_COLLECT = 1000;

    private final static int PROGRESS_PACK = 300;

    private final static char PATH_SEPARATOR = '.';

    // hack for changing current properties file...
    public static String absPropertiesFilename = null;

    protected IStructuredSelection lastSelection = null;

    /**
     * Constructor for Action1.
     */
    public BuildFatJar() {
        super();
    }

    /**
     * @see IObjectActionDelegate#setActivePart(IAction, IWorkbenchPart)
     */
    public void setActivePart(IAction action, IWorkbenchPart targetPart) {
    }

    /**
     * @see IActionDelegate#run(IAction)
     */
    public void run(IAction action) {
        if ((lastSelection != null) && !lastSelection.isEmpty()) {
            Object sel = lastSelection.getFirstElement();
            if (sel instanceof IJavaProject) {
                absPropertiesFilename = null;
                if (action.getId().equals("net.sf.fjep.fatjar.buildFatJar"))
                    runBuildFatJar(lastSelection.toList());
                else
                    System.err.println("unknown action-id for BuildFatJar: " + action.getId());
            } else if (sel instanceof IFile) {
                for (Iterator iter = lastSelection.iterator(); iter.hasNext();) {
                    Object ifile = iter.next();
                    if (ifile instanceof IFile)
                        buildFatJarConditional(action, ifile);
                }
            }
        }

    }

    private void buildFatJarConditional(IAction action, Object sel) {
        IFile settings = (IFile) sel;
        String projectName = settings.getProject().getName();
        IJavaProject jproject = JavaModelManager.getJavaModelManager().getJavaModel().getJavaProject(projectName);
        absPropertiesFilename = settings.getLocation().toOSString();
        if (action.getId().equals("net.sf.fjep.fatjar.buildFatJarFromSettings")) {
            List jplist = new ArrayList();
            jplist.add(jproject);
            runBuildFatJar(jplist);
        } else if (action.getId().equals("net.sf.fjep.fatjar.quickBuildFatJarFromSettings")) {
            // TODO: Add collections for quick build
            runQuickBuildFatJarFromSettings(jproject);
        } else {
            System.err.println("unknown action-id for BuildFatJar: " + action.getId());
        }
    }

    /**
     * @param jproject
     * @param settings
     */
    private void runQuickBuildFatJarFromSettings(IJavaProject jproject) {

        try {
            doQuickBuildFatJar(jproject);
        } catch (Exception e) {
            e.printStackTrace();
            Shell shell = new Shell();
            MessageDialog.openInformation(shell, "Fat Jar Plug-In ERROR", e.getMessage());
        }
    }

    protected void runBuildFatJar(List jprojects) {

        boolean ok = false;
        try {
            ok = doBuildFatJar(jprojects);
        } catch (Exception e) {
            e.printStackTrace();
            Shell shell = new Shell();
            MessageDialog.openInformation(shell, "Fat Jar Plug-In ERROR", e.getMessage());
        }
    }

    public void runBuildConfiguredFatJar(IJavaProject jproject, Properties props, Set excludes,
            ArrayList includes) {

        // Create a progress bar
        Shell shell = new Shell();
        ProgressMonitorDialog progressmonitordialog = new ProgressMonitorDialog(shell);
        progressmonitordialog.open();
        IProgressMonitor iprogressmonitor = progressmonitordialog.getProgressMonitor();
        iprogressmonitor.beginTask("Build Fat jar",
                PROGRESS_CLEAN + PROGRESS_COLLECT + PROGRESS_PACK + PROGRESS_CLEAN);

        // Make One-JAR?
        boolean onejar = new Boolean(props.getProperty(AguiPlugin.ONEJAR_CHECKBOX)).booleanValue();
        String expand = props.getProperty(AguiPlugin.ONEJAR_EXPAND);

        String jarfile = null;
        try {
            saveConfig(jproject, props);

            doCleanFatJar(jproject, iprogressmonitor);
            doCollectFatJar(jproject, iprogressmonitor, excludes, includes, onejar, expand);
            jarfile = doPackFatJar(jproject, iprogressmonitor);
            doCleanFatJar(jproject, iprogressmonitor);
            try {
                jproject.getProject().refreshLocal(IResource.DEPTH_ONE, null);
            } catch (CoreException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
            MessageDialog.openInformation(shell, "Fat Jar Plug-In ERROR", e.getMessage());
        }

        progressmonitordialog.close();
        MessageDialog.openInformation(shell, "Fat Jar Plug-In", "built " + jarfile);
    }

    private boolean doBuildFatJar(List jprojects) {

        boolean ok = false;
        Shell shell = new Shell();
        JProjectConfiguration jprojectConf = new JProjectConfiguration(jprojects);
        jprojectConf.setPropertiesFilename(absPropertiesFilename);
        FJExpWizard wizard = new FJExpWizard(jprojectConf);
        ok = wizard.execute(shell);
        if (ok) {
            try {
                for (Iterator iter = jprojects.iterator(); iter.hasNext();) {
                    IJavaProject jproject = (IJavaProject) iter.next();
                    jproject.getProject().refreshLocal(IResource.DEPTH_ONE, null);
                }
            } catch (CoreException e) {
                e.printStackTrace();
            }
        }
        return ok;
    }

    // TODO: beta1 - change to launchconfig
    public void doQuickBuildFatJar(IJavaProject jproject) {
        Properties props = getFatJarProperties(jproject);
        Set excludes = getExcludes(jproject, props.getProperty("excludes"));
        ArrayList includes = getIncludes(jproject, props.getProperty("includes"));

        // Create a progress bar
        Shell shell = new Shell();
        ProgressMonitorDialog progressmonitordialog = new ProgressMonitorDialog(shell);
        progressmonitordialog.open();
        IProgressMonitor iprogressmonitor = progressmonitordialog.getProgressMonitor();
        iprogressmonitor.beginTask("Quick Build Fat jar",
                PROGRESS_CLEAN + PROGRESS_COLLECT + PROGRESS_PACK + PROGRESS_CLEAN);

        // Make One-JAR?
        boolean onejar = new Boolean(props.getProperty(AguiPlugin.ONEJAR_CHECKBOX)).booleanValue();
        String expand = props.getProperty(AguiPlugin.ONEJAR_EXPAND);

        String jarfile = null;
        try {
            // saveConfig(jproject, props);

            doCleanFatJar(jproject, iprogressmonitor);
            doCollectFatJar(jproject, iprogressmonitor, excludes, includes, onejar, expand);
            jarfile = doPackFatJar(jproject, iprogressmonitor);
            doCleanFatJar(jproject, iprogressmonitor);
            try {
                jproject.getProject().refreshLocal(IResource.DEPTH_ONE, null);
            } catch (CoreException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
            MessageDialog.openInformation(shell, "Fat Jar Plug-In ERROR", e.getMessage());
        }

        progressmonitordialog.close();
        MessageDialog.openInformation(shell, "Fat Jar Plug-In", "built " + jarfile);
        // testExtensionPoints(jarfile);

    }

    /**
     * @param jarfile
     */
    private void testExtensionPoints(String jarfile) {

        try {
            System.out.println("dumping contents in jar using extension net.sf.fjep.fatjar.fatjarJarUtil");
            System.out.println("--- " + jarfile + " ---");
            IExtensionRegistry reg = Platform.getExtensionRegistry();
            IExtensionPoint exp = reg.getExtensionPoint("net.sf.fjep.fatjar.jarutil");
            IExtension[] extensions = exp.getExtensions();
            IConfigurationElement[] elements = extensions[0].getConfigurationElements();
            IJarUtilFactory ju = (IJarUtilFactory) elements[0].createExecutableExtension("class");
            IFileSystemSource fss = ju.createJARFileSystemSource(jarfile, "");
            while (fss.hasMoreElements()) {
                IFileSystemElement fse = fss.nextElement();
                System.out.println(fse.getFullName());
            }
            System.out.println("--- finished ---");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @param jproject
     * @param property
     * @return
     */
    private ArrayList getIncludes(IJavaProject jproject, String includeString) {

        ArrayList result = new ArrayList();
        IWorkspaceRoot root = jproject.getProject().getWorkspace().getRoot();
        if ((includeString != null) && !includeString.trim().equals("")) {
            String[] includes = includeString.split("[;]");
            for (int i = 0; i < includes.length; i++) {
                String include = includes[i];
                if (include.startsWith("<inc|"))
                    include = include.replaceFirst("[<]inc[|](.*)[>]", "/$1|").replaceAll("[~]", "").replace('/',
                            File.separatorChar);
                String absPath = absProjectPath(root, include);
                result.add(absPath);
            }
        }
        return result;
    }

    /**
     * @param jproject
     * @param property
     * @return
     */
    private Set getExcludes(IJavaProject jproject, String excludeString) {

        Set result = new HashSet();
        IWorkspaceRoot root = jproject.getProject().getWorkspace().getRoot();
        if ((excludeString != null) && !excludeString.trim().equals("")) {
            String[] excludes = excludeString.split("[;]");
            for (int i = 0; i < excludes.length; i++) {
                String exclude = excludes[i];
                String absPath = exclude;
                if (exclude.startsWith("<po|")) {
                    String javaProjectName = exclude.replaceFirst("[<]po[|](.*)[>](.*)", "$1");
                    exclude = exclude.replaceFirst("[<]po[|](.*)[>](.*)", "$2").replaceAll("[~]", "").replace('/',
                            File.separatorChar);
                    try {
                        IJavaProject javaProject = JavaModelManager.getJavaModelManager().getJavaModel()
                                .getJavaProject(javaProjectName);
                        if (javaProject != null) {
                            String projectOutput = javaProject.getOutputLocation().toOSString();
                            exclude = projectOutput + File.separatorChar + exclude;
                        }
                    } catch (JavaModelException e) {
                        e.printStackTrace();
                    }
                    absPath = absProjectPath(root, exclude);
                } else if (exclude.startsWith("<cl|")) {
                    exclude = exclude.replaceFirst("[<]cl[|](.*)[>]", "$1/").replaceAll("[~]", "").replace('/',
                            File.separatorChar);
                    absPath = exclude;
                } else if (exclude.startsWith("<jar|")) {
                    exclude = exclude.replaceFirst("[<]jar[|](.*)[>]", "$1");
                    // absolute path is unknown here, but the check for excudes
                    // recognises jars to exclude without path
                    absPath = exclude;
                }
                if (absPath.endsWith(File.separator)) {
                    absPath = absPath.substring(0, absPath.length() - 1);
                }
                result.add(absPath);
            }
        }
        return result;
    }

    private void saveConfig(IJavaProject jproject, Properties props) {

        boolean ok = true;
        try {
            String propertiesFile = getPropertiesFilename(jproject);
            File f = new File(propertiesFile);
            if (f.exists() && f.canRead()) {
                // check for changes
                try {
                    InputStream in = new FileInputStream(f);
                    Properties oldProps = new Properties();
                    oldProps.load(in);
                    in.close();
                    if (oldProps.equals(props)) {
                        ok = false;
                    }
                } catch (Exception ignore) {
                }

                // allow check out of properties if readonly
                if (ok) {
                    String projectDir = getProjectDir(jproject);
                    if (propertiesFile.startsWith(projectDir)) {
                        String relFile = propertiesFile.substring(projectDir.length() + 1);
                        IFile editFile = jproject.getProject().getFile(relFile);
                        if (editFile != null) {
                            IStatus status = AguiPlugin.askFileWriteAccess(editFile);
                            ok = (status == null) || status.isOK();
                            if (!ok) {
                                Shell shell = new Shell();
                                MessageDialog.openInformation(shell, "Fat Jar Plug-In Warning",
                                        "could not save current settings: " + status.toString());
                            }
                        }
                    }
                }
            }

            if (ok) {
                OutputStream out = new FileOutputStream(f);
                props.store(out, "Fat Jar Configuration File");
                out.flush();
                out.close();
                try {
                    jproject.getProject().refreshLocal(IResource.DEPTH_ONE, null);
                } catch (CoreException e) {
                    e.printStackTrace();
                }
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static Properties getFatJarProperties(IJavaProject jproject) {

        Properties props = new Properties();
        try {
            String propertiesFile = getPropertiesFilename(jproject);
            File f = new File(propertiesFile);
            if (!f.exists()) {
                props.setProperty("jarname.isextern", "false");
                props.setProperty("jarname", getJarName(jproject));
                props.setProperty("manifest.file", "<createnew>");
                props.setProperty("manifest.mainclass", "");
                props.setProperty("manifest.classpath", "");
                props.setProperty("manifest.mergeall", getManifestMergeAll(jproject));
                props.setProperty("includes", "notyetsupported");
                props.setProperty("excludes", "notyetsupported");
            } else {
                InputStream in = new FileInputStream(f);
                props.load(in);
                in.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return props;
    }

    private String doCollectFatJar(IJavaProject jproject, IProgressMonitor iprogressmonitor, Set excludes,
            ArrayList includes, boolean onejar, String onejarexpand) throws IOException {

        Shell shell = new Shell();

        String jartype = onejar ? "One-JAR" : "Fat Jar";

        iprogressmonitor.subTask("Collect " + jartype + " - reading classpath");

        Vector childProjects = new Vector();
        Vector jarFiles = new Vector();
        Vector classesDirs = new Vector();
        Vector libDirs = new Vector();
        Vector libNames = new Vector();

        getChildProjects(jproject, childProjects, false);
        for (int i = childProjects.size() - 1; i >= 0; i--) {
            IJavaProject jChildProject = (IJavaProject) childProjects.get(i);
            if (onejar) {
                // Ferenc: libNames have to be updated for every new entry in
                // libdirs.
                // dirname is not unique, so using enumeration of childproject
                // name
                String childProjectName = jChildProject.getElementName();
                int current = libDirs.size();
                getClassPathJars(jChildProject, jarFiles, libDirs, true);
                for (int j = 1; j <= libDirs.size() - current; j++) {
                    String libDir = (String) libDirs.get(j - 1);
                    int start = libDir.indexOf(childProjectName);
                    if (start >= 0) {
                        start += childProjectName.length() + 1;
                        String libName = libDir.substring(start).replace(File.separatorChar, PATH_SEPARATOR);
                        libNames.add(libName);
                    } else {
                        libNames.add(childProjectName + PATH_SEPARATOR + Integer.toString(i));
                    }
                }
                getClassesDir(jChildProject, libDirs);
                libNames.add(childProjectName);
            } else {
                getClassPathJars(jChildProject, jarFiles, classesDirs, true);
                getClassesDir(jChildProject, classesDirs);
            }
        }

        if (onejar) {
            int current = libDirs.size();
            String projectName = jproject.getElementName();
            getClassPathJars(jproject, jarFiles, libDirs, false);
            for (int i = 1; i <= libDirs.size() - current; i++) {
                String libDir = (String) libDirs.get(i - 1);
                int start = libDir.indexOf(projectName);
                if (start >= 0) {
                    start += projectName.length() + 1;
                    String libName = libDir.substring(start).replace(File.separatorChar, PATH_SEPARATOR);
                    libNames.add(libName);
                } else {
                    libNames.add(projectName + PATH_SEPARATOR + Integer.toString(i));
                }
            }
        } else {
            getClassPathJars(jproject, jarFiles, classesDirs, false);
        }
        getClassesDir(jproject, classesDirs);
        String projectDir = getProjectDir(jproject);
        String projectName = getProjectName(jproject);
        String tempdir = configGetTempBuildDir(jproject);
        boolean mergeManifests = configGetManifestMergeAll(jproject);

        mkdir(tempdir);

        ManifestData manifestData = null;
        if (mergeManifests)
            manifestData = new ManifestData();
        iprogressmonitor.worked(PROGRESS_COLLECT * 1 / 20);

        for (int i = 0; i < jarFiles.size(); i++) {
            String jarFile = (String) jarFiles.get(i);
            String jarname = File.separatorChar + jarFile;
            jarname = jarname.substring(jarname.lastIndexOf(File.separatorChar) + 1);
            iprogressmonitor.subTask("Collect " + jartype + " - extracting " + jarname);
            if (!checkFileInList(jarFile, excludes) && !checkFileInList(jarname, excludes))
                extractJar(jarFile, tempdir, manifestData, onejar);
            iprogressmonitor.worked(PROGRESS_COLLECT * 15 / 20 / jarFiles.size());
        }

        for (int i = 0; i < classesDirs.size(); i++) {
            String classesDir = (String) classesDirs.get(i);
            iprogressmonitor.subTask("Collect " + jartype + " - copying class files " + classesDir);
            if (!onejar) {
                copyFiles(classesDir, tempdir, excludes, jproject);
            } else {
                // there is only one element in classesDirs if onejar is true,
                // so this code is only executed once.
                String mainClass = configGetMainClass(jproject);
                if (mainClass == null || mainClass.length() == 0)
                    throw new IOException("Main-Class manifest entry cannot be missing in a One-JAR application");
                String classPath = configGetClassPath(jproject);
                copyJar(classesDir, tempdir, excludes, mainClass, classPath, "main/main.jar", jproject);
                copyOneJARBootFiles(tempdir);
            }
            iprogressmonitor.worked(PROGRESS_COLLECT * 2 / 20 / classesDirs.size());
        }

        // One-JAR supporting libraries taken from other projects classes.
        for (int i = 0; i < libDirs.size(); i++) {
            String classesDir = (String) libDirs.get(i);
            copyJar(classesDir, tempdir, excludes, null, null, "lib/" + libNames.get(i) + ".jar", jproject);
        }

        for (int i = 0; i < includes.size(); i++) {
            String include = (String) includes.get(i);
            String[] basedir_relpath = include.split("[|]");
            String baseDir = basedir_relpath[0];
            String relPath = "";
            if (basedir_relpath.length > 1)
                relPath = File.separatorChar + basedir_relpath[1];
            iprogressmonitor.subTask("Collect " + jartype + " - including resource files " + relPath);
            String src = baseDir + relPath;
            String dest = tempdir + relPath;
            File fsrc = new File(src);
            File fdest = new File(dest);
            fdest.getParentFile().mkdirs();
            recursiveCopyFiles(fsrc, fdest);
        }
        iprogressmonitor.worked(PROGRESS_COLLECT * 1 / 20);

        iprogressmonitor.subTask("Collect " + jartype + " creating manifest");
        mkdir(tempdir + File.separator + "META-INF");
        if (manifestData == null)
            manifestData = new ManifestData();
        String manifestFile = configGetManifestFile(jproject);
        if (manifestFile.equals("<createnew>")) {
            manifestData.clearMainSection();
            manifestData.addMainSectionLine("Manifest-Version: 1.0");
            if (onejar) {
                manifestData.addMainSectionLine("Created-By: Fat Jar/One-JAR Eclipse Plug-In");
                manifestData.addMainSectionLine("Main-Class: com.simontuffs.onejar.Boot");
                if (onejarexpand.length() > 0)
                    manifestData.addMainSectionLine("One-Jar-Expand: " + onejarexpand);

            } else {
                manifestData.addMainSectionLine("Created-By: Fat Jar Eclipse Plug-In");
                String mainClass = configGetMainClass(jproject);
                if ((mainClass != null) && !mainClass.equals(""))
                    manifestData.addMainSectionLine("Main-Class: " + mainClass);
                String classPath = configGetClassPath(jproject);
                if ((classPath != null) && !classPath.equals(""))
                    manifestData.addMainSectionLine("Class-Path: " + classPath);
            }
        } else {
            manifestData.addFile(projectDir + File.separator + manifestFile);
        }
        String manifestText = manifestData.toString();
        writeTextToFile(new File(tempdir + File.separator + "META-INF" + File.separator + "MANIFEST.MF"),
                manifestText);
        iprogressmonitor.worked(PROGRESS_COLLECT * 1 / 20);

        return tempdir;
    }

    private boolean checkFileInList(String filename, Set files) {
        boolean result = false;
        if (files != null) {
            if (files.contains(filename))
                result = true;
            else {
                File f = new File(filename);
                try {
                    String abs = f.getCanonicalPath();
                    if (files.contains(abs))
                        result = true;
                } catch (IOException e) {
                }
            }
        }
        return result;
    }

    public FJTree buildTree(IJavaProject jproject) {
        String projectName = jproject.getElementName();
        FJTree rootNode = new FJTree(null, FJTree.NT_ROOT, "Fat Jar Tree for Project " + projectName, jproject,
                FJTree.CS_CHECKED);
        Vector childProjects = new Vector();
        Vector jarFiles = new Vector();
        Vector classesDirs = new Vector();

        getClassPathJars(jproject, jarFiles, classesDirs, false);
        getClassesDir(jproject, classesDirs);
        String classesDir = (String) classesDirs.lastElement();
        rootNode.addChild(FJTree.NT_PROJECT_OUTPUT, "Project '" + jproject.getElementName() + "' output",
                new File(classesDir), FJTree.CS_CHECKED);
        for (int i = 0; i < classesDirs.size() - 1; i++) {
            classesDir = (String) classesDirs.get(i);
            rootNode.addChild(FJTree.NT_CLASSES, "Classes '" + classesDir + "'", new File(classesDir),
                    FJTree.CS_CHECKED);
        }

        getChildProjects(jproject, childProjects, false);
        for (int i = 0; i < childProjects.size(); i++) {
            IJavaProject jChildProject = (IJavaProject) childProjects.get(i);
            String childProjectName = jChildProject.getElementName();
            classesDirs.clear();
            getClassPathJars(jChildProject, jarFiles, classesDirs, true);
            getClassesDir(jChildProject, classesDirs);
            classesDir = (String) classesDirs.lastElement();
            rootNode.addChild(FJTree.NT_PROJECT_OUTPUT, "Project '" + childProjectName + "' output",
                    new File(classesDir), FJTree.CS_CHECKED);
            for (int j = 0; j < classesDirs.size() - 1; j++) {
                classesDir = (String) classesDirs.get(j);
                rootNode.addChild(FJTree.NT_CLASSES, "Classes '" + classesDir + "'", new File(classesDir),
                        FJTree.CS_CHECKED);
            }
        }

        for (int i = 0; i < jarFiles.size(); i++) {
            int checkd = FJTree.CS_CHECKED;
            String jarFile = (String) jarFiles.get(i);
            if (jarFile.endsWith(AguiConstants.AGUI_SDK_JAR)) {
                checkd = FJTree.CS_UNCHECKED;
            }
            String jarname = File.separatorChar + jarFile;
            jarname = jarname.substring(jarname.lastIndexOf(File.separatorChar) + 1);
            rootNode.addChild(FJTree.NT_JAR, jarname, new File(jarFile), checkd);
        }
        // include AguiManifest.xml
        String aguiManifestPath = BaseProjectHelper.getManifest(jproject.getProject()).getRawLocation().toFile()
                .getAbsolutePath();
        rootNode.addChild(FJTree.NT_FILE, AguiConstants.AGUI_MANIFEST, new File(aguiManifestPath),
                FJTree.CS_CHECKED);
        // res
        String aguiResPath = BaseProjectHelper.getResFolder(jproject.getProject()).getRawLocation().toFile()
                .getAbsolutePath();
        rootNode.addChild(FJTree.NT_DIR, AguiConstants.FD_RES, new File(aguiResPath), FJTree.CS_CHECKED);

        return rootNode;
    }

    private void getChildProjects(IJavaProject jproject, Vector projects, boolean exportedOnly) {

        IClasspathEntry[] cpes = jproject.readRawClasspath();
        if (cpes != null) {
            for (int i = 0; i < cpes.length; i++) {
                IClasspathEntry cpe = JavaCore.getResolvedClasspathEntry(cpes[i]);
                if (cpe == null) {
                    System.err.println("Error: cpes[" + i + "]=" + cpes[i] + " does not resolve");
                    continue;
                }
                int kind = cpe.getEntryKind();
                String name = relPath(cpe.getPath());
                if (kind == IClasspathEntry.CPE_CONTAINER) {
                    try {
                        IClasspathContainer container = JavaCore.getClasspathContainer(cpe.getPath(), jproject);
                        if ((container.getKind() == IClasspathContainer.K_APPLICATION)
                                || (container.getKind() == IClasspathContainer.K_SYSTEM)) {
                            IClasspathEntry[] cpes2 = container.getClasspathEntries();
                            for (int j = 0; j < cpes2.length; j++) {
                                IClasspathEntry cpe2 = cpes2[j];
                                int kind2 = cpe2.getEntryKind();
                                String name2 = relPath(cpe2.getPath());
                                if (name2 == null) {
                                    System.err.println("invalid classpath entry: " + cpe2.toString());
                                } else {
                                    if (kind2 == IClasspathEntry.CPE_PROJECT) {
                                        if (!exportedOnly || cpe2.isExported()) {
                                            if (!projects.contains(name2)) {
                                                IJavaProject jChildProject2 = jproject.getJavaModel()
                                                        .getJavaProject(name2);
                                                projects.add(jChildProject2);
                                                getChildProjects(jChildProject2, projects, true);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    } catch (JavaModelException e) {
                    }
                } else if (kind == IClasspathEntry.CPE_PROJECT) {
                    if (name == null) {
                        System.err.println("invalid classpath entry: " + cpe.toString());
                    } else {
                        if (!exportedOnly || cpe.isExported()) {
                            if (!projects.contains(name)) {
                                IJavaProject jChildProject = jproject.getJavaModel().getJavaProject(name);
                                projects.add(jChildProject);
                                getChildProjects(jChildProject, projects, true);
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * do some security checking, that not the whole harddisc is scratched. if
     * tempDir is not a subdirectory of the project dir, then use
     * "TempFatjarBuildDir"
     */
    private String configGetTempBuildDir(IJavaProject jproject) {

        String projectDir = getProjectDir(jproject);
        String absProject = projectDir + File.separatorChar;
        String buildDir = "TempFatjarBuildDir_erase_me";
        String result = absProject + buildDir;
        return result;
    }

    private boolean configGetJarNameIsExtern(IJavaProject jproject) {
        Properties props = getProperties(jproject);
        String result = props.getProperty("jarname.isextern", "false");
        return result.equalsIgnoreCase("true");
    }

    private String configGetJarName(IJavaProject jproject) {
        Properties props = getProperties(jproject);
        String result = props.getProperty("jarname", getJarName(jproject));
        if (!configGetJarNameIsExtern(jproject))
            result = getProjectDir(jproject) + File.separator + result;
        return result;
    }

    private String configGetManifestFile(IJavaProject jproject) {
        Properties props = getProperties(jproject);
        String result = props.getProperty("manifest.file", "<createnew>");
        return result;
    }

    private String configGetMainClass(IJavaProject jproject) {
        Properties props = getProperties(jproject);
        String result = props.getProperty("manifest.mainclass", "");
        return result;
    }

    private String configGetClassPath(IJavaProject jproject) {
        Properties props = getProperties(jproject);
        String result = props.getProperty("manifest.classpath", "");
        return result;
    }

    private boolean configGetManifestMergeAll(IJavaProject jproject) {
        Properties props = getProperties(jproject);
        String result = props.getProperty("manifest.mergeall", "true");
        return result.equalsIgnoreCase("true");
    }

    private Properties getProperties(IJavaProject jproject) {
        Properties props = new Properties();
        try {
            String propertiesFile = getPropertiesFilename(jproject);
            File f = new File(propertiesFile);
            String msg;
            if (f.exists()) {
                InputStream in = new FileInputStream(f);
                props.load(in);
                in.close();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return props;
    }

    private String doPackFatJar(IJavaProject jproject, IProgressMonitor iprogressmonitor) {

        String tempdir = configGetTempBuildDir(jproject);
        String jarName = configGetJarName(jproject);
        boolean ok = checkWritable(jproject, jarName);
        if (!ok) {
            jarName = "(no write access)";
        } else {
            iprogressmonitor.subTask("Pack Fat Jar - " + jarName);
            packJar(jarName, tempdir);
            iprogressmonitor.worked(PROGRESS_PACK);
        }
        return jarName;
    }

    private boolean checkWritable(IJavaProject jproject, String filename) {

        boolean result = true;
        String projectDir = getProjectDir(jproject) + File.separator;
        if (filename.startsWith(projectDir)) {
            String relFilename = filename.substring(projectDir.length());
            IFile editFile = jproject.getProject().getFile(relFilename);
            IStatus status = AguiPlugin.askFileWriteAccess(editFile);
            if ((status != null) && !status.isOK()) {
                result = false;
                Shell shell = new Shell();
                MessageDialog.openInformation(shell, "Fat Jar Plug-In ERROR",
                        "no write acces for output jar '" + relFilename + "': " + status.toString());
            }
        }
        return result;
    }

    /**
     * 50 progress
     */
    private String doCleanFatJar(IJavaProject jproject, IProgressMonitor iprogressMonitor) {

        String tempdir = configGetTempBuildDir(jproject);
        iprogressMonitor.subTask("Clean Fat Jar - remove " + tempdir);
        recursiveRm(new File(tempdir));
        iprogressMonitor.worked(50);
        return tempdir;
    }

    public static String getJarName(IJavaProject jproject) {
        IPreferenceStore store = AguiPlugin.getDefault().getPreferenceStore();
        String result = store.getString(FatJarPreferencePage.P_JARNAME);
        return result.replaceAll("[<]project[>]", getProjectName(jproject));
    }

    public static String getManifestMergeAll(IJavaProject jproject) {
        IPreferenceStore store = AguiPlugin.getDefault().getPreferenceStore();
        boolean result = store.getBoolean(FatJarPreferencePage.P_MERGEMANIFEST);
        return (result ? "true" : "false");
    }

    public static String getManifestRemoveSigners(IJavaProject jproject) {
        IPreferenceStore store = AguiPlugin.getDefault().getPreferenceStore();
        boolean result = store.getBoolean(FatJarPreferencePage.P_REMOVESIGNERS);
        return (result ? "true" : "false");
    }

    public static boolean getScmAutoCheckout() {
        IPreferenceStore store = AguiPlugin.getDefault().getPreferenceStore();
        boolean result = store.getBoolean(FatJarPreferencePage.P_SCMAUTOCHECKOUT);
        return result;
    }

    private static String getRelPropertiesFilename(IJavaProject jproject) {
        Preferences prefs = AguiPlugin.getDefault().getPluginPreferences();
        String result = prefs.getString(FatJarPreferencePage.P_CONFIGFILE);
        if (Strings.isNullOrEmpty(result)) {
            return result = getProjectName(jproject) + "-agui.properties";
        }
        return result.replaceAll("[<]project[>]", getProjectName(jproject));
    }

    public static String getPropertiesFilename(IJavaProject jproject) {
        String result = absPropertiesFilename;
        if (result == null)
            result = getProjectDir(jproject) + File.separator + getRelPropertiesFilename(jproject);
        return result;
    }

    private void recursiveJarFilesExclude(JarOutputStream jarro, File rootDir, String filename, Set excludes) {

        try {
            File fsrc;
            if (filename == null)
                fsrc = rootDir;
            else
                fsrc = new File(rootDir, filename);
            if (!checkFileInList(fsrc, excludes)) {
                if (fsrc.isDirectory()) {
                    if (filename == null)
                        filename = "";
                    else {
                        filename += File.separator;
                        JarEntry entry = new JarEntry(filename.replace('\\', '/'));
                        jarro.putNextEntry(entry);
                        jarro.closeEntry();
                    }
                    String[] filenames = fsrc.list();
                    for (int i = 0; i < filenames.length; i++) {
                        recursiveJarFilesExclude(jarro, rootDir, filename + filenames[i], excludes);
                    }
                } else {
                    JarEntry entry = new JarEntry(filename.replace('\\', '/'));
                    entry.setSize(fsrc.length());
                    entry.setTime(fsrc.lastModified());
                    jarro.putNextEntry(entry);
                    FileInputStream in = new FileInputStream(fsrc);
                    byte[] buffer = new byte[4096];
                    int cnt = in.read(buffer);
                    while (cnt > 0) {
                        jarro.write(buffer, 0, cnt);
                        cnt = in.read(buffer);
                    }
                    jarro.closeEntry();
                    in.close();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void recursiveJarFiles(JarOutputStream jarro, File rootDir, String filename) {

        try {
            File fsrc;
            if (filename == null)
                fsrc = rootDir;
            else
                fsrc = new File(rootDir, filename);
            if (fsrc.isDirectory()) {
                if (filename == null)
                    filename = "";
                else {
                    filename += File.separator;
                    JarEntry entry = new JarEntry(filename.replace('\\', '/'));
                    jarro.putNextEntry(entry);
                    jarro.closeEntry();
                }
                String[] filenames = fsrc.list();
                for (int i = 0; i < filenames.length; i++) {
                    recursiveJarFiles(jarro, rootDir, filename + filenames[i]);
                }
            } else {
                JarEntry entry = new JarEntry(filename.replace('\\', '/'));
                entry.setSize(fsrc.length());
                entry.setTime(fsrc.lastModified());
                jarro.putNextEntry(entry);
                FileInputStream in = new FileInputStream(fsrc);
                byte[] buffer = new byte[4096];
                int cnt = in.read(buffer);
                while (cnt > 0) {
                    jarro.write(buffer, 0, cnt);
                    cnt = in.read(buffer);
                }
                jarro.closeEntry();
                in.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void packJar(String jarName, String tempdir) {

        try {
            OutputStream out = new FileOutputStream(jarName);
            JarOutputStream jarro = new JarOutputStream(out);
            recursiveJarFiles(jarro, new File(tempdir), null);
            jarro.flush();
            jarro.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static String getProjectName(IJavaProject jproject) {
        IProject project = jproject.getProject();
        String projectName = project.getName();
        return projectName;
    }

    private void extractJar(String jarfilename, String destdir, ManifestData md, boolean onejar) {

        try {
            if (!onejar) {
                JarFile jarFile = new JarFile(jarfilename);
                Enumeration enumeration = jarFile.entries();
                while (enumeration.hasMoreElements()) {
                    JarEntry jarEntry = (JarEntry) enumeration.nextElement();
                    String entryName = jarEntry.getName();
                    if (jarEntry.isDirectory()) {
                        mkdir(destdir + File.separator + entryName);
                    } else {
                        mkParentDirs(destdir + File.separator + entryName);
                        InputStream in = jarFile.getInputStream(jarEntry);
                        writeToFile(in, destdir + File.separator + entryName);
                        in.close();
                        if ((md != null) && (entryName.equalsIgnoreCase("META-INF/MANIFEST.MF"))) {
                            md.addFile(destdir + File.separator + entryName);
                        }
                    }
                }
                jarFile.close();
            } else {
                // One-JAR just copies the JAR files in to a "lib" directory.
                FileInputStream input = new FileInputStream(new File(jarfilename));
                String output = destdir + "/lib/" + new File(jarfilename).getName();
                ;
                mkParentDirs(output);
                writeToFile(input, output);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void mkParentDirs(String filename) {
        String normFilename = filename.replace('\\', File.separatorChar).replace('/', File.separatorChar);
        int pos = normFilename.lastIndexOf(File.separatorChar);
        String dir = normFilename.substring(0, pos);
        File f = new File(dir);
        f.mkdirs();
    }

    private void writeTextToFile(File f, String text) {
        try {
            OutputStream out = new FileOutputStream(f);
            out.write(text.getBytes());
            out.flush();
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private void writeToFile(InputStream in, String filename) {
        writeToFile(in, new File(filename));
    }

    private void writeToFile(InputStream in, File fdest) {

        try {
            FileOutputStream out = new FileOutputStream(fdest);
            byte[] buffer = new byte[4096];
            int cnt = in.read(buffer);
            while (cnt > 0) {
                out.write(buffer, 0, cnt);
                cnt = in.read(buffer);
            }
            out.flush();
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void copyJar(String classesDir, String tempdir, Set excludes, String mainclass, String classpath,
            String jar, IJavaProject jproject) throws IOException {
        // Copy classes into a "main.jar" file under "main" subdirectory.

        // stop if dir is excluded
        if (checkFileInList(classesDir, excludes))
            return;

        // exclude tempdir if it is beyond classes
        if (checkSubDir(new File(classesDir), new File(tempdir))) {
            try {
                String abs = new File(tempdir).getCanonicalPath();
                if (excludes == null)
                    excludes = new HashSet();
                excludes.add(abs);
            } catch (IOException e) {
            }
        }

        // exclude output jarfile if it is beyond srcDir to avoid doubling with
        // each build
        if (checkSubDir(new File(classesDir), new File(configGetJarName(jproject)))) {
            try {
                String abs = new File(configGetJarName(jproject)).getCanonicalPath();
                if (excludes == null)
                    excludes = new HashSet();
                excludes.add(abs);
            } catch (IOException e) {
            }
        }

        File maindir = new File(tempdir, jar);
        maindir.getParentFile().mkdirs();
        JarOutputStream output = new JarOutputStream(new FileOutputStream(maindir));

        // Write the manifest which would have been present in the original
        // Fat-Jar file.
        JarEntry man = new JarEntry("META-INF/MANIFEST.MF");
        output.putNextEntry(man);
        ManifestData manifestData = new ManifestData();
        manifestData.addMainSectionLine("Created-By: Fat Jar/One-JAR Eclipse Plug-In");
        manifestData.addMainSectionLine("Main-Class: " + mainclass);
        if ((classpath != null) && !classpath.equals(""))
            manifestData.addMainSectionLine("Class-Path: " + classpath);
        output.write(manifestData.toString().getBytes());

        if ((excludes != null) || (excludes.size() > 0))
            recursiveJarFilesExclude(output, new File(classesDir), null, excludes);
        else
            recursiveJarFiles(output, new File(classesDir), null);

        output.close();
    }

    private void copyOneJARBootFiles(String tempdir) throws IOException {
        // Put the OneJAR Boot files at the top of the tree.
        JarInputStream jis = new JarInputStream(AguiPlugin.class.getResourceAsStream(AguiPlugin.ONE_JAR_BOOT));
        JarEntry entry = (JarEntry) jis.getNextEntry();
        while (entry != null) {
            File dest = new File(tempdir, entry.getName());
            if (dest.getName().endsWith(".class") || entry.getName().startsWith("doc")) {
                dest.getParentFile().mkdirs();
                writeToFile(jis, dest);
            }
            entry = (JarEntry) jis.getNextEntry();
        }
    }

    private void copyFiles(String srcDir, String destDir, Set excludes, IJavaProject jproject) {

        if (!checkFileInList(srcDir, excludes)) {
            File fsrc = new File(srcDir);
            File fdest = new File(destDir);

            // exclude destDir if it is beyond srcDir to avoid an endless
            // recursion
            if (checkSubDir(fsrc, fdest)) {
                try {
                    String abs = fdest.getCanonicalPath();
                    if (excludes == null)
                        excludes = new HashSet();
                    excludes.add(abs);
                } catch (IOException e) {
                }
            }

            // exclude output jarfile if it is beyond srcDir to avoid doubling
            // with each build
            if (checkSubDir(fsrc, new File(configGetJarName(jproject)))) {
                try {
                    String abs = new File(configGetJarName(jproject)).getCanonicalPath();
                    if (excludes == null)
                        excludes = new HashSet();
                    excludes.add(abs);
                } catch (IOException e) {
                }
            }

            if ((excludes != null) || (excludes.size() > 0))
                recursiveCopyFilesExclude(fsrc, fdest, excludes);
            else
                recursiveCopyFiles(fsrc, fdest);
        }
    }

    /**
     * @return true if fchild is descendant of fparent, false on any error
     */
    private boolean checkSubDir(File fparent, File fchild) {
        boolean result = false;
        try {
            result = fchild.getCanonicalPath().startsWith(fparent.getCanonicalPath());
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    private void recursiveCopyFiles(File fsrc, File fdest) {

        if (fsrc.isDirectory()) {
            fdest.mkdirs();
            String[] filenames = fsrc.list();
            for (int i = 0; i < filenames.length; i++) {
                File fchildsrc = new File(fsrc, filenames[i]);
                File fchilddest = new File(fdest, filenames[i]);
                recursiveCopyFiles(fchildsrc, fchilddest);
            }
        } else {
            copyFile(fsrc, fdest);
        }
    }

    /**
     * copy all files recursive from fsrc to fdest, skip all (src-)files in
     * exclude
     */
    private void recursiveCopyFilesExclude(File fsrc, File fdest, Set excludes) {

        if (!checkFileInList(fsrc, excludes)) {
            if (fsrc.isDirectory()) {
                fdest.mkdirs();
                String[] filenames = fsrc.list();
                for (int i = 0; i < filenames.length; i++) {
                    File fchildsrc = new File(fsrc, filenames[i]);
                    File fchilddest = new File(fdest, filenames[i]);
                    recursiveCopyFilesExclude(fchildsrc, fchilddest, excludes);
                }
            } else {
                copyFile(fsrc, fdest);
            }
        }
    }

    private boolean checkFileInList(File file, Set files) {
        boolean result = false;
        String abs = null;
        try {
            abs = file.getCanonicalPath();
            if (files.contains(abs))
                result = true;
        } catch (IOException e) {
        }
        return result;
    }

    private void copyFile(String src, String dest) {
        copyFile(new File(src), new File(dest));
    }

    private void copyFile(File fsrc, File fdest) {

        try {
            FileInputStream in = new FileInputStream(fsrc);
            writeToFile(in, fdest);
            in.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void mkdir(String dir) {
        File f = new File(dir);
        f.mkdirs();
    }

    private void recursiveRm(File f) {
        if (f.isDirectory()) {
            String[] filenames = f.list();
            for (int i = 0; i < filenames.length; i++)
                recursiveRm(new File(f, filenames[i]));
            f.delete();
        } else {
            f.delete();
        }
    }

    /**
     * Add all jars and class-folders referenced by jproject to jarfiles /
     * classesDirs. If exportedOnly is true, then only jars/class-folders which
     * are marked as exported will be added.
     * 
     * JRE_LIB (.../jre/lib/rt.jar) is ignored and not added to jarfiles
     * 
     * @param jproject
     * @param jarfiles
     * @param classesDirs
     * @param exportedOnly
     */
    private void getClassPathJars(IJavaProject jproject, Vector jarfiles, Vector classesDirs,
            boolean exportedOnly) {

        IProject project = jproject.getProject();
        IWorkspace workspace = project.getWorkspace();
        IWorkspaceRoot workspaceRoot = workspace.getRoot();
        String rootDir = absPath(workspaceRoot.getLocation());
        IClasspathEntry[] cpes = jproject.readRawClasspath();
        // cpes = jproject.getResolvedClasspath(true);
        if (cpes != null) {
            for (int i = 0; i < cpes.length; i++) {
                IClasspathEntry cpe = JavaCore.getResolvedClasspathEntry(cpes[i]);
                if ((cpe != null) && (!exportedOnly || cpe.isExported())) {
                    int kind = cpe.getEntryKind();
                    String dir = relPath(cpe.getPath());
                    if (kind == IClasspathEntry.CPE_CONTAINER) {
                        try {
                            IClasspathContainer container = JavaCore.getClasspathContainer(cpe.getPath(), jproject);
                            if ((container.getKind() == IClasspathContainer.K_APPLICATION)
                                    || (container.getKind() == IClasspathContainer.K_SYSTEM)) {
                                IClasspathEntry[] cpes2 = container.getClasspathEntries();
                                for (int j = 0; j < cpes2.length; j++) {
                                    IClasspathEntry cpe2 = cpes2[j];
                                    int kind2 = cpe2.getEntryKind();
                                    String dir2 = relPath(cpe2.getPath());
                                    String jar2 = absOrProjectPath(workspaceRoot, dir2);
                                    if (jar2 == null) {
                                        System.err.println("invalid classpath entry: " + cpe2.toString());
                                    } else {
                                        File f2 = new File(jar2);
                                        if (f2.isDirectory()) {
                                            if (!classesDirs.contains(jar2)) {
                                                classesDirs.add(jar2);
                                            }
                                        } else { // assume jar file
                                            if (!jarfiles.contains(jar2)) {
                                                jarfiles.add(jar2);
                                            }
                                        }
                                    }
                                }
                            }
                        } catch (JavaModelException e) {
                        }
                    } else if (kind == IClasspathEntry.CPE_LIBRARY) {
                        String jar = absOrProjectPath(workspaceRoot, dir);
                        if (jar == null) {
                            System.err.println("invalid classpath entry: " + cpe.toString());
                        } else {

                            // ignore JRE_LIB
                            if (!jar.replace(File.separatorChar, '/').toLowerCase().endsWith("/jre/lib/rt.jar")) {
                                File f = new File(jar);
                                if (f.isDirectory()) {
                                    if (!classesDirs.contains(jar)) {
                                        classesDirs.add(jar);
                                    }
                                } else { // assume jar file
                                    if (!jarfiles.contains(jar)) {
                                        jarfiles.add(jar);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public String absOrProjectPath(IWorkspaceRoot workspaceRoot, String absOrProjectpath) {
        String result = absOrProjectpath;
        try {
            File f = new File(absOrProjectpath);
            if (!f.exists()) {
                result = absProjectPath(workspaceRoot, absOrProjectpath);
            }
        } catch (Exception e) {
        }
        return result;
    }

    /**
     * get absolute path for /project/xyz by resolving "project" location
     */
    public static String absProjectPath(IWorkspaceRoot workspaceRoot, String projectpath) {

        int n = projectpath.indexOf(File.separatorChar, 1);
        String jarProjectName;
        String jarProjectPath;
        if (n != -1) {
            jarProjectName = projectpath.substring(1, n);
            jarProjectPath = projectpath.substring(n);
        } else {
            jarProjectName = projectpath.substring(1);
            jarProjectPath = "";
        }
        IProject jarProject = workspaceRoot.getProject(jarProjectName);
        String jarProjectRoot = absPath(jarProject.getLocation());
        String result = null;
        if (jarProjectRoot != null)
            result = jarProjectRoot + jarProjectPath;
        return result;
    }

    private void getClassesDir(IJavaProject jproject, Vector classesDirs) {

        IProject project = jproject.getProject();
        IWorkspaceRoot workspaceRoot = project.getWorkspace().getRoot();
        String outputDir = null;
        try {
            outputDir = absProjectPath(workspaceRoot, relPath(jproject.getOutputLocation()));
        } catch (JavaModelException e) {
            e.printStackTrace();
        }
        if (!classesDirs.contains(outputDir))
            classesDirs.add(outputDir);
    }

    public static String getProjectDir(IJavaProject jproject) {

        IProject project = jproject.getProject();
        String projectDir = absPath(project.getLocation());
        return projectDir;
    }

    /**
     * convert IPath to absolute string path
     * 
     * @param path
     * @return
     */
    private static String absPath(IPath path) {

        String result = null;
        if (path != null) {
            File f = path.toFile();
            try {
                result = f.getCanonicalPath();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    /**
     * convert IPath to relative string path
     * 
     * @param path
     * @return
     */
    private String relPath(IPath path) {

        String result = null;
        File f = path.toFile();
        result = f.getPath();
        return result;
    }

    /**
     * @see IActionDelegate#selectionChanged(IAction, ISelection)
     */
    public void selectionChanged(IAction action, ISelection selection) {

        lastSelection = null;
        if ((selection != null) && !selection.isEmpty()) {
            lastSelection = (IStructuredSelection) selection;
        }
    }

}