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) 8.5.2.0 (2023/07/14) Delete
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         * @return      正常実行時 true
105         */
106        @Override
107        public boolean run( final DocletEnvironment docEnv ) {
108                try( DocTreeWriter writer = new DocTreeWriter( outfile,ENCODE ) ) {
109                        // 5.7.1.1 (2013/12/13) タグのインデントを止める。
110                        writer.printTag( "<?xml version=\"1.0\" encoding=\"", ENCODE, "\" ?>" );
111                        writer.printTag( "<javadoc>" );
112                        writer.printTag( "  <version>",version,"</version>" );
113                        writer.printTag( "  <description></description>" );
114                        writeContents( docEnv,writer );
115                        writer.printTag( "</javadoc>" );
116                }
117                catch( final Throwable th ) {
118                        reporter.print(Kind.ERROR, th.getMessage());
119                }
120
121                return true;
122        }
123
124        /**
125         * DocletEnvironmentよりコンテンツを作成します。
126         *
127         * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
128         * @og.rev 8.0.2.1 (2021/12/10) コメント分割で『。』と半角の『。』の両方対応しておく。
129         *
130         * @param       docEnv  ドックレットの最上位
131         * @param       writer  DocTreeWriterオブジェクト
132         */
133        private void writeContents( final DocletEnvironment docEnv, final DocTreeWriter writer ) {
134//              docUtil = docEnv.getDocTrees();
135//              eleUtil = docEnv.getElementUtils();
136
137//              // get the DocTrees utility class to access document comments
138                final DocTrees docUtil = docEnv.getDocTrees();
139                final Elements eleUtil  = docEnv.getElementUtils();
140
141                // クラス単位にループする。
142                for( final TypeElement typEle : ElementFilter.typesIn(docEnv.getIncludedElements())) {
143                        if( !typEle.getModifiers().contains( Modifier.PUBLIC ) ) { continue; }
144
145                        final String fullName   = String.valueOf( typEle.getQualifiedName() ) ;
146//                      final String fullName = String.valueOf(typEle);
147        //              System.out.println(typEle.getKind() + ":" + fullName);
148                        writer.setClassName( fullName );
149
150                        final AttKeySet attSet = getAttGroupName( typEle,eleUtil ) ;
151                        if( attSet == null ) { continue; }                                                                      // map に登録されていない。
152
153                        final int ed = fullName.lastIndexOf( '.' );
154                        String attKey = null;
155                        if( ed > 0 ) {
156                                final String name = fullName.substring( ed+1 );
157                                attKey = attSet.getAttKey( name );
158                        }
159                        if( attKey == null ) { continue; }                                                                      // 対象クラス名が、一致しない。
160
161                        final DocCommentTree docTree = docUtil.getDocCommentTree(typEle);       // ドキュメンテーション・コメントが見つからない場合、null が返る。
162
163//                      final List<? extends DocTree> desc      = docTree == null ? EMPTY_LIST : docTree.getFirstSentence();
164//                      final List<? extends DocTree> cmnt      = docTree == null ? EMPTY_LIST : docTree.getFullBody();
165                        final String[] cmnts = getTitleCmnt( docTree );                                         // 8.0.2.1 (2021/12/10)
166
167                        final Map<String,List<String>> blkTagMap = blockTagsMap(docTree);
168                        final String smplTags   = getBlockTag( OG_FOR_SMPL, blkTagMap, "" );
169
170//                      String smplTags = "";
171//                      if( docTree != null ) {
172//                              for( final DocTree dt : docTree.getBlockTags() ) {
173//                                      final String tag = String.valueOf(dt);
174//                                      if( tag.contains( OG_FOR_SMPL ) ) {
175//                                              smplTags = tag.substring( OG_FOR_SMPL.length() + 1 ).trim();
176//                                      }
177//                              }
178//                      }
179
180                        // 5.7.1.1 (2013/12/13) タグのインデントを止める。
181                        writer.printTag( "<classDoc>" );
182                        writer.printTag( "  <attClass>"         ,fullName                               ,"</attClass>"          );
183                        writer.printTag( "  <seq>"                      ,attSet.getSeq()                ,"</seq>"                       );
184                        writer.printTag( "  <attKey>"           ,attKey                                 ,"</attKey>"            );
185                        writer.printTag( "  <valueName>"        ,attSet.getValueName()  ,"</valueName>"         );
186//                      writer.printTag( "  <description>"      ,desc                                   ,"</description>"       );
187                        writer.printTag( "  <description>"      ,cmnts[0]                               ,"</description>"       );      // 8.0.2.1 (2021/12/10)
188//                      writer.printTag( "  <contents>"         ,cmnt                                   ,"</contents>"          );
189                        writer.printTag( "  <contents>"         ,cmnts[1]                               ,"</contents>"          );      // 8.0.2.1 (2021/12/10)
190                        writer.printTag( "  <formSample>"       ,smplTags                               ,"</formSample>"        );
191                        writer.printTag( "</classDoc>" );
192                }
193        }
194
195        /**
196         * 指定の ClassDoc が、処理する属性クラスのMapに含まれている場合、
197         * その AttKeySet クラスのオブジェクトを返します。
198         * 存在しない場合、null を返します。
199         *
200         * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
201         *
202         * @param       typEle  TypeElementオブジェクト
203         * @param       eleUtil Elementsユーティリティ
204         * @return      typEleに対応する AttKeySetオブジェクト
205         */
206        private static AttKeySet getAttGroupName( final TypeElement typEle,final Elements eleUtil ) {
207                if( typEle == null ) { return null; }
208
209                final String fullName = String.valueOf(typEle);
210                AttKeySet attKey = ATT_KEY_MAP.get( fullName );
211                if( attKey == null ) {
212                        for( final TypeElement tp : eleUtil.getAllTypeElements(fullName) ) {
213                                if( typEle.equals( tp ) ) { continue; }
214                                attKey = getAttGroupName( tp,eleUtil );
215                                if( attKey != null ) { return attKey; }
216                        }
217
218                        final TypeMirror stype = typEle.getSuperclass();                                        // 親クラスタイプ
219                        if( stype != null ) {
220                                final String suCls = String.valueOf( stype );
221                                attKey = ATT_KEY_MAP.get( suCls );                                                              // 親クラス
222                                if( attKey == null ) {
223                                        for( final TypeElement tp : eleUtil.getAllTypeElements(suCls) ) {
224                                                if( typEle.equals( tp ) ) { continue; }
225                                                attKey = getAttGroupName( tp,eleUtil );
226                                                if( attKey != null ) { return attKey; }
227                                        }
228                                }
229                        }
230
231                        if( attKey == null ) {
232                                for( final TypeMirror itype : typEle.getInterfaces() ) {                // 直近インターフェース
233                                        final String intFce = String.valueOf( itype );
234                                        attKey = ATT_KEY_MAP.get( intFce );
235                                        if( attKey != null ) { return attKey; }
236                                        else {
237                                                for( final TypeElement tp : eleUtil.getAllTypeElements(intFce) ) {
238                                                        if( typEle.equals( tp ) ) { continue; }
239                                                        attKey = getAttGroupName( tp,eleUtil );
240                                                        if( attKey != null ) { return attKey; }
241                                                }
242                                        }
243                                }
244                        }
245                }
246
247                return attKey;
248        }
249
250        /**
251         * 属性情報を管理する、AttKeySet クラスです。
252         *
253         * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
254         *
255         * @version     4.0
256         * @author      Kazuhiko Hasegawa
257         * @since       JDK5.0,
258         */
259        private static final class AttKeySet {
260                private final String searchKey ;
261                private final int    len ;
262                private final String seq ;
263                private final String valueName ;
264
265                /**
266                 * コンストラクター
267                 *
268                 * @param       searchKey       検索キー
269                 * @param       seq                     シーケンス番号
270                 * @param       valueName       属性名
271                 */
272                /* default */ AttKeySet( final String searchKey,final int seq,final String valueName ) {
273                        this.searchKey  = searchKey ;
274                        this.seq                = String.valueOf( seq );
275                        this.valueName  = valueName ;
276
277                        len = searchKey.length();
278                }
279
280                /**
281                 * シーケンス番号を返します。
282                 *
283                 * @return      シーケンス番号
284                 *
285                 */
286                /* default */ String getSeq() {
287                        return seq;
288                }
289
290                /**
291                 * 属性名を返します。
292                 *
293                 * @return      属性名
294                 */
295                /* default */ String getValueName() {
296                        return valueName;
297                }
298
299                /**
300                 * クラス名の先頭一致の場合の、**** 部分を返します。
301                 * インターフェースも扱えるように修正しましたので、先頭が _ の場合は、
302                 * _ を削除して返します。
303                 *
304                 * @param       name    クラスの名称 (例:DBCellEditor_**** , ViewForm_****)
305                 * @return      クラス名の**** 部分
306                 */
307                /* default */ String getAttKey( final String name ) {
308                        // 6.1.0.0 (2014/12/26) refactoring
309                        String rtn = null;                                                                                              // 一致しなかった。
310
311                        if( name.equals( searchKey ) ) {                                                                // 完全一致:インターフェース
312                                rtn =  "(Interface)" + name ;
313                        }
314                        else if( name.indexOf( searchKey ) == 0 ) {                                             // 先頭一致した。
315                                rtn = name.substring( len );
316                                if( rtn.charAt(0) == '_' ) { rtn = rtn.substring( 1 ); }        // 先頭が _ の場合は、_ を削除
317                        }
318
319                        return rtn ;
320                }
321        }
322
323        /**
324         * サポートされているすべてのオプションを返します。
325         *
326         * @return      サポートされているすべてのオプションを含むセット、存在しない場合は空のセット
327         */
328        @Override
329        public Set<? extends Option> getSupportedOptions() {
330                final Option[] options = {
331                        new AbstractOption( "-outfile", "-version" ) {
332
333                                /**
334                                 * 必要に応じてオプションと引数を処理します。
335                                 *
336                                 * @param       opt                     オプション名
337                                 * @param       arguments       引数をカプセル化したリスト
338                                 * @return      操作が成功した場合はtrue、そうでない場合はfalse
339                                 */
340                                @Override
341                                public boolean process(final String opt, final List<String> arguments) {
342                                        if( "-outfile".equalsIgnoreCase(opt) ) {
343                                                outfile = arguments.get(0);
344                                        }
345                                        else if( "-version".equalsIgnoreCase(opt) ) {
346                                                version = arguments.get(0);
347                                        }
348                                        return true;
349                                }
350                        }
351                };
352                return new HashSet<>(Arrays.asList(options));
353        }
354}