org.eclipse.emf.mwe.utils.StandaloneSetup.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.emf.mwe.utils.StandaloneSetup.java

Source

/*******************************************************************************
 * Copyright (c) 2005, 2009 committers of openArchitectureWare and others.
 * 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:
 *     committers of openArchitectureWare - initial API and implementation
 *******************************************************************************/
package org.eclipse.emf.mwe.utils;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipException;

import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EPackage.Registry;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.emf.ecore.plugin.EcorePlugin;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.URIConverter;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
import org.eclipse.emf.mwe.core.ConfigurationException;
import org.eclipse.emf.mwe.core.resources.ResourceLoaderFactory;
import org.w3c.dom.Document;

/**
 * Initializes EMF support. Allows to register additional Packages.
 * 
 * <h1>Configuration</h1>
 * 
 * <h2>platformUri</h2> 
 * Set the path to the root of the platform, usually ".." 
 * 
 * <h3>Explicit platform mapping</h3>
 * If no .project files are available, an explicit mapping of projectName to
 * path may be established.
 * 
 * <pre>
 * projectMapping = { 
 *   projectName = 'org.acme.myproject' 
 *   path = '../org.acme.myproject'
 * }
 * </pre>
 * 
 * <h2>URI Mapping</h2> 
 * Map one URI to another. This is for example required when some resource refers to another with
 * <tt>platform:/plugin</tt> URIs. Platform plugin URIs cannot be resolved in standalone mode, thus these
 * URIs must be mapped to file or platform resource URIs.
 * 
 * <pre>
 * uriMap = {
 *   from = "platform:/plugin/org.eclipse.emf.ecore/model/Ecore.ecore"
 *   to = "platform:/resource/myproject/model/Ecore.ecore"
 * }
 * </pre>
 * 
 * <h2>Bundle name mapping</h2>
 * In the case that the folder name of a project does not match the bundle name, maps the bundle name to the real
 * directory name on the platform.
 * 
 * <pre>
 * bundleNameMap = {
 *   from = "my.bundle.name"
 *   to = "bundledirectoryname"
 * }
 * </pre>
 */
public class StandaloneSetup {

    private static String platformRootPath = null;
    private Map<String, String> bundleNameMapping = new HashMap<String, String>();

    public static String getPlatformRootPath() {
        return platformRootPath;
    }

    private Log log = LogFactory.getLog(getClass());
    static {
        Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap()
                .put(Resource.Factory.Registry.DEFAULT_EXTENSION, new XMIResourceFactoryImpl());
        EPackage.Registry.INSTANCE.put(EcorePackage.eINSTANCE.getNsURI(), EcorePackage.eINSTANCE);
    }

    private boolean ignoreBrokenProjectFiles = false;

    /**
     * Allows to ignore exception that occur while reading {@code .project} or {@code Manifest.MF}
     * files.
     * 
     * Default is {@code false}.
     */
    public void setIgnoreBrokenProjectFiles(boolean ignoreBrokenProjectFiles) {
        this.ignoreBrokenProjectFiles = ignoreBrokenProjectFiles;
    }

    public boolean isIgnoreBrokenProjectFiles() {
        return ignoreBrokenProjectFiles;
    }

    public void setLogResourceUriMap(boolean doLog) {
        if (!doLog)
            return;
        List<Entry<String, URI>> entrySet = new ArrayList<Entry<String, URI>>(
                EcorePlugin.getPlatformResourceMap().entrySet());
        Collections.sort(entrySet, new Comparator<Entry<String, URI>>() {
            public int compare(Entry<String, URI> o1, Entry<String, URI> o2) {
                return o1.getKey().compareTo(o2.getKey());
            }
        });
        for (Entry<String, URI> entry : entrySet) {
            log.info(entry.getKey() + " - " + entry.getValue());
        }
    }

    public void setScanClassPath(boolean doScan) {
        if (!doScan)
            return;
        String property = System.getProperty("java.class.path");
        String separator = System.getProperty("path.separator");
        Set<File> scanned = new HashSet<File>();
        if (property != null) {
            String[] entries = property.split(separator);
            for (String entry : entries) {
                File file = new File(entry);
                scanned.add(file);
                doRegisterResourceMapping(file);
            }
        }
        ClassLoader classLoader = getClass().getClassLoader();
        if (classLoader instanceof URLClassLoader) {
            @SuppressWarnings("resource")
            URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
            URL[] urLs = urlClassLoader.getURLs();
            for (URL url : urLs) {
                File file;
                try {
                    file = new File(url.toURI());
                    if (scanned.add(file))
                        doRegisterResourceMapping(file);
                } catch (URISyntaxException e) {
                    log.debug("Couldn't convert url '" + url + "' to a file : " + e.getMessage());
                }
            }
        }
    }

