    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;
    019import org.opengion.fukurou.util.StringUtil;
    020
    021import com.sun.javadoc.RootDoc;
    022import com.sun.javadoc.ClassDoc;
    023import com.sun.javadoc.MethodDoc;
    024import com.sun.javadoc.Type;
    025import com.sun.javadoc.Tag;
    026import java.util.Map;
    027import java.util.HashMap;
    028import java.io.IOException;
    029
    030/**
    031 * ソースコメントから、タグ情報を取り出す Doclet クラスです。
    032 * この Doclet は、":org.opengion.hayabusa.taglib" のみ対象として処理します。
    033 * og.formSample , og.tag , og.group タグを切り出します。
    034 *
    035 * @version  4.0
    036 * @author   Kazuhiko Hasegawa
    037 * @since    JDK5.0,
    038 */
    039public final class DocletTaglib {
    040        private static Map<String,String> map = new HashMap<String,String>();
    041
    042        private static final String OG_FOR_SMPL = "og.formSample";
    043        private static final String OG_TAG_NAME = "og.tag";
    044        private static final String OG_GROUP    = "og.group";
    045
    046        private static final String OG_TAG_CLASS = "org.opengion.hayabusa.taglib";
    047        private static final String ENCODE = "UTF-8";
    048
    049        /**
    050         * すべてが staticメソッドなので、コンストラクタを呼び出さなくしておきます。
    051         *
    052         */
    053        private DocletTaglib() {}
    054
    055        /**
    056         * Doclet のエントリポイントメソッドです。
    057         *
    058         * @og.rev 5.7.1.1 (2013/12/13) タグのインデントを止める。
    059         *
    060         * @param       root    エントリポイントのRootDocオブジェクト
    061         *
    062         * @return      正常実行時 true
    063         */
    064        public static boolean start( final RootDoc root ) {
    065                String version = DocletUtil.getOption( "-version" , root.options() );
    066                String file    = DocletUtil.getOption( "-outfile" , root.options() );
    067
    068                DocletTagWriter writer = null;
    069                try {
    070                        writer = new DocletTagWriter( file,ENCODE );
    071
    072                        // 5.7.1.1 (2013/12/13) タグのインデントを止める。
    073                        writer.printTag( "<?xml version=\"1.0\" encoding=\"", ENCODE, "\" ?>" );
    074                        writer.printTag( "<javadoc>" );
    075                        writer.printTag(   "<version>",version,"</version>" );
    076                        writer.printTag(   "<description></description>" );
    077                        writeContents( root.classes(),writer );
    078                        writer.printTag( "</javadoc>" );
    079                }
    080                catch( IOException ex ) {
    081                        LogWriter.log( ex );
    082                }
    083                finally {
    084                        if( writer != null ) { writer.close(); }
    085                }
    086                return true;
    087        }
    088
    089        /**
    090         * ClassDoc 配列よりコンテンツを作成します。
    091         *
    092         * @og.rev 5.5.4.1 (2012/07/06) コメントは文字列でなく、Tag配列として処理させる。
    093         * @og.rev 5.6.6.1 (2013/07/12) og.group を、tagGroup として独立させる。
    094         * @og.rev 5.7.1.1 (2013/12/13) cmnt と tags の間に改行をセット
    095         * @og.rev 5.7.1.1 (2013/12/13) タグのインデントを止める。
    096         *
    097         * @param classes       ClassDoc配列
    098         * @param writer        DocletTagWriterオブジェクト
    099         */
    100        private static void writeContents( final ClassDoc[] classes,final DocletTagWriter writer ) {
    101                for(int i=0; i< classes.length; i++) {
    102                        ClassDoc classDoc      = classes[i] ;
    103                        String   classFullName = classDoc.qualifiedName() ;
    104
    105                        if( ! classDoc.isPublic() ||
    106                                classFullName.indexOf( OG_TAG_CLASS ) < 0 ) { continue; }
    107
    108                        Tag[]  desc = classDoc.firstSentenceTags();
    109//                      String cmnt = DocletUtil.htmlFilter( classDoc.commentText() );                  // 5.5.4.1 (2012/07/06)
    110                        Tag[]  cmnt = classDoc.inlineTags();                                                                    // 5.5.4.1 (2012/07/06)
    111                        Tag[] smplTags  = classDoc.tags(OG_FOR_SMPL);
    112                        Tag[] grpTags   = classDoc.tags(OG_GROUP);
    113
    114                        // 5.7.1.1 (2013/12/13) タグのインデントを止める。
    115                        writer.printTag( "<classDoc>" );
    116                        writer.printTag(   "<tagClass>"           ,classFullName  ,"</tagClass>" );
    117                        // 5.6.6.1 (2013/07/12) og.group を、tagGroup として独立させる。
    118                        writer.printTag(   "<tagGroup>"           ,makeGroupTag( grpTags )        ,"</tagGroup>" );
    119                        writer.printTag(   "<description>"        ,desc                                           ,"</description>" );
    120//                      writer.printTag(   "<description>" );
    121////            writer.printTag(         makeGroupTag( grpTags )   );           // 5.6.6.1 (2013/07/12)
    122//                      writer.printTag(         desc      );
    123//                      writer.printTag( "</description>" );
    124                        writer.printTag(   "<contents>"           ,cmnt                   ,"</contents>" );
    125                        writer.printTag(   "<formSample>" ,smplTags               ,"</formSample>" );
    126
    127                        map.clear();
    128                        String className = classDoc.name();
    129                        while(  ! "BodyTagSupport".equals( className ) &&
    130                                        ! "TagSupport".equals( className ) ) {
    131                                String extendFlag = "false";
    132                                if( "HTMLTagSupport".equals( className ) ) {
    133                                        extendFlag = "true" ;
    134                                }
    135                                MethodDoc[] methods = classDoc.methods();
    136                                for(int j=0; j < methods.length; j++) {
    137                                        if( ! methods[j].isPublic() ) { continue; }
    138                                        Tag[] tags = methods[j].tags(OG_TAG_NAME);
    139                                        if(tags.length > 0) {
    140                                                String methodName = DocletUtil.removeSetter( methods[j].name() );
    141                                                if( map.containsKey( methodName ) ) { continue; }
    142                                                map.put( methodName,className );
    143                                                Tag[] ftag = methods[j].firstSentenceTags();
    144//                                              cmnt = DocletUtil.htmlFilter( methods[j].commentText() );               // 5.5.4.1 (2012/07/06)
    145                                                cmnt = methods[j].inlineTags();                                                                 // 5.5.4.1 (2012/07/06)
    146
    147                        // 5.7.1.1 (2013/12/13) タグのインデントを止める。
    148                                                writer.printTag(   "<method>" );
    149                                                writer.printTag(     "<name>"             ,methodName     ,"</name>" );
    150                                                writer.printTag(     "<htmlExtend>"       ,extendFlag     ,"</htmlExtend>" );
    151                                                writer.printTag(     "<description>",ftag         ,"</description>" );
    152                                                // 5.7.1.1 (2013/12/13) cmnt と tags の間に改行をセット
    153                                                writer.printTag(     "<contents>" ,cmnt           ,"" );
    154                                                writer.printTag( ""                                     ,tags           ,"</contents>" );
    155//                                              writer.printTag( "    <contents>" );
    156//                                              writer.printTag(        cmnt );
    157//                                              writer.printTag(        tags );
    158//                                              writer.printTag( "    </contents>" );
    159                                                writer.printTag(   "</method>");
    160                                        }
    161                                }
    162                                Type type = classDoc.superclassType();
    163                                if( type == null ) { break; }
    164                                classDoc  = type.asClassDoc() ;
    165                                className = classDoc.name();
    166                        }
    167                        writer.printTag( "  </classDoc>" );
    168                }
    169        }
    170
    171        /**
    172         * タグ配列を受け取り、タグ出力します。
    173         * 複数のタグを出力する場合に、カンマ区切り文字で連結します。
    174         *
    175         * @og.rev 5.5.4.1 (2012/07/06) DocletUtil.htmlFilter → StringUtil.htmlFilter に変更
    176         * @og.rev 5.6.6.1 (2013/07/12) og.group の表示方法を変更する。
    177         *
    178         * @param       tag タグ配列
    179         *
    180         * @return      タグ出力文字列
    181         */
    182        private static String makeGroupTag( final Tag[] tag ) {
    183                StringBuilder but = new StringBuilder( 200 );
    184                for( int i=0; i<tag.length; i++ ) {
    185//                      String data = DocletUtil.htmlFilter( tag[i].text() );
    186                        String data = StringUtil.htmlFilter( tag[i].text() );           // 5.5.4.1 (2012/07/06) DocletUtil → StringUtil に変更
    187                        if( i > 0 ) { but.append( "," ); }
    188//                      but.append( data );
    189                        but.append( "【" ).append( data ).append( "】" );                 // 5.6.6.1 (2013/07/12) og.group の表示方法を変更
    190                }
    191                return but.toString() ;                                                                                 // 5.6.6.1 (2013/07/12)
    192//              if( but.length() > 0 ) {
    193//                      return "【" + but.toString() + "】" ;
    194//              }
    195//              else {
    196//                      return "";
    197//              }
    198        }
    199
    200        /**
    201         * カスタムオプションを使用するドックレットの必須メソッド optionLength(String) です。
    202         *
    203         * ドックレットに認識させる各カスタムオプションに、 optionLength がその
    204         * オプションを構成する要素 (トークン) の数を返さなければなりません。
    205         * このカスタムオプションでは、 -tag オプションそのものと
    206         * その値の 2 つの要素で構成されるので、作成するドックレットの
    207         * optionLengthメソッドは、 -tag オプションに対して 2 を返さなくては
    208         * なりません。また、認識できないオプションに対しては、0 を返します。
    209         *
    210         * @param option カスタムオプションのキーワード
    211         *
    212         * @return 要素 (トークン) の数
    213         */
    214        public static int optionLength( final String option ) {
    215                if(option.equalsIgnoreCase("-version")) {
    216                        return 2;
    217                }
    218                else if(option.equalsIgnoreCase("-outfile")) {
    219                        return 2;
    220                }
    221                return 0;
    222        }
    223}