    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                        Tag[]  cmnt = classDoc.inlineTags();                                                                    // 5.5.4.1 (2012/07/06)
    110                        Tag[] smplTags  = classDoc.tags(OG_FOR_SMPL);
    111                        Tag[] grpTags   = classDoc.tags(OG_GROUP);
    112
    113                        // 5.7.1.1 (2013/12/13) タグのインデントを止める。
    114                        writer.printTag( "<classDoc>" );
    115                        writer.printTag(   "<tagClass>"         ,classFullName  ,"</tagClass>" );
    116                        // 5.6.6.1 (2013/07/12) og.group を、tagGroup として独立させる。
    117                        writer.printTag(   "<tagGroup>"         ,makeGroupTag( grpTags )        ,"</tagGroup>" );
    118                        writer.printTag(   "<description>"      ,desc                                           ,"</description>" );
    119                        writer.printTag(   "<contents>"         ,cmnt                   ,"</contents>" );
    120                        writer.printTag(   "<formSample>"       ,smplTags               ,"</formSample>" );
    121
    122                        map.clear();
    123                        String className = classDoc.name();
    124                        while(  ! "BodyTagSupport".equals( className ) &&
    125                                        ! "TagSupport".equals( className ) ) {
    126                                String extendFlag = "false";
    127                                if( "HTMLTagSupport".equals( className ) ) {
    128                                        extendFlag = "true" ;
    129                                }
    130                                MethodDoc[] methods = classDoc.methods();
    131                                for(int j=0; j < methods.length; j++) {
    132                                        if( ! methods[j].isPublic() ) { continue; }
    133                                        Tag[] tags = methods[j].tags(OG_TAG_NAME);
    134                                        if(tags.length > 0) {
    135                                                String methodName = DocletUtil.removeSetter( methods[j].name() );
    136                                                if( map.containsKey( methodName ) ) { continue; }
    137                                                map.put( methodName,className );
    138                                                Tag[] ftag = methods[j].firstSentenceTags();
    139                                                cmnt = methods[j].inlineTags();                                                                 // 5.5.4.1 (2012/07/06)
    140
    141                        // 5.7.1.1 (2013/12/13) タグのインデントを止める。
    142                                                writer.printTag(   "<method>" );
    143                                                writer.printTag(     "<name>"           ,methodName     ,"</name>" );
    144                                                writer.printTag(     "<htmlExtend>"     ,extendFlag     ,"</htmlExtend>" );
    145                                                writer.printTag(     "<description>",ftag               ,"</description>" );
    146                                                // 5.7.1.1 (2013/12/13) cmnt と tags の間に改行をセット
    147                                                writer.printTag(     "<contents>"       ,cmnt           ,"" );
    148                                                writer.printTag( ""                                     ,tags           ,"</contents>" );
    149                                                writer.printTag(   "</method>");
    150                                        }
    151                                }
    152                                Type type = classDoc.superclassType();
    153                                if( type == null ) { break; }
    154                                classDoc  = type.asClassDoc() ;
    155                                className = classDoc.name();
    156                        }
    157                        writer.printTag( "  </classDoc>" );
    158                }
    159        }
    160
    161        /**
    162         * タグ配列を受け取り、タグ出力します。
    163         * 複数のタグを出力する場合に、カンマ区切り文字で連結します。
    164         *
    165         * @og.rev 5.5.4.1 (2012/07/06) DocletUtil.htmlFilter → StringUtil.htmlFilter に変更
    166         * @og.rev 5.6.6.1 (2013/07/12) og.group の表示方法を変更する。
    167         *
    168         * @param       tag タグ配列
    169         *
    170         * @return      タグ出力文字列
    171         */
    172        private static String makeGroupTag( final Tag[] tag ) {
    173                StringBuilder but = new StringBuilder( 200 );
    174                for( int i=0; i<tag.length; i++ ) {
    175                        String data = StringUtil.htmlFilter( tag[i].text() );           // 5.5.4.1 (2012/07/06) DocletUtil → StringUtil に変更
    176                        if( i > 0 ) { but.append( "," ); }
    177                        but.append( "【" ).append( data ).append( "】" );                 // 5.6.6.1 (2013/07/12) og.group の表示方法を変更
    178                }
    179                return but.toString() ;                                                                                 // 5.6.6.1 (2013/07/12)
    180        }
    181
    182        /**
    183         * カスタムオプションを使用するドックレットの必須メソッド optionLength(String) です。
    184         *
    185         * ドックレットに認識させる各カスタムオプションに、 optionLength がその
    186         * オプションを構成する要素 (トークン) の数を返さなければなりません。
    187         * このカスタムオプションでは、 -tag オプションそのものと
    188         * その値の 2 つの要素で構成されるので、作成するドックレットの
    189         * optionLengthメソッドは、 -tag オプションに対して 2 を返さなくては
    190         * なりません。また、認識できないオプションに対しては、0 を返します。
    191         *
    192         * @param option カスタムオプションのキーワード
    193         *
    194         * @return 要素 (トークン) の数
    195         */
    196        public static int optionLength( final String option ) {
    197                if("-version".equalsIgnoreCase(option)) {
    198                        return 2;
    199                }
    200                else if("-outfile".equalsIgnoreCase(option)) {
    201                        return 2;
    202                }
    203                return 0;
    204        }
    205}