All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.mihosoft.vmf.vmftext.vmtemplates.model-converter.vm Maven / Gradle / Ivy

The newest version!
#*
 * Copyright 2017-2018 Michael Hoffer . All rights reserved.
 * Copyright 2017-2018 Goethe Center for Scientific Computing, University Frankfurt. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * If you use this software for scientific research then please cite the following publication(s):
 *
 * M. Hoffer, C. Poliwoda, & G. Wittum. (2013). Visual reflection library:
 * a framework for declarative GUI programming on the Java platform.
 * Computing and Visualization in Science, 2013, 16(4),
 * 181–192. http://doi.org/10.1007/s00791-014-0230-y
 *#
package ${packageName};

// java core imports
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;

// antlr4 imports
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.ANTLRErrorListener;
import org.antlr.v4.runtime.ANTLRErrorStrategy;

// model imports
import ${modelPackageName}.*;
import static ${packageName}.${Util.firstToUpper($model.grammarName)}Parser.*;

public final class ${Util.firstToUpper($model.grammarName)}ModelParser {

    public ${Util.firstToUpper($model.grammarName)}ModelParser() {
        //
    }

    private final List errorListeners = new ArrayList<>();
    private ANTLRErrorStrategy errorHandler;
    private boolean consoleOutputDisabled;

    public void setConsoleOutputDisabled(boolean state) {
        this.consoleOutputDisabled = state;
    }

    public boolean isConsoleOutputDisabled() {
        return this.consoleOutputDisabled;
    }

    public List getErrorListeners() {
        return this.errorListeners;
    }

    public void setErrorHandler(ANTLRErrorStrategy errorHandler) {
        this.errorHandler = errorHandler;
    }

    public ${Util.firstToUpper($model.grammarName)}Model parse(InputStream codeStream) throws IOException{
        CharStream input = CharStreams.fromStream(codeStream);

        ${Util.firstToUpper($model.grammarName)}Lexer lexer = new ${Util.firstToUpper($model.grammarName)}Lexer(input);

        for(ANTLRErrorListener l : errorListeners) {
            lexer.addErrorListener(l);
        }

        if(isConsoleOutputDisabled()) {
            lexer.removeErrorListener(org.antlr.v4.runtime.ConsoleErrorListener.INSTANCE);
        }

        CommonTokenStream tokens = new CommonTokenStream(lexer);
        ${Util.firstToUpper($model.grammarName)}Parser parser = new ${Util.firstToUpper($model.grammarName)}Parser(tokens);

        if(errorHandler!=null) {
            parser.setErrorHandler(errorHandler);
        }

        for(ANTLRErrorListener l : errorListeners) {
            parser.addErrorListener(l);
        }

        if(isConsoleOutputDisabled()) {
            parser.removeErrorListener(org.antlr.v4.runtime.ConsoleErrorListener.INSTANCE);
        }

        return parse(parser.${model.rootClass().name}());
    }

    public ${Util.firstToUpper($model.grammarName)}Model parse(File code) throws IOException{
        try(FileInputStream codeStream = new FileInputStream(code)) {
            return parse(codeStream);
        }
    }

    public ${Util.firstToUpper($model.grammarName)}Model parse(String code) {
        try(InputStream codeStream = new ByteArrayInputStream(code.getBytes(StandardCharsets.UTF_8.name()))) {
            return parse(codeStream);
        } catch(Exception ex) {
            throw new RuntimeException(ex);
        }
    }

        

    public ${Util.firstToUpper($model.grammarName)}Model parse(${model.rootClass().nameWithUpper()}Context ctx) {

      ${Util.firstToUpper($model.grammarName)}Model model = ${Util.firstToUpper($model.grammarName)}Model.newInstance();

      parse(model, ctx);

      return model;
    }
    
    
    
    public ${Util.firstToUpper($model.grammarName)}Model parse(${Util.firstToUpper($model.grammarName)}Model model, InputStream codeStream) throws IOException{
      CharStream input = CharStreams.fromStream(codeStream);

      ${Util.firstToUpper($model.grammarName)}Lexer lexer = new ${Util.firstToUpper($model.grammarName)}Lexer(input);

      for(ANTLRErrorListener l : errorListeners) {
          lexer.addErrorListener(l);
      }

      if(isConsoleOutputDisabled()) {
          lexer.removeErrorListener(org.antlr.v4.runtime.ConsoleErrorListener.INSTANCE);
      }

      CommonTokenStream tokens = new CommonTokenStream(lexer);
      ${Util.firstToUpper($model.grammarName)}Parser parser = new ${Util.firstToUpper($model.grammarName)}Parser(tokens);

      if(errorHandler!=null) {
          parser.setErrorHandler(errorHandler);
      }

      for(ANTLRErrorListener l : errorListeners) {
          parser.addErrorListener(l);
      }

      if(isConsoleOutputDisabled()) {
          parser.removeErrorListener(org.antlr.v4.runtime.ConsoleErrorListener.INSTANCE);
      }

      return parse(model, parser.${model.rootClass().name}());
  }

