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>
}
「FreeMarkerのエラーメッセージを抑制することができないようです」のログメッセージを参照してください。次に、 'mConfig.setLogTemplateExceptions(false)'を使います。 (また、コンソールでメッセージを見た場合、適切にログを設定する必要があります。) – ddekany
@ddekany Ahh、ありがとう。私はそれを逃した - 確かにそれを試してみます。 – Chris