s2jdbc-genでコメントを管理したい
前置き
管理したいですよね。
というわけで調べてみたらこういうエントリがありました。
http://d.hatena.ne.jp/akiraneko/20081009/1223562878
DB → コメントSQLなAntタスクです。
しかし…そうじゃなくて、Entity → コメントSQLがやりたい。
やりかたもこのエントリをぱくれば参考にすればできそうです。
主な内容
当然ですが、classファイルにはコメントは含まれていないので、ソースファイルからコメントを取得します。
コメントのために新しいアノテーションが必要だなんて嫌ですし。
というわけで、Javadocの力を借ります。
準備
Javadocを使うのでtools.jarが必要です。
s2jdbc-gen-build.xmlで定義したclasspathに放り込みます。/WEB-INF/libあたり。
s2jdbc-gen-build.xml
<taskdef name="gen-comment" classname="org.seasar.extension.jdbc.gen.task.GenerateCommentTask" classpathref="classpath"/> <target name="gen-comment"> <gen-comment classpathdir="${classpathdir}" rootpackagename="${rootpackagename}" entitypackagename="${entitypackagename}" env="${env}" jdbcmanagername="${jdbcmanagername}" javafiledestdir="${javafiledestdir}" javafileencoding="${javafileencoding}" classpathref="classpath" /> </target>
基本的にはgen-ddlをぱくります。
ソースからコメントを読み込むので、javafiledestdirとjavafileencodingを追加します。
GenerateCommentTask
GenerateDdlTaskをぱくります。
protected GenerateCommentCommand command = new GenerateCommentCommand(); public void setJavaFiledestDir(File JavaFileDestDir) { command.setJavaFileDestDir(JavaFileDestDir); } public void setJavaFileEncoding(String javaFileEncoding) { command.setJavaFileEncoding(javaFileEncoding); }
s2jdbc-gen-build.xmlに追加した属性をGenerateCommentCommandにセットするために、セッター?を追加します。
CommandはGenerateCommentCommandに差し替えておきます。
GenerateCommentCommand
GenerateDdlCommandをぱくります。
ちょっと長いので分割して載せます。
protected File javaFileDestDir = new File(new File("src", "main"), "java"); protected String javaFileEncoding = "UTF-8"; /* setter getter は省略 */
s2jdbc-gen-build.xmlに追加した属性です。
最初、getterはなくていいと思って書かなかったら怒られました(どうでもいいですね)。
protected static ThreadLocal<GenerateCommentCommand> self = new ThreadLocal<GenerateCommentCommand>(); @Override protected void doExecute() throws Throwable { self.set(this); com.sun.tools.javadoc.Main.execute(new String[]{ "-doclet", "org.seasar.extension.jdbc.gen.internal.command.GenerateCommentCommand", "-sourcepath", FileUtil.getCanonicalPath(javaFileDestDir), "-encoding", javaFileEncoding, "-subpackages", rootPackageName }); } public static boolean start(RootDoc root) { self.get().createComment(root); return true; }
まずdoExecute()が実行され、ThreadLocalに自分自身を格納します。
これは後続するMain.execute()のために必要な措置です。
Main.execute()はソースファイルのコメントを読み込むメソッドです。
docletオプションを指定しないならソースファイルのコメントを読み込んだ後に標準のDocletが呼び出され、おなじみのJavadocが生成されます。
docletオプションを指定した場合はソースファイルのコメントを読み込んだ後に指定したクラスのpublic static boolean start(RootDoc root)が呼び出され、その後の処理がこのメソッドに委ねられます。
しかし。
しょっぱいことにこのメソッドstaticなんです。
ここはぱくったGenerateDdlCommandの力を最大限発揮するために、start()が実行されたらすぐに処理をThreadLocalに格納したGenerateCommentCommandインスタンスに戻します。
protected void createComment(RootDoc root) { BufferedWriter out = null; try { File dir = ddlVersionDirectoryTree.getCurrentVersionDirectory().getCreateDirectory().createChild("050-user").asFile(); dir.mkdir(); File oFile = new File(dir, "comment.sql"); out = new BufferedWriter(new OutputStreamWriter( FileOutputStreamUtil.create(oFile), Charset.forName(ddlFileEncoding))); List<EntityMeta> entityList = entityMetaReader.read(); PackageDoc packageDoc = root.packageNamed(ClassUtil.concatName(rootPackageName, entityPackageName)); for (EntityMeta entity: entityList) { ClassDoc classDoc = packageDoc.findClass(entity.getEntityClass().getSimpleName()); String tableName = entity.getTableMeta().getFullName(); // getCommentSqlはコメントSQLを組み立てます。 out.write(getCommentSql("table", tableName, classDoc.commentText())); // createFieldDocMapは引数のクラスとその親クラスのフィールドをMapにして返します。 Map<String, FieldDoc> fieldMap = createFieldDocMap(classDoc); for (int i=0; i<entity.getColumnPropertyMetaSize(); i++) { PropertyMeta property = entity.getColumnPropertyMeta(i); FieldDoc fieldDoc = fieldMap.get(property.getField().getName()); String columnName = tableName + "." + property.getColumnMeta().getName(); out.write(getCommentSql("column", columnName , fieldDoc.commentText())); } } } catch (IOException e) { throw new IORuntimeException(e); } finally { CloseableUtil.close(out); } }
読み込んだソースのコメントをコメントSQLにして出力します。
entityMetaReaderがすばらしいです。
read()ってするだけでEntityとTableとPropertyとColumnの情報がまるわかりです。
protected Map<String, FieldDoc> createFieldDocMap(ClassDoc classDoc) { Map<String, FieldDoc> fieldMap = CollectionsUtil.newHashMap(); for (FieldDoc fDoc: classDoc.fields()) { fieldMap.put(fDoc.name(), fDoc); } for (ClassDoc superDoc = classDoc.superclass(); !Object.class.getName().equals(superDoc.qualifiedName()); superDoc = superDoc.superclass()) { if (isMappedSuperclass(superDoc)) { for (FieldDoc fDoc: superDoc.fields()) { fieldMap.put(fDoc.name(), fDoc); } } } return fieldMap; }
引数のクラスとその親クラスのフィールドをMapにして返します。
ただし、@MappedSuperclassが付与されていない親クラスは除きます。
protected boolean isMappedSuperclass(ClassDoc classDoc) { for (AnnotationDesc desc: classDoc.annotations()) { if (MappedSuperclass.class.getName().equals(desc.annotationType().qualifiedName())) { return true; } } return false; }
引数のクラスに@MappedSuperclassが付与されているかどうかを判断します。
protected String getCommentSql(String objectType, String objectName, String comment) { String commentOut = ""; if(StringUtil.isBlank(comment)){ comment = ""; commentOut = "-- "; } return String.format("%scomment on %s %s is '%s';" + System.getProperty("line.separator"), commentOut, objectType, objectName, comment); }
コメントSQLを組み立てます。
以上です。
entityMetaReaderのおかげですごくやりやすかったです。
gen-entityのほうは…どうしよう。