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.util.LogWriter; 019 020import java.util.Set; 021import java.util.HashSet; 022import java.io.IOException; 023 024import com.sun.javadoc.RootDoc; 025import com.sun.javadoc.ClassDoc; 026import com.sun.javadoc.MethodDoc; 027import com.sun.javadoc.FieldDoc; 028import com.sun.javadoc.Doc; 029import com.sun.javadoc.ConstructorDoc; 030import com.sun.javadoc.ExecutableMemberDoc; 031import com.sun.javadoc.Type; 032import com.sun.javadoc.Parameter; 033import com.sun.javadoc.Tag; 034import com.sun.javadoc.SourcePosition; 035import com.sun.javadoc.AnnotationDesc; 036import com.sun.javadoc.AnnotationTypeDoc; 037 038/** 039 * ソースコメントから、タグ情報を取り出す Doclet クラスです。 040 * クラスファイルの仕様を表現する為、og.formSample , og.rev , og.group , 041 * version , author , since の各タグコメントより値を抽出します。 042 * また、各クラスの継承関係、インターフェース、メソッドなども抽出します。 043 * これらの抽出結果をDB化し、EXCELファイルに帳票出力する事で、クラスファイルの 044 * ソースから仕様書を逆作成します。 045 * 046 * @version 4.0 047 * @author Kazuhiko Hasegawa 048 * @since JDK5.0, 049 */ 050public final class DocletSpecific { 051 private static final String SELECT_PACKAGE = "org.opengion" ; 052 private static final boolean NOT_PRIVATE = false ; 053 private static final String ENCODE = "UTF-8"; 054 055 private static final String OG_FOR_SMPL = "og.formSample"; 056 private static final String OG_REV = "og.rev"; 057 private static final String OG_GROUP = "og.group"; 058 private static final String DOC_VERSION = "version"; 059 private static final String DOC_AUTHOR = "author"; 060 private static final String DOC_SINCE = "since"; 061 062 private static final String DOC_PARAM = "param"; // 5.1.9.0 (2010/08/01) チェック用 063 private static final String DOC_RETURN = "return"; // 5.1.9.0 (2010/08/01) チェック用 064 065 private static final String CONSTRUCTOR = "コンストラクタ" ; 066 private static final String METHOD = "メソッド" ; 067 private static final Set<String> methodSet = new HashSet<String>(); 068 069 private static int debugLevel = 0; // 0:なし 1:最小チェック 2:日本語化 3:体裁 070 071 // 5.1.9.0 (2010/08/01) ソースチェック用(半角文字+空白文字のみ) 072 private static java.util.regex.Pattern PTN = java.util.regex.Pattern.compile("[\\w\\s]+"); 073 074 /** 075 * すべてが staticメソッドなので、コンストラクタを呼び出さなくしておきます。 076 * 077 */ 078 private DocletSpecific() {} 079 080 /** 081 * Doclet のエントリポイントメソッドです。 082 * 083 * @og.rev 5.5.4.1 (2012/07/06) Tag出力時の CR → BR 変換を行わない様にする。 084 * @og.rev 5.7.1.1 (2013/12/13) タグのインデントを止める。 085 * 086 * @param root エントリポイントのRootDocオブジェクト 087 * 088 * @return 正常実行時 true 089 */ 090 public static boolean start( final RootDoc root ) { 091 String version = DocletUtil.getOption( "-version" , root.options() ); 092 String file = DocletUtil.getOption( "-outfile" , root.options() ); 093 String dbgLvl = DocletUtil.getOption( "-debugLevel" , root.options() ); // 5.5.4.1 (2012/07/06) パラメータ引数 094 if( dbgLvl != null ) { debugLevel = Integer.parseInt( dbgLvl ); } 095 096 DocletTagWriter writer = null; 097 try { 098 writer = new DocletTagWriter( file,ENCODE ); // 5.5.4.1 (2012/07/06) 099 100 // 5.7.1.1 (2013/12/13) タグのインデントを止める。 101 writer.printTag( "<?xml version=\"1.0\" encoding=\"", ENCODE, "\" ?>" ); 102 writer.printTag( "<javadoc>" ); 103 writer.printTag( "<version>",version,"</version>" ); 104 writer.printTag( "<description></description>" ); 105 writeContents( root.classes(),writer ); 106 writer.printTag( "</javadoc>" ); 107 } 108 catch( IOException ex ) { 109 LogWriter.log( ex ); 110 } 111 finally { 112 if( writer != null ) { writer.close(); } 113 } 114 return true; 115 } 116 117 /** 118 * ClassDoc 配列よりコンテンツを作成します。 119 * 120 * @og.rev 5.5.4.1 (2012/07/06) コメントは文字列でなく、Tag配列として処理させる。 121 * @og.rev 5.6.6.0 (2013/07/05) VERSION staticフィールドと、@og.rev コメントの比較チェック 122 * @og.rev 5.7.1.1 (2013/12/13) タグのインデントを止める。 123 * 124 * @param classes ClassDoc配列 125 * @param writer Tagを書き出すWriterオブジェクト 126 */ 127 private static void writeContents( final ClassDoc[] classes,final DocletTagWriter writer ) { 128 for(int i=0; i< classes.length; i++) { 129 ClassDoc classDoc = classes[i] ; 130 String className = classDoc.name(); 131 String fullName = classDoc.qualifiedName() ; 132 String modifiers = (classDoc.modifiers() 133 + ( classDoc.isClass() ? " class" : "" ) ).trim(); 134 135 Type superType = classDoc.superclassType(); 136 String superClass = ( superType == null ) ? "" : superType.qualifiedTypeName(); 137 138 Type[] interfaceTypes = classDoc.interfaceTypes(); 139 StringBuilder buf = new StringBuilder( 200 ); 140 for( int j=0; j<interfaceTypes.length; j++ ) { 141 buf.append( interfaceTypes[j].qualifiedTypeName() ).append( "," ); 142 } 143 if( interfaceTypes.length > 0 ) { buf.deleteCharAt( buf.length()-1 ); } 144 String intFase = buf.toString(); 145 146 Tag[] desc = classDoc.firstSentenceTags(); 147 Tag[] cmnt = classDoc.inlineTags(); // 5.5.4.1 (2012/07/06) 148 Tag[] smplTags = classDoc.tags(OG_FOR_SMPL); 149 Tag[] revTags = classDoc.tags(OG_REV); 150 Tag[] createVer = classDoc.tags(DOC_VERSION); 151 Tag[] author = classDoc.tags(DOC_AUTHOR); 152 Tag[] since = classDoc.tags(DOC_SINCE); 153 Tag[] grpTags = classDoc.tags(OG_GROUP); 154 155 // 5.7.1.1 (2013/12/13) タグのインデントを止める。 156 writer.printTag( "<classDoc>" ); 157 writer.printTag( "<fullName>" ,fullName ,"</fullName>" ); 158 writer.printTag( "<modifiers>" ,modifiers ,"</modifiers>" ); 159 writer.printTag( "<className>" ,className ,"</className>" ); 160 writer.printTag( "<superClass>" ,superClass ,"</superClass>" ); 161 writer.printTag( "<interface>" ,intFase ,"</interface>" ); 162 writer.printTag( "<createVer>" ,createVer ,"</createVer>" ); 163 writer.printTag( "<author>" ,author ,"</author>" ); 164 writer.printTag( "<since>" ,since ,"</since>" ); 165 writer.printTag( "<description>" ,desc ,"</description>" ); 166 writer.printTag( "<contents>" ,cmnt ,"</contents>" ); 167 writer.printTag( "<classGroup>" ); 168 writer.printCSVTag( grpTags ); 169 writer.printTag( "</classGroup>" ); 170 writer.printTag( "<formSample>" ,smplTags ,"</formSample>" ); 171 writer.printTag( "<history>" ,revTags ,"</history>" ); 172 173 // 5.1.9.0 (2010/08/01) ソースチェック用(コメントや概要が無い場合。スーパークラスは省く) 174 if( debugLevel >= 2 && ( cmnt.length == 0 || desc.length == 0 ) && superClass.length() == 0 ) { 175 System.err.println( "警告2:コメントC=\t" + classDoc.position() ); 176 } 177 178 int extendFlag = 0; // 0:オリジナル 1:org.opengion関連Extend 2:Java関連Extend 179 // while( fullName.startsWith( SELECT_PACKAGE ) ) { 180 181 // 5.6.6.0 (2013/07/05) VERSION staticフィールドと、@og.rev コメントの比較チェック 182 // while 以下で、fullName と classDoc を順番に上にさかのぼっているので、先にチェックします。 183 checkTag2( classDoc ); 184 185 while( true ) { 186 ConstructorDoc[] cnstrctrs = classDoc.constructors( false ); // 5.1.9.0 (2010/08/01) チェック用 187 for(int j=0; j < cnstrctrs.length; j++) { 188 if( isAction( cnstrctrs[j],extendFlag ) ) { 189 if( extendFlag < 2 ) { checkTag( cnstrctrs[j] ); } // 5.5.4.1 (2012/07/06) チェックを分離 190 menberTag( cnstrctrs[j],CONSTRUCTOR,writer,extendFlag ); 191 } 192 } 193 194 MethodDoc[] methods = classDoc.methods( false ); // 5.1.9.0 (2010/08/01) チェック用 195 for(int j=0; j < methods.length; j++) { 196 if( isAction( methods[j],extendFlag ) ) { 197 if( extendFlag < 2 ) { checkTag( methods[j] ); } // 5.5.4.1 (2012/07/06) チェックを分離 198 menberTag( methods[j],METHOD,writer,extendFlag ); 199 } 200 } 201 202 // 対象クラス(オリジナル)から、上に上がっていく。 203 Type type = classDoc.superclassType(); 204 if( type == null ) { break; } 205 classDoc = type.asClassDoc() ; 206 fullName = classDoc.qualifiedName(); 207 // java.lang.Object クラスは対象が多いため、処理しません。 208 if( "java.lang.Object".equals( fullName ) || classDoc.isEnum() ) { 209 break; 210 } 211 else if( fullName.startsWith( SELECT_PACKAGE ) ) { 212 extendFlag = 1; 213 } 214 else { 215 extendFlag = 2; 216 } 217 } 218 219 writer.printTag( " </classDoc>" ); 220 } 221 } 222 223 /** 224 * メンバークラスのXML化を行うかどうか[true/false]を判定します。 225 * 226 * 以下の条件に合致する場合は、処理を行いません。(false を返します。) 227 * 228 * 1.同一クラスを処理中にEXTENDで継承元をさかのぼる場合、すでに同じシグネチャのメソッドが 229 * 存在している。 230 * 2.NOT_PRIVATE が true の時の private メソッド 231 * 3.extendFlag が 0以上(1,2)の時の private メソッド 232 * 4.メソッド名におかしな記号(<など)が含まれている場合 233 * 234 * @og.rev 5.5.4.1 (2012/07/06) メソッドの重複処理判定は、クラス名も含めて行う 235 * 236 * @param menber ExecutableMemberDocオブジェクト 237 * @param extendFlag 0:オリジナル 1:org.opengion関連Extend 2:Java関連Extend 238 * 239 * @return XML化を行うかどうか[true/false] 240 */ 241 private static boolean isAction( final ExecutableMemberDoc menber,final int extendFlag ) { 242 String menberName = menber.name() ; 243 boolean rtn = ( ! methodSet.add( menber.toString() ) ) // 5.5.4.1 (2012/07/06) メソッドの重複処理判定は、クラス名も含めて行う 244 || ( NOT_PRIVATE && menber.isPrivate() ) 245 || ( extendFlag > 0 && menber.isPrivate() ) 246 || ( menberName.charAt(0) == '<' ) ; 247 248 return ! rtn ; 249 } 250 251 /** 252 * param,return 等の整合性をチェックします。 253 * 254 * @og.rev 5.5.4.1 (2012/07/06) 新規作成。 255 * @og.rev 5.6.6.1 (2013/07/12) Deprecated アノテーション のチェック 256 * 257 * @param menber ExecutableMemberDocオブジェクト 258 */ 259 private static void checkTag( final ExecutableMemberDoc menber ) { 260 261 // 親が Enum クラスの場合、処理しません。 262 Type prntType = menber.containingClass().superclassType(); 263 String prntClass = ( prntType == null ) ? "" : prntType.qualifiedTypeName(); 264 if( "java.lang.Enum".equals( prntClass ) ) { return; } 265 266 SourcePosition posi = menber.position(); 267 String modifiers = null; 268 269 if( menber instanceof MethodDoc ) { 270 // メソッドの処理(コンストラクターを省く) 271 Type rtnType = ((MethodDoc)menber).returnType(); 272 String typNm = rtnType.typeName(); 273 274 StringBuilder modifyBuf = new StringBuilder( 200 ); 275 modifyBuf.append( menber.modifiers() ).append( " " ).append( typNm ); 276 if( rtnType.dimension() != null ) { modifyBuf.append( rtnType.dimension() ); } 277 modifiers = modifyBuf.toString(); 278 279 String wormMsg = "=\t" + posi + "\t" + modifiers ; 280 281 // 5.1.9.0 (2010/08/01) ソースチェック用(@return との整合性チェック) 282 Tag[] docReturn = menber.tags(DOC_RETURN); // 5.1.9.0 (2010/08/01) チェック用 283 if( docReturn.length > 0 ) { 284 String data = docReturn[0].text().trim(); // 5.5.4.1 (2012/07/06) trim でスペース等の削除 285 wormMsg = wormMsg + "\t" + data ; 286 287 // 5.5.4.1 (2012/07/06) ソースチェック用(@return と引数の個数が異なる場合) 288 if( debugLevel >= 1 && "void".equals( typNm ) ) { 289 System.err.println( "警告1:RTNコメント不要" + wormMsg ); 290 } 291 // 「@return 解説」 の形式で、解説に日本語がなければ、警告 292 if( debugLevel >= 2 && PTN.matcher( data ).matches() ) { 293 System.err.println( "警告2:RTN未解説" + wormMsg ); 294 } 295 // 「@return String」 の形式の場合は警告 296 if( debugLevel >= 2 && data.equals( typNm ) ) { 297 System.err.println( "警告2:RTN一致" + wormMsg ); 298 } 299 // 「@return String[]」 など、配列や、<String>などが含まれる場合は警告 300 if( debugLevel >= 2 && ( data.indexOf( "[]" ) >= 0 || data.indexOf( '<' ) >= 0 ) ) { 301 System.err.println( "警告2:RTN配列" + wormMsg ); 302 } 303 // 「@return String 解説」 の場合は警告(後ろにスペースか、タブがある場合) 304 if( debugLevel >= 3 && (data.indexOf( typNm + " " ) >= 0 || data.indexOf( typNm + "\t" ) >= 0 ) ) { 305 System.err.println( "警告3:RTNタイプ" + wormMsg ); 306 } 307 // 「@return xxxx 解説」 の場合で、最初のスペースまでが、すべて英数字のみの場合は警告 308 int adrs1 = data.indexOf( ' ' ); 309 if( debugLevel >= 3 && adrs1 > 0 ) { 310 boolean flag = true; 311 for( int j=0; j<adrs1; j++ ) { 312 char ch = data.charAt( j ); 313 if( ( ch < '0' || ch > '9' ) && ( ch < 'a' || ch > 'z' ) && ( ch < 'A' || ch > 'Z' ) && ch != '[' && ch != ']' ) { 314 flag = false; // 英数字でない記号が現れた場合 315 break; 316 } 317 } 318 if( flag ) { // すべてが英数字の場合は、 319 System.err.println( "警告3:RTN値" + wormMsg ); 320 } 321 } 322 } 323 else { // Tag上には、@return 記述が存在しない。 324 // 5.5.4.1 (2012/07/06) ソースチェック用(@return と引数の個数が異なる場合) 325 if( debugLevel >= 1 && !"void".equals( typNm ) ) { 326 System.err.println( "警告1:RTNコメントなし" + wormMsg ); 327 } 328 } 329 330 // オーバーライドチェック:アノテーションの記述漏れ 331 // その逆は、コンパイラが警告してくれる。 332 MethodDoc mdoc= ((MethodDoc)menber).overriddenMethod(); 333 if( debugLevel >= 3 && mdoc != null ) { 334 AnnotationDesc[] annotations = menber.annotations(); 335 // 本来は、Override の有無を調べるべきだが、Deprecated と SuppressWarnings の付いている 336 // 旧のメソッドに、いちいちOverrideを付けないので、何もなければと条件を緩めます。 337 if( annotations.length == 0 ) { 338 System.err.println( "警告3:@Overrideなし" + wormMsg ); 339 } 340 } 341 } 342 343 Parameter[] prm = menber.parameters(); 344 345 // 5.1.9.0 (2010/08/01) ソースチェック用(@param と引数の個数が異なる場合) 346 Tag[] docParam = menber.tags(DOC_PARAM); // 5.1.9.0 (2010/08/01) チェック用 347 if( debugLevel >= 1 && docParam.length != prm.length ) { 348 System.err.println( "警告1:PRM個数違い=\t" + posi ); 349 } 350 351 for( int k=0; k<prm.length; k++ ) { 352 String typNm = prm[k].type().typeName(); 353 String prmNm = prm[k].name(); 354 355 // 5.1.9.0 (2010/08/01) ソースチェック用(@param と引数の個数が異なる場合) 356 if( docParam.length > k ) { 357 String data = docParam[k].text().trim(); // 5.5.4.1 (2012/07/06) trim でスペース等の削除 358 String data2 = data.replaceAll( prmNm,"" ).trim(); 359 String data3 = data2.replaceAll( typNm,"" ).replaceAll( "\\[\\]","" ).trim(); 360 String wormMsg = "=\t" + posi + "\t" + data ; 361 362 // 「@param aaa 解説」形式で、aaa(引数名)がない場合 363 if( debugLevel >= 1 && data.indexOf( prmNm ) < 0 ) { 364 System.err.println( "警告1:PRM引数名" + wormMsg ); 365 } 366 // 引数の文字列の長さが、1文字の場合 367 if( debugLevel >= 2 && prmNm.length() == 1 ) { 368 System.err.println( "警告2:PRM短い" + wormMsg ); 369 } 370 // 「@param aaa 解説」形式で、解説に日本語がない、または、解説がなければ、警告 371 if( debugLevel >= 2 && ( PTN.matcher( data2 ).matches() || data3.length() == 0 ) ) { 372 System.err.println( "警告2:PRM未解説" + wormMsg ); 373 } 374 // 「@param aaa String[]」など、配列や、<String>などが含まれる場合は警告 375 if( debugLevel >= 2 && ( data.indexOf( "[]" ) >= 0 || data.indexOf( '<' ) >= 0 ) ) { 376 System.err.println( "警告2:PRM配列" + wormMsg ); 377 } 378 // 「@param aaa 解説」形式で、String が有って、その後ろにスペースか、タブがあれば警告 379 // data2 を使うのは、パラメータ名(xxxMap)にタイプ名(Map)が含まれているケースの対応 380 if( debugLevel >= 3 && (data2.indexOf( typNm + " " ) >= 0 || data2.indexOf( typNm + "\t" ) >= 0 ) ) { 381 System.err.println( "警告3:PRMタイプ" + wormMsg ); 382 } 383 } 384 } 385 386 Tag[] desc = menber.firstSentenceTags(); 387 Tag[] cmnt = menber.inlineTags(); // 5.5.4.1 (2012/07/06) 388 389 // 5.1.9.0 (2010/08/01) ソースチェック用 390 if( ( cmnt.length == 0 || desc.length == 0 ) // コメントや概要が無い 391 && menber instanceof MethodDoc // メソッドに限定 392 && !menber.isSynthetic() // コンパイラによって合成されていない 393 && !menber.isNative() // ネイティブメソッドでない 394 && debugLevel >= 2 ) { // debugLevel が 2 以上 395 396 // さらに、親が Enum クラス以外 397 System.err.println( "警告2:コメントM=" + "\t" + posi + "\t" + menber.name() ); 398 } 399 400 // 5.6.6.1 (2013/07/12) Deprecated アノテーション のチェック 401 AnnotationDesc[] descList = menber.annotations(); 402 for( int i=0; i<descList.length; i++ ) { 403 AnnotationTypeDoc annDoc = descList[i].annotationType(); 404 if( "Deprecated".equalsIgnoreCase( annDoc.name() ) ) { 405 String text = menber.commentText(); 406 if( text != null && text.indexOf( "【廃止】" ) < 0 ) { 407 System.err.println( "警告5:【廃止】=" + "\t" + posi + "\t" + menber.name() ); 408 } 409 } 410 } 411 } 412 413 /** 414 * VERSION staticフィールドと、@og.rev コメントの比較チェックを行います。 415 * エンジン内部では、serialVersionUID は、この、VERSION を元に作成しているため、 416 * その値もチェックします。 417 * 418 * @og.rev 5.6.6.0 (2013/07/05) 新規作成 419 * @og.rev 5.7.1.1 (2013/12/13) VERSION の値を、Class.forName ではなく、FieldDoc から取得する。 420 * @og.rev 6.0.0.1 (2014/04/25) fullName 削除 421 * 422 * @param classDoc ClassDocオブジェクト 423 */ 424// private static void checkTag2( final String fullName, final ClassDoc classDoc ) { 425 private static void checkTag2( final ClassDoc classDoc ) { 426 FieldDoc cnstVarFld = findFieldDoc( classDoc , "VERSION" ) ; // VERSION 文字列 例:5.6.6.0 (2013/07/05) 427 428 // VERSION 文字列 か、serialVersionUID のどちらかがあれば処理します。 429 if( cnstVarFld != null ) { // 5.7.1.1 (2013/12/13) cnstVarFid のみ初めにチェック 430 String cnstVar = cnstVarFld.constantValueExpression() ; 431 if( cnstVar != null ) { 432 if( cnstVar.length() > 1 && 433 cnstVar.charAt(0) == '"' && cnstVar.charAt(cnstVar.length()-1) == '"' ) { 434 cnstVar = cnstVar.substring( 1,cnstVar.length()-1 ); 435 } 436 } 437 else { 438 cnstVar = "0.0.0.0 (0000/00/00)"; // 初期値 439 } 440 441 String maxRev = cnstVar ; // 5.7.1.1 (2013/12/13) 初期値 442 int lenVar = maxRev.length(); // 比較時に使用する長さ 443 boolean isChange = false; // max が入れ替わったら、true 444 445 // 本体、コンストラクタ、フィールド、メソッド内から、最大の @og.rev の値を取得します。 446 Doc[][] docs = new Doc[4][] ; 447 448 docs[0] = new Doc[] { classDoc } ; 449 docs[1] = classDoc.constructors( false ) ; 450 docs[2] = classDoc.fields( false ) ; 451 docs[3] = classDoc.methods( false ) ; 452 453 for( int i=0; i<docs.length; i++ ) { 454 for( int j=0; j < docs[i].length; j++ ) { 455 Doc doc = docs[i][j]; 456 457 Tag[] revTags = doc.tags(OG_REV); 458 for( int k=0 ; k<revTags.length; k++ ) { 459 String rev = revTags[k].text(); 460 461 if( rev.length() < lenVar ) { 462 System.err.println( "警告4:og.revが短い=" + "\t" + rev + "\t" + doc.position() ); 463 continue; 464 } 465 466 rev = rev.substring( 0,lenVar ); 467 468 if( maxRev.compareTo( rev ) < 0 ) { // revTags の og.rev が大きい場合 469 maxRev = rev ; 470 // posi = doc.position(); // 最後に入れ替わった位置 = 最大のrevの位置 471 isChange = true; 472 } 473 } 474 } 475 } 476 477 // VERSION 文字列 の定義があり、かつ、max の入れ替えが発生した場合のみ、警告4:VERSIONが古い 478 if( isChange ) { // 5.7.1.1 (2013/12/13) 入れ替えが発生した場合 479 System.err.println( "警告4:VERSIONが古い=" + "\t" + cnstVar + " ⇒ " + maxRev + "\t" + cnstVarFld.position() ); 480 } 481 482 // serialVersionUID の定義がある。 483 FieldDoc seriUIDFld = findFieldDoc( classDoc , "serialVersionUID" ) ; // serialVersionUID 例:566020130705L 484 if( seriUIDFld != null ) { // 5.7.1.1 (2013/12/13) 485 StringBuilder buf = new StringBuilder(); 486 // maxRev は、最大の Revか、初期のVERSION文字列 例:5.6.6.0 (2013/07/05) 487 for( int i=0; i<maxRev.length(); i++ ) { // 488 char ch = maxRev.charAt( i ); 489 if( ch >= '0' && ch <= '9' ) { buf.append( ch ); } // 数字だけ取り出す。 例:566020130705 490 } 491 buf.append( 'L' ); // 強制的に、L を追加する。 492 String maxSeriUID = buf.toString() ; 493 494 // 5.7.1.1 (2013/12/13) 値の取出し。Long型を表す "L" も含まれている。 495 String seriUID = seriUIDFld.constantValueExpression() ; 496 if( !maxSeriUID.equals( seriUID ) ) { // 一致しない 497 System.err.println( "警告4:serialVersionUIDが古い=" + "\t" + seriUID + " ⇒ " + maxSeriUID + "\t" + seriUIDFld.position() ); 498 } 499 } 500 } 501 } 502 503 /** 504 * メンバークラス(コンストラクタ、メソッド)をXML化します。 505 * 506 * @og.rev 5.5.4.1 (2012/07/06) コメントは文字列でなく、Tag配列として処理させる。 507 * 508 * @param menber ExecutableMemberDocオブジェクト 509 * @param menberType メンバータイプ(コンストラクタ、メソッド) 510 * @param writer Tagを書き出すWriterオブジェクト 511 * @param extendFlag 0:オリジナル 1::org.opengion関連Extend 2:Java関連Extend 512 */ 513 private static void menberTag( final ExecutableMemberDoc menber, 514 final String menberType, 515 final DocletTagWriter writer, 516 final int extendFlag ) { 517 518 final String modifiers ; 519 if( menber instanceof MethodDoc ) { 520 // メソッドの処理 521 Type rtnType = ((MethodDoc)menber).returnType(); 522 StringBuilder modifyBuf = new StringBuilder( 200 ); 523 modifyBuf.append( menber.modifiers() ); 524 // modifyBuf.append( " " ).append( rtnType.qualifiedTypeName() ); 525 modifyBuf.append( " " ).append( rtnType.typeName() ); 526 if( rtnType.dimension() != null ) { modifyBuf.append( rtnType.dimension() ); } 527 528 modifiers = modifyBuf.toString(); 529 } 530 else { 531 // コンストラクター処理 532 modifiers = menber.modifiers(); 533 } 534 535 String menberName = menber.name(); 536 537 StringBuilder sigBuf = new StringBuilder( 200 ); 538 sigBuf.append( menberName ).append( "(" ) ; 539 Parameter[] prm = menber.parameters(); 540 541 for( int k=0; k<prm.length; k++ ) { 542 Type ptyp = prm[k].type(); 543 String prmNm =prm[k].name(); 544 545 sigBuf.append( ptyp.typeName() ).append( ptyp.dimension() ).append( " " ) 546 .append( prmNm ).append( "," ); 547 } 548 549 if( prm.length > 0 ) { sigBuf.deleteCharAt( sigBuf.length()-1 ); } 550 sigBuf.append( ")" ); 551 String signature = sigBuf.toString(); 552 553 Tag[] desc = menber.firstSentenceTags(); 554 Tag[] cmnt = menber.inlineTags(); // 5.5.4.1 (2012/07/06) 555 Tag[] tags = menber.tags(); 556 Tag[] revTags = menber.tags(OG_REV); 557 String extend = String.valueOf( extendFlag ); 558 String extClass = ( extendFlag == 0 ) ? "" : menber.containingClass().qualifiedName() ; 559 560 String position = String.valueOf( menber.position().line() ); 561 562 writer.printTag( " <menber>" ); 563 writer.printTag( " <type>" ,menberType ,"</type>" ); 564 writer.printTag( " <name>" ,menberName ,"</name>" ); 565 writer.printTag( " <modifiers>" ,modifiers ,"</modifiers>" ); 566 writer.printTag( " <signature>" ,signature ,"</signature>" ); 567 writer.printTag( " <position>" ,position ,"</position>" ); 568 writer.printTag( " <extendClass>",extClass ,"</extendClass>" ); 569 writer.printTag( " <extendFlag>" ,extend ,"</extendFlag>" ); 570 writer.printTag( " <description>",desc ,"</description>" ); 571 writer.printTag( " <contents>" ,cmnt ,"</contents>" ); 572 writer.printTag( " <tagText>" ); 573 writer.printTagsInfo( tags ); 574 writer.printTag( " </tagText>" ); 575 writer.printTag( " <history>" ,revTags ,"</history>" ); 576 writer.printTag( " </menber>"); 577 } 578 579 /** 580 * 指定のキーの FieldDoc オブジェクトを、ClassDoc から見つけて返します。 581 * 582 * キー情報は、大文字、小文字の区別なく、最初に見つかった、フィールド名の 583 * FieldDoc オブジェクトを見つけます。 584 * 内部的には、ループを回して検索していますので、非効率です。 585 * 見つからない場合は、null を返します。 586 * 587 * @og.rev 5.7.1.1 (2013/12/13) 新規作成 588 * 589 * @param classDoc 検索元のClassDoc 590 * @param key 検索するキー 591 * @return FieldDocオブジェクト 592 */ 593 private static FieldDoc findFieldDoc( final ClassDoc classDoc ,final String key ) { 594 FieldDoc rtn = null; 595 596 FieldDoc[] fld = classDoc.fields( false ) ; 597 598 for( int i=0; i<fld.length; i++ ) { 599 if( key.equalsIgnoreCase( fld[i].name() ) ) { 600 rtn = fld[i]; 601 break; 602 } 603 } 604 return rtn; 605 } 606 607 /** 608 * カスタムオプションを使用するドックレットの必須メソッド optionLength(String) です。 609 * 610 * ドックレットに認識させる各カスタムオプションに、 optionLength がその 611 * オプションを構成する要素 (トークン) の数を返さなければなりません。 612 * このカスタムオプションでは、 -tag オプションそのものと 613 * その値の 2 つの要素で構成されるので、作成するドックレットの 614 * optionLengthメソッドは、 -tag オプションに対して 2 を返さなくては 615 * なりません。また、認識できないオプションに対しては、0 を返します。 616 * 617 * @param option オプション文字列 618 * 619 * @return 要素(トークン) の数 620 */ 621 public static int optionLength( final String option ) { 622 if("-version".equalsIgnoreCase(option)) { 623 return 2; 624 } 625 else if("-outfile".equalsIgnoreCase(option)) { 626 return 2; 627 } 628 else if("-debugLevel".equalsIgnoreCase(option)) { 629 return 2; 630 } 631 return 0; 632 } 633}