/* * ****************************************************************************** * 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.codegen.mc2cd; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Strings.isNullOrEmpty; import static de.monticore.codegen.GeneratorHelper.AST_DOT_PACKAGE_SUFFIX_DOT; import java.io.IOException; import java.io.StringReader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.Lists; import de.monticore.ast.ASTNode; import de.monticore.codegen.GeneratorHelper; import de.monticore.codegen.cd2java.ast.AstGeneratorHelper; import de.monticore.generating.templateengine.reporting.Reporting; import de.monticore.grammar.grammar._ast.ASTAttributeInAST; import de.monticore.grammar.grammar._ast.ASTClassProd; import de.monticore.grammar.grammar._ast.ASTConstant; import de.monticore.grammar.grammar._ast.ASTConstantGroup; import de.monticore.grammar.grammar._ast.ASTEnumProd; import de.monticore.grammar.grammar._ast.ASTGenericType; import de.monticore.grammar.grammar._ast.ASTMCGrammar; import de.monticore.grammar.grammar._ast.ASTNonTerminal; import de.monticore.grammar.grammar._ast.ASTNonTerminalSeparator; import de.monticore.grammar.grammar._ast.ASTTerminal; import de.monticore.grammar.symboltable.MCGrammarSymbol; import de.monticore.grammar.symboltable.MCProdComponentSymbol; import de.monticore.grammar.symboltable.MCProdSymbol; import de.monticore.io.paths.IterablePath; import de.monticore.prettyprint.IndentPrinter; import de.monticore.types.types._ast.ASTPrimitiveType; import de.monticore.types.types._ast.ASTSimpleReferenceType; import de.monticore.types.types._ast.ASTType; import de.monticore.types.types._ast.ASTTypeArgument; import de.monticore.types.types._ast.ASTVoidType; import de.monticore.types.types._ast.TypesNodeFactory; import de.monticore.umlcd4a.cd4analysis._ast.ASTCD4AnalysisNode; import de.monticore.umlcd4a.cd4analysis._ast.ASTCDAttribute; import de.monticore.umlcd4a.cd4analysis._ast.ASTCDClass; import de.monticore.umlcd4a.cd4analysis._ast.ASTCDCompilationUnit; import de.monticore.umlcd4a.cd4analysis._ast.ASTCDInterface; import de.monticore.umlcd4a.cd4analysis._ast.ASTCDParameter; import de.monticore.umlcd4a.cd4analysis._ast.ASTModifier; import de.monticore.umlcd4a.cd4analysis._ast.ASTStereoValue; import de.monticore.umlcd4a.cd4analysis._ast.CD4AnalysisNodeFactory; import de.monticore.umlcd4a.cd4analysis._parser.CD4AnalysisParser; import de.monticore.umlcd4a.prettyprint.CDPrettyPrinterConcreteVisitor; import de.monticore.utils.ASTNodes; import de.se_rwth.commons.Names; import de.se_rwth.commons.logging.Log; public final class TransformationHelper { public static final String DEFAULT_FILE_EXTENSION = ".java"; public final static String GENERATED_CLASS_SUFFIX = "TOP"; public static final String AST_PREFIX = "AST"; public static final String AST_PACKAGE_SUFFIX = "_ast"; private TransformationHelper() { // noninstantiable } public static String getClassProdName(ASTClassProd classProd) { return classProd.getName(); } public static String typeToString(ASTType type) { if (type instanceof ASTSimpleReferenceType) { return Names.getQualifiedName( ((ASTSimpleReferenceType) type).getNames()); } else if (type instanceof ASTPrimitiveType) { return type.toString(); } return ""; } public static String typeReferenceToString(ASTGenericType typeReference) { return typeReference.getTypeName(); } /** * Pretty prints a CD AST to a String object. * * @param cdCompilationUnit the top node of the CD AST to be pretty printed */ // TODO: should be placed somewhere in the UML/P CD project public static String prettyPrint(ASTCD4AnalysisNode astNode) { // set up objects CDPrettyPrinterConcreteVisitor prettyPrinter = new CDPrettyPrinterConcreteVisitor( new IndentPrinter()); // run, check result and return String prettyPrintedAst = prettyPrinter.prettyprint(astNode); checkArgument(!isNullOrEmpty(prettyPrintedAst)); return prettyPrintedAst; } public static Optional<String> getUsageName(ASTNode root, ASTNode successor) { List<ASTNode> intermediates = ASTNodes .getIntermediates(root, successor); for (ASTNode ancestor : Lists.reverse(intermediates)) { if (ancestor instanceof ASTConstantGroup) { return Optional.ofNullable(((ASTConstantGroup) ancestor) .getUsageName().orElse(null)); } if (ancestor instanceof ASTNonTerminal) { return Optional.ofNullable(((ASTNonTerminal) ancestor) .getUsageName().orElse(null)); } if (ancestor instanceof ASTNonTerminalSeparator) { return Optional.ofNullable(((ASTNonTerminalSeparator) ancestor) .getUsageName().orElse(null)); } if (ancestor instanceof ASTTerminal) { return Optional.ofNullable(((ASTTerminal) ancestor).getUsageName() .orElse(null)); } if (ancestor instanceof ASTAttributeInAST) { return ((ASTAttributeInAST) ancestor).getName(); } } return Optional.empty(); } public static Optional<String> getName(ASTNode node) { try { Method getNameMethod = node.getClass().getMethod("getName"); String name = (String) getNameMethod.invoke(node); return Optional.ofNullable(name); } catch (ClassCastException | InvocationTargetException | IllegalAccessException | IllegalArgumentException | NoSuchMethodException | SecurityException e) { return Optional.empty(); } } public static ASTModifier createFinalModifier() { ASTModifier modifier = CD4AnalysisNodeFactory.createASTModifier(); modifier.setFinal(true); return modifier; } public static ASTCDParameter createParameter(String typeName, String parameterName) { ASTCDParameter parameter = CD4AnalysisNodeFactory .createASTCDParameter(); parameter.setType(TransformationHelper.createSimpleReference(typeName)); parameter.setName(parameterName); return parameter; } public static ASTModifier createPrivateModifier() { ASTModifier modifier = CD4AnalysisNodeFactory.createASTModifier(); modifier.setPrivate(true); return modifier; } public static ASTModifier createStaticModifier() { ASTModifier modifier = CD4AnalysisNodeFactory.createASTModifier(); modifier.setStatic(true); return modifier; } public static ASTModifier createProtectedModifier() { ASTModifier modifier = CD4AnalysisNodeFactory.createASTModifier(); modifier.setProtected(true); return modifier; } public static ASTModifier createPublicModifier() { ASTModifier modifier = CD4AnalysisNodeFactory.createASTModifier(); modifier.setPublic(true); return modifier; } public static ASTSimpleReferenceType createSimpleReference( String typeName, String... generics) { ASTSimpleReferenceType reference = TypesNodeFactory .createASTSimpleReferenceType(); // set the name of the type ArrayList<String> name = new ArrayList<String>(); name.add(typeName); reference.setNames(name); // set generics if (generics.length > 0) { List<ASTTypeArgument> typeArguments = new ArrayList<>(); for (String generic : generics) { typeArguments.add(createSimpleReference(generic)); } reference.setTypeArguments(TypesNodeFactory .createASTTypeArguments(typeArguments)); } return reference; } public static ASTVoidType createVoidType() { return TypesNodeFactory.createASTVoidType(); } public static String grammarName2PackageName(MCGrammarSymbol grammar) { return grammar.getFullName() + "."; } public static String getPackageName(MCProdSymbol symbol) { // return grammar.getName().toLowerCase() + AST_DOT_PACKAGE_SUFFIX_DOT; return getGrammarName(symbol) + "."; } public static String getAstPackageName( ASTCDCompilationUnit cdCompilationUnit) { String packageName = Names .getQualifiedName(cdCompilationUnit.getPackage()); if (!packageName.isEmpty()) { packageName = packageName + "."; } return packageName + cdCompilationUnit.getCDDefinition().getName().toLowerCase() + AST_DOT_PACKAGE_SUFFIX_DOT; } public static String getPackageName( ASTCDCompilationUnit cdCompilationUnit) { String packageName = Names .getQualifiedName(cdCompilationUnit.getPackage()); if (!packageName.isEmpty()) { packageName = packageName + "."; } return packageName + cdCompilationUnit.getCDDefinition().getName() + "."; } public static String getBaseNodeName( ASTCDCompilationUnit cdCompilationUnit) { return getAstPackageName(cdCompilationUnit) + GeneratorHelper.getASTNodeBaseType(cdCompilationUnit.getCDDefinition().getName()); } public static Set<String> getAllGrammarConstants(ASTMCGrammar grammar) { Set<String> constants = new HashSet<>(); MCGrammarSymbol grammarSymbol = MCGrammarSymbolTableHelper .getMCGrammarSymbol(grammar).get(); Preconditions.checkState(grammarSymbol != null); for (MCProdComponentSymbol component : grammarSymbol.getProds().stream() .flatMap(p -> p.getProdComponents().stream()).collect(Collectors.toSet())) { if (component.isConstantGroup()) { for (MCProdComponentSymbol subComponent : component.getSubProdComponents()) { if (subComponent.isConstant()) { constants.add(subComponent.getName()); } } } } for (MCProdSymbol type : grammarSymbol.getProds()) { if (type.isEnum() && type.getAstNode().isPresent() && type.getAstNode().get() instanceof ASTEnumProd) { for (ASTConstant enumValue : ((ASTEnumProd) type.getAstNode().get()).getConstants()) { String humanName = enumValue.getHumanName().isPresent() ? enumValue.getHumanName().get() : enumValue.getName(); constants.add(humanName); } } } return constants; } public static java.util.Optional<ASTCDAttribute> createAttributeUsingCdParser( String toParse) throws IOException { checkArgument(!Strings.isNullOrEmpty(toParse)); java.util.Optional<ASTCDAttribute> astCDAttribute = (new CD4AnalysisParser()) .parseCDAttribute(new StringReader(toParse)); return astCDAttribute; } /** * Checks if a handwritten class with the given qualifiedName (dot-separated) * exists on the target path * * @param qualifiedName name of the class to search for * @return true if a handwritten class with the qualifiedName exists */ public static boolean existsHandwrittenClass(IterablePath targetPath, String qualifiedName) { Path handwrittenFile = Paths.get(Names .getPathFromPackage(qualifiedName) + DEFAULT_FILE_EXTENSION); Log.debug("Checking existence of handwritten class " + qualifiedName + " by searching for " + handwrittenFile.toString(), TransformationHelper.class.getName()); boolean result = targetPath.exists(handwrittenFile); if (result) { Reporting.reportUseHandwrittenCodeFile(targetPath.getResolvedPath(handwrittenFile).get(), handwrittenFile); } else { Reporting.reportUseHandwrittenCodeFile(null, handwrittenFile); } return result; } public static String getQualifiedTypeNameAndMarkIfExternal( ASTGenericType ruleReference, ASTMCGrammar grammar, ASTCDClass cdClass) { Optional<MCProdSymbol> typeSymbol = resolveAstRuleType(ruleReference); String qualifiedRuleName = getQualifiedAstName( typeSymbol, ruleReference, grammar); if (!typeSymbol.isPresent()) { addStereoType(cdClass, MC2CDStereotypes.EXTERNAL_TYPE.toString(), qualifiedRuleName); } return qualifiedRuleName; } // TODO GV: remove this if CDInterface and CDClass have a common type CDType public static String getQualifiedTypeNameAndMarkIfExternal( ASTGenericType ruleReference, ASTMCGrammar grammar, ASTCDInterface interf) { Optional<MCProdSymbol> typeSymbol = resolveAstRuleType(ruleReference); String qualifiedRuleName = getQualifiedAstName( typeSymbol, ruleReference, grammar); if (!typeSymbol.isPresent()) { addStereoType(interf, MC2CDStereotypes.EXTERNAL_TYPE.toString(), qualifiedRuleName); } return qualifiedRuleName; } public static Optional<MCProdSymbol> resolveAstRuleType(ASTGenericType type) { if (!type.getNames().isEmpty()) { String simpleName = type.getNames().get(type.getNames().size() - 1); if (!simpleName.startsWith(AST_PREFIX)) { return Optional.empty(); } Optional<MCProdSymbol> ruleSymbol = MCGrammarSymbolTableHelper.resolveRule(type, simpleName .substring(AST_PREFIX.length())); if (ruleSymbol.isPresent() && istPartOfGrammar(ruleSymbol.get())) { return ruleSymbol; } } return Optional.empty(); } // TODO GV, PN: change it public static boolean istPartOfGrammar(MCProdSymbol rule) { return rule.getEnclosingScope().getAstNode().isPresent() && rule.getEnclosingScope().getAstNode().get() instanceof ASTMCGrammar; } public static String getAstPackage(MCProdSymbol rule) { return AstGeneratorHelper.getAstPackage(Names.getQualifier(rule.getFullName()).toLowerCase()); } public static String getGrammarName(MCProdSymbol rule) { return Names.getQualifier(rule.getFullName()); } public static String getGrammarNameAsPackage(MCProdSymbol rule) { return getGrammarName(rule) + "."; } public static boolean checkIfExternal(ASTGenericType type) { return !resolveAstRuleType(type).isPresent(); } public static String getQualifiedAstName( Optional<MCProdSymbol> typeSymbol, ASTGenericType type, ASTMCGrammar grammar) { if (!typeSymbol.isPresent()) { return type.getTypeName(); } if (type.getNames().size() > 1) { return type.getTypeName(); } String refGrammarName = getGrammarName(typeSymbol.get()); if (grammar.getSymbol().isPresent() && grammar.getSymbol().get().getFullName().equals(refGrammarName)) { return type.getTypeName(); } return refGrammarName + "." + type.getTypeName(); } public static void addStereoType(ASTCDClass type, String stereotypeName, String stereotypeValue) { if (!type.getModifier().isPresent()) { type.setModifier(CD4AnalysisNodeFactory.createASTModifier()); } addStereotypeValue(type.getModifier().get(), stereotypeName, stereotypeValue); } public static void addStereoType(ASTCDInterface type, String stereotypeName, String stereotypeValue) { if (!type.getModifier().isPresent()) { type.setModifier(CD4AnalysisNodeFactory.createASTModifier()); } addStereotypeValue(type.getModifier().get(), stereotypeName, stereotypeValue); } public static void addStereoType(ASTCDAttribute attribute, String stereotypeName, String stereotypeValue) { if (!attribute.getModifier().isPresent()) { attribute.setModifier(CD4AnalysisNodeFactory.createASTModifier()); } addStereotypeValue(attribute.getModifier().get(), stereotypeName, stereotypeValue); } public static void addStereotypeValue(ASTModifier astModifier, String stereotypeName, String stereotypeValue) { if (!astModifier.getStereotype().isPresent()) { astModifier.setStereotype(CD4AnalysisNodeFactory .createASTStereotype()); } List<ASTStereoValue> stereoValueList = astModifier.getStereotype().get() .getValues(); ASTStereoValue stereoValue = CD4AnalysisNodeFactory .createASTStereoValue(); stereoValue.setName(stereotypeName); stereoValue.setValue(stereotypeValue); stereoValueList.add(stereoValue); } }