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