org.eclipse.gmf.tests.setup.GenProjectBaseSetup.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.gmf.tests.setup.GenProjectBaseSetup.java

Source

/*
 * Copyright (c) 2005, 2008 Borland Software Corporation
 * 
 * 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:
 *    Artem Tikhomirov (Borland) - initial API and implementation
 */
package org.eclipse.gmf.tests.setup;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.emf.codegen.ecore.genmodel.GenClass;
import org.eclipse.emf.codegen.ecore.genmodel.GenClassifier;
import org.eclipse.emf.codegen.ecore.genmodel.GenModel;
import org.eclipse.emf.codegen.ecore.genmodel.GenPackage;
import org.eclipse.emf.codegen.ecore.genmodel.generator.GenBaseGeneratorAdapter;
import org.eclipse.emf.codegen.util.CodeGenUtil;
import org.eclipse.emf.common.util.BasicMonitor;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EcorePackage;
import org.eclipse.gmf.codegen.gmfgen.GenDiagram;
import org.eclipse.gmf.codegen.gmfgen.GenEditorGenerator;
import org.eclipse.gmf.internal.common.codegen.GeneratorBase;
import org.eclipse.gmf.tests.CompileUtil;
import org.eclipse.gmf.tests.Plugin;
import org.eclipse.gmf.tests.gen.GenDiagramMutator;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.junit.Assert;
import org.osgi.framework.Constants;

/**
 * Generates and (by default) compiles gmf plugins.
 * @author artem
 */
public class GenProjectBaseSetup {

    protected final Set<String> projectsToInit = new LinkedHashSet<String>();

    private CompileUtil compileUtil;

    private GeneratorConfiguration myGeneratorFactory;

    private static final String INTERFACE_TEMPLATE = "{0} public interface {1} {2} '{ }'";

    public GenProjectBaseSetup(GeneratorConfiguration generatorFactory) {
        myGeneratorFactory = generatorFactory;
    }

    @Deprecated
    public void generateAndCompile(DiaGenSource diaGenSource) throws Exception {
        generateAndCompile(diaGenSource.getGenDiagram().getEditorGen());
    }

    public void generateAndCompile(GenEditorGenerator genEditor, GenDiagramMutator... mutators) throws Exception {
        projectsToInit.clear(); //just in case
        compileUtil = new CompileUtil();
        final GenDiagram d = genEditor.getDiagram();
        generateDiagramPrerequisites(d);
        if (mutators == null || mutators.length == 0) {
            generateDiagramPlugin(d);
        } else {
            for (GenDiagramMutator next : mutators) {
                next.doMutate(genEditor);
                try {
                    generateDiagramPlugin(d);
                } finally {
                    next.undoMutate(genEditor);
                }
            }
        }
        for (String pluginID : projectsToInit) {
            IProject p = ResourcesPlugin.getWorkspace().getRoot().getProject(pluginID);
            hookProjectBuild(p);
        }
        compileUtil = null;
    }

    protected void generateDiagramPrerequisites(GenDiagram d) throws Exception {
        final GenModel domainGenModel = d.getEditorGen().getDomainGenModel();
        if (domainGenModel != null) {
            generateEMFCode(domainGenModel);
            projectsToInit.add(domainGenModel.getModelPluginID());
            projectsToInit.add(domainGenModel.getEditPluginID());
        }
    }

    protected void generateDiagramPlugin(GenDiagram d) throws Exception {
        GeneratorBase generator = myGeneratorFactory.createGenerator(d);
        generator.run();
        hookGeneratorStatus(generator.getRunStatus());
        final String gmfEditorId = d.getEditorGen().getPlugin().getID();
        final IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(gmfEditorId);
        hookExtraCodeGeneration(d.getEditorGen(), project);
        RuntimeWorkspaceSetup.get().getReadyToStartAsBundle(project);

        projectsToInit.add(gmfEditorId);
        hookJDTStatus(project);
    }

    /**
     * no-op by default, subclasses may provide custom code generation in this method
     * XXX think about more oo way to inject extra codegen artifacts, or even special GMFGen model entities?
     */
    protected void hookExtraCodeGeneration(GenEditorGenerator genEditor, IProject project) throws Exception {
    }

