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