  public ${Util.firstToUpper($model.grammarName)}Model parse(${Util.firstToUpper($model.grammarName)}Model model, File code) throws IOException{
      try(FileInputStream codeStream = new FileInputStream(code)) {
          return parse(model, codeStream);
      }
  }

  public ${Util.firstToUpper($model.grammarName)}Model parse(${Util.firstToUpper($model.grammarName)}Model model, String code) {
      try(InputStream codeStream = new ByteArrayInputStream(code.getBytes(StandardCharsets.UTF_8.name()))) {
          return parse(model, codeStream);
      } catch(Exception ex) {
          throw new RuntimeException(ex);
      }
  }
    

  public ${Util.firstToUpper($model.grammarName)}Model parse(${Util.firstToUpper($model.grammarName)}Model model, ${model.rootClass().nameWithUpper()}Context ctx) {

      model.setRoot(convert(ctx));

      return model;
  }

#foreach( $rcls in ${model.ruleClasses} )##foreach rule class
// ---- ${rcls.nameWithUpper()} CLASS PARSING BEGIN ----

#if(${rcls.getChildClasses().isEmpty()})## if rcls has no classes (we are no rule with labeled rule alts)

    public ${rcls.nameWithUpper()} parse${rcls.nameWithUpper()}(InputStream codeStream) throws IOException {
        return parse(${rcls.nameWithUpper()}.newInstance(), codeStream); 
    }

    public ${rcls.nameWithUpper()} parse${rcls.nameWithUpper()}(File code) throws IOException {
        return parse(${rcls.nameWithUpper()}.newInstance(), code); 
    }

    public ${rcls.nameWithUpper()} parse${rcls.nameWithUpper()}(String code) {
        return parse(${rcls.nameWithUpper()}.newInstance(), code); 
    }
#else

    public ${rcls.nameWithUpper()} parse${rcls.nameWithUpper()}(InputStream codeStream) throws IOException {
      CharStream input = CharStreams.fromStream(codeStream);

      ${Util.firstToUpper($model.grammarName)}Lexer lexer = new ${Util.firstToUpper($model.grammarName)}Lexer(input);

      for(ANTLRErrorListener l : errorListeners) {
          lexer.addErrorListener(l);
      }

      if(isConsoleOutputDisabled()) {
          lexer.removeErrorListener(org.antlr.v4.runtime.ConsoleErrorListener.INSTANCE);
      }

      CommonTokenStream tokens = new CommonTokenStream(lexer);
      ${Util.firstToUpper($model.grammarName)}Parser parser = new ${Util.firstToUpper($model.grammarName)}Parser(tokens);

      if(errorHandler!=null) {
          parser.setErrorHandler(errorHandler);
      }

      for(ANTLRErrorListener l : errorListeners) {
          parser.addErrorListener(l);
      }

      if(isConsoleOutputDisabled()) {
          parser.removeErrorListener(org.antlr.v4.runtime.ConsoleErrorListener.INSTANCE);
      }

#if(!${rcls.getChildClasses().isEmpty()})## if rcls has child classes    
      // we use a delegating convert
      return convert(parser.${rcls.nameWithLower()}());
#elseif(!${rcls.getSuperClass()})## if rcls has super-class 
      // we use a non-delegating convert ${rcls.getSuperClass().nameWithLower()}
      return convert(parser.${rcls.nameWithLower()}());  
#else
      // we use a delegating convert (we are the labeled alt)  
      return (${rcls.nameWithUpper()}) convert(parser.${rcls.getSuperClass().nameWithLower()}());
#end##  if rcls has child classes    
  }

    public ${rcls.nameWithUpper()} parse${rcls.nameWithUpper()}(File code) throws IOException {
      try(FileInputStream codeStream = new FileInputStream(code)) {
          return parse${rcls.nameWithUpper()}(codeStream);
      }
    }

    public ${rcls.nameWithUpper()} parse${rcls.nameWithUpper()}(String code) {
      try(InputStream codeStream = new ByteArrayInputStream(code.getBytes(StandardCharsets.UTF_8.name()))) {
          return parse${rcls.nameWithUpper()}(codeStream);
      } catch(Exception ex) {
          throw new RuntimeException(ex);
      }
    }   
   
#end## if rcls has no classes (we are no rule with labeled rule alts)
    
