001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.fukurou.taglet; 017 018import org.opengion.fukurou.system.LogWriter; 019 020import com.sun.javadoc.RootDoc; 021import com.sun.javadoc.ClassDoc; 022import com.sun.javadoc.Type; 023import com.sun.javadoc.Tag; 024 025import java.util.Map; 026import java.util.HashMap; 027import java.io.IOException; 028 029/** 030 * ソースコメントから、属性情報を取り出す Doclet クラスです。 031 * 032 * @version 4.0 033 * @author Kazuhiko Hasegawa 034 * @since JDK5.0, 035 */ 036@SuppressWarnings(value={"deprecation","removal"}) // Ver7.0.0.0 , 7.2.2.0 (2020/03/27) 037public final class DocletPlugin { 038 /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 */ 039 // 6.4.3.3 (2016/03/04) 匿名クラスとインスタンス初期化子による、Mapの初期化処理。 040 /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */ 041 private static final Map<String,AttKeySet> ATT_KEY_MAP = new HashMap<>(); 042 static { 043 int no = 0; // 7.2.2.0 (2020/03/27) 連番の自動生成 044 ATT_KEY_MAP.put( "org.opengion.hayabusa.db.Query" , new AttKeySet( "Query" ,no++, "queryType" )); 045 ATT_KEY_MAP.put( "org.opengion.hayabusa.db.CellRenderer" , new AttKeySet( "Renderer" ,no++, "renderer" )); 046 ATT_KEY_MAP.put( "org.opengion.hayabusa.db.CellEditor" , new AttKeySet( "Editor" ,no++, "editor" )); 047 ATT_KEY_MAP.put( "org.opengion.hayabusa.db.DBType" , new AttKeySet( "DBType" ,no++, "dbType" )); 048 ATT_KEY_MAP.put( "org.opengion.hayabusa.db.TableFilter" , new AttKeySet( "TableFilter" ,no++, "tableFilter" )); 049 ATT_KEY_MAP.put( "org.opengion.hayabusa.db.Selection" , new AttKeySet( "Selection" ,no++, "selection" )); 050 ATT_KEY_MAP.put( "org.opengion.hayabusa.db.DBConstValue" , new AttKeySet( "DBConstValue" ,no++, "cnstVal" )); // 5.6.3.3 (2013/04/19) 051 ATT_KEY_MAP.put( "org.opengion.hayabusa.html.ViewForm" , new AttKeySet( "ViewForm" ,no++, "viewFormType" )); 052 ATT_KEY_MAP.put( "org.opengion.hayabusa.io.TableWriter" , new AttKeySet( "TableWriter" ,no++, "writerClass" )); 053 ATT_KEY_MAP.put( "org.opengion.hayabusa.io.TableReader" , new AttKeySet( "TableReader" ,no++, "readerClass" )); 054// ATT_KEY_MAP.put( "org.opengion.hayabusa.report.DBTableReport" , new AttKeySet( "DBTableReport" ,no++, "tableReport" )); // 7.0.1.5 (2018/12/10) 削除 055 ATT_KEY_MAP.put( "org.opengion.hayabusa.resource.CalendarQuery" , new AttKeySet( "CalendarQuery" ,no++, "calDB" )); 056 ATT_KEY_MAP.put( "org.opengion.hayabusa.resource.CalendarData" , new AttKeySet( "CalendarData" ,no++, "calData" )); // 5.6.3.3 (2013/04/19) 057 ATT_KEY_MAP.put( "org.opengion.fukurou.process.HybsProcess" , new AttKeySet( "Process" ,no++, "process" )); 058// ATT_KEY_MAP.put( "org.opengion.fukurou.transfer.TransferExec" , new AttKeySet( "TransferExec" ,no++, "kbExec" )); // 5.5.3.5 (2012/06/21) 059// ATT_KEY_MAP.put( "org.opengion.fukurou.transfer.TransferRead" , new AttKeySet( "TransferRead" ,no++, "kbRead" )); // 5.5.3.5 (2012/06/21) 060 ATT_KEY_MAP.put( "org.opengion.fukurou.util.HybsTimerTask" , new AttKeySet( "Daemon" ,no++, "daemon" )); // 4.3.4.4 (2009/01/01) 061 ATT_KEY_MAP.put( "org.opengion.fukurou.util.ConnectIF " , new AttKeySet( "ConnectIF" ,no++, "connIF" )); // 5.6.3.3 (2013/04/19) 062 ATT_KEY_MAP.put( "org.opengion.fukurou.xml.JspParserFilter" , new AttKeySet( "JspCreate" ,no++, "jspParser" )); // 5.6.3.3 (2013/04/19) 063 } 064 065 private static final String OG_FOR_SMPL = "og.formSample"; 066 private static final String ENCODE = "UTF-8"; 067 068 /** 069 * すべてが staticメソッドなので、コンストラクタを呼び出さなくしておきます。 070 * 071 */ 072 private DocletPlugin() {} 073 074 /** 075 * Doclet のエントリポイントメソッドです。 076 * 077 * @og.rev 5.7.1.1 (2013/12/13) タグのインデントを止める。 078 * 079 * @param root ドキュメントルートオブジェクト 080 * 081 * @return 正常実行時 true 082 */ 083 public static boolean start( final RootDoc root ) { 084 final String version = DocletUtil.getOption( "-version" , root.options() ); 085 final String file = DocletUtil.getOption( "-outfile" , root.options() ); 086 087 DocletTagWriter writer = null; 088 try { 089 writer = new DocletTagWriter( file,ENCODE ); 090 091 // 5.7.1.1 (2013/12/13) タグのインデントを止める。 092 writer.printTag( "<?xml version=\"1.0\" encoding=\"", ENCODE, "\" ?>" ); 093 writer.printTag( "<javadoc>" ); 094 writer.printTag( " <version>",version,"</version>" ); 095 writer.printTag( " <description></description>" ); 096 writeContents( root.classes(),writer ); 097 writer.printTag( "</javadoc>" ); 098 } 099 catch( final IOException ex ) { 100 LogWriter.log( ex ); 101 } 102 finally { 103 if( writer != null ) { writer.close(); } 104 } 105 return true; 106 } 107 108 /** 109 * ClassDoc 配列よりコンテンツを作成します。 110 * インターフェースも処理の対象とします。 111 * 112 * @og.rev 5.5.4.1 (2012/07/06) コメントは文字列でなく、Tag配列として処理させる。 113 * @og.rev 5.7.1.1 (2013/12/13) タグのインデントを止める。 114 * 115 * @param classes ClassDoc配列 116 * @param writer DocletTagWriterオブジェクト 117 */ 118 private static void writeContents( final ClassDoc[] classes,final DocletTagWriter writer ) { 119 for( int i=0; i< classes.length; i++ ) { 120 final ClassDoc classDoc = classes[i] ; 121 if( ! classDoc.isPublic() ) { continue; } 122 123 final AttKeySet attSet = getAttGroupName( classDoc ) ; 124 if( attSet == null ) { continue; } // map に登録されていない。 125 126 final String attKey = attSet.getAttKey( classDoc.name() ); 127 if( attKey == null ) { continue; } // 対象クラス名が、一致しない。 128 129 final String attClass = classDoc.qualifiedName() ; // Class Full Name 130 final Tag[] desc = classDoc.firstSentenceTags(); 131 final Tag[] cmnt = classDoc.inlineTags(); // 5.5.4.1 (2012/07/06) 132 final Tag[] smplTags = classDoc.tags(OG_FOR_SMPL); 133 134 // 5.7.1.1 (2013/12/13) タグのインデントを止める。 135 writer.printTag( "<classDoc>" ); 136 writer.printTag( " <attClass>" ,attClass ,"</attClass>" ); 137 writer.printTag( " <seq>" ,attSet.getSeq() ,"</seq>" ); 138 writer.printTag( " <attKey>" ,attKey ,"</attKey>" ); 139 writer.printTag( " <valueName>" ,attSet.getValueName() ,"</valueName>" ); 140 writer.printTag( " <description>" ,desc ,"</description>" ); 141 writer.printTag( " <contents>" ,cmnt ,"</contents>" ); 142 writer.printTag( " <formSample>" ,smplTags ,"</formSample>" ); 143 writer.printTag( "</classDoc>" ); 144 } 145 } 146 147 /** 148 * 指定の ClassDoc が、処理する属性クラスのMapに含まれている場合、 149 * その AttKeySet クラスのオブジェクトを返します。 150 * 存在しない場合、null を返します。 151 * 152 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。変数名も変えておきます。 153 * 154 * @param classDoc ClassDocオブジェクト 155 * 156 * @return ClassDocに対応する AttKeySetオブジェクト 157 */ 158 private static AttKeySet getAttGroupName( final ClassDoc classDoc ) { 159 if( classDoc == null ) { return null; } 160 161 final String classFullName = classDoc.qualifiedName() ; 162 AttKeySet attKey = ATT_KEY_MAP.get( classFullName ); //6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。変数名も変えておきます。 163 if( attKey == null ) { 164 final Type type = classDoc.superclassType(); // 親クラスタイプ 165 if( type != null ) { 166 attKey = getAttGroupName( type.asClassDoc() ); // 親クラス 167 } 168 169 if( attKey == null ) { 170 final Type[] itface = classDoc.interfaceTypes(); // 直近インターフェース 171 for( int i=0; i<itface.length; i++ ) { 172 attKey = getAttGroupName( itface[i].asClassDoc() ); 173 if( attKey != null ) { break; } 174 } 175 } 176 } 177 return attKey; 178 } 179 180 /** 181 * カスタムオプションを使用するドックレットの必須メソッド optionLength(String) です。 182 * 183 * ドックレットに認識させる各カスタムオプションに、 optionLength がその 184 * オプションを構成する要素 (トークン) の数を返さなければなりません。 185 * このカスタムオプションでは、 -tag オプションそのものと 186 * その値の 2 つの要素で構成されるので、作成するドックレットの 187 * optionLengthメソッドは、 -tag オプションに対して 2 を返さなくては 188 * なりません。また、認識できないオプションに対しては、0 を返します。 189 * 190 * @param option オプション文字列 191 * 192 * @return 要素 (トークン) の数 193 */ 194 public static int optionLength( final String option ) { 195 if( "-version".equalsIgnoreCase(option) ) { 196 return 2; 197 } 198 else if( "-outfile".equalsIgnoreCase(option) ) { 199 return 2; 200 } 201 return 0; 202 } 203 204 /** 205 * 属性情報を管理する、AttKeySet クラスです。 206 * 207 * @og.rev 6.3.9.1 (2015/11/27) 別ファイルで管理していた、パッケージプライベートclass を、内部staticクラスに移動。 208 * 209 * @version 4.0 210 * @author Kazuhiko Hasegawa 211 * @since JDK5.0, 212 */ 213 private static final class AttKeySet { 214 private final String searchKey ; 215 private final int len ; 216 private final String seq ; 217 private final String valueName ; 218 219 /** 220 * コンストラクター 221 * 222 * @param searchKey 検索キー 223 * @param seq シーケンス番号 224 * @param valueName 属性名 225 * 226 */ 227 AttKeySet( final String searchKey,final int seq,final String valueName ) { 228 this.searchKey = searchKey ; 229 this.seq = String.valueOf( seq ); 230 this.valueName = valueName ; 231 232 len = searchKey.length(); 233 } 234 235 /** 236 * シーケンス番号を返します。 237 * 238 * @return シーケンス番号 239 * 240 */ 241 /* default */ String getSeq() { 242 return seq; 243 } 244 245 /** 246 * 属性名を返します。 247 * 248 * @return 属性名 249 * 250 */ 251 /* default */ String getValueName() { 252 return valueName; 253 } 254 255 /** 256 * クラス名の先頭一致の場合の、**** 部分を返します。 257 * インターフェースも扱えるように修正しましたので、先頭が _ の場合は、 258 * _ を削除して返します。 259 * 260 * @param name クラスの名称 (例:DBCellEditor_**** , ViewForm_****) 261 * @return クラス名の**** 部分 262 */ 263 /* default */ String getAttKey( final String name ) { 264 // 6.1.0.0 (2014/12/26) refactoring 265 String rtn = null; // 一致しなかった。 266 267 if( name.equals( searchKey ) ) { // 完全一致:インターフェース 268 rtn = "(Interface)" + name ; 269 } 270 else if( name.indexOf( searchKey ) == 0 ) { // 先頭一致した。 271 rtn = name.substring( len ); 272 if( rtn.charAt(0) == '_' ) { rtn = rtn.substring( 1 ); } // 先頭が _ の場合は、_ を削除 273 } 274 275 return rtn ; 276 } 277 } 278}