    protected void doRegisterResourceMapping(File file) {
        try {
            File f = file.getCanonicalFile();
            if (f.getPath().endsWith(".jar")) {
                registerBundle(f);
            } else if (!scanFolder(f)) {
                File dotProject = findProjectFileForPossibleClassesFolder(f);
                if (dotProject != null)
                    registerProject(dotProject);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    protected File findProjectFileForPossibleClassesFolder(File f) {
        File parentFile = f.getParentFile();
        if (parentFile != null) {
            if (f.getName().equals("bin")) {
                File projectFile = new File(parentFile, ".project");
                if (projectFile.exists()) {
                    return projectFile;
                }
            }
            if (f.getName().equals("classes")) {
                File grandParentFile = parentFile.getParentFile();
                if (grandParentFile != null) {
                    if (parentFile.getName().equals("target")) {
                        File projectFile = new File(grandParentFile, ".project");
                        if (projectFile.exists()) {
                            return projectFile;
                        }
                    }
                }
            }
        }
        log.warn("No project file found for " + f.getPath()
                + ". The folder was neither an Eclipse 'bin' folder, nor a Maven 'target/classes' folder.");
        return null;
    }

    /**
     * sets the platform uri for standalone execution
     * 
     * @param pathToPlatform
     */
    public void setPlatformUri(String pathToPlatform) {
        File f = new File(pathToPlatform);
        if (!f.exists())
            throw new ConfigurationException("The platformUri location '" + pathToPlatform + "' does not exist");
        if (!f.isDirectory())
            throw new ConfigurationException("The platformUri location must point to a directory");
        String path = f.getAbsolutePath();
        try {
            path = f.getCanonicalPath();
        } catch (IOException e) {
            log.error("Error when registering platform location", e);
        }
        if (platformRootPath == null || !platformRootPath.equals(path)) {
            platformRootPath = path;
            log.info("Registering platform uri '" + path + "'");
            if (f.exists()) {
                if (!scanFolder(f))
                    log.warn("No projects found in platform location '" + pathToPlatform + "'\n"
                            + "because there are no '.project' files.\n" + "Please use explicit project mappings:\n"
                            + "    projectMapping = { projectName = 'com.acme' path = '../path/com.acme' }.");
            }
        }
    }

    public void addProjectMapping(ProjectMapping projectMapping) {
        String projectName = projectMapping.getProjectName();
        if (isEmptyOrNullString(projectName)) {
            throw new ConfigurationException("ProjectName must not be empty");
        }
        String path = projectMapping.getPath();
        if (isEmptyOrNullString(path)) {
            throw new ConfigurationException("Path must not be empty");
        }

        File f = new File(path);
        if (!f.exists()) {
            throw new ConfigurationException("The project's path '" + path + "' does not exist");
        }
        try {
            URI uri = URI.createFileURI(f.getCanonicalPath() + File.separator);
            EcorePlugin.getPlatformResourceMap().put(projectName, uri);
            if (bundleNameMapping.get(projectName) != null) {
                EcorePlugin.getPlatformResourceMap().put(bundleNameMapping.get(projectName), uri);
            }
            log.info("Registering project " + projectName + " at '" + uri + "'");
        } catch (IOException e) {
            handleException(f, e);
        }
    }

    private boolean isEmptyOrNullString(String string) {
        return string == null || string.length() == 0;
    }

    protected boolean scanFolder(File f) {
        return scanFolder(f, new HashSet<String>());
    }

    protected boolean scanFolder(File f, Set<String> visitedPathes) {
        try {
            if (!visitedPathes.add(f.getCanonicalPath()))
                return true;
        } catch (IOException e) {
            log.error(e.getMessage(), e);
            return true;
        }
        File[] files = f.listFiles();
        boolean containsProject = false;
        File dotProject = null;
        if (files != null) {
            for (File file : files) {
                if (file.exists() && file.isDirectory() && !file.getName().startsWith(".")) {
                    containsProject |= scanFolder(file, visitedPathes);
                } else if (".project".equals(file.getName())) {
                    dotProject = file;
                } else if (file.getName().endsWith(".jar")) {
                    registerBundle(file);
                }
            }
        }
        if (!containsProject && dotProject != null)
            registerProject(dotProject);
        return containsProject || dotProject != null;
    }

    protected void registerBundle(File file) {
        JarFile jarFile = null;
        try {
            jarFile = new JarFile(file);
            log.debug("Trying to determine project name from Manifest for " + jarFile.getName());
            String name = getBundleNameFromManifest(jarFile);
            if (name == null) {
                log.debug("Trying to determine project name from file name for " + jarFile.getName());
                name = getBundleNameFromJarName(jarFile.getName());
            }
            if (name != null) {
                final int indexOf = name.indexOf(';');
                if (indexOf > 0)
                    name = name.substring(0, indexOf);
                String path = "archive:" + file.getCanonicalFile().toURI() + "!/";
                URI uri = URI.createURI(path);
                registerMapping(name, uri);
            } else {
                log.debug("Could not determine project name for " + jarFile.getName()
                        + ". No project mapping will be added.");
            }
        } catch (ZipException e) {
            log.warn("Could not open Jar file " + file.getAbsolutePath() + ".");
        } catch (Exception e) {
            handleException(file, e);
        } finally {
            try {
                if (jarFile != null)
                    jarFile.close();
            } catch (IOException e) {
                log.error(e.getMessage(), e);
            }
        }
    }

    protected String getBundleNameFromManifest(JarFile jarFile) throws IOException {
        Manifest manifest = jarFile.getManifest();
        if (manifest != null) {
            return manifest.getMainAttributes().getValue("Bundle-SymbolicName");
        }
        return null;
    }

    protected static final Pattern JAR_NAME_PATTERN = Pattern.compile("(.*?)(-.*)?\\.jar");

    protected String getBundleNameFromJarName(String jarFileName) {
        File file = new File(jarFileName);
        Matcher matcher = JAR_NAME_PATTERN.matcher(file.getName());
        if (matcher.matches()) {
            return matcher.group(1);
        }
        return null;
    }

    private void handleException(File file, Exception exception) {
        if (isIgnoreBrokenProjectFiles()) {
            try {
                log.warn("Couldn't read " + file.getCanonicalPath());
            } catch (IOException e) {
                log.warn("Couldn't read " + file.getAbsolutePath());
            }
        } else {
            throw new RuntimeException(exception);
        }
    }

    protected void registerMapping(String name, URI uri) {
        Map<String, URI> map = EcorePlugin.getPlatformResourceMap();
        if (log.isDebugEnabled())
            log.debug("Registering project " + name + " at '" + uri + "'");
        URI existing = map.put(name, uri);
        if (existing != null) {
            if (!existing.equals(uri)) {
                if (existing.isArchive() == uri.isArchive())
                    log.warn("Skipping conflicting project " + name + " at '" + existing + "' and using '" + uri
                            + "' instead.");
                else if (!existing.isArchive() && uri.isArchive()) {
                    if (log.isDebugEnabled())
                        log.debug("Skipping duplicate project " + name + " at '" + uri + "' and using '" + existing
                                + "' instead (folders win over JARs).");
                    map.put(name, existing);
                } else {
                    if (log.isDebugEnabled())
                        log.debug("Skipping duplicate project " + name + " at '" + existing + "' and using '" + uri
                                + "' instead (folders win over JARs).");
                }
            }
        } else {
            String mappedName = bundleNameMapping.get(name);
            if (mappedName != null)
                registerMapping(mappedName, uri);
        }
    }

    protected void registerProject(File file) {
        try {
            Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder()
                    .parse(new FileInputStream(file));
            String name = document.getDocumentElement().getElementsByTagName("name").item(0).getTextContent();

            URI uri = URI.createFileURI(file.getParentFile().getCanonicalPath() + File.separator);
            registerMapping(name, uri);
        } catch (Exception e) {
            handleException(file, e);
        }
    }

    /**
     * 
     * @param uriMap
     */
    public void addUriMap(final Mapping uriMap) {
        log.info("Adding URI mapping from '" + uriMap.getFrom() + "' to '" + uriMap.getTo() + "'");
        final URI baseUri = URI.createURI(uriMap.getFrom());
        final URI mappedUri = URI.createURI(uriMap.getTo());
        if (mappedUri == null)
            throw new ConfigurationException("cannot make URI out of " + uriMap.getTo());
        else {
            URIConverter.URI_MAP.put(baseUri, mappedUri);
        }
    }

    /**
     * Adds an extension
     * 
     * @param m
     *            <tt>from</tt>: extension name, <tt>to</tt> factory classname
     * @throws ConfigurationException
     *             <ul>
     *             <li>The factory class for the extension cannot be found
     *             <li>The inner factory class for the extension cannot be found
     *             </ul>
     */
    public void addExtensionMap(final Mapping m) throws ConfigurationException {
        log.info("Adding Extension mapping from '" + m.getFrom() + "' to '" + m.getTo() + "'");
        try {
            // locate the factory class of the extension
            Class<?> factoryClass = ResourceLoaderFactory.createResourceLoader().loadClass(m.getTo());
            if (factoryClass == null)
                throw new ConfigurationException(
                        "cannot find class " + m.getTo() + " for extension " + m.getFrom());
            Object factoryInstance = null;
            if (factoryClass.isInterface()) {
                final Class<?>[] innerClasses = factoryClass.getDeclaredClasses();
                factoryClass = null;
                for (int j = 0; j < innerClasses.length; j++) {
                    if (Resource.Factory.class.isAssignableFrom(innerClasses[j])) {
                        factoryClass = innerClasses[j];
                    }
                }
                if (factoryClass == null)
                    throw new ConfigurationException(
                            "cannot find inner factory class " + m.getTo() + " for extension " + m.getFrom());
                final Field instanceField = factoryClass.getField("INSTANCE");
                factoryInstance = instanceField.get(null);
            } else {
                factoryInstance = factoryClass.newInstance();
            }
            Resource.Factory.Registry.INSTANCE.getExtensionToFactoryMap().put(m.getFrom(), factoryInstance);
        } catch (final Exception e) {
            throw new ConfigurationException(e);
        }
    }

    public void addRegisterGeneratedEPackage(String interfacename) {
        Class<?> clazz = ResourceLoaderFactory.createResourceLoader().loadClass(interfacename);
        if (clazz == null)
            throw new ConfigurationException("Couldn't find an interface " + interfacename);
        try {
            EPackage pack = (EPackage) clazz.getDeclaredField("eINSTANCE").get(null);
            registry.put(pack.getNsURI(), pack);
            log.info("Adding generated EPackage '" + interfacename + "'");

        } catch (Exception e) {
            throw new ConfigurationException("Couldn't register " + interfacename
                    + ". Is it the generated EPackage interface? : " + e.getMessage());
        }
    }

    protected ResourceSet resourceSet = new ResourceSetImpl();
    protected Registry registry = EPackage.Registry.INSTANCE;

    public void setResourceSet(ResourceSet resourceSet) {
        log.info(
                "Using resourceSet registry. The registered Packages will not be registered in the global EPackage.Registry.INSTANCE!");
        this.resourceSet = resourceSet;
        this.registry = resourceSet.getPackageRegistry();
    }

    public void setResourceSetImpl(ResourceSetImpl resourceSet) {
        setResourceSet(resourceSet);
    }

    protected GenModelHelper createGenModelHelper() {
        return new GenModelHelper();
    }

    public void addRegisterGenModelFile(String fileName) {
        createGenModelHelper().registerGenModel(resourceSet, createURI(fileName));
    }

    public void addRegisterEcoreFile(String fileName) throws IllegalArgumentException, SecurityException {
        Resource res = resourceSet.getResource(createURI(fileName), true);
        if (res == null)
            throw new ConfigurationException("Couldn't find resource under  " + fileName);
        if (!res.isLoaded()) {
            try {
                res.load(null);
            } catch (IOException e) {
                throw new ConfigurationException(
                        "Couldn't load resource under  " + fileName + " : " + e.getMessage());
            }
        }
        List<EObject> result = res.getContents();
        for (EObject object : result) {
            if (object instanceof EPackage) {
                registerPackage(fileName, object);
            }
            for (final TreeIterator<EObject> it = object.eAllContents(); it.hasNext();) {
                EObject child = it.next();
                if (child instanceof EPackage) {
                    registerPackage(fileName, child);
                }
            }
        }
    }

    public EPackage getPackage(String nsUri) {
        return (EPackage) registry.get(nsUri);
    }

    public void addBundleNameMap(Mapping mapping) {
        bundleNameMapping.put(mapping.getFrom(), mapping.getTo());
    }

    private URI createURI(String path) {
        if (path == null)
            throw new IllegalArgumentException();

        URI uri = URI.createURI(path);
        if (uri.isRelative()) {
            URI resolvedURI = URI.createFileURI(new File(path).getAbsolutePath());
            return resolvedURI;
        }
        return uri;
    }

    private void registerPackage(String fileName, EObject object) {
        String nsUri = ((EPackage) object).getNsURI();
        if (registry.get(nsUri) == null) {
            registry.put(nsUri, object);
            log.info("Adding dynamic EPackage '" + nsUri + "' from '" + fileName + "'");
        } else if (log.isDebugEnabled()) {
            log.debug("Dynamic EPackage '" + nsUri + "' from '" + fileName + "' already in the registry!");
        }
    }
}