    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;            // 7.4.4.0 (2021/06/30) openGionV8事前準備(taglet2→taglet)
    017
    018import jdk.javadoc.doclet.DocletEnvironment      ;
    019// import jdk.javadoc.doclet.Doclet  ;
    020// import jdk.javadoc.doclet.Reporter ;
    021// import javax.lang.model.element.Element      ;
    022import javax.lang.model.element.Modifier ;
    023import javax.lang.model.element.TypeElement;
    024// import javax.lang.model.element.ElementKind  ;
    025// import javax.lang.model.element.VariableElement;
    026// import javax.lang.model.SourceVersion         ;
    027import javax.lang.model.type.TypeMirror;
    028// import javax.lang.model.type.PrimitiveType;
    029import javax.lang.model.util.ElementFilter       ;
    030import javax.lang.model.util.Elements ;
    031// import javax.lang.model.util.Types    ;
    032import javax.tools.Diagnostic.Kind       ;
    033import com.sun.source.doctree.DocCommentTree  ;
    034import com.sun.source.util.DocTrees  ;
    035// import com.sun.source.doctree.DocTree  ;
    036
    037// import java.util.Locale ;
    038import java.util.Set;
    039import java.util.List;
    040import java.util.HashSet;
    041import java.util.Arrays;
    042import java.util.Map;
    043import java.util.HashMap;
    044
    045// import java.io.IOException;
    046// import java.io.File;
    047// import java.io.PrintWriter;
    048
    049// import org.opengion.fukurou.util.FileUtil;
    050// import org.opengion.fukurou.util.StringUtil;
    051
    052/**
    053 * ソースコメントから、パラメータ情報を取り出す Doclet クラスです。
    054 * og.paramLevel タグと og.cryptography タグを切り出します。
    055 * これらは、ｼｽﾃﾑパラメータとしてGE12ﾃｰﾌﾞﾙに設定される値をクラスより抽出する
    056 * のに使用します。
    057 *
    058 * @version  7.3
    059 * @author      Kazuhiko Hasegawa
    060 * @since        JDK11.0,
    061 */
    062public class DocTreePlugin extends AbstractDocTree {
    063        private static final String OG_FOR_SMPL  = "og.formSample";
    064
    065        /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。  */
    066        // 6.4.3.3 (2016/03/04) 匿名クラスとインスタンス初期化子による、Mapの初期化処理。
    067        /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */
    068        private static final Map<String,AttKeySet> ATT_KEY_MAP = new HashMap<>();
    069        static {
    070                int no = 0;                             // 7.2.2.0 (2020/03/27) 連番の自動生成
    071                ATT_KEY_MAP.put( "org.opengion.hayabusa.db.Query"                                       , new AttKeySet( "Query"                        ,no++, "queryType"              ));
    072                ATT_KEY_MAP.put( "org.opengion.hayabusa.db.CellRenderer"                        , new AttKeySet( "Renderer"                     ,no++, "renderer"               ));
    073                ATT_KEY_MAP.put( "org.opengion.hayabusa.db.CellEditor"                          , new AttKeySet( "Editor"                       ,no++, "editor"                 ));
    074                ATT_KEY_MAP.put( "org.opengion.hayabusa.db.DBType"                                      , new AttKeySet( "DBType"                       ,no++, "dbType"                 ));
    075                ATT_KEY_MAP.put( "org.opengion.hayabusa.db.TableFilter"                         , new AttKeySet( "TableFilter"          ,no++, "tableFilter"    ));
    076                ATT_KEY_MAP.put( "org.opengion.hayabusa.db.Selection"                           , new AttKeySet( "Selection"            ,no++, "selection"              ));
    077                ATT_KEY_MAP.put( "org.opengion.hayabusa.db.DBConstValue"                        , new AttKeySet( "DBConstValue"         ,no++, "cnstVal"                ));             // 5.6.3.3 (2013/04/19)
    078                ATT_KEY_MAP.put( "org.opengion.hayabusa.html.ViewForm"                          , new AttKeySet( "ViewForm"                     ,no++, "viewFormType"   ));
    079                ATT_KEY_MAP.put( "org.opengion.hayabusa.io.TableWriter"                         , new AttKeySet( "TableWriter"          ,no++, "writerClass"    ));
    080                ATT_KEY_MAP.put( "org.opengion.hayabusa.io.TableReader"                         , new AttKeySet( "TableReader"          ,no++, "readerClass"    ));
    081//              ATT_KEY_MAP.put( "org.opengion.hayabusa.report.DBTableReport"           , new AttKeySet( "DBTableReport"        ,no++, "tableReport"    ));             // 7.0.1.5 (2018/12/10) 削除
    082                ATT_KEY_MAP.put( "org.opengion.hayabusa.resource.CalendarQuery"         , new AttKeySet( "CalendarQuery"        ,no++, "calDB"                  ));
    083                ATT_KEY_MAP.put( "org.opengion.hayabusa.resource.CalendarData"          , new AttKeySet( "CalendarData"         ,no++, "calData"                ));             // 5.6.3.3 (2013/04/19)
    084                ATT_KEY_MAP.put( "org.opengion.fukurou.process.HybsProcess"                     , new AttKeySet( "Process"                      ,no++, "process"                ));
    085//              ATT_KEY_MAP.put( "org.opengion.fukurou.transfer.TransferExec"           , new AttKeySet( "TransferExec"         ,no++, "kbExec"                 ));             // 5.5.3.5 (2012/06/21)
    086//              ATT_KEY_MAP.put( "org.opengion.fukurou.transfer.TransferRead"           , new AttKeySet( "TransferRead"         ,no++, "kbRead"                 ));             // 5.5.3.5 (2012/06/21)
    087                ATT_KEY_MAP.put( "org.opengion.fukurou.util.HybsTimerTask"                      , new AttKeySet( "Daemon"                       ,no++, "daemon"                 ));             // 4.3.4.4 (2009/01/01)
    088                ATT_KEY_MAP.put( "org.opengion.fukurou.util.ConnectIF   "                       , new AttKeySet( "ConnectIF"            ,no++, "connIF"                 ));             // 5.6.3.3 (2013/04/19)
    089                ATT_KEY_MAP.put( "org.opengion.fukurou.xml.JspParserFilter"                     , new AttKeySet( "JspCreate"            ,no++, "jspParser"              ));             // 5.6.3.3 (2013/04/19)
    090        }
    091
    092        private String version  ;
    093        private String outfile  ;
    094
    095//      private DocTrees docUtil;
    096//      private Elements eleUtil ;
    097
    098        /**
    099         * Doclet のエントリポイントメソッドです(昔の startメソッド)。
    100         *
    101         * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
    102         *
    103         * @param docEnv ドックレットを1回呼び出す操作環境
    104         *
    105         * @return 正常実行時 true
    106         */
    107        @Override
    108        public boolean run( final DocletEnvironment docEnv ) {
    109                try( DocTreeWriter writer = new DocTreeWriter( outfile,ENCODE ) ) {
    110                        // 5.7.1.1 (2013/12/13) タグのインデントを止める。
    111                        writer.printTag( "<?xml version=\"1.0\" encoding=\"", ENCODE, "\" ?>" );
    112                        writer.printTag( "<javadoc>" );
    113                        writer.printTag( "  <version>",version,"</version>" );
    114                        writer.printTag( "  <description></description>" );
    115                        writeContents( docEnv,writer );
    116                        writer.printTag( "</javadoc>" );
    117                }
    118                catch( final Throwable th ) {
    119                        reporter.print(Kind.ERROR, th.getMessage());
    120                }
    121
    122                return true;
    123        }
    124
    125        /**
    126         * DocletEnvironmentよりコンテンツを作成します。
    127         *
    128         * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
    129         * @og.rev 8.0.2.1 (2021/12/10) ｺﾒﾝﾄ分割で『。』と半角の『｡』の両方対応しておく。
    130         *
    131         * @param docEnv        ドックレットの最上位
    132         * @param writer        DocTreeWriterオブジェクト
    133         */
    134        private void writeContents( final DocletEnvironment docEnv, final DocTreeWriter writer ) {
    135//              docUtil = docEnv.getDocTrees();
    136//              eleUtil = docEnv.getElementUtils();
    137
    138//              // get the DocTrees utility class to access document comments
    139                final DocTrees docUtil = docEnv.getDocTrees();
    140                final Elements eleUtil  = docEnv.getElementUtils();
    141
    142                // クラス単位にループする。
    143                for( final TypeElement typEle : ElementFilter.typesIn(docEnv.getIncludedElements())) {
    144                        if( !typEle.getModifiers().contains( Modifier.PUBLIC ) ) { continue; }
    145
    146                        final String fullName   = String.valueOf( typEle.getQualifiedName() ) ;
    147//                      final String fullName = String.valueOf(typEle);
    148        //              System.out.println(typEle.getKind() + ":" + fullName);
    149                        writer.setClassName( fullName );
    150
    151                        final AttKeySet attSet = getAttGroupName( typEle,eleUtil ) ;
    152                        if( attSet == null ) { continue; }              // map に登録されていない。
    153
    154                        final int ed = fullName.lastIndexOf( '.' );
    155                        String attKey = null;
    156                        if( ed > 0 ) {
    157                                final String name = fullName.substring( ed+1 );
    158                                attKey = attSet.getAttKey( name );
    159                        }
    160                        if( attKey == null ) { continue; }              // 対象クラス名が、一致しない。
    161
    162                        final DocCommentTree docTree = docUtil.getDocCommentTree(typEle);               // ﾄﾞｷｭﾒﾝﾃｰｼｮﾝ･ｺﾒﾝﾄが見つからない場合、null が返る。
    163
    164//                      final List<? extends DocTree> desc      = docTree == null ? EMPTY_LIST : docTree.getFirstSentence();
    165//                      final List<? extends DocTree> cmnt      = docTree == null ? EMPTY_LIST : docTree.getFullBody();
    166                        final String[] cmnts = getTitleCmnt( docTree );                                                 // 8.0.2.1 (2021/12/10)
    167
    168                        final Map<String,List<String>> blkTagMap = blockTagsMap(docTree);
    169                        final String smplTags   = getBlockTag( OG_FOR_SMPL, blkTagMap, "" );
    170
    171//                      String smplTags = "";
    172//                      if( docTree != null ) {
    173//                              for( final DocTree dt : docTree.getBlockTags() ) {
    174//                                      final String tag = String.valueOf(dt);
    175//                                      if( tag.contains( OG_FOR_SMPL ) ) {
    176//                                              smplTags = tag.substring( OG_FOR_SMPL.length() + 1 ).trim();
    177//                                      }
    178//                              }
    179//                      }
    180
    181                        // 5.7.1.1 (2013/12/13) タグのインデントを止める。
    182                        writer.printTag( "<classDoc>" );
    183                        writer.printTag( "  <attClass>"         ,fullName                               ,"</attClass>"          );
    184                        writer.printTag( "  <seq>"                      ,attSet.getSeq()                ,"</seq>"                       );
    185                        writer.printTag( "  <attKey>"           ,attKey                                 ,"</attKey>"            );
    186                        writer.printTag( "  <valueName>"        ,attSet.getValueName()  ,"</valueName>"         );
    187//                      writer.printTag( "  <description>"      ,desc                                   ,"</description>"       );
    188                        writer.printTag( "  <description>"      ,cmnts[0]                               ,"</description>"       );              // 8.0.2.1 (2021/12/10)
    189//                      writer.printTag( "  <contents>"         ,cmnt                                   ,"</contents>"          );
    190                        writer.printTag( "  <contents>"         ,cmnts[1]                               ,"</contents>"          );              // 8.0.2.1 (2021/12/10)
    191                        writer.printTag( "  <formSample>"       ,smplTags                               ,"</formSample>"        );
    192                        writer.printTag( "</classDoc>" );
    193                }
    194        }
    195
    196        /**
    197         * 指定の ClassDoc が、処理する属性クラスのMapに含まれている場合、
    198         * その AttKeySet クラスのオブジェクトを返します。
    199         * 存在しない場合、null を返します。
    200         *
    201         * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
    202         *
    203         * @param       typEle TypeElementオブジェクト
    204         * @param       eleUtil Elementsユーティリティ
    205         *
    206         * @return      typEleに対応する AttKeySetオブジェクト
    207         */
    208        private static AttKeySet getAttGroupName( final TypeElement typEle,final Elements eleUtil ) {
    209                if( typEle == null ) { return null; }
    210
    211                final String fullName = String.valueOf(typEle);
    212                AttKeySet attKey = ATT_KEY_MAP.get( fullName );
    213                if( attKey == null ) {
    214                        for( final TypeElement tp : eleUtil.getAllTypeElements(fullName) ) {
    215                                if( typEle.equals( tp ) ) { continue; }
    216                                attKey = getAttGroupName( tp,eleUtil );
    217                                if( attKey != null ) { return attKey; }
    218                        }
    219
    220                        final TypeMirror stype = typEle.getSuperclass();                        // 親クラスタイプ
    221                        if( stype != null ) {
    222                                final String suCls = String.valueOf( stype );
    223                                attKey = ATT_KEY_MAP.get( suCls );      // 親クラス
    224                                if( attKey == null ) {
    225                                        for( final TypeElement tp : eleUtil.getAllTypeElements(suCls) ) {
    226                                                if( typEle.equals( tp ) ) { continue; }
    227                                                attKey = getAttGroupName( tp,eleUtil );
    228                                                if( attKey != null ) { return attKey; }
    229                                        }
    230                                }
    231                        }
    232
    233                        if( attKey == null ) {
    234                                for( final TypeMirror itype : typEle.getInterfaces() ) {                // 直近インターフェース
    235                                        final String intFce = String.valueOf( itype );
    236                                        attKey = ATT_KEY_MAP.get( intFce );
    237                                        if( attKey != null ) { return attKey; }
    238                                        else {
    239                                                for( final TypeElement tp : eleUtil.getAllTypeElements(intFce) ) {
    240                                                        if( typEle.equals( tp ) ) { continue; }
    241                                                        attKey = getAttGroupName( tp,eleUtil );
    242                                                        if( attKey != null ) { return attKey; }
    243                                                }
    244                                        }
    245                                }
    246                        }
    247                }
    248
    249                return attKey;
    250        }
    251
    252        /**
    253         * 属性情報を管理する、AttKeySet クラスです。
    254         *
    255         * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
    256         *
    257         * @version  4.0
    258         * @author   Kazuhiko Hasegawa
    259         * @since    JDK5.0,
    260         */
    261        private static final class AttKeySet {
    262                private final String searchKey ;
    263                private final int    len ;
    264                private final String seq ;
    265                private final String valueName ;
    266
    267                /**
    268                 * コンストラクター
    269                 *
    270                 * @param searchKey  検索キー
    271                 * @param seq        シーケンス番号
    272                 * @param valueName  属性名
    273                 *
    274                 */
    275                /* default */ AttKeySet( final String searchKey,final int seq,final String valueName ) {
    276                        this.searchKey          = searchKey ;
    277                        this.seq                = String.valueOf( seq );
    278                        this.valueName          = valueName ;
    279
    280                        len = searchKey.length();
    281                }
    282
    283                /**
    284                 * シーケンス番号を返します。
    285                 *
    286                 * @return シーケンス番号
    287                 *
    288                 */
    289                /* default */ String getSeq() {
    290                        return seq;
    291                }
    292
    293                /**
    294                 * 属性名を返します。
    295                 *
    296                 * @return 属性名
    297                 *
    298                 */
    299                /* default */ String getValueName() {
    300                        return valueName;
    301                }
    302
    303                /**
    304                 * クラス名の先頭一致の場合の、**** 部分を返します。
    305                 * インターフェースも扱えるように修正しましたので、先頭が _ の場合は、
    306                 * _ を削除して返します。
    307                 *
    308                 * @param name クラスの名称 (例：DBCellEditor_**** , ViewForm_****)
    309                 * @return クラス名の**** 部分
    310                 */
    311                /* default */ String getAttKey( final String name ) {
    312                        // 6.1.0.0 (2014/12/26) refactoring
    313                        String rtn = null;                                                              // 一致しなかった。
    314
    315                        if( name.equals( searchKey ) ) {                                // 完全一致：インターフェース
    316                                rtn =  "(Interface)" + name ;
    317                        }
    318                        else if( name.indexOf( searchKey ) == 0 ) {             // 先頭一致した。
    319                                rtn = name.substring( len );
    320                                if( rtn.charAt(0) == '_' ) { rtn = rtn.substring( 1 ); }        // 先頭が _ の場合は、_ を削除
    321                        }
    322
    323                        return rtn ;
    324                }
    325        }
    326
    327        /**
    328         * サポートされているすべてのオプションを返します。
    329         *
    330         * @return サポートされているすべてのオプションを含むセット、存在しない場合は空のセット
    331         */
    332        @Override
    333        public Set<? extends Option> getSupportedOptions() {
    334                final Option[] options = {
    335                        new AbstractOption( "-outfile", "-version" ) {
    336
    337                                /**
    338                                 * 必要に応じてオプションと引数を処理します。
    339                                 *
    340                                 * @param  opt オプション名
    341                                 * @param  arguments 引数をカプセル化したリスト
    342                                 * @return 操作が成功した場合はtrue、そうでない場合はfalse
    343                                 */
    344                                @Override
    345                                public boolean process(final String opt, final List<String> arguments) {
    346                                        if( "-outfile".equalsIgnoreCase(opt) ) {
    347                                                outfile = arguments.get(0);
    348                                        }
    349                                        else if( "-version".equalsIgnoreCase(opt) ) {
    350                                                version = arguments.get(0);
    351                                        }
    352                                        return true;
    353                                }
    354                        }
    355                };
    356                return new HashSet<>(Arrays.asList(options));
    357        }
    358}