2016-04-30 5 views
0

FreeMarkerを使用してテンプレートベースのコードジェネレータを構築しています。ユーザーは任意の言語でコードを生成できるため、データモデルに言語固有の設定(パッケージなど)を提供することは適切ではありません。ただし、FreeMarkerテンプレートで定義されている場合は、それらを定義する必要があります(オプションでない限り)。FreeMarkerで未定義の変数を検出する

このコードでは、FreeMarkerによってスローされた例外を使用して欠損値を検出します。次に、それらに一時的な値を設定して、他の欠損値が見つかるようにします。

値がルートデータモデルにある場合、これはうまくいきます(ただし、FreeMarkerのエラーメッセージを抑制することはできません)。しかし、欠落している変数の1つが深いレベルになるとすぐに、テンプレート全体を解析して問題を把握する必要があるようです。

これは、欠落値を検出してユーザーにオンザフライを促すためです。 Javaを生成している場合は、パッケージを要求する可能性があります。 C++?たぶんプラグマディレクティブ。

とにかく、これをより効果的に行う方法を知っている人はいますか?

作業用コードとテンプレートが続きます。

出典FMCodeGenTest.java

package codegen; 

import freemarker.cache.*; 
import freemarker.core.ParseException; 
import freemarker.template.*; 
import java.io.*; 
import java.util.*; 

public class FMCodeGenTest { 
    private Configuration mConfig = null; 
    private HashMap mDataModel = null; 
    private Template mTemplate = null; 


    public void init() { 
     mConfig = new Configuration(Configuration.VERSION_2_3_22); 

     try { 
      mConfig.setDirectoryForTemplateLoading(new File("./templates")); 
      mConfig.setDefaultEncoding("UTF-8"); 
      mConfig.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); 
     } catch (IOException ie) { 
      System.out.println("Error reading templates."); 
     }  
    } 

    public void buildDataModel() { 
     mDataModel = new HashMap(); 
     mDataModel.put("user", "Foo"); 

     ArrayList vars = new ArrayList(); 
     mDataModel.put("vars", vars); 

     HashMap var = new HashMap(); 
     vars.add(var); 
     var.put("name", "apple"); 
     var.put("type", "String"); 
    } 

    public void getTemplate() { 
     try { 
      mTemplate = mConfig.getTemplate("java_error.ftl"); 

     } catch (MalformedTemplateNameException ex) { 
      System.out.println("Malformed Template Name : " + ex.getMessage()); 

     } catch (ParseException ex) { 
      System.out.println("Parse Error : " + ex.getMessage()); 

     } catch (IOException ex) { 
      System.out.println("IO Exception : " + ex.getMessage()); 
     } 

    } 

    public void detectUndefinedVariables() { 
     boolean hasBadVars = false; 

     do { 
      hasBadVars = false; 

      try { 
       mTemplate.process(mDataModel, new NullWriter()); 

      } catch (TemplateException ex) { 
       hasBadVars = true; 

       mDataModel.put(ex.getBlamedExpressionString(), "<temporary value>"); 

      } catch (IOException ex) { 
       System.out.println("IO Exception : " + ex.getMessage()); 
      } 
     } while (hasBadVars); 
    } 


    public void generateCode() { 
     /* Merge data-model with template */ 
     Writer out = new OutputStreamWriter(System.out); 
     try { 
      mTemplate.process(mDataModel, out); 

     } catch (TemplateException ex) { 
      System.out.println("Template Exception : " + ex.getMessage()); 

     } catch (IOException ex) { 
      System.out.println("IO Exception : " + ex.getMessage()); 
     } 
    } 

    static public void main(String [] args) { 
     FMCodeGenTest test = new FMCodeGenTest(); 

     test.init(); 

     test.buildDataModel(); 

     test.getTemplate(); 

     test.detectUndefinedVariables(); 

     test.generateCode(); 
    } 
} 

テンプレートjava_error.ftl

package ${package}; 

/** 
* 
* @author ${user} 
*/ 
public class ${name} { 
    <#list vars as var> 
    private ${var.type} _${var.name}; 
    nontrivial ${var.notthere}; 
    </#list> 
} 
+0

「FreeMarkerのエラーメッセージを抑制することができないようです」のログメッセージを参照してください。次に、 'mConfig.setLogTemplateExceptions(false)'を使います。 (また、コンソールでメッセージを見た場合、適切にログを設定する必要があります。) – ddekany

+0

@ddekany Ahh、ありがとう。私はそれを逃した - 確かにそれを試してみます。 – Chris

答えて

1

私はこれがないInvalidReferenceException例外をキャッチしてますが、特殊なデータ・モデルを使用して行われるべきだと思います。データモデルそのものは、不足している変数の入力を求めます。したがって、ユーザーが提供する値をデータモデルに追加する場所を常に知っていて、テンプレート自体を処理する必要はありません。

+0

私はこの勧告をよく理解していません。この考え方は、単一のデータモデルからコードを生成することです。言語固有の変数が設定されていることを(テンプレートから)ユーザーにどのように伝えますか? – Chris

+1

質問では、あなたは「オンザフライでユーザーにプロンプ​​トを出す」と言いました。 'null 'を返すときはいつでも、' TemplateHashModel.get'の中でそれを行うことができます。それは元の質問で捕捉して分析したい 'InvalidReferenceException'につながるものです(' null'を返します)。エラー状況自体を防ぐので、欠落している変数を検出した後にテンプレートを再実行する必要はありません。 – ddekany

+0

ありがとうございます。私のデータモデルをTemplateHashModelに入れることで、nullが返されたとき(たとえば、データモデルに値がない値が要求されたときなど)に、ユーザーとのやり取りを行うことができました。 – Chris