要件
・複数のデータクラスを指定可能
・除外名のリストに設定した項目はマージ対象外
・分離名のリストに設定した項目は名称+データ型名に名前を変更して対象とする
※例えば、クラスAでString型のhogeとクラスBでInteger型のhogeがあれば
hogeStringとhogeIntegerとして両方を出力対象とする。
・getterとsetterを生成する
・基底クラス指定可能
・インターフェース指定可能
・出力クラス名指定可能
・出力パッケージ名指定可能
・データクラスが対象なのでシリアルバージョンUIDはデフォルトで固定出力
public static final long serialVersionUID = 1L;は固定
・import文も自動出力
・フォーマットは整えず、自動生成後にフォーマッターで設定する前提とする
クラス構成
develop_util.data_class_merge
┗/JavaSampleCode/src/develop_util/data_class_merge/DataClassMerge.java(メインの自動生成処理)
develop_util.data_class_merge.conf
┣/JavaSampleCode/src/develop_util/data_class_merge/conf/DataClassDefinitionIntf.java(設定ファイルインターフェース)
┗/JavaSampleCode/src/develop_util/data_class_merge/conf/MergeHogeDefinition.java(設定ファイル。出力対象ごとに定義する)
develop_util.data_class_merge.sample_data_class
┣/JavaSampleCode/src/develop_util/data_class_merge/sample_data_class/Hage.java(テスト用マージ元クラス1)
┣/JavaSampleCode/src/develop_util/data_class_merge/sample_data_class/Hige.java(テスト用マージ元クラス2)
┣/JavaSampleCode/src/develop_util/data_class_merge/sample_data_class/Hoge.java(テスト用マージ元クラス3)
┣/JavaSampleCode/src/develop_util/data_class_merge/sample_data_class/Line.java(テスト用マージ元クラスから参照されるデータクラス1)
┣/JavaSampleCode/src/develop_util/data_class_merge/sample_data_class/Other.java(テスト用マージ元クラスから参照されるデータクラス1)
┗/JavaSampleCode/src/develop_util/data_class_merge/sample_data_class/Parent.java(テスト用マージ元クラスに指定される基底クラス)
サンプルコード
自動生成本体
DataClassMerge
package develop_util.data_class_merge; import java.lang.reflect.Field; import java.util.HashSet; import java.util.List; import java.util.Set; import develop_util.data_class_merge.conf.DataClassDefinitionIntf; import develop_util.data_class_merge.conf.MergeHogeDefinition; /** * Dataクラスマージツール。 */ public class DataClassMerge { private static String template = ""; private static final String PACKAGE = "$package"; private static final String IMPORT = "$import"; private static final String CLASS_INFO = "$class_info"; private static final String AUTHOR = "$author"; private static final String CLASS_NAME = "$class_name"; private static final String EXTENDS = "$extends"; private static final String IMPLEMENTS = "$implements"; private static final String FIELDS = "$fields"; private static final String GETTERS = "$getters"; private static final String SETTERS = "$setters"; static { template += "package $package;\n"; template += "$import\n"; template += "\n"; template += "/**\n"; template += " *\n"; template += " * $class_info.\n"; template += " *\n"; template += " * @author $author\n"; template += " */\n"; template += "public class $class_name $extends $implements {\n"; template += "public static final long serialVersionUID = 1L;\n"; template += "$fields\n"; template += "$setters\n"; template += "$getters\n"; template += "}\n"; } /** * マージ定義情報を元にデータクラスのマージ結果を標準出力する。 * * @param definition マージ定義情報 */ public void mergeDataClass(DataClassDefinitionIntf definition) { String out = template; Set<String> importSet = new HashSet<String>(); out = out.replace(DataClassMerge.PACKAGE, definition.getOutputPackage()); out = out.replace(DataClassMerge.CLASS_INFO, definition.getOutputClassJavaDoc()); out = out.replace(DataClassMerge.AUTHOR, definition.getAutor()); out = out.replace(DataClassMerge.CLASS_NAME, definition.getOutputClassName()); Class<?> extendsClass = definition.getExtendsClass(); if (extendsClass != null) { out = out.replace(DataClassMerge.EXTENDS, "extends " + extendsClass.getSimpleName()); importSet.add(extendsClass.getCanonicalName()); } else { out = out.replace(DataClassMerge.EXTENDS, ""); } Class<?> implementsInterface = definition.getImplementsInterface(); if (implementsInterface != null) { out = out.replace(DataClassMerge.IMPLEMENTS, "implements " + implementsInterface.getSimpleName()); importSet.add(implementsInterface.getCanonicalName()); } else { out = out.replace(DataClassMerge.IMPLEMENTS, ""); } out = setFields(out, definition, importSet); StringBuilder imports = new StringBuilder(); for (String importPackage : importSet) { imports.append("import " + importPackage + ";\n"); } out = out.replace(DataClassMerge.IMPORT, imports.toString()); System.out.println(out); } /** * Field,setter,getterの設定 * * @param out テンプレート * @param definition 入力クラス情報 * @param importSet import文作成用Set * @return 置換後文字列 */ private String setFields(String out, DataClassDefinitionIntf definition, Set<String> importSet) { List<Class<?>> classes = definition.getBaseClasses(); StringBuilder fieldOut = new StringBuilder(); StringBuilder getterOut = new StringBuilder(); StringBuilder setterOut = new StringBuilder(); Set<String> nameSet = new HashSet<String>(); for (Class<?> clazz : classes) { for (Field field : clazz.getDeclaredFields()) { String variable = field.getName(); String typeName = field.getType().getSimpleName(); if (variable == "serialVersionUID") { continue; } if (definition.getExcludeNames().contains(variable)) { continue; } List<String> separateNames = definition.getSeparateNames(); variable = getSepareteVariableName(variable, typeName, separateNames); if (nameSet.contains(variable)) { continue; } if (field.getType().getSimpleName().equals("List")) { // List時 String generic = field.getGenericType().toString(); String simpleGeneric = generic.substring(generic.lastIndexOf(".") + 1); String genericType = typeName + "<" + simpleGeneric; // フィールド設定 fieldOut.append("private " + genericType + " " + variable + ";\n"); // Getter設定 setGetter(getterOut, variable, genericType); // Setter設定 setSetter(setterOut, variable, genericType); // import設定(List) importSet.add(field.getType().getName()); // import設定(ジェネリックの型) importSet.add(generic.substring(generic.indexOf("<") + 1, generic.indexOf(">"))); } else { // List以外 // フィールド設定 fieldOut.append("private " + typeName + " " + variable + ";\n"); // Getter設定 setGetter(getterOut, variable, typeName); // Setter設定 setSetter(setterOut, variable, typeName); // import設定 importSet.add(field.getType().getName()); } nameSet.add(variable); } } out = out.replace(DataClassMerge.FIELDS, fieldOut.toString()); out = out.replace(DataClassMerge.GETTERS, getterOut.toString()); out = out.replace(DataClassMerge.SETTERS, setterOut.toString()); return out; } private String getSepareteVariableName(String variable, String typeName, List<String> separateNames) { if (separateNames != null && separateNames.size() != 0) { for (String separete : separateNames) { if (variable.equals(separete)) { variable = variable + typeName; } } } return variable; } private void setSetter(StringBuilder setterOut, String variable, String genericType) { setterOut.append("public void set" + getVariableNameFromUpperName(variable) + "(" + genericType + " " + variable + ") {\n"); setterOut.append("this." + variable + " = " + variable + ";\n"); setterOut.append("}\n"); } private void setGetter(StringBuilder getterOut, String variable, String genericType) { getterOut.append("public " + genericType + " get" + getVariableNameFromUpperName(variable) + "() {\n"); getterOut.append("return this." + variable + ";\n"); getterOut.append("}\n"); } public static String getVariableNameFromUpperName(String upperName) { return upperName.substring(0, 1).toUpperCase() + upperName.substring(1); } public static void main(String[] args) { DataClassMerge merge = new DataClassMerge(); merge.mergeDataClass(new MergeHogeDefinition()); } }
出力内容定義インターフェース
DataClassDefinitionIntf
package develop_util.data_class_merge.conf; import java.util.List; public interface DataClassDefinitionIntf { /** * 出力クラス名を指定する。 * @return 出力クラス名 */ String getOutputClassName(); /** * 出力クラスJavaDoc概要を指定する。 * @return 出力クラスJavaDoc概要 */ String getOutputClassJavaDoc(); /** * 作成者を指定する。 * @return 作成者 */ String getAutor(); /** * 出力先パッケージを指定する。 * @return 出力先パッケージ */ String getOutputPackage(); /** * マージするクラスを列挙する。 * * @return マージするクラスのリスト */ List<Class<?>> getBaseClasses(); /** * 除外するフィールド名を列挙する。 * * @return 除外するフィールド名のリスト */ List<String> getExcludeNames(); /** * 分割するフィールド名を列挙する。 * * @return 分割するフィールド名のリスト */ List<String> getSeparateNames(); /** * 継承クラスを指定する。 * * @return 継承クラス */ Class<?> getExtendsClass(); /** * 実装インターフェースを指定する。 * * @return 実装インターフェース */ Class<?> getImplementsInterface(); }
テスト用出力内容定義クラス
MergeHogeDefinition
package develop_util.data_class_merge.conf; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import develop_util.data_class_merge.sample_data_class.Hage; import develop_util.data_class_merge.sample_data_class.Hige; import develop_util.data_class_merge.sample_data_class.Hoge; import develop_util.data_class_merge.sample_data_class.Parent; public class MergeHogeDefinition implements DataClassDefinitionIntf { @Override public List<Class<?>> getBaseClasses() { List<Class<?>> classes = new ArrayList<Class<?>>(); classes.add(Hoge.class); classes.add(Hige.class); classes.add(Hage.class); return classes; } @Override public List<String> getExcludeNames() { return Arrays.asList("exclude"); } @Override public List<String> getSeparateNames() { return Arrays.asList("separate"); } @Override public Class<?> getExtendsClass() { return Parent.class; } @Override public String getOutputPackage() { return "develop_util.data_class_merge.output"; } @Override public String getOutputClassName() { return "MergeHoge"; } @Override public Class<?> getImplementsInterface() { return Serializable.class; } @Override public String getOutputClassJavaDoc() { return "マージ版ほげクラス"; } @Override public String getAutor() { return "もよもと"; } }
以下、テスト用データクラス
Hage.java
package develop_util.data_class_merge.sample_data_class; import java.math.BigDecimal; import java.util.List; public class Hage extends Parent { private String hageSt; private BigDecimal hageBi; private Other other; private List<Line> lines; private Other exclude; private Other separate; // ※getter、setterは略(実際はある) }
Hige.java
package develop_util.data_class_merge.sample_data_class; import java.math.BigDecimal; import java.util.List; public class Hige extends Parent { private String higeSt; private BigDecimal higeBi; private Other other; private List<Line> lines; private BigDecimal exclude; private BigDecimal separate; // ※getter、setterは略(実際はある)
package develop_util.data_class_merge.sample_data_class; import java.math.BigDecimal; import java.util.List; public class Hoge extends Parent { private String hogeSt; private BigDecimal hogeBi; private Other other; private List<Line> lines; private String exclude; private String separate; // ※getter、setterは略(実際はある) }
Line.java
package develop_util.data_class_merge.sample_data_class; public class Line { private String line; public String getLine() { return line; } public void setLine(String line) { this.line = line; } }
Other.java
package develop_util.data_class_merge.sample_data_class; public class Other { private String other; public String getOther() { return other; } public void setOther(String other) { this.other = other; } }
Parent.java
package develop_util.data_class_merge.sample_data_class; public class Parent { private String parent; public String getParent() { return parent; } public void setParent(String parent) { this.parent = parent; } }
出力結果(フォーマットをかけたもの)
package develop_util.data_class_merge.output; import develop_util.data_class_merge.sample_data_class.Other; import develop_util.data_class_merge.sample_data_class.Line; import java.util.List; import develop_util.data_class_merge.sample_data_class.Parent; import java.math.BigDecimal; import java.lang.String; import java.io.Serializable; /** * * マージ版ほげクラス. * * @author もよもと */ public class MergeHoge extends Parent implements Serializable { public static final long serialVersionUID = 1L; private String hogeSt; private BigDecimal hogeBi; private Other other; private List<Line> lines; private String separateString; private String higeSt; private BigDecimal higeBi; private BigDecimal separateBigDecimal; private String hageSt; private BigDecimal hageBi; private Other separateOther; public void setHogeSt(String hogeSt) { this.hogeSt = hogeSt; } public void setHogeBi(BigDecimal hogeBi) { this.hogeBi = hogeBi; } public void setOther(Other other) { this.other = other; } public void setLines(List<Line> lines) { this.lines = lines; } public void setSeparateString(String separateString) { this.separateString = separateString; } public void setHigeSt(String higeSt) { this.higeSt = higeSt; } public void setHigeBi(BigDecimal higeBi) { this.higeBi = higeBi; } public void setSeparateBigDecimal(BigDecimal separateBigDecimal) { this.separateBigDecimal = separateBigDecimal; } public void setHageSt(String hageSt) { this.hageSt = hageSt; } public void setHageBi(BigDecimal hageBi) { this.hageBi = hageBi; } public void setSeparateOther(Other separateOther) { this.separateOther = separateOther; } public String getHogeSt() { return this.hogeSt; } public BigDecimal getHogeBi() { return this.hogeBi; } public Other getOther() { return this.other; } public List<Line> getLines() { return this.lines; } public String getSeparateString() { return this.separateString; } public String getHigeSt() { return this.higeSt; } public BigDecimal getHigeBi() { return this.higeBi; } public BigDecimal getSeparateBigDecimal() { return this.separateBigDecimal; } public String getHageSt() { return this.hageSt; } public BigDecimal getHageBi() { return this.hageBi; } public Other getSeparateOther() { return this.separateOther; } }