org.emftext.sdk.ui.syntaxgenerator.jobs.SyntaxGenerationProcess.java Source code

Java tutorial

Introduction

Here is the source code for org.emftext.sdk.ui.syntaxgenerator.jobs.SyntaxGenerationProcess.java

Source

/*******************************************************************************
 * Copyright (c) 2006-2012
 * Software Technology Group, Dresden University of Technology
 * DevBoost GmbH, Berlin, Amtsgericht Charlottenburg, HRB 140026
 * 
 * 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:
 *   Software Technology Group - TU Dresden, Germany;
 *   DevBoost GmbH - Berlin, Germany
 *      - initial API and implementation
 ******************************************************************************/
/**
 *
 */
package org.emftext.sdk.ui.syntaxgenerator.jobs;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.codegen.ecore.genmodel.GenClass;
import org.eclipse.emf.codegen.ecore.genmodel.GenFeature;
import org.eclipse.emf.codegen.ecore.genmodel.GenModel;
import org.eclipse.emf.codegen.ecore.genmodel.GenPackage;
import org.eclipse.emf.common.CommonPlugin;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EObjectContainmentEList;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.emftext.sdk.EMFTextSDKPlugin;
import org.emftext.sdk.codegen.util.GenClassCache;
import org.emftext.sdk.codegen.util.GenClassUtil;
import org.emftext.sdk.concretesyntax.AtomicRegex;
import org.emftext.sdk.concretesyntax.Choice;
import org.emftext.sdk.concretesyntax.CompoundDefinition;
import org.emftext.sdk.concretesyntax.ConcreteSyntax;
import org.emftext.sdk.concretesyntax.ConcretesyntaxFactory;
import org.emftext.sdk.concretesyntax.ConcretesyntaxPackage;
import org.emftext.sdk.concretesyntax.CsString;
import org.emftext.sdk.concretesyntax.Definition;
import org.emftext.sdk.concretesyntax.FontStyle;
import org.emftext.sdk.concretesyntax.Import;
import org.emftext.sdk.concretesyntax.NormalTokenDefinition;
import org.emftext.sdk.concretesyntax.PlaceholderInQuotes;
import org.emftext.sdk.concretesyntax.PlaceholderUsingSpecifiedToken;
import org.emftext.sdk.concretesyntax.Rule;
import org.emftext.sdk.concretesyntax.Sequence;
import org.emftext.sdk.concretesyntax.Terminal;
import org.emftext.sdk.concretesyntax.CompleteTokenDefinition;
import org.emftext.sdk.concretesyntax.TokenDirective;
import org.emftext.sdk.concretesyntax.TokenStyle;
import org.emftext.sdk.generatorconfig.ClassContext;
import org.emftext.sdk.generatorconfig.ClassContextDefinition;
import org.emftext.sdk.generatorconfig.ClassRule;
import org.emftext.sdk.generatorconfig.ClassRuleReference;
import org.emftext.sdk.generatorconfig.FeatureContext;
import org.emftext.sdk.generatorconfig.FeatureContextDefinition;
import org.emftext.sdk.generatorconfig.FeatureReference;
import org.emftext.sdk.generatorconfig.FeatureRule;
import org.emftext.sdk.generatorconfig.FeatureRuleReference;
import org.emftext.sdk.generatorconfig.Features;
import org.emftext.sdk.generatorconfig.GeneratorConfig;
import org.emftext.sdk.generatorconfig.GeneratorRule;
import org.emftext.sdk.generatorconfig.GeneratorconfigFactory;
import org.emftext.sdk.generatorconfig.GeneratorconfigPackage;

/**
 * Implements the generation process to derive a configurable syntax definition for a given metamodel
 *
 *
 * @author Christian Wende, Moritz Bartl
 *
 */
public class SyntaxGenerationProcess implements IRunnableWithProgress {

    private static final String KEYWORD_VIOLETT = "7F0055";

    private static final GenClassUtil genClassUtil = new GenClassUtil();
    private final GenClassCache genClassCache = new GenClassCache();

    private final IFile file;
    private ConcretesyntaxFactory concretesyntaxFactory;
    private ConcreteSyntax cSyntax;
    private GeneratorConfig genConfig;

    private NormalTokenDefinition comment;
    private NormalTokenDefinition floatToken;
    private NormalTokenDefinition intToken;

    public SyntaxGenerationProcess(IFile file) {
        this.file = file;
    }

    public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
        ResourceSet rs = new ResourceSetImpl();
        Resource genResource = rs.getResource(URI.createPlatformResourceURI(file.getFullPath().toString(), true),
                true);
        final GenModel genModel = (GenModel) genResource.getContents().get(0);

