pt.org.aguiaj.core.AguiaJActivator.java Source code

Java tutorial

Introduction

Here is the source code for pt.org.aguiaj.core.AguiaJActivator.java

Source

/*******************************************************************************
 * Copyright (c) 2014 Andre L. Santos.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Andre L Santos - developer
 ******************************************************************************/
package pt.org.aguiaj.core;

import static pt.org.aguiaj.extensibility.AguiaJContribution.ACCESSOR_POLICY_DESCRIPTION;
import static pt.org.aguiaj.extensibility.AguiaJContribution.ACCESSOR_POLICY_ID;
import static pt.org.aguiaj.extensibility.AguiaJContribution.EXTENSION_ACCESSOR_POLICY;
import static pt.org.aguiaj.extensibility.AguiaJContribution.EXTENSION_OBJECT_WIDGET;
import static pt.org.aguiaj.extensibility.AguiaJContribution.OBJECT_CONTRACT_PROXY;
import static pt.org.aguiaj.extensibility.AguiaJContribution.OBJECT_WIDGET_ALLOWIMPORT;
import static pt.org.aguiaj.extensibility.AguiaJContribution.OBJECT_WIDGET_EXCLUDE;
import static pt.org.aguiaj.extensibility.AguiaJContribution.OBJECT_WIDGET_GROUP;
import static pt.org.aguiaj.extensibility.AguiaJContribution.OBJECT_WIDGET_ICON;
import static pt.org.aguiaj.extensibility.AguiaJContribution.OBJECT_WIDGET_ID;
import static pt.org.aguiaj.extensibility.AguiaJContribution.OBJECT_WIDGET_INCLUDE;
import static pt.org.aguiaj.extensibility.AguiaJContribution.OBJECT_WIDGET_METHOD;
import static pt.org.aguiaj.extensibility.AguiaJContribution.OBJECT_WIDGET_METHOD_ID;
import static pt.org.aguiaj.extensibility.AguiaJContribution.OBJECT_WIDGET_PROMOTE;
import static pt.org.aguiaj.extensibility.AguiaJContribution.OBJECT_WIDGET_VIEW;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.ILogListener;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.InvalidRegistryObjectException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;

import pt.org.aguiaj.classes.ClassMemberFilter;
import pt.org.aguiaj.classes.ClassModel;
import pt.org.aguiaj.classes.ClassesView;
import pt.org.aguiaj.classes.NameBasedFilter;
import pt.org.aguiaj.common.AguiaJImage;
import pt.org.aguiaj.common.SWTUtils;
import pt.org.aguiaj.core.commands.ReloadClassesCommand;
import pt.org.aguiaj.core.exceptions.ActiveExceptionHandler;
import pt.org.aguiaj.core.typewidgets.ActiveTypeWidget;
import pt.org.aguiaj.extensibility.AccessorMethodDetectionPolicy;
import pt.org.aguiaj.extensibility.AguiaJContribution;
import pt.org.aguiaj.extensibility.VisualizationWidget;
import pt.org.aguiaj.extensibility.contracts.ContractDecorator;
import pt.org.aguiaj.standard.GetIsAccessorPolicy;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;

/**
 * The activator class controls the plug-in life cycle
 */
public class AguiaJActivator extends AbstractUIPlugin {
    // The shared instance
    private static AguiaJActivator plugin;

    private Multimap<String, Class<?>> packagesClasses;
    private IPath workingDir;

    private Multimap<String, Class<?>> plugins; // id -> class[]

    private Map<String, File> pluginClassFiles;

    private Map<Class<?>, Image> pluginTypeImages;

    private static final AccessorMethodDetectionPolicy defaultPolicy = new GetIsAccessorPolicy();
    private Map<String, Class<? extends AccessorMethodDetectionPolicy>> accessorPolicies;

