    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
    018// import jdk.javadoc.doclet.DocletEnvironment;
    019import jdk.javadoc.doclet.Doclet;
    020import jdk.javadoc.doclet.Reporter;
    021// import javax.lang.model.element.Element;
    022// import javax.lang.model.element.Modifier;
    023// import javax.lang.model.element.TypeElement;
    024// import javax.lang.model.element.ElementKind;
    025// import javax.lang.model.element.VariableElement;
    026import javax.lang.model.SourceVersion;
    027// import javax.lang.model.util.ElementFilter;
    028// import javax.lang.model.util.Elements;
    029import javax.tools.Diagnostic.Kind;
    030import com.sun.source.doctree.DocCommentTree;
    031// import com.sun.source.util.DocTrees;
    032import com.sun.source.doctree.DocTree;
    033
    034import java.util.Locale;
    035// import java.util.Set;
    036import java.util.List;
    037// import java.util.HashSet;
    038import java.util.Arrays;
    039import java.util.ArrayList;
    040
    041import java.util.Map;
    042import java.util.HashMap;
    043
    044// import java.io.IOException;
    045// import java.io.File;
    046// import java.io.PrintWriter;
    047
    048// import org.opengion.fukurou.util.FileUtil;
    049// import org.opengion.fukurou.util.StringUtil;
    050
    051/**
    052 * ｿｰｽｺﾒﾝﾄから、ﾊﾟﾗﾒｰﾀ情報を取り出す Doclet ｸﾗｽです。
    053 * og.paramLevel ﾀｸﾞと og.cryptography ﾀｸﾞを切り出します。
    054 * これらは、ｼｽﾃﾑﾊﾟﾗﾒｰﾀとしてGE12ﾃｰﾌﾞﾙに設定される値をｸﾗｽより抽出する
    055 * のに使用します。
    056 *
    057 * @version     7.3
    058 * @author      Kazuhiko Hasegawa
    059 * @since       JDK11.0,
    060 */
    061public abstract class AbstractDocTree implements Doclet {
    062        /** ｴﾝｺｰﾄﾞ {@value} */
    063        public static final String ENCODE = "UTF-8";
    064
    065        /** 情報の出力 */
    066        protected Reporter reporter;                                                                    // JavaDocの情報、警告、ｴﾗｰ情報の出力
    067
    068        /** 空DocTreeﾘｽﾄ */
    069        protected static final List<DocTree> EMPTY_LIST = List.of();    // ｾﾞﾛ要素を含む変更不可能なﾘｽﾄ
    070
    071        /**
    072         * ﾃﾞﾌｫﾙﾄｺﾝｽﾄﾗｸﾀｰ
    073         *
    074         * @og.rev 7.3.0.0 (2021/01/06) PMD refactoring. Each class should declare at least one constructor.
    075         */
    076        public AbstractDocTree() { super(); }           // これも、自動的に呼ばれるが、空のﾒｿｯﾄﾞを作成すると警告されるので、明示的にしておきます。
    077
    078        /**
    079         * 指定されたﾛｹｰﾙとｴﾗｰ･ﾚﾎﾟｰﾀでこのﾄﾞｯｸﾚｯﾄを初期化します。
    080         *
    081         * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
    082         *
    083         * @param       locale          使用されるﾛｹｰﾙ
    084         * @param       reporter        使用するﾚﾎﾟｰﾀ
    085         */
    086        @Override       // Doclet
    087        public void init(final Locale locale, final Reporter reporter) {
    088                reporter.print(Kind.NOTE, getName() + " Start!: ");             // NOTE:情報 WARNING:警告 ERROR:ｴﾗｰ
    089                this.reporter = reporter;
    090        }
    091
    092        /**
    093         * ﾄﾞｯｸﾚｯﾄを識別する名前を返します。
    094         *
    095         * @return      名前
    096         */
    097        @Override       // Doclet
    098        public String getName() {
    099                return getClass().getSimpleName();
    100        }
    101
    102        /**
    103         * Doclet.Option を継承し、共通ﾒｿｯﾄﾞを実装したabstractｸﾗｽ
    104         *
    105         * 単純に、ﾒｿｯﾄﾞのOverrideで共通化しているだけです。
    106         */
    107        protected static abstract class AbstractOption implements Option {
    108                private final List<String> someOption ;
    109
    110                /**
    111                 * ｺﾝｽﾄﾗｸﾀｰ。
    112                 *
    113                 * @param       prm     引数に対応するｷｰの可変引数
    114                 */
    115                AbstractOption( final String... prm ) {
    116                        super();
    117                        someOption = Arrays.asList( prm );
    118                }
    119
    120                /**
    121                 * このｵﾌﾟｼｮﾝが消費する引数の数を返します。
    122                 *
    123                 * @return      消費された引数の数
    124                 */
    125                @Override       // Doclet.Option
    126                public int getArgumentCount() {
    127                        return 1;
    128                }
    129
    130                /**
    131                 * ｵﾌﾟｼｮﾝの説明を返します。
    132                 *
    133                 * @return      設定されている場合はdescription、そうでない場合は空のString
    134                 */
    135                @Override       // Doclet.Option
    136                public String getDescription() {
    137                        return "an option with aliases";
    138                }
    139
    140                /**
    141                 * ｵﾌﾟｼｮﾝの種類を返します。
    142                 *
    143                 * @return      このオプションの種類
    144                 */
    145                @Override       // Doclet.Option
    146                public Option.Kind getKind() {
    147                        return Option.Kind.STANDARD;
    148                }
    149
    150                /**
    151                 * ｵﾌﾟｼｮﾝを識別するために使用される可能性のある名前のﾘｽﾄを返します。
    152                 *
    153                 * @return      ｵﾌﾟｼｮﾝの名前ﾘｽﾄ
    154                 */
    155                @Override       // Doclet.Option
    156                public List<String> getNames() {
    157                        return someOption;
    158                }
    159
    160                /**
    161                 * ｵﾌﾟｼｮﾝのﾊﾟﾗﾒｰﾀを返します。
    162                 *
    163                 * @return      設定されている場合はﾊﾟﾗﾒｰﾀ、それ以外の場合は空のString
    164                 */
    165                @Override       // Doclet.Option
    166                public String getParameters() {
    167                        return "file";
    168                }
    169        }
    170
    171        /**
    172         * このﾄﾞｯｸﾚｯﾄでｻﾎﾟｰﾄされているJavaﾌﾟﾛｸﾞﾗﾐﾝｸﾞ言語のﾊﾞｰｼﾞｮﾝを返します。
    173         *
    174         * @return      通常は最新バージョン
    175         */
    176        @Override       // Doclet
    177        public SourceVersion getSupportedSourceVersion() {
    178                // support the latest release
    179                return SourceVersion.latest();
    180        }
    181
    182        /* ************************************************************************************ */
    183
    184//      /**
    185//       * 指定の文字列から、開始文字と終了文字の間を切り抜いて返します。
    186//       * 開始文字か、終了文字のどちらかが存在しない場合は、ｵﾘｼﾞﾅﾙの文字列を返します。
    187//       *
    188//       * @param       org     ｵﾘｼﾞﾅﾙの文字列
    189//       * @param       stCh    開始文字
    190//       * @param       edCh    終了文字
    191//       * @return      切り抜かれた文字列
    192//       */
    193//      protected String cut( final String org , final char stCh , final char edCh ) {
    194//              final int st = org.indexOf( stCh );
    195//              final int ed = st > 0 ? org.indexOf( edCh , st ) : -1;
    196//
    197//              return ed > 0 ? org.substring( st+1,ed ) : org ;
    198//      }
    199
    200//      /**
    201//       * 指定の文字列から、開始文字と終了文字の間を切り抜いて返します。
    202//       * 開始文字か、終了文字のどちらかが存在しない場合は、ｵﾘｼﾞﾅﾙの文字列を返します。
    203//       *
    204//       * @param       org     ｵﾘｼﾞﾅﾙの文字列
    205//       * @param       omit    先頭から削除する文字列
    206//       * @return      切り抜かれた文字列
    207//       */
    208//      protected String cutTag( final String org , final String omit ) {
    209//              return org.substring( 1+omit.length() ).trim();
    210//      }
    211
    212        /**
    213         * BlockTagsのｷｰとﾘｽﾄのMapを作成して返します。
    214         * ｷｰと値を分離します。同じｷｰが複数存在しますので、それらは Listに入れて返します。
    215         * docTreeは、null の場合もあるので、その場合は、空のMapを返します。
    216         *
    217         * @param       docTree DocCommentTreeｵﾌﾞｼﾞｪｸﾄ
    218         * @return      BlockTagsのｷｰとﾘｽﾄのMap
    219         */
    220        protected Map<String,List<String>> blockTagsMap( final DocCommentTree docTree ) {
    221                final Map<String,List<String>> rtnMap = new HashMap<>();
    222
    223                if( docTree != null ) {
    224                        for( final DocTree dt : docTree.getBlockTags() ) {
    225                                final String tag = String.valueOf(dt).trim();
    226                                final int ad = tag.indexOf( ' ' );                                                                      // 最初のｽﾍﾟｰｽで分離
    227                                final String key = ad > 0 ? tag.substring( 1,ad ).trim() : tag ;        // 最初の文字列は、@ なので。
    228                                final String val = ad > 0 ? tag.substring( ad+1 ).trim() : "" ;
    229
    230                                rtnMap.computeIfAbsent(key, k -> new ArrayList<String>()).add( val );
    231                        }
    232                }
    233
    234                return rtnMap;
    235        }
    236
    237        /**
    238         * blockTagsMapで作成されたMapｵﾌﾞｼﾞｪｸﾄから、文字列を作成します。
    239         * ｷｰがMapに存在しない場合は、空文字列を返します。
    240         * docTreeは、null の場合もあるので、その場合は、空のMapを返します。
    241         *
    242         * @param       key                     blockTagのｷｰ
    243         * @param       blcMap          blockTagsMapで作成されたMapｵﾌﾞｼﾞｪｸﾄ
    244         * @param       delimiter       複数ﾀｸﾞを連結する場合の、区切り文字
    245         * @return      指定のﾀｸﾞの文字列
    246         */
    247        protected String getBlockTag( final String key,final Map<String,List<String>> blcMap,final String delimiter ) {
    248                final List<String> blkList = blcMap.get( key );
    249
    250                return blkList == null ? "" : String.join( delimiter,blkList );
    251        }
    252
    253        /**
    254         * DocTreeﾘｽﾄから、最初の1文(改行か『。』まで)と本文の文字列を切り出します。
    255         *
    256         * title が、docTree.getFirstSentence(); でうまく取れていないようです。
    257         * docTree.getFullBody() で取ってきた最初の1文と、残りの本文を切り出します。
    258         *
    259         * @og.rev 8.0.1.0 (2021/10/29) titleを改行か『。』までの文字列を切り出す。
    260         * @og.rev 8.0.2.1 (2021/12/10) ｺﾒﾝﾄ分割で『。』と半角の『。』の両方対応しておく。
    261         * @og.rev 8.0.2.1 (2021/12/10) DocTreeParam → AbstractDocTree に移動
    262         * @og.rev 8.5.1.0 (2023/06/30) docTreeの変換がunicodeの為、正しく動作しなかった『。』変換対応
    263         *
    264         * @param       docTree DocCommentTreeｵﾌﾞｼﾞｪｸﾄ
    265         * @return      最初の1文(改行か『。』まで)と本文の文字列を分割した配列
    266         */
    267        protected String[] getTitleCmnt( final DocCommentTree docTree ) {
    268                final String[] rtns = { "","" };                                                                                // 初期値
    269
    270                if( docTree == null ) { return rtns; }
    271                final List<? extends DocTree> doc = docTree.getFullBody();
    272                if( doc.isEmpty() ) { return rtns; }
    273//              final String body = String.valueOf( doc.get(0) );
    274
    275                final StringBuilder buf = new StringBuilder();                                                  // 8.0.2.1 (2021/12/10) & で切れる？
    276                doc.forEach( val -> buf.append(val) );
    277                final String body = buf.toString();
    278
    279//              int st = body.indexOf( '。' );
    280//              int st = Math.max( body.indexOf( '。' ) , body.indexOf( '｡' ) );         // 8.0.2.1 (2021/12/10)
    281                int st = Math.max( body.indexOf( "u3002" ) , body.indexOf( "uff61" ) ); // 8.5.1.0 (2023/06/27) Modify
    282
    283                if( st < 0 ) {
    284                        st = body.indexOf( '\n' );
    285                }
    286
    287                if( st >= 0 ) {
    288                        st += 5;                                                                                                                        // 8.5.1.0 (2023/06/27) Add
    289                        rtns[0] = body.substring( 0,st ).trim();                                                        // 一応、ｽﾍﾟｰｽは削除する。
    290                        rtns[1] = body.substring( st ).trim();                                                          // 一応、ｽﾍﾟｰｽは削除する。
    291                        if( rtns[1].isEmpty() ) { rtns[1] = rtns[0]; }
    292                }
    293                else {
    294                        rtns[0] = body.trim();
    295                        rtns[1] = rtns[0];                                                                                                      // 8.0.2.1 (2021/12/10) 同じ値をｾｯﾄする。
    296                }
    297
    298                return rtns ;
    299        }
    300}