org.eclipse.xtext.xtext.RuleNames.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.xtext.xtext.RuleNames.java

Source

/*******************************************************************************
 * Copyright (c) 2015 itemis AG (http://www.itemis.eu) 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
 *******************************************************************************/
package org.eclipse.xtext.xtext;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.EnumRule;
import org.eclipse.xtext.Grammar;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.IGrammarAccess;
import org.eclipse.xtext.ParserRule;
import org.eclipse.xtext.TerminalRule;

import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.inject.Inject;
import com.google.inject.Singleton;

/**
 * Different kinds of name mapping for rules in a grammar.
 * 
 * @author Sebastian Zarnekow - Initial contribution and API
 * @since 2.9
 */
@Singleton
public class RuleNames {

    static class Adapter extends AdapterImpl {
        private RuleNames ruleNames;

        Adapter(RuleNames ruleNames) {
            this.ruleNames = ruleNames;
        }

        @Override
        public boolean isAdapterForType(Object type) {
            return RuleNames.class.equals(type);
        }

        public RuleNames getRuleNames() {
            return ruleNames;
        }
    }

    private final Grammar contextGrammar;

    /**
     * All rules by simple name.
     */
    private final ListMultimap<String, AbstractRule> simpleNameToRules;
    /**
     * A mapping from qualified name to rule.
     */
    private final Map<String, AbstractRule> qualifiedNameToRule;
    /**
     * The name that is unique or qualified depending on the occurrence.
     * It may either be a mapping from {@code ID} to rule or {@code com.acme.ID} to
     * rule depending on the fact if the ID rule is overridden in the context grammar.
     */
    private final BiMap<String, AbstractRule> nameToRule;
    /**
     * The name that is used in code, e.g. the grammar access method names.
     */
    private final BiMap<String, AbstractRule> uniqueNameToRule;
    /**
     * The name that is used in the generated Antlr grammar.
     */
    private final BiMap<String, AbstractRule> antlrNameToRule;

    private final List<AbstractRule> allRules;

    public static RuleNames getRuleNames(Grammar grammar, boolean cache) {
        if (cache) {
            Adapter adapter = (Adapter) EcoreUtil.getAdapter(grammar.eAdapters(), RuleNames.class);
            if (adapter == null) {
                return new RuleNames(grammar, true);
            }
            return adapter.getRuleNames();
        }
        return new RuleNames(grammar, false);
    }

    public static RuleNames getRuleNames(AbstractRule rule) {
        Adapter adapter = (Adapter) EcoreUtil.getAdapter(rule.eAdapters(), RuleNames.class);
        if (adapter == null) {
            throw new IllegalStateException("Cannot find adapter");
        }
        return adapter.getRuleNames();
    }

    public static RuleNames tryGetRuleNames(AbstractRule rule) {
        Adapter adapter = (Adapter) EcoreUtil.getAdapter(rule.eAdapters(), RuleNames.class);
        if (adapter == null) {
            return null;
        }
        return adapter.getRuleNames();
    }

    public static void ensureAdapterInstalled(Grammar grammar) {
        getRuleNames(grammar, true);
    }

    @Inject
    public RuleNames(IGrammarAccess grammarAccess) {
        this(grammarAccess.getGrammar(), false);
    }

    public RuleNames(Grammar grammar, boolean installAdapter) {
        this.contextGrammar = grammar;
        Adapter adapter = new Adapter(this);
        if (installAdapter) {
            installAdapterIfMissing(adapter, grammar);
        }
        List<AbstractRule> allRules = GrammarUtil.allRules(grammar);
        ImmutableListMultimap.Builder<String, AbstractRule> simpleNameToRulesBuilder = ImmutableListMultimap
                .builder();
        ImmutableMap.Builder<String, AbstractRule> qualifiedNameToRuleBuilder = ImmutableMap.builder();
        ImmutableBiMap.Builder<String, AbstractRule> uniqueNameToRuleBuilder = ImmutableBiMap.builder();
        ImmutableBiMap.Builder<String, AbstractRule> antlrNameToRuleBuilder = ImmutableBiMap.builder();

        Map<String, AbstractRule> names = Maps.newHashMap();
        Set<String> usedAntlrNames = Sets.newHashSet();
        Set<String> usedUniqueNames = Sets.newHashSet();
        for (AbstractRule rule : allRules) {
            String name = rule.getName();
            simpleNameToRulesBuilder.put(name, rule);
            String qualifiedName = getQualifiedName(rule);
            qualifiedNameToRuleBuilder.put(qualifiedName, rule);
            String uniqueName = name;
            String antlrRuleName;
            if (names.containsKey(name)) {
                name = qualifiedName;
                uniqueName = getInheritedUniqueName(rule, usedUniqueNames);
                antlrRuleName = getInheritedAntlrRuleName(rule, usedAntlrNames);
            } else {
                antlrRuleName = getDefaultAntlrRuleName(rule);
            }
            names.put(name, rule);
            if (!usedUniqueNames.add(uniqueName)) {
                throw new IllegalStateException(uniqueName);
            }
            uniqueNameToRuleBuilder.put(uniqueName, rule);
            if (!usedAntlrNames.add(antlrRuleName)) {
                throw new IllegalStateException(antlrRuleName);
            }
            antlrNameToRuleBuilder.put(antlrRuleName, rule);
            if (installAdapter) {
                installAdapterIfMissing(adapter, rule);
            }
        }
        simpleNameToRules = simpleNameToRulesBuilder.build();
        qualifiedNameToRule = qualifiedNameToRuleBuilder.build();
        nameToRule = ImmutableBiMap.copyOf(names);
        uniqueNameToRule = uniqueNameToRuleBuilder.build();
        antlrNameToRule = antlrNameToRuleBuilder.build();
        this.allRules = ImmutableList.copyOf(allRules);
    }