    private void generateEMFCode(GenModel domainGenModel) {
        domainGenModel.setCanGenerate(true);
        org.eclipse.emf.codegen.ecore.generator.Generator gen = new org.eclipse.emf.codegen.ecore.generator.Generator();
        gen.setInput(domainGenModel);
        gen.generate(domainGenModel, GenBaseGeneratorAdapter.MODEL_PROJECT_TYPE, new BasicMonitor());
        gen.generate(domainGenModel, GenBaseGeneratorAdapter.EDIT_PROJECT_TYPE, new BasicMonitor());

        fixInstanceClasses(domainGenModel);
        RuntimeWorkspaceSetup.get().getReadyToStartAsBundle(
                ResourcesPlugin.getWorkspace().getRoot().getProject(domainGenModel.getModelPluginID()));
        if (!domainGenModel.getModelPluginID().equals(domainGenModel.getEditPluginID())) {
            RuntimeWorkspaceSetup.get().getReadyToStartAsBundle(
                    ResourcesPlugin.getWorkspace().getRoot().getProject(domainGenModel.getEditPluginID()));
        }
    }

    private void fixInstanceClasses(GenModel domainGenModel) {
        @SuppressWarnings("serial")
        class MultiMap<K, V> extends HashMap<K, LinkedList<V>> {

            public void putOne(K key, V value) {
                LinkedList<V> allForKey = get(key);
                if (allForKey == null) {
                    allForKey = new LinkedList<V>();
                    put(key, allForKey);
                }
                allForKey.add(value);
            }
        }
        final MultiMap<String, GenClassifier> allInstanceClassNames = new MultiMap<String, GenClassifier>();
        for (GenPackage nextPackage : domainGenModel.getGenPackages()) {
            for (GenClassifier nextGenClassifier : nextPackage.getGenClassifiers()) {
                if (nextGenClassifier.getEcoreClassifier()
                        .eIsSet(EcorePackage.Literals.ECLASSIFIER__INSTANCE_CLASS_NAME)) {
                    EClassifier ecoreClassifier = nextGenClassifier.getEcoreClassifier();
                    allInstanceClassNames.putOne(ecoreClassifier.getInstanceClassName(), nextGenClassifier);
                }
            }
        }
        if (allInstanceClassNames.isEmpty()) {
            return;
        }

        IPackageFragmentRoot theRoot = null;
        IFile manifestFile = null;
        try {
            String pluginID = domainGenModel.getModelPluginID();
            IProject pluginProject = ResourcesPlugin.getWorkspace().getRoot().getProject(pluginID);
            IJavaProject javaProject = JavaCore.create(pluginProject);
            Assert.assertNotNull("Generated EMF model project is not a java project", javaProject);
            IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots();
            for (int i = 0; i < roots.length && theRoot == null; i++) {
                if (!roots[i].isReadOnly()) {
                    theRoot = roots[i];
                }
            }

            manifestFile = pluginProject.getFile(JarFile.MANIFEST_NAME);
            Assert.assertNotNull("Writable project root not found in the generated project", theRoot);
            Assert.assertTrue("Manifest was not generated", manifestFile != null && manifestFile.exists());

            Manifest manifest = new Manifest(manifestFile.getContents());

            Attributes attributes = manifest.getMainAttributes();
            StringBuffer exportedPackages = new StringBuffer(attributes.getValue(Constants.EXPORT_PACKAGE));

            for (String instanceClassName : allInstanceClassNames.keySet()) {
                List<GenClassifier> eClassifiers = allInstanceClassNames.get(instanceClassName);
                Assert.assertFalse(eClassifiers.isEmpty());
                generateUserInterface(instanceClassName, eClassifiers, theRoot, exportedPackages);
            }

            attributes.putValue(Constants.EXPORT_PACKAGE, exportedPackages.toString());
            ByteArrayOutputStream contents = new ByteArrayOutputStream();
            manifest.write(contents);
            manifestFile.setContents(new ByteArrayInputStream(contents.toByteArray()), true, true,
                    new NullProgressMonitor());
        } catch (Exception e) {
            e.printStackTrace();
            Assert.fail(e.getMessage());
        }
    }