        URI configURI = URI.createPlatformResourceURI(
                file.getFullPath().removeFileExtension().addFileExtension("generatorconfig").toString(), true);
        if (configURI != null && configURI.isPlatform()) {
            IResource workspaceMember = ResourcesPlugin.getWorkspace().getRoot()
                    .findMember(configURI.toPlatformString(true));
            if (workspaceMember != null) {
                Resource configResource = rs.getResource(configURI, true);
                genConfig = (GeneratorConfig) configResource.getContents().get(0);
            } else {
                configURI = URI.createURI(
                        "platform:/plugin/org.emftext.sdk.generatorconfig/metamodel/default.generatorconfig", true);
                Resource configResource = rs.getResource(configURI, true);
                genConfig = (GeneratorConfig) configResource.getContents().get(0);
            }
        }

        concretesyntaxFactory = ConcretesyntaxPackage.eINSTANCE.getConcretesyntaxFactory();
        URI uri = URI.createPlatformResourceURI(
                file.getFullPath().removeFileExtension().addFileExtension("cs").toString(), true);
        Resource csResource = null;
        if (uri != null && uri.isPlatform()) {
            IResource workspaceMember = ResourcesPlugin.getWorkspace().getRoot()
                    .findMember(uri.toPlatformString(true));
            if (workspaceMember != null) {
                csResource = rs.getResource(uri, true);
            } else {
                csResource = rs.createResource(uri);
            }
        }

        EObject currentSyntax = null;
        if (csResource != null && csResource.getContents().size() > 0) {
            currentSyntax = csResource.getContents().get(0);
        }

        if (currentSyntax instanceof ConcreteSyntax) {
            cSyntax = (ConcreteSyntax) currentSyntax;
        } else {
            cSyntax = concretesyntaxFactory.createConcreteSyntax();
            csResource.getContents().add(cSyntax);
        }

        Map<String, Rule> genClass2RuleCache = new HashMap<String, Rule>();
        for (Rule rule : cSyntax.getRules()) {
            genClass2RuleCache.put(genClassCache.getQualifiedInterfaceName(rule.getMetaclass()), rule);
        }

        //String csPackageName = (cSyntax.getPackage().getBasePackage()==null?"":cSyntax.getPackage().getBasePackage()+".")+cSyntax.getPackage().getEcorePackage().getName()+".resource."+cSyntax.getName();
        List<GenPackage> allGenPackagesWithClassifiers = genModel.getAllGenAndUsedGenPackagesWithClassifiers();

        cSyntax.setPackage(allGenPackagesWithClassifiers.get(0));
        cSyntax.setName(cSyntax.getPackage().getNSName());
        generateRules(genClass2RuleCache, cSyntax.getPackage(), "");

        for (int i = 1; i < allGenPackagesWithClassifiers.size(); i++) {
            GenPackage currentPkg = allGenPackagesWithClassifiers.get(i);

            Import imp = concretesyntaxFactory.createImport();
            cSyntax.getImports().add(imp);
            imp.setPackage(currentPkg);
            String prefix = currentPkg.getQualifiedPackageName();
            imp.setPrefix(prefix);

            generateRules(genClass2RuleCache, currentPkg, prefix);

        }
        generateTokenstyles();

