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