    public AguiaJActivator() {
        plugin = this;

        workingDir = Platform.getLocation();
        packagesClasses = ArrayListMultimap.create();

        ActiveTypeWidget.loadTypeWidgets();
        ActiveDefaultObjectWidgetExtension.loadExtensions();
        ActiveExceptionHandler.loadExceptionHandlers();

        plugins = ArrayListMultimap.create();

        pluginClassFiles = Maps.newHashMap();
        pluginTypeImages = Maps.newHashMap();

        accessorPolicies = Maps.newHashMap();

        Platform.addLogListener(new ILogListener() {

            @Override
            public void logging(IStatus status, String plugin) {
                if (status.getException() != null) {
                    StringWriter sw = new StringWriter();
                    PrintWriter pw = new PrintWriter(sw);
                    status.getException().printStackTrace(pw);
                    String trace = sw.toString();
                    if (trace.contains("aguiaj")) {
                        MessageDialog.openWarning(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
                                "Error", trace);
                    }
                }
            }
        });
    }

    public IPath getWorkingDirectory() {
        return workingDir;
    }

    public Multimap<String, Class<?>> getPackagesClasses() {
        return Multimaps.unmodifiableMultimap(packagesClasses);
    }

    public void start(BundleContext context) throws Exception {
        super.start(context);
        getPreferenceStore().addPropertyChangeListener(new IPropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent event) {
                ReloadClassesCommand reload = new ReloadClassesCommand();
                try {
                    reload.execute(null);
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        });
        loadObjectWidgetPlugins();
        loadAccessorPolicyPlugins();

        ClassModel.getInstance().addDefaultClasses();

        for (String pluginID : plugins.keySet())
            registerPluginImages(pluginID);
    }

    public void stop(BundleContext context) throws Exception {
        plugin = null;
        super.stop(context);
    }

    /**
     * Returns the shared instance
     *
     * @return the shared instance
     */
    public static AguiaJActivator getInstance() {
        return plugin;
    }

    /**
     * Returns an image descriptor for the image file at the given
     * plug-in relative path
     *
     * @param path the path
     * @return the image descriptor
     */

    public static ImageDescriptor getImageDescriptor(String path) {
        return imageDescriptorFromPlugin(AguiaJContribution.AGUIAJ_PLUGIN, path);
    }

    public Image getPluginTypeIcon(Class<?> type) {
        return pluginTypeImages.get(type);
    }

    protected void initializeImageRegistry(ImageRegistry registry) {
        super.initializeImageRegistry(registry);

        Bundle bundle = Platform.getBundle(AguiaJContribution.AGUIAJ_PLUGIN);

        for (AguiaJImage image : AguiaJImage.values()) {
            ImageDescriptor imageDesc = ImageDescriptor
                    .createFromURL(FileLocator.find(bundle, image.getPath(), null));
            registry.put(image.getId(), imageDesc);
        }
    }

    private void registerPluginImages(String id) {
        ImageRegistry registry = getImageRegistry();
        Bundle bundle = Platform.getBundle(id);
        IPath imagesPath = new Path("images");
        URL imagesFolder = FileLocator.find(bundle, imagesPath, null);
        if (imagesFolder != null) {
            try {
                imagesFolder = FileLocator.toFileURL(imagesFolder);
            } catch (IOException e1) {
                e1.printStackTrace();
            }

            File dir = new File(imagesFolder.getPath());

            if (dir != null && dir.isDirectory()) {
                for (String file : dir.list()) {
                    URL fileURL = FileLocator.find(bundle, imagesPath.append(file), null);
                    ImageDescriptor imageDesc = ImageDescriptor.createFromURL(fileURL);
                    registry.put(file.substring(0, file.indexOf('.')), imageDesc);
                }
            }
        }
    }

    public void reloadClasses() {
        loadClasses(workingDir);
    }

    public static void handlePluginError(String message) {
        SWTUtils.showMessage("Plugin Error", message, SWT.ICON_ERROR);
    }

    public static String getActivePlugin() {
        return ClassesView.getInstance().getActivePlugin();
    }