    public ${rcls.nameWithUpper()} parse(${rcls.nameWithUpper()} ${rcls.nameWithLower()}, InputStream codeStream) throws IOException {
      CharStream input = CharStreams.fromStream(codeStream);

      ${Util.firstToUpper($model.grammarName)}Lexer lexer = new ${Util.firstToUpper($model.grammarName)}Lexer(input);

      for(ANTLRErrorListener l : errorListeners) {
          lexer.addErrorListener(l);
      }

      if(isConsoleOutputDisabled()) {
          lexer.removeErrorListener(org.antlr.v4.runtime.ConsoleErrorListener.INSTANCE);
      }

      CommonTokenStream tokens = new CommonTokenStream(lexer);
      ${Util.firstToUpper($model.grammarName)}Parser parser = new ${Util.firstToUpper($model.grammarName)}Parser(tokens);

      if(errorHandler!=null) {
          parser.setErrorHandler(errorHandler);
      }

      for(ANTLRErrorListener l : errorListeners) {
          parser.addErrorListener(l);
      }

      if(isConsoleOutputDisabled()) {
          parser.removeErrorListener(org.antlr.v4.runtime.ConsoleErrorListener.INSTANCE);
      }

#if(!${rcls.getChildClasses().isEmpty()})## if rcls has child classes

      ParserRuleContext parsedCtx = parser.${rcls.nameWithLower()}();

      // check whether parser result matches expected rule type
      if(!(parsedCtx instanceof ${rcls.nameWithUpper()}Context)) {
          throw new ClassCastException("Parser found a '"+parsedCtx.getClass().getSimpleName()+"' instead of the requested '${rcls.nameWithUpper()}Context'.");
      }

      // we use a delegating convert
      return convert((${rcls.nameWithUpper()}Context)parsedCtx, ${rcls.nameWithLower()});
#elseif(!${rcls.getSuperClass()})## if rcls has super-class 

      ParserRuleContext parsedCtx = parser.${rcls.nameWithLower()}();

      // check whether parser result matches expected rule type
      if(!(parsedCtx instanceof ${rcls.nameWithUpper()}Context)) {
          throw new ClassCastException("Parser found a '"+parsedCtx.getClass().getSimpleName()+"' instead of the requested '${rcls.nameWithUpper()}Context'.");
      }

      // we use a non-delegating convert
      return convert((${rcls.nameWithUpper()}Context)parsedCtx, ${rcls.nameWithLower()});
#else

      ParserRuleContext parsedCtx = parser.${rcls.getSuperClass().nameWithLower()}();

      // check whether parser result matches expected rule type
      if(!(parsedCtx instanceof ${rcls.nameWithUpper()}Context)) {
          throw new ClassCastException("Parser found a '"+parsedCtx.getClass().getSimpleName()+"' instead of the requested '${rcls.nameWithUpper()}Context'.");
      }

      // we use a delegating convert (we are the labeled alt)  
      return (${rcls.nameWithUpper()}) convert((${rcls.getSuperClass().nameWithUpper()}Context)parsedCtx, ${rcls.nameWithLower()});
#end##  if rcls has child classes    
  }

  public ${rcls.nameWithUpper()} parse(${rcls.nameWithUpper()} ${rcls.nameWithLower()}, File code) throws IOException{
      try(FileInputStream codeStream = new FileInputStream(code)) {
          return parse(${rcls.nameWithLower()}, codeStream);
      }
  }

  public ${rcls.nameWithUpper()} parse(${rcls.nameWithUpper()} ${rcls.nameWithLower()}, String code) {
      try(InputStream codeStream = new ByteArrayInputStream(code.getBytes(StandardCharsets.UTF_8.name()))) {
          return parse(${rcls.nameWithLower()}, codeStream);
      } catch(Exception ex) {
          throw new RuntimeException(ex);
      }
  }


// ---- ${rcls.nameWithUpper()} CLASS PARSING END ----
#end##foreach rule class    

#foreach( $rcls in ${model.ruleClasses} )


#if($rcls.getChildClasses().isEmpty())## if rcls has no child classes

    private ${rcls.nameWithUpper()} convert(${rcls.nameWithUpper()}Context ctx) {
        return convert(ctx, ${rcls.nameWithUpper()}.newInstance());
    }