    private void generateUserInterface(String fqClassName, List<GenClassifier> realizations,
            IPackageFragmentRoot projectRoot, StringBuffer exportedPackages) {
        String extendsList = buildExtendsListForUserInterface(realizations);

        String className = CodeGenUtil.getSimpleClassName(fqClassName);
        String packageName = CodeGenUtil.getPackageName(fqClassName);
        if (packageName == null) {
            packageName = "";
        }
        String packagePrefix = packageName;
        try {
            IPackageFragment pkgFragment = projectRoot.createPackageFragment(packageName, true,
                    new NullProgressMonitor());
            if (packagePrefix.length() > 0) {
                packagePrefix = "package " + packagePrefix + ";";
            }
            pkgFragment.createCompilationUnit(className + ".java",
                    MessageFormat.format(INTERFACE_TEMPLATE,
                            new Object[] { packagePrefix, className, extendsList.toString() }),
                    true, new NullProgressMonitor());
        } catch (JavaModelException e) {
            Assert.fail(e.getMessage());
        }
        if (packageName.length() > 0 && exportedPackages.indexOf(packageName) == -1) {
            exportedPackages.append(",");
            exportedPackages.append(packageName);
        }
    }

    /**
     * @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=416943, instance interfaces must (since Kepler) extend interfaces for respected EMF superclasses
     */
    private String buildExtendsListForUserInterface(List<GenClassifier> realisations) {
        if (realisations.size() == 1 && realisations.get(0) instanceof GenClass) {
            GenClass theOnly = (GenClass) realisations.get(0);
            return theOnly.getInterfaceExtends();
        }
        SortedSet<GenClass> supers = new TreeSet<GenClass>(new Comparator<GenClass>() {

            @Override
            public int compare(GenClass o1, GenClass o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });
        for (GenClassifier next : realisations) {
            if (next instanceof GenClass) {
                supers.addAll(((GenClass) next).getExtendedGenClasses());
            }
        }
        StringBuffer result = new StringBuffer();
        result.append("org.eclipse.emf.ecore.EObject");
        for (GenClass next : supers) {
            if (result.length() > 0) {
                result.append(", ");
            }
            result.append(next.getQualifiedInterfaceName());
        }

        return "extends " + result.toString();
    }

    public List<String> getGeneratedProjectNames() {
        return Collections.unmodifiableList(new LinkedList<String>(projectsToInit));
    }

    protected void hookProjectBuild(IProject p) throws Exception {
        IStatus s = compileUtil.build(p);
        if (!s.isOK()) {
            Plugin.getInstance().getLog().log(s);
            if (s.getException() != null) {
                s.getException().printStackTrace(System.err);
            } else {
                System.err.println("hookProjectBuild failed without exception:" + s);
                LinkedList<IStatus> ch = new LinkedList<IStatus>(Arrays.asList(s.getChildren()));
                while (!ch.isEmpty()) {
                    IStatus f = ch.removeFirst();
                    if (f.getException() != null) {
                        System.err.println("============> Nested exception in the status:");
                        f.getException().printStackTrace(System.err);
                    }
                    ch.addAll(Arrays.asList(f.getChildren()));
                }
            }
            Assert.fail(s.getMessage());
        }
    }

    // FIXME turn verification back once figure templates are ok
    protected void hookJDTStatus(IProject p) throws Exception {
        //      JDTUtil jdtUtil = new JDTUtil(p);
        //      IStatus jdtStatus = jdtUtil.collectProblems();
        //      if (!jdtStatus.isOK()) {
        //         Plugin.logError(jdtStatus.getMessage());
        //         Assert.fail(jdtStatus.getMessage());
        //      }
    }

    protected void hookGeneratorStatus(IStatus generatorStatus) {
        if (!generatorStatus.isOK()) {
            Plugin.getInstance().getLog().log(generatorStatus);
        }
        if (generatorStatus.getSeverity() == IStatus.ERROR) {
            Assert.fail("GMF editor generation produced errors:" + generatorStatus.toString()); //$NON-NLS-1$
        }
    }
}