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.ThrowUtil; 019import static org.opengion.fukurou.system.HybsConst.CR; // 6.1.0.0 (2014/12/26) refactoring 020import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE; // 6.1.0.0 (2014/12/26) refactoring 021 022import java.lang.reflect.Field; 023import java.security.AccessController; // 6.1.0.0 (2014/12/26) findBugs 024import java.security.PrivilegedAction; // 6.1.0.0 (2014/12/26) findBugs 025 026import com.sun.javadoc.Tag; 027import com.sun.javadoc.Doc; 028import com.sun.javadoc.ClassDoc; 029import com.sun.javadoc.ProgramElementDoc; 030 031/** 032 * Doclet を処理するプログラムで共通して使用される簡易メソッド群(ユーティリティクラス)です。 033 * 034 * @version 4.0 035 * @author Kazuhiko Hasegawa 036 * @since JDK5.0, 037 */ 038@SuppressWarnings(value={"deprecation","removal"}) // Ver7.0.0.0 , 7.2.2.0 (2020/03/27) 039public final class DocletUtil { 040 041 private static final String CLS = "org.opengion.fukurou.system.BuildNumber"; // package.class 042 private static final String FLD = "VERSION_NO"; // field 043 private static final String VERSION_NO = getStaticField( CLS , FLD ); // 6.4.1.1 (2016/01/16) versionNo → VERSION_NO refactoring 044 045 /** 046 * すべてが staticメソッドなので、コンストラクタを呼び出さなくしておきます。 047 * 048 */ 049 private DocletUtil() {} 050 051 /** 052 * target 文字列に含まれる from 文字列を to 文字列に置き換えます。 053 * 054 * @param target 元の文字列 055 * @param from 置換元FROM 056 * @param to 置換先TO 057 * 058 * @return 変換後文字列 059 */ 060 public static String replace( final String target,final String from,final String to ) { 061 if( target == null || from == null || to == null ) { return target; } 062 final StringBuilder strBuf = new StringBuilder( BUFFER_MIDDLE ); 063 064 int start = 0; 065 int end = target.indexOf( from,start ); 066 while( end >= 0 ) { 067 strBuf.append( target.substring( start,end ) ); 068 strBuf.append( to ); 069 start = end + from.length(); 070 end = target.indexOf( from,start ); 071 } 072 strBuf.append( target.substring( start ) ); 073 074 return strBuf.toString(); 075 } 076 077 /** 078 * セッターメソッドの setXXXX の set を削除し、次の文字を小文字化します。 079 * つまり、セッターメソッドから属性値を推測します。 080 * (超特殊処理)セッターメソッドのset以下2文字目が大文字の場合は、 081 * 1文字目も大文字と考えて小文字化を行いません。 082 * 例えば、setSYS や setUSER など、RequestValueTag.javaに使用するケースです。 083 * 084 * @param target 処理対象となる文字列 085 * 086 * @return オプション文字列 087 */ 088 public static String removeSetter( final String target ) { 089 if( target != null && target.startsWith( "set" ) ) { 090 char[] chs = target.toCharArray(); 091 if( chs.length > 4 && !( chs[4] >= 'A' && chs[4] <= 'Z' ) ) { 092 chs[3] = Character.toLowerCase( chs[3] ) ; 093 } 094 return new String( chs,3,chs.length-3 ); 095 } 096 return target; 097 } 098 099 /** 100 * オプション配列文字列より、指定のキーに対応するオプション値を返します。 101 * 102 * @param key キー 103 * @param options オプション配列文字列 104 * 105 * @return オプション文字列 106 */ 107 public static String getOption( final String key , final String[][] options ) { 108 String rtn = ""; 109 if( key == null || options == null ) { return rtn; } 110 111 for( int i=0; i<options.length; i++ ) { 112 if( key.equalsIgnoreCase( options[i][0] ) ) { 113 rtn = options[i][1]; 114 break ; 115 } 116 } 117 return rtn ; 118 } 119 120 /** 121 * {@og.value package.class#field} 形式のvalueタグを文字列に置き換えます。 122 * 123 * 処理的には、リフレクションで、値を取得します。値は、staticフィールドのみ取得可能です。 124 * 125 * @og.rev 5.5.4.1 (2012/07/06) 新規追加 126 * @og.rev 5.5.5.6 (2012/08/31) クラス名の取得で、ProgramElementDoc で処理するように変更 127 * 128 * @param tag Tagオブジェクト 129 * 130 * @return valueタグの解析結果の文字列 131 */ 132 public static String valueTag( final Tag tag ) { 133 return valueTag( tag.text() , tag ); 134 } 135 136 /** 137 * {@og.value package.class#field} 形式のvalueタグを文字列に置き換えます。 138 * 139 * 処理的には、リフレクションで、値を取得します。値は、staticフィールドのみ取得可能です。 140 * 141 * @og.rev 5.5.4.1 (2012/07/06) 新規追加 142 * @og.rev 5.5.5.6 (2012/08/31) クラス名の取得で、ProgramElementDoc で処理するように変更 143 * @og.rev 6.3.5.1 (2015/08/12) HybsSystem とSystemData のみパッケージ無しでも処理できるように対応 144 * 145 * @param txt Tagテキスト (og.value 以下の文字列のみ) 146 * @param tag Tagオブジェクト (正確な class 名を求める場合に使用) 147 * 148 * @return valueタグの解析結果の文字列 149 */ 150 public static String valueTag( final String txt , final Tag tag ) { 151 String text = txt; 152 if( txt != null ) { 153 String cls = null; // package.class 154 String fld = null; // field 155 // package.class#field 形式の解析。 156 final int adrs = txt.indexOf('#') ; 157 if( adrs > 0 ) { 158 cls = txt.substring( 0,adrs ); // package.class 159 fld = txt.substring( adrs+1 ); // field 160 } 161 else if( adrs == 0 ) { 162 fld = txt.substring( 1 ); // #field 163 } 164 else { 165 final String errMsg = "警告:{@value package.class#field} 形式のフィールド名 #field がありません。" + CR 166 + txt + CR ; 167 System.err.println( errMsg ); 168 // # を付け忘れたと考え、自分自身のクラスを利用 169 fld = txt; // field 170 } 171 172 // package.class をきちんと作成する。 173 final Doc doc = tag.holder(); 174 175 // 5.5.5.6 (2012/08/31) ProgramElementDoc で処理するように変更 176 if( doc instanceof ProgramElementDoc ) { 177 final ProgramElementDoc pdoc = (ProgramElementDoc)doc; 178 final ClassDoc cdoc = pdoc.containingClass(); 179 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..; 180 if( cdoc == null ) { 181 if( cls == null ) { // package.class が登録されていない場合。 182 cls = pdoc.qualifiedName(); 183 } 184 else if( cls.indexOf('.') < 0 ) { 185 // 6.3.5.1 (2015/08/12) HybsSystem とSystemData のみパッケージ無しでも処理できるように対応 186 if( "HybsSystem".equals( cls ) || "SystemData".equals( cls ) ) { 187 cls = "org.opengion.hayabusa.common." + cls ; 188 } 189 else { // 同一パッケージ内と仮定 190 final String tmp = pdoc.qualifiedName(); 191 final int ad = tmp.lastIndexOf('.'); 192 if( ad > 0 ) { 193 cls = tmp.substring( 0,ad+1 ) + cls ; 194 } 195 } 196 } 197 } 198 else { 199 if( cls == null ) { // package.class が登録されていない場合。 200 cls = cdoc.qualifiedName() ; 201 } 202 else if( cls.indexOf('.') < 0 ) { // class のみが登録されている場合。findClass で、package 込の正式クラス名を検索する。 203 final ClassDoc fdoc = cdoc.findClass( cls ); 204 if( fdoc != null ) { 205 cls = fdoc.qualifiedName() ; 206 } 207 } 208 } 209 } 210 211 // 6.3.5.1 (2015/08/12) HybsSystem とSystemData のみパッケージ無しでも処理できるように対応 212 if( "HybsSystem".equals( cls ) || "SystemData".equals( cls ) ) { 213 cls = "org.opengion.hayabusa.common." + cls ; 214 } 215 216 // 5.6.3.3 (2013/04/19) メソッド化で共有します。 217 text = getStaticField( cls,fld ); 218 } 219 return text ; 220 } 221 222 /** 223 * {@og.doc03Link queryType Query_**** クラス} 形式のdoc03Linkタグをリンク文字列に置き換えます。 224 * 225 * <a href="/gf/jsp/DOC03/index.jsp?command=NEW↦GAMENID=DOC03&VERNO=X.X.X.X&VALUENAME=queryType" target="CONTENTS">Query_**** クラス</a> 226 * のようなリンクを作成します。 227 * 第一引数は、VALUENAME の引数です。 228 * それ以降のテキストは、リンク文字列のドキュメントになります。 229 * DOC03 画面へのリンクを作成するに当たり、バージョンが必要です。org.opengion.fukurou.system.BuildNumber#VERSION_NO から取得しますが、 230 * パッケージの優先順の関係で、リフレクションを使用します。 231 * 232 * @og.rev 5.6.3.3 (2013/04/19) 新規作成 233 * @og.rev 6.0.2.0 (2014/09/19) 処理を #doc03LinkTag( String ) で行う。 234 * 235 * @param tag Tagオブジェクト 236 * 237 * @return valueタグの解析結果の文字列 238 * @og.rtnNotNull 239 */ 240 public static String doc03LinkTag( final Tag tag ) { 241 return doc03LinkTag( tag.text() ); 242 } 243 244 /** 245 * {@og.doc03Link queryType Query_****クラス} 形式のdoc03Linkタグをリンク文字列に置き換えます。 246 * 247 * <a href="/gf/jsp/DOC03/index.jsp?command=NEW&GAMENID=DOC03&VERNO=X.X.X.X&VALUENAME=queryType" 248 * target="CONTENTS" >Query_****クラス</a> 249 * のようなリンクを作成します。 250 * 第一引数は、VALUENAME の引数です。 251 * それ以降のテキストは、リンク文字列のドキュメントになります。 252 * DOC03 画面へのリンクを作成するに当たり、バージョンが必要です。 253 * org.opengion.fukurou.system.BuildNumber#VERSION_NO から取得しますが、 254 * パッケージの優先順の関係で、リフレクションを使用します。 255 * 256 * @og.rev 6.0.2.0 (2014/09/19) 新規追加 257 * 258 * @param txt Tagテキスト(og.doc03Link 以下の文字列のみ) 259 * 260 * @return valueタグの解析結果の文字列 261 * @og.rtnNotNull 262 */ 263 public static String doc03LinkTag( final String txt ) { 264 if( txt != null ) { 265 final String text = txt.trim(); 266 267 final int adrs = text.indexOf(' ') ; // 最初のスペースで分離します。 268 if( adrs > 0 ) { 269 final String valnm = text.substring( 0,adrs ).trim(); // VALUENAME 270 final String body = text.substring( adrs+1 ).trim(); // ドキュメント 271 272 return "<a href=\"/gf/jsp/DOC03/index.jsp?command=NEW&GAMENID=DOC03" 273 + "&VERNO=" + VERSION_NO 274 + "&VALUENAME=" + valnm 275 + "\" target=\"CONTENTS\">" 276 + body 277 + "</a>" ; 278 } 279 } 280 return "doc03Link 不明:" + txt ; 281 } 282 283 /** 284 * このタグレットがインラインタグで {@link XXXX YYYY} を処理するように 285 * 用意された、カスタムメソッドです。 286 * 287 * @og.rev 6.0.2.0 (2014/09/19) 新規追加 288 * 289 * @param txt オリジナルの文字列 290 * 291 * @return インラインタグの link を処理した結果の文字列 292 * @og.rtnNotNull 293 */ 294 public static String linkTag( final String txt ) { 295 // {@link XXXX YY} の XXXX YY部分を処理 296 if( txt != null ) { 297 final String text = txt.trim() ; 298 299 final int adrs = text.indexOf( ' ' ); 300 if( adrs > 0 ) { 301 final String xxx = text.substring( 0,adrs ).trim(); // 前半:アドレス変換 302 final String yyy = text.substring( adrs ).trim(); // 後半:ラベル 303 final String zzz = xxx.replace( '.','/' ); 304 305 return "<a href=\"../../../../" + zzz + ".html\" " 306 + "title=\"" + xxx + "\">" + yyy + "</a>" ; 307 } 308 } 309 310 return ""; 311 } 312 313 /** 314 * パッケージ.クラス名 と、フィールド名 から、staticフィールドの値を取得します。 315 * 316 * Field fldObj = Class.forName( cls ).getDeclaredField( fld ); で、Fieldオブジェクトを呼出し、 317 * String.valueOf( fldObj.get( null ) ); で、値を取得しています。 318 * static フィールドは、引数 null で値を取得できます。 319 * 320 * ※ 超特殊処理として、cls名が、HybsSystem とSystemData のみ、パッケージ無しで処理 321 * できるように対応します。 322 * 323 * 例; 324 * String cls = "org.opengion.fukurou.system.BuildNumber"; // package.class 325 * String fld = "VERSION_NO"; // field 326 * 327 * @og.rev 5.6.3.3 (2013/04/19) 新規作成 328 * @og.rev 6.4.2.0 (2016/01/29) StringUtil#ogStackTrace(Throwable) を、ThrowUtil#ogStackTrace(String,Throwable) に置き換え。 329 * 330 * @param cls パッケージ.クラス名 331 * @param fld フィールド名 332 * @return 取得値 333 */ 334 public static String getStaticField( final String cls , final String fld ) { 335 String txt = null; 336 try { 337 final Field fldObj = Class.forName( cls ).getDeclaredField( fld ); 338 // privateフィールドへのアクセス。(セキュリティーマネージャーによってアクセス制限がかけられていない場合) 339 // 6.1.0.0 (2014/12/26) findBugs: Bug type DP_DO_INSIDE_DO_PRIVILEGED (click for details) 340 // reflect.Field.setAccessible(boolean) の呼び出しは doPrivileged ブロックの中から呼び出すべきです。 341 if( !fldObj.isAccessible() ) { 342 AccessController.doPrivileged( new PrivilegedAction<DocletUtil>() { 343 /** 344 * 特権を有効にして実行する PrivilegedAction<T> の run() メソッドです。 345 * 346 * このメソッドは、特権を有効にしたあとに AccessController.doPrivileged によって呼び出されます。 347 * 348 * @return DocletUtilオブジェクト 349 */ 350 public DocletUtil run() { 351 // privileged code goes here 352 fldObj.setAccessible( true ); 353 return null; // nothing to return 354 } 355 }); 356 } 357 txt = String.valueOf( fldObj.get( null ) ); // static フィールドは、引数 null で値を取得 358 } 359 // 6.0.2.5 (2014/10/31) findBugs:Bug type REC_CATCH_EXCEPTION 対応 360 catch( final Throwable th ) { 361 // 6.4.2.0 (2016/01/29) StringUtil#ogStackTrace(Throwable) を、ThrowUtil#ogStackTrace(String,Throwable) に置き換え。 362 final String errMsg = "package.class=[" + cls + "],field=[" + fld + "] の取得に失敗しました。" + CR ; 363 System.err.println( ThrowUtil.ogStackTrace( errMsg , th ) ); 364 } 365 366 return txt ; 367 } 368}