    private ${rcls.nameWithUpper()} convert(${rcls.nameWithUpper()}Context ctx, ${rcls.nameWithUpper()} ${rcls.nameWithLower()}) {

        // ----------------------------------------
        // Initializing Rule Instance
        // ----------------------------------------

        // create model instance
        // ${rcls.nameWithUpper()} ${rcls.nameWithLower()} = ${rcls.nameWithUpper()}.newInstance();

        // set code range (converted from antlr start and stop tokens)
        ${rcls.nameWithLower()}.setCodeRange(ctxToCodeRange(ctx));

        // ----------------------------------------
        // Converting Properties
        // ----------------------------------------
#foreach( $p in $rcls.properties )

#if($p.type.ruleType)
#if($p.type.arrayType)
        // convert elements of rule attribute $rcls.nameWithUpper().$p.nameWithLower()
        List<${p.type.asJavaTypeNameNoCollections()}> convertedElements$p.nameWithUpper() = ctx.${p.nameWithLower()}.stream().
            map(entry->convert(entry)).collect(Collectors.toList());
        // assign elements to model entity
        ${rcls.nameWithLower()}.get${p.nameWithUpper()}().addAll(convertedElements$p.nameWithUpper());
#else
        // convert and assign rule attribute $rcls.nameWithUpper().$p.nameWithLower()
        // if it is present
        if(ctx.${p.nameWithLower()}!=null) {
            ${rcls.nameWithLower()}.set${p.nameWithUpper()}(convert(ctx.${p.nameWithLower()}));
        }
#end## if array type
#else## if rule type
#if($p.type.arrayType)
        // convert elements of rule attribute $rcls.nameWithUpper().$p.nameWithLower()

        // proper conversion of primitives, e.g., lexer rules to int, double etc. depends on type mappings
        // pseudo-code:
        //
        // if p.type needs conversion then
        //   typemapping =  getTypeMappings(p.type.antlrRule)
        //   List  convertedElements = ... map(entry->typemapping.convert(entry)) ...
        //
           List<$model.getTypeMappings().targetTypeNameOfMapping($rcls.nameWithUpper(), $p.type.getAntlrRuleName())> convertedElements$p.nameWithUpper() =
           ctx.${p.nameWithLower()}.stream().map(entry->${model.getTypeMappings().conversionCodeOfMappingStringToType($rcls.nameWithUpper(), $p.type.getAntlrRuleName())}).
             collect(Collectors.toList());

        // FALLBACK: for lexer rules without conversion rules
        // List convertedElements$p.nameWithUpper() = ctx.${p.nameWithLower()}.stream().
        //    map(entry->entry.getText()).collect(Collectors.toList());
        // assign elements to model entity
        ${rcls.nameWithLower()}.get${p.nameWithUpper()}().addAll(convertedElements$p.nameWithUpper());
#else## if array type
        // TODO proper conversion of primitives, e.g., lexer rules to int, double etc.
        // convert and assign rule attribute $rcls.nameWithUpper().$p.nameWithLower()
        // if it is present
        if(ctx.${p.nameWithLower()}!=null) {
            // FALLBACK:
            // ${rcls.nameWithLower()}.set${p.nameWithUpper()}(ctx.${p.nameWithLower()}.getText());
            Token entry = ctx.${p.nameWithLower()};
            ${rcls.nameWithLower()}.set${p.nameWithUpper()}(${model.getTypeMappings().conversionCodeOfMappingStringToType($rcls.nameWithUpper(), $p.type.getAntlrRuleName())});
        }
#end## if array type
#end## if rule type
#end## foreach property in rule class

        return ${rcls.nameWithLower()};
    }
#else## if rcls has no child classes
    private ${rcls.nameWithUpper()} convert(${rcls.nameWithUpper()}Context ctx) {
        // ----------------------------------------
        // Delegating to labeled alternatives:
        //
        // -> rules with alt-labels are never used
        //    directly but always delegate to child
        //    rules which inherit from the rule
        // ----------------------------------------

        // -- BEGIN: delegation
#foreach( $r in $rcls.childClasses )
        if(ctx instanceof ${r.nameWithUpper()}Context) {
            return convert((${r.nameWithUpper()}Context)ctx);
        }
#end## for-each child rule
        // -- END:   delegation

        // WARNING: 10.11.2017 This should not happen:
        //                     The current design does not use rules with alt labeled directly but
        //                     only sub classes
        // TODO: 10.11.2017    enforce validation/testing for this specific case
        System.err.println("VMF[WARNING]: rule-class '${r.nameWithUpper()}' must delegate to child classes.");
        return null;
    }