    public static URL getPluginFolder(String pluginId) {

        URL url = Platform.getBundle(pluginId).getEntry("/");
        try {
            url = FileLocator.resolve(url);
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        return url;
    }

    public Map<String, File> getPluginClassFiles() {
        return Collections.unmodifiableMap(pluginClassFiles);
    }

    public String getPluginLocation(Class<?> aClass) {
        File f = pluginClassFiles.get(aClass.getName());
        Path path = new Path(f.getAbsolutePath());
        return path.removeLastSegments(1).toOSString();
    }

    public String getPluginJarLocation(final String pluginId) {
        URL pluginFolder = getPluginFolder(pluginId);
        Path path = new Path(pluginFolder.getPath());

        String jarLocation = path.toString();
        if (jarLocation.endsWith("!\\"))
            jarLocation = jarLocation.substring(0, jarLocation.length() - 2);

        if (jarLocation.startsWith("file:\\"))
            jarLocation = jarLocation.substring("file:\\".length());

        if (jarLocation.endsWith("!/"))
            jarLocation = jarLocation.substring(0, jarLocation.length() - 2);

        if (jarLocation.startsWith("file:"))
            jarLocation = jarLocation.substring("file:".length());

        //      if(!jarLocation.endsWith(".jar"))
        //         jarLocation += "bin";

        return jarLocation;
    }

    public Set<String> getPluginIds() {
        Set<String> ids = Sets.newHashSet();
        for (String id : plugins.keySet())
            if (!id.equals(AguiaJContribution.AGUIAJ_PLUGIN))
                ids.add(id);

        return ids;
    }

    public Set<String> getPluginPackages() {
        Set<String> packs = Sets.newHashSet();
        for (Class<?> c : plugins.values())
            packs.add(c.getPackage().getName());

        return packs;
    }

    public boolean isPluginPackage(String packageName) {
        for (Class<?> c : plugins.values())
            if (c.getPackage().getName().equals(packageName))
                return true;

        return false;
    }

    public String getPluginId(Class<?> clazz) {
        for (String id : plugins.keySet())
            if (plugins.get(id).contains(clazz))
                return id;

        return null;
    }

    public Collection<Class<?>> getPackageClasses(String packageName) {
        Set<Class<?>> classes = Sets.newHashSet();
        for (Class<?> c : plugins.values())
            if (c.getPackage().getName().equals(packageName))
                classes.add(c);

        return classes;
    }

    public Collection<Class<?>> getAllPluginClasses() {
        return Collections.unmodifiableCollection(plugins.values());
    }

    public Collection<Class<?>> getPluginClasses(String pluginId) {
        return Collections.unmodifiableCollection(plugins.get(pluginId));
    }

    //   public String getPluginName(String pluginId) {
    //      return pluginNames.get(pluginId);
    //   }

    Set<String> getAccessorPolicies() {
        return accessorPolicies.keySet();
    }

    AccessorMethodDetectionPolicy getAccessorPolicy() {
        String name = AguiaJParam.ACCESSOR_POLICY.getString();
        Class<? extends AccessorMethodDetectionPolicy> policyClass = accessorPolicies.get(name);
        if (policyClass != null) {
            try {
                return policyClass.newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        return defaultPolicy;
    }

    private void loadObjectWidgetPlugins() {
        IConfigurationElement[] config = Platform.getExtensionRegistry()
                .getConfigurationElementsFor(EXTENSION_OBJECT_WIDGET);

        for (final IConfigurationElement e : config) {
            String pluginID = e.getContributor().getName();
            if (e.getName().equals(OBJECT_WIDGET_GROUP))
                handleObjectWidgets(e, pluginID);
        }
    }

    @SuppressWarnings("unchecked")
    private void handleObjectWidgets(final IConfigurationElement e, String pluginID) {
        for (final IConfigurationElement objWidget : e.getChildren()) {
            final String className = objWidget.getAttribute(OBJECT_WIDGET_ID);

            if (className == null)
                continue;

            String allowImportText = objWidget.getAttribute(OBJECT_WIDGET_ALLOWIMPORT);
            boolean allowImport = Boolean.parseBoolean(allowImportText);

            IPath pluginPath = new Path(getPluginFolder(pluginID).getPath());

            IPath path = pluginPath.append("bin");
            for (String s : className.split("\\."))
                path = path.append(s);
            path = path.addFileExtension("class");

            try {
                Bundle bundle = Platform.getBundle(pluginID);
                Class<?> clazz = null;
                try {
                    clazz = bundle.loadClass(className);
                } catch (ClassNotFoundException ex2) {
                    AguiaJActivator.handlePluginError("Check plugin.xml, class not found: " + className);
                    continue;
                }

                try {
                    handleIcon(pluginID, objWidget, clazz);

                    for (Method promotion : createPromotionsToAccessor(objWidget, clazz))
                        ClassModel.getInstance().addAccessorPromotion(promotion);

                    for (ClassMemberFilter f : createFilters(objWidget, clazz))
                        ClassModel.getInstance().addFilter(f);

                    Class<? extends VisualizationWidget<?>> view = null;

                    if (objWidget.getAttribute(OBJECT_WIDGET_VIEW) != null) {
                        view = (Class<? extends VisualizationWidget<?>>) bundle
                                .loadClass(objWidget.getAttribute(OBJECT_WIDGET_VIEW));
                    }

                    Class<? extends ContractDecorator<?>> contractClass = null;
                    if (objWidget.getAttribute(OBJECT_CONTRACT_PROXY) != null) {
                        contractClass = (Class<? extends ContractDecorator<?>>) bundle
                                .loadClass(objWidget.getAttribute(OBJECT_CONTRACT_PROXY));
                        if (!clazz.isAssignableFrom(contractClass)) {
                            AguiaJActivator.handlePluginError(
                                    "contract proxy must be type-compatible with " + clazz.getName());
                        }
                        try {
                            contractClass.getConstructor(clazz);
                        } catch (NoSuchMethodException ex) {
                            AguiaJActivator
                                    .handlePluginError("contract proxy must have a constructor that receives a "
                                            + clazz.getName() + " object.");
                            continue;
                        }
                    }

                    ClassModel.getInstance().addPluginClass(clazz, view, contractClass, allowImport, pluginID);

                    for (Class<?> inner : clazz.getClasses()) {
                        ClassModel.getInstance().addPluginClass(inner, null, null, allowImport, pluginID);
                    }
                } catch (Exception ex2) {
                    AguiaJActivator.handlePluginError(ex2.getMessage());
                    continue;
                }

                plugins.put(pluginID, clazz);

                for (Class<?> inner : clazz.getClasses())
                    plugins.put(pluginID, inner);

                pluginClassFiles.put(className, path.toFile());

            } catch (InvalidRegistryObjectException e1) {
                e1.printStackTrace();
            }
        }
    }

    private void handleIcon(String pluginID, final IConfigurationElement objWidget, Class<?> clazz) {
        String iconPath = objWidget.getAttribute(OBJECT_WIDGET_ICON);
        if (iconPath != null) {
            ImageDescriptor imgDesc = imageDescriptorFromPlugin(pluginID, iconPath);
            if (imgDesc != null)
                pluginTypeImages.put(clazz, imgDesc.createImage());
        } else
            pluginTypeImages.put(clazz, AguiaJImage.nextTypeIcon().getImage());
    }

    private List<Method> createPromotionsToAccessor(IConfigurationElement objWidget, Class<?> clazz) {
        List<Method> promotions = Lists.newArrayList();

        for (IConfigurationElement prom : objWidget.getChildren(OBJECT_WIDGET_PROMOTE)) {
            List<String> methodNames = getMethodNames(prom);
            for (Method m : clazz.getMethods())
                if (methodNames.contains(m.getName()))
                    promotions.add(m);
        }
        return promotions;
    }

    @SuppressWarnings("unchecked")
    private void loadAccessorPolicyPlugins() {
        IConfigurationElement[] config = Platform.getExtensionRegistry()
                .getConfigurationElementsFor(EXTENSION_ACCESSOR_POLICY);

        for (final IConfigurationElement e : config) {
            String pluginID = e.getContributor().getName();
            String className = e.getAttribute(ACCESSOR_POLICY_ID);
            String desc = e.getAttribute(ACCESSOR_POLICY_DESCRIPTION);
            Bundle bundle = Platform.getBundle(pluginID);
            Class<? extends AccessorMethodDetectionPolicy> clazz = null;
            try {
                clazz = (Class<? extends AccessorMethodDetectionPolicy>) bundle.loadClass(className);
                accessorPolicies.put(desc, clazz);
            } catch (ClassNotFoundException e1) {
                e1.printStackTrace();
            }
        }
    }

    private List<ClassMemberFilter> createFilters(final IConfigurationElement objWidget, Class<?> clazz) {
        List<ClassMemberFilter> filters = Lists.newArrayList();

        for (IConfigurationElement ex : objWidget.getChildren(OBJECT_WIDGET_EXCLUDE))
            filters.add(new NameBasedFilter(clazz, getMethodNames(ex), true));

        for (IConfigurationElement ex : objWidget.getChildren(OBJECT_WIDGET_INCLUDE))
            filters.add(new NameBasedFilter(clazz, getMethodNames(ex), false));

        return filters;
    }

    private List<String> getMethodNames(IConfigurationElement ex) {
        List<String> methodNames = Lists.newArrayList();
        for (IConfigurationElement m : ex.getChildren(OBJECT_WIDGET_METHOD)) {
            String name = m.getAttribute(OBJECT_WIDGET_METHOD_ID);
            methodNames.add(name);
        }
        return methodNames;
    }

    public void loadClasses(IPath workingDir) {
        assert workingDir.toFile().exists() && workingDir.toFile().isDirectory();

        this.workingDir = workingDir;

        List<IPath> workingDirs = new ArrayList<IPath>();
        workingDirs.add(workingDir);

        IPath dir = workingDir;
        if (dir.lastSegment().equals("bin"))
            dir = dir.removeLastSegments(1);

        addDependencyPaths(workingDirs, dir);

        AguiaClassLoader.newClassLoader();

        packagesClasses = readClasses(workingDirs);

        // try bin directory, if exists      
        if (packagesClasses.isEmpty()) {
            IPath binDirPath = workingDir.append("bin");
            File binDir = binDirPath.toFile();
            if (binDir.exists() && binDir.isDirectory()) {
                workingDirs.add(binDirPath);
                Multimap<String, Class<?>> tmp = readClasses(workingDirs);
                if (!tmp.isEmpty()) {
                    packagesClasses = tmp;
                    this.workingDir = binDirPath;
                }
            }
        }

        // try a bin directory on parent folder, if exists      
        if (packagesClasses.isEmpty()) {
            IPath binTry = workingDir.removeLastSegments(1).append("bin");
            File binTryDir = binTry.toFile();
            if (binTryDir.exists() && binTryDir.isDirectory()) {
                workingDirs.add(binTry);
                Multimap<String, Class<?>> tmp = readClasses(workingDirs);
                if (!tmp.isEmpty()) {
                    packagesClasses = tmp;
                    this.workingDir = binTry;
                }
            }
        }

        // filter non-visible classes
        for (Iterator<Class<?>> it = packagesClasses.values().iterator(); it.hasNext();) {
            Class<?> c = it.next();
            if (!ClassModel.getInspector().isClassVisible(c))
                it.remove();
        }
    }

    private void addDependencyPaths(List<IPath> workingDirs, IPath dir) {
        IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();

        try {
            for (IProject proj : projects) {
                if (proj.getLocation().equals(dir) && proj.hasNature(JavaCore.NATURE_ID)) {
                    IJavaProject javaProj = (IJavaProject) proj.getNature(JavaCore.NATURE_ID);
                    IClasspathEntry[] classpath = javaProj.getRawClasspath();
                    for (IClasspathEntry entry : classpath) {
                        if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
                            for (IProject p : projects) {
                                if (p.isOpen() && p.hasNature(JavaCore.NATURE_ID)
                                        && p.getLocation().lastSegment().equals(entry.getPath().lastSegment())) {
                                    IJavaProject javaProj2 = (IJavaProject) proj.getNature(JavaCore.NATURE_ID);
                                    IPath path = p.getLocation()
                                            .append(javaProj2.getOutputLocation().lastSegment());
                                    workingDirs.add(path);
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static Multimap<String, Class<?>> readClasses(List<IPath> workingDirs) {
        Multimap<String, Class<?>> ret = ArrayListMultimap.create();
        for (IPath path : workingDirs) {
            Map<String, Set<Class<?>>> classes = ReflectionUtils.readClassFiles(path);

            if (ret.isEmpty()) {
                for (String key : classes.keySet()) {
                    ret.putAll(key, classes.get(key));
                }
            }
        }
        return ret;
    }

}