/*
* ******************************************************************************
* MontiCore Language Workbench
* Copyright (c) 2015, MontiCore, All rights reserved.
*
* This project is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this project. If not, see <http://www.gnu.org/licenses/>.
* ******************************************************************************
*/
package de.monticore.grammar;
import de.monticore.grammar.grammar._ast.*;
import de.monticore.utils.ASTNodes;
import de.monticore.ast.ASTNode;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static com.google.common.collect.Lists.newArrayList;
import static de.monticore.codegen.mc2cd.TransformationHelper.getName;
import static de.monticore.codegen.mc2cd.TransformationHelper.getUsageName;
import static de.monticore.utils.ASTNodes.getIntermediates;
import static de.monticore.utils.ASTNodes.getSuccessors;
import static java.util.Collections.max;
/**
* Denotes the multiplicity of nonterminals in a MC grammar such as '*', '+', or '?'.
*
* @author Sebastian Oberhoff
*/
public enum Multiplicity {
/**
* No quantifier present
*/
STANDARD,
/**
* '?' quantifier present
*/
OPTIONAL,
/**
* '*' or '+' quantifier present
*/
LIST;
public static Multiplicity determineMultiplicity(ASTNode rootNode, ASTNode astNode) {
if (astNode instanceof ASTAttributeInAST) {
return multiplicityOfAttributeInAST((ASTAttributeInAST) astNode);
}
return multiplicityOfASTNode(rootNode, astNode);
}
public static Multiplicity multiplicityOfAttributeInAST(ASTAttributeInAST attributeInAST) {
if (!attributeInAST.getCard().isPresent()) {
return STANDARD;
}
ASTCard cardinality = attributeInAST.getCard().get();
if (!cardinality.getMax().isPresent() || cardinality.isUnbounded()
|| "*".equals(cardinality.getMax().get())
|| getMaxCardinality(cardinality) != 1) {
return LIST;
}
else {
if (!cardinality.getMin().isPresent() || getMinCardinality(cardinality)==0) {
return OPTIONAL;
}
}
return STANDARD;
}
private static int getMaxCardinality(ASTCard cardinality) {
return Integer.parseInt(cardinality.getMax().get());
}
private static int getMinCardinality(ASTCard cardinality) {
return Integer.parseInt(cardinality.getMin().get());
}
private static Multiplicity multiplicityOfASTNode(ASTNode rootNode, ASTNode astNode) {
Multiplicity byAlternative = multiplicityByAlternative(rootNode, astNode);
Multiplicity byDuplicates = multiplicityByDuplicates(rootNode, astNode);
Multiplicity byIteration = multiplicityByIteration(rootNode, astNode);
ArrayList<Multiplicity> newArrayList = newArrayList(byDuplicates, byIteration, byAlternative);
return max(newArrayList);
}
public static Multiplicity multiplicityByAlternative(ASTNode rootNode, ASTNode astNode) {
List<ASTNode> intermediates = getIntermediates(rootNode, astNode);
boolean containedInAlternative = false;
for (ASTNode intermediate: intermediates) {
if (intermediate instanceof ASTClassProd) {
containedInAlternative |= ((ASTClassProd) intermediate).getAlts().size()>1;
} else if (intermediate instanceof ASTBlock) {
containedInAlternative |= ((ASTBlock) intermediate).getAlts().size()>1;
}
}
return containedInAlternative ? OPTIONAL : STANDARD;
}
public static Multiplicity multiplicityByDuplicates(ASTNode rootNode, ASTNode astNode) {
boolean hasDuplicate = getAllNodesInRelatedRuleComponents(rootNode, astNode)
.anyMatch(sibling -> areDuplicates(rootNode, astNode, sibling));
if (hasDuplicate) {
return LIST;
}
else {
return STANDARD;
}
}
private static boolean areDuplicates(ASTNode rootNode, ASTNode firstNode, ASTNode secondNode) {
Optional<String> firstName = getName(firstNode);
Optional<String> firstUsageName = getUsageName(rootNode, firstNode);
Optional<String> secondName = getName(secondNode);
Optional<String> secondUsageName = getUsageName(rootNode, secondNode);
boolean bothUsageNamesAbsent = !firstUsageName.isPresent() && !secondUsageName.isPresent();
boolean namesMatch = firstName.equals(secondName);
boolean usageNamesMatch = firstUsageName.equals(secondUsageName);
return (bothUsageNamesAbsent && namesMatch) || (!bothUsageNamesAbsent && usageNamesMatch);
}
private static Stream<ASTNode> getAllNodesInRelatedRuleComponents(ASTNode rootNode,
ASTNode astNode) {
Set<ASTRuleComponent> ancestorRuleComponents = getIntermediates(rootNode, astNode).stream()
.filter(ASTRuleComponent.class::isInstance)
.map(ASTRuleComponent.class::cast)
.collect(Collectors.toSet());
return getIntermediates(rootNode, astNode).stream()
.filter(ASTAlt.class::isInstance)
.map(ASTAlt.class::cast)
.flatMap(alt -> alt.getComponents().stream())
.filter(ruleComponent -> !ancestorRuleComponents.contains(ruleComponent))
.flatMap(ruleComponent -> getSuccessors(ruleComponent, ASTNode.class).stream());
}
public static Multiplicity multiplicityByIteration(ASTNode rootNode, ASTNode astNode) {
Multiplicity multiplicity = STANDARD;
for (ASTNode intermediate : ASTNodes.getIntermediates(rootNode, astNode)) {
int iteration = getIterationInt(intermediate);
if (iteration == ASTConstantsGrammar.PLUS || iteration == ASTConstantsGrammar.STAR) {
multiplicity = LIST;
}
if (iteration == ASTConstantsGrammar.QUESTION && multiplicity != LIST) {
multiplicity = OPTIONAL;
}
}
return multiplicity;
}
private static int getIterationInt(ASTNode ancestor) {
int iteration = ASTConstantsGrammar.DEFAULT;
if (ancestor instanceof ASTBlock) {
iteration = ((ASTBlock) ancestor).getIteration();
}
if (ancestor instanceof ASTNonTerminal) {
iteration = ((ASTNonTerminal) ancestor).getIteration();
}
return iteration;
}
}