    private void installAdapterIfMissing(Adapter adapter, EObject context) {
        if (EcoreUtil.getAdapter(context.eAdapters(), RuleNames.class) == null) {
            context.eAdapters().add(adapter);
        } else {
            throw new IllegalStateException("Duplicate adapter");
        }
    }

    public List<AbstractRule> getRulesBySimpleName(String name) {
        return simpleNameToRules.get(name);
    }

    public AbstractRule getRuleByQualifiedName(String name) {
        return qualifiedNameToRule.get(name);
    }

    public String getQualifiedName(AbstractRule rule) {
        return GrammarUtil.getGrammar(rule).getName() + "." + rule.getName();
    }

    public String getUniqueRuleName(AbstractRule rule) {
        return uniqueNameToRule.inverse().get(rule);
    }

    public AbstractRule getRuleByUniqueName(String uniqueName) {
        return uniqueNameToRule.get(uniqueName);
    }

    public String getAntlrRuleName(AbstractRule rule) {
        return antlrNameToRule.inverse().get(rule);
    }

    public String getAntlrRuleName(AbstractRule rule, int paramConfig) {
        String result = antlrNameToRule.inverse().get(rule);
        int idx;
        if (result.startsWith("super")) {
            idx = 5;
        } else if (result.startsWith("rule")) {
            idx = 4;
        } else {
            throw new IllegalArgumentException(result);
        }
        if (paramConfig != 0) {
            result = (idx == 4 ? "norm" : "normSuper") + paramConfig + "_" + result.substring(idx);
        }
        return result;
    }

    public AbstractRule getRuleByAntlrName(String name) {
        return antlrNameToRule.get(name);
    }

    public String getBestRuleName(AbstractRule rule) {
        return nameToRule.inverse().get(rule);
    }

    public Grammar getContextGrammar() {
        return contextGrammar;
    }

    public List<AbstractRule> getAllRules() {
        return allRules;
    }

    public Iterable<ParserRule> getAllParserRules() {
        return Iterables.filter(allRules, ParserRule.class);
    }

    private String getInheritedAntlrRuleName(AbstractRule rule, Set<String> usedNames) {
        if (rule instanceof ParserRule || rule instanceof EnumRule) {
            String candidate = "super" + rule.getName();
            int i = 1;
            while (usedNames.contains(candidate)) {
                candidate = "super" + i + rule.getName();
                i++;
            }
            return candidate;
        }
        if (rule instanceof TerminalRule) {
            String candidate = "SUPER_" + rule.getName();
            int i = 1;
            while (usedNames.contains(candidate)) {
                candidate = "SUPER_" + i + "_" + rule.getName();
                i++;
            }
            return candidate;
        }
        throw new IllegalArgumentException(rule.eClass().getName());
    }

    private String getInheritedUniqueName(AbstractRule rule, Set<String> usedNames) {
        String grammarName = GrammarUtil.getSimpleName(GrammarUtil.getGrammar(rule));
        String candidate = grammarName + rule.getName();
        int i = 1;
        while (usedNames.contains(candidate)) {
            candidate = grammarName + i + rule.getName();
            i++;
        }
        return candidate;
    }

    private String getDefaultAntlrRuleName(AbstractRule rule) {
        if (rule instanceof ParserRule || rule instanceof EnumRule) {
            return "rule" + rule.getName();
        }
        if (rule instanceof TerminalRule) {
            return "RULE_" + rule.getName().toUpperCase();
        }
        throw new IllegalArgumentException(rule.eClass().getName());
    }

}