        try {
            csResource.save(null);
        } catch (IOException e) {
            // TODO cwende: this exception should be shown to the user
            EMFTextSDKPlugin.logError("Exception while saving resource.", e);
        }
    }

    private void generateTokenstyles() {
        TreeIterator<EObject> allContents = cSyntax.eAllContents();
        HashMap<String, TokenStyle> cachedStyles = new HashMap<String, TokenStyle>();
        for (TokenStyle style : cSyntax.getTokenStyles()) {
            cachedStyles.put(style.getTokenName(), style);
        }

        while (allContents.hasNext()) {
            EObject object = (EObject) allContents.next();
            if (object instanceof CsString) {
                CsString s = (CsString) object;
                if (cachedStyles.get(s.getValue()) != null)
                    continue;
                if (!s.getValue().matches("\\w*"))
                    continue;

                TokenStyle tokenStyle = concretesyntaxFactory.createTokenStyle();
                tokenStyle.setTokenName(s.getValue());
                tokenStyle.setRgb(KEYWORD_VIOLETT);
                tokenStyle.getFontStyles().add(FontStyle.BOLD);

                cSyntax.getTokenStyles().add(tokenStyle);
                cachedStyles.put(s.getValue(), tokenStyle);

            }
        }
    }

    private void generateRules(Map<String, Rule> genClass2Rule, GenPackage pkg, String prefix) {
        EList<GenClass> genClasses = pkg.getGenClasses();
        Set<EClassifier> containedClasses = new HashSet<EClassifier>();
        for (GenClass genClass : genClasses) {
            List<GenFeature> allChildrenFeatures = genClass.getAllChildrenFeatures();
            for (GenFeature genFeature : allChildrenFeatures) {
                EClassifier type = genFeature.getEcoreFeature().getEType();
                if (!type.equals(genClass.getEcoreClass())) {
                    containedClasses.add(type);
                }
            }
        }
        for (GenClass genClass : genClasses) {
            if (genClassUtil.isConcrete(genClass)
                    && !containsSelfOfSuper(containedClasses, genClass.getEcoreClass())) {
                cSyntax.getStartSymbols().add(genClass);
            }
        }

        boolean newRuleGenerated = false;
        for (GenClass genClass : genClasses) {
            if (genClass2Rule.get(genClassCache.getQualifiedInterfaceName(genClass)) == null) {
                Rule newRule = generateRule(genClass);
                if (newRule != null) {
                    cSyntax.getRules().add(newRule);
                }
                newRuleGenerated = true;
            }
        }
        if (newRuleGenerated) {
            generateStandardTokens();
        }
    }

    private Rule generateRule(GenClass genClass) {
        if (genClassUtil.isNotConcrete(genClass)) {
            return null;
        }
        Rule newRule = concretesyntaxFactory.createRule();
        newRule.setMetaclass(genClass);
        Choice newChoice = concretesyntaxFactory.createChoice();
        newRule.setDefinition(newChoice);

        Sequence ruleSequence = concretesyntaxFactory.createSequence();

        List<GenFeature> genClassFeatures = genClass.getAllGenFeatures();
        generateClassSyntax(ruleSequence, genClass, genClassFeatures, getConfigRule(genClass));
        newChoice.getOptions().add(ruleSequence);
        return newRule;
    }

    private void generateClassSyntax(Sequence ruleSequence, GenClass genClass, List<GenFeature> genClassFeatures,
            GeneratorRule generatorRule) {
        Collection<Definition> definitions = EcoreUtil.copyAll(generatorRule.getDefinition().getParts());

        for (Definition d : definitions) {
            if (d instanceof ClassContext) {
                if (d instanceof ClassRuleReference) {
                    GeneratorRule subGeneratorRule = ((ClassRuleReference) d).getRule();
                    generateClassSyntax(ruleSequence, genClass, genClassFeatures, subGeneratorRule);
                } else if (d instanceof ClassContextDefinition) {
                    // use getDefinition(class)
                    ClassContextDefinition ccDefinition = (ClassContextDefinition) d;
                    ruleSequence.getParts().addAll(ccDefinition.getDefinition(genClass));
                } else if (d instanceof FeatureReference) {
                    String featureName = ((FeatureReference) d).getFeatureName();
                    for (GenFeature genFeature : genClassFeatures) {
                        if (genFeature.getName().equals(featureName)) {
                            generateFeature(ruleSequence, genFeature, getFeatureRule(genClass));
                            genClassFeatures.remove(genFeature);
                            break;
                        }
                    }
                } else if (d instanceof Features) {
                    //  generate all features
                    for (GenFeature genFeature : genClassFeatures) {
                        generateFeature(ruleSequence, genFeature, getFeatureRule(genClass));
                    }
                }
            } else if (d instanceof FeatureContext) {
                //TODO: proper warning?
                throw new RuntimeException("Misusage of FeatureContext in class rule (SyntaxGenerator Config)");
            } else {
                ruleSequence.getParts().add(d);
            }
        }
    }

    private void generateFeature(Sequence ruleSequence, GenFeature genFeature, FeatureRule featureRule) {
        Choice featureSyntaxChoice = concretesyntaxFactory.createChoice();

        Sequence innerSequence = concretesyntaxFactory.createSequence();
        generateFeatureSyntax(innerSequence, genFeature, featureRule);
        featureSyntaxChoice.getOptions().add(innerSequence);

        CompoundDefinition innerCompound = concretesyntaxFactory.createCompoundDefinition();
        innerCompound.setDefinitions(featureSyntaxChoice);

        int lowerBound = genFeature.getEcoreFeature().getLowerBound();
        int upperBound = genFeature.getEcoreFeature().getUpperBound();

        if (lowerBound == 1) {
            if (upperBound != 1) {
                innerCompound.setCardinality(concretesyntaxFactory.createPLUS());
            }
        } else {
            if (upperBound == 1) {
                innerCompound.setCardinality(concretesyntaxFactory.createQUESTIONMARK());
            } else {
                innerCompound.setCardinality(concretesyntaxFactory.createSTAR());
            }
        }

        ruleSequence.getParts().add(innerCompound);
    }

    private void generateFeatureSyntax(Sequence ruleSequence, GenFeature genFeature, GeneratorRule featureRule) {

        if (genFeature.isBooleanType()) {
            addBooleanModifier(ruleSequence, genFeature);
        } else {
            Collection<Definition> definitions = EcoreUtil.copyAll(featureRule.getDefinition().getParts());

            for (Definition d : definitions) {
                if (d instanceof FeatureContext) {
                    if (d instanceof FeatureRuleReference) {
                        GeneratorRule subGeneratorRule = ((FeatureRuleReference) d).getRule();
                        generateFeatureSyntax(ruleSequence, genFeature, subGeneratorRule);
                    } else if (d instanceof FeatureContextDefinition) {
                        FeatureContextDefinition fcDefinition = (FeatureContextDefinition) d;
                        ruleSequence.getParts().addAll(fcDefinition.getDefinition(genFeature));
                    }
                } else if (d instanceof ClassContext) {
                    //TODO: proper warning?
                    throw new RuntimeException("Misusage of FeatureContext in class rule (SyntaxGenerator Config)");
                } else {
                    ruleSequence.getParts().add(d);
                }
            }

        }
    }

    private GeneratorRule getConfigRule(GenClass genClass) {
        return genConfig.getClassRules().get(0);
    }

    private FeatureRule getFeatureRule(GenClass genClass) {
        return genConfig.getFeatureRules().get(0);
    }

    private void generateStandardTokens() {
        List<CompleteTokenDefinition> toRemove = new ArrayList<CompleteTokenDefinition>();
        EList<TokenDirective> existing = cSyntax.getTokens();
        for (TokenDirective tokenDirective : existing) {
            if (tokenDirective instanceof CompleteTokenDefinition) {
                CompleteTokenDefinition def = (CompleteTokenDefinition) tokenDirective;
                if (def.getName().equals("INTEGER") || def.getName().equals("FLOAT")
                        || def.getName().equals("COMMENT")) {
                    toRemove.add(def);
                }
            }
        }

        cSyntax.getTokens().removeAll(toRemove);
        intToken = createToken("INTEGER", "('-')?('1'..'9')('0'..'9')*|'0'");
        floatToken = createToken("FLOAT", "('-')?(('1'..'9') ('0'..'9')* | '0') '.' ('0'..'9')+ ");
        comment = createToken("COMMENT", "'//'(~('\\n'|'\\r'|'\\uffff'))*");

        cSyntax.getTokens().add(comment);
        cSyntax.getTokens().add(intToken);
        cSyntax.getTokens().add(floatToken);
    }

    private NormalTokenDefinition createToken(String name, String expression) {
        NormalTokenDefinition newToken = concretesyntaxFactory.createNormalTokenDefinition();
        newToken.setName(name);
        AtomicRegex regex = concretesyntaxFactory.createAtomicRegex();
        regex.setAtomicExpression(expression);
        newToken.getRegexParts().add(regex);
        return newToken;
    }

    /*   private Sequence generateFeatureSyntax(GenFeature genFeature) {
          Sequence innerSequence = concretesyntaxFactory.createSequence();
        
          CsString nameKeyword = concretesyntaxFactory.createCsString();
          String name = genFeature.getEcoreFeature().getName();
          nameKeyword.setValue(name);
        
          innerSequence.getParts().add(nameKeyword);
          CsString colon = concretesyntaxFactory.createCsString();
          colon.setValue(":");
          innerSequence.getParts().add(colon);
        
        
          innerSequence.getParts().add(content);
          return innerSequence;
       } */

    private void addBooleanModifier(Sequence ruleSequence, GenFeature genFeature) {
        PlaceholderUsingSpecifiedToken adjective = concretesyntaxFactory.createPlaceholderUsingSpecifiedToken();
        adjective.setCardinality(concretesyntaxFactory.createQUESTIONMARK());
        adjective.setFeature(genFeature);
        ruleSequence.getParts().add(adjective);
    }

    private boolean containsSelfOfSuper(Set<EClassifier> containedClasses, EClass ecoreClass) {
        if (containedClasses.contains(ecoreClass))
            return true;
        for (EClass superClass : ecoreClass.getEAllSuperTypes()) {
            if (containedClasses.contains(superClass))
                return true;
        }
        return false;
    }
}