    private ${rcls.nameWithUpper()} convert(${rcls.nameWithUpper()}Context ctx, ${rcls.nameWithUpper()} ${rcls.nameWithLower()}) {
        // ----------------------------------------
        // Delegating to labeled alternatives:
        //
        // -> rules with alt-labels are never used
        //    directly but always delegate to child
        //    rules which inherit from the rule
        // ----------------------------------------

        // -- BEGIN: delegation
#foreach( $r in $rcls.childClasses )
        if(ctx instanceof ${r.nameWithUpper()}Context) {
            return convert((${r.nameWithUpper()}Context)ctx, (${r.nameWithUpper()}) ${rcls.nameWithLower()});
        }
#end## for-each child rule
        // -- END:   delegation

        // WARNING: 10.11.2017 This should not happen:
        //                     The current design does not use rules with alt labeled directly but
        //                     only sub classes
        // TODO: 10.11.2017    enforce validation/testing for this specific case
        System.err.println("VMF[WARNING]: rule-class '${r.nameWithUpper()}' must delegate to child classes.");
        return null;
    }
#end## if rcls has no child classes


#end## foreach rule class

    // --------------------------------------------------------------------------------
    // UTILITY METHODS
    // --------------------------------------------------------------------------------

    public static CodeLocation tokenToCodeLocationStart(Token t) {
      return CodeLocation.newBuilder().
        withIndex(t.getStartIndex()).
          withCharPosInLine(t.getCharPositionInLine()).withLine(t.getLine()).build();
    }

    public static CodeLocation tokenToCodeLocationStop(Token t, ParserRuleContext ctx) {
        int startIndex = t.getStartIndex();
        int stopIndex = t.getStopIndex();
        int length = stopIndex - startIndex;

        return CodeLocation.newBuilder().
            withIndex(stopIndex).
            withCharPosInLine(t.getCharPositionInLine()+length).
            withLine(/*TODO 14.02.2018 does not work for multiple-line-tokens*/t.getLine()).build();
    }

    public static CodeRange ctxToCodeRange(ParserRuleContext ctx) {
      // consider for stop line: https://stackoverflow.com/a/17487805
      CodeRange.Builder builder = CodeRange.newBuilder().withStart(tokenToCodeLocationStart(ctx.start));
      CodeLocation start = tokenToCodeLocationStart(ctx.start);
      CodeLocation stop;

      if(ctx.stop==null) {
        // if no stop is present use start token
        stop = tokenToCodeLocationStop(ctx.start,ctx);
        builder.withStop(stop);
      } else {
        stop = tokenToCodeLocationStop(ctx.stop,ctx);
        builder.withStop(stop);
      }

      return builder.withLength(stop.getIndex()-start.getIndex()+1/*+1 because of inclusive vs. exclusive*/).build();
    }

// TODO remove this class if compile time conversion works
/*
    // --------------------------------------------------------------------------------
    // TYPE CONVERSION UTILITY CLASS
    // --------------------------------------------------------------------------------

    public TypeConversionRegistry getTypeConversionRegistry() {
        return typeConversionRegistry;
    }


    private static class TypeConversionRegistryImpl implements TypeConversionRegistry {

      private final Map> converterMap = new HashMap<>();

      @Override
      public void registerConverter(TypeConverter t, String ruleClsName, String propertyRuleName, String outputTypeName) {
        getConverterMap(ruleClsName).put(propertyRuleName,t);
      }

      @Override
      public void registerConverter(TypeConverter t, String propertyRuleName, String outputTypeName) {
        getConverterMap("").put(propertyRuleName,t);
      }

      @Override
      public Object convert(String ruleClsName, String propertyRuleName, Object obj) {

        System.out.println("> conversion requested for '"+ruleClsName+"::"+propertyRuleName + "'");

        Map converters =
        getConverterMap(ruleClsName);

        if(converters.containsKey(propertyRuleName)) {
            System.out.println(" -> found converter registered for property type '"+propertyRuleName+"'.");

            TypeConverter tC = converters.get(propertyRuleName);

            return tC.convert(obj);
        }

        System.out.println(" -> looking for global converter for property type '"+propertyRuleName+"'.");

        Map globalConverters =
        getConverterMap("");

        if(globalConverters.containsKey(propertyRuleName)) {
            System.out.println("-> found global converter for property type '"+propertyRuleName+"'.");

            return globalConverters.get(propertyRuleName);
        }

            throw new RuntimeException("Cannot find requested type conversion for'"
              +ruleClsName+"::"+propertyRuleName + "'");
        }

        private Map getConverterMap(String ruleClassName) {

        Map result = converterMap.get(ruleClassName);


        if(result==null) {
            result = new HashMap();
            converterMap.put(ruleClassName, result);
        }

        return result;
      }
    } // end TypeConversionRegistryImpl

*/

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy