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 */
016// 7.4.4.0 (2021/06/30) openGionV8事前準備(taglet2→taglet)
017//package org.opengion.fukurou.taglet2;
018package org.opengion.fukurou.taglet;
019
020import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;      // 6.1.0.0 (2014/12/26) refactoring
021
022import jdk.javadoc.doclet.DocletEnvironment      ;
023// import jdk.javadoc.doclet.Doclet  ;
024// import jdk.javadoc.doclet.Reporter ;
025import javax.lang.model.element.Element ;
026import javax.lang.model.element.Modifier ;
027import javax.lang.model.element.TypeElement;
028import javax.lang.model.element.ElementKind     ;
029import javax.lang.model.element.VariableElement;
030import javax.lang.model.element.ExecutableElement;
031// import javax.lang.model.SourceVersion ;
032import javax.lang.model.type.TypeMirror;
033import javax.lang.model.type.TypeKind;
034import javax.lang.model.util.ElementFilter ;
035import javax.lang.model.util.Elements ;
036import javax.lang.model.util.Types ;
037import javax.tools.Diagnostic.Kind ;
038import com.sun.source.doctree.DocCommentTree ;
039import com.sun.source.util.DocTrees  ;
040// import com.sun.source.util.DocSourcePositions ;
041import com.sun.source.doctree.DocTree  ;
042
043// import java.util.Locale ;
044import java.util.Set;
045import java.util.List;
046import java.util.HashSet;
047import java.util.Arrays;
048// import java.util.stream.Stream;                                                                              // 6.4.3.4 (2016/03/11)
049// import java.util.stream.Collectors;                                                                  // 6.4.3.4 (2016/03/11)
050// import java.util.regex.Pattern;                                                                              // 6.3.9.1 (2015/11/27) final化に伴う整理
051
052// import java.io.IOException;
053// import java.io.File;
054// import java.io.PrintWriter;
055import java.util.stream.Stream;                                                                         // 6.4.3.4 (2016/03/11)
056import java.util.stream.Collectors;                                                                     // 6.4.3.4 (2016/03/11)
057import java.util.Map;
058
059// import org.opengion.fukurou.util.FileUtil;
060// import org.opengion.fukurou.util.StringUtil;
061
062/**
063 * ソースコメントから、パラメータ情報を取り出す Doclet クラスです。
064 * og.paramLevel タグと og.cryptography タグを切り出します。
065 * これらは、システムパラメータとしてGE12テーブルに設定される値をクラスより抽出する
066 * のに使用します。
067 *
068 * @version  7.3
069 * @author      Kazuhiko Hasegawa
070 * @since        JDK11.0,
071 */
072public class DocTreeSpecific extends AbstractDocTree {
073        private static final String  SELECT_PACKAGE     = "org.opengion" ;
074        private static final boolean USE_PRIVATE        = false ;
075
076        private static final String     OG_FOR_SMPL             = "og.formSample";
077        private static final String     OG_REV                  = "og.rev";
078        private static final String     OG_GROUP                = "og.group";
079        private static final String     DOC_VERSION             = "version";
080        private static final String     DOC_AUTHOR              = "author";
081        private static final String     DOC_SINCE               = "since";
082
083//      private static final String OG_TAG_NAME         = "og.tag";                             // 6.1.2.0 (2015/01/24) チェック用
084//      private static final String     DOC_PARAM               = "param";                              // 5.1.9.0 (2010/08/01) チェック用
085//      private static final String     DOC_RETURN              = "return";                             // 5.1.9.0 (2010/08/01) チェック用
086
087        private static final String     CONSTRUCTOR             = "コンストラクタ" ;
088        private static final String     METHOD                  = "メソッド" ;
089
090        private String  version         ;
091        private String  outfile         ;
092        private int             debugLevel      ;               // 0:なし 1:最小チェック 2:日本語化 3:体裁 4:Verチェック 5:taglibラベル
093
094        private final Set<String> mtdClsSet = new HashSet<>();
095
096        // 5.1.9.0 (2010/08/01) ソースチェック用(半角文字+空白文字のみ)
097//      private static final Pattern PTN = Pattern.compile("[\\w\\s]+");                // 6.3.9.1 (2015/11/27)
098
099//      private DocTrees docUtil;
100//      private Elements eleUtil ;
101        private Types    typUtil ;
102
103        /**
104         * デフォルトコンストラクター
105         *
106         * @og.rev 7.3.0.0 (2021/01/06) PMD refactoring. Each class should declare at least one constructor.
107         */
108        public DocTreeSpecific() { super(); }           // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。
109
110        /**
111         * Doclet のエントリポイントメソッドです(昔の startメソッド)。
112         *
113         * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
114         *
115         * @param docEnv ドックレットを1回呼び出す操作環境
116         *
117         * @return 正常実行時 true
118         */
119        @Override
120        public boolean run( final DocletEnvironment docEnv ) {
121                try( DocTreeWriter writer = new DocTreeWriter( outfile,ENCODE ) ) {
122                        writer.printTag( "<?xml version=\"1.0\" encoding=\"", ENCODE , "\" ?>" );
123                        writer.printTag( "<javadoc>" );
124                        writer.printTag( "  <version>",version,"</version>" );
125                        writer.printTag( "  <description></description>" );
126                        writeContents( docEnv,writer );
127                        writer.printTag( "</javadoc>" );
128                }
129                catch( final Throwable th ) {
130                        reporter.print(Kind.ERROR, th.getMessage());
131                }
132
133                return true;
134        }
135
136        /**
137         * DocletEnvironmentよりコンテンツを作成します。
138         *
139         * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
140         *
141         * @param docEnv        ドックレットの最上位
142         * @param writer        DocTreeWriterオブジェクト
143         */
144        private void writeContents( final DocletEnvironment docEnv, final DocTreeWriter writer ) {
145//              docUtil = docEnv.getDocTrees();
146                final DocTrees docUtil = docEnv.getDocTrees();
147                final Elements eleUtil = docEnv.getElementUtils();
148                typUtil = docEnv.getTypeUtils();
149
150                // クラス単位にループする。
151                for( final TypeElement typEle : ElementFilter.typesIn(docEnv.getIncludedElements())) {
152                        final String fullName   = String.valueOf( typEle.getQualifiedName() ) ;
153                        writer.setClassName( fullName );
154
155                        final String className = String.valueOf( typEle.getSimpleName() );
156
157                        final StringBuilder modiBuf = new StringBuilder();
158                        typEle.getModifiers().forEach( modi -> modiBuf.append( modi ).append( ' ' ) );
159                        final ElementKind eleKind = typEle.getKind();
160                        if( ElementKind.CLASS.equals( eleKind ) ) { modiBuf.append( "class" ); }
161                        final String modifiers = modiBuf.toString().trim();
162
163                        final TypeMirror superType      = typEle.getSuperclass();
164                        final String superClass = TypeKind.NONE.equals( superType.getKind() ) ? "" : superType.toString();
165
166                        final String intFase = Stream.of( typEle.getInterfaces() )
167                                                                                .map( typ -> String.valueOf(typ) )
168                                                                                .collect( Collectors.joining( "," ) );
169
170                        final DocCommentTree docTree = docUtil.getDocCommentTree(typEle);               // ドキュメンテーション・コメントが見つからない場合、null が返る。
171
172                        final List<? extends DocTree> desc      = docTree == null ? EMPTY_LIST : docTree.getFirstSentence();
173                        final List<? extends DocTree> cmnt      = docTree == null ? EMPTY_LIST : docTree.getFullBody();
174
175                        final Map<String,List<String>> blkTagMap = blockTagsMap(docTree);
176                        final String smplTags   = getBlockTag( OG_FOR_SMPL, blkTagMap, ""   );
177                        final String revTags    = getBlockTag( OG_REV     , blkTagMap, "\n" );
178                        final String createVer  = getBlockTag( DOC_VERSION, blkTagMap, ""   );
179                        final String author             = getBlockTag( DOC_AUTHOR , blkTagMap, ""   );
180                        final String since              = getBlockTag( DOC_SINCE  , blkTagMap, ""   );
181                        final String grpTags    = getBlockTag( OG_GROUP   , blkTagMap, ","  );
182
183                        // 5.7.1.1 (2013/12/13) タグのインデントを止める。
184                        writer.printTag( "<classDoc>" );
185                        writer.printTag( "  <fullName>"         ,fullName               ,"</fullName>"          );
186                        writer.printTag( "  <modifiers>"        ,modifiers              ,"</modifiers>"         );
187                        writer.printTag( "  <className>"        ,className              ,"</className>"         );
188                        writer.printTag( "  <superClass>"       ,superClass             ,"</superClass>"        );
189                        writer.printTag( "  <interface>"        ,intFase                ,"</interface>"         );
190                        writer.printTag( "  <createVer>"        ,createVer              ,"</createVer>"         );
191                        writer.printTag( "  <author>"           ,author                 ,"</author>"            );
192                        writer.printTag( "  <since>"            ,since                  ,"</since>"                     );
193                        writer.printTag( "  <description>"      ,desc                   ,"</description>"       );
194                        writer.printTag( "  <contents>"         ,cmnt                   ,"</contents>"          );
195                        writer.printTag( "  <classGroup>"       ,grpTags                ,"</classGroup>"        );
196                        writer.printTag( "  <formSample>"       ,smplTags               ,"</formSample>"        );
197                        writer.printTag( "  <history>"          ,revTags                ,"</history>"           );
198
199
200                        // 5.1.9.0 (2010/08/01) ソースチェック用(コメントや概要が無い場合。スーパークラスは省く)
201                        if( debugLevel >= 2 && ( cmnt.isEmpty() || desc.isEmpty() ) && superClass.isEmpty() ) {
202        //                      final DocSourcePositions srcPos = docUtil.getSourcePositions();
203        //                      System.err.println( "警告2:コメントC=\t" + srcPos );
204                                System.err.println( "②警告2:コメントC=\t" + typEle );
205                        }
206
207                        int extendFlag = 0;             // 0:オリジナル 1:org.opengion関連Extend 2:Java関連Extend
208
209                        // 6.4.3.0 (2016/02/05) PMDチェックのDocletでのフォロー。
210        //              checkPMD( typEle );
211
212                        // 5.6.6.0 (2013/07/05) VERSION staticフィールドと、@og.rev コメントの比較チェック
213                        // while 以下で、fullName と classDoc を順番に上にさかのぼっているので、先にチェックします。
214        //              checkTag2( typEle );
215
216                        mtdClsSet.clear();
217                        TypeElement loopEle = typEle;
218                        String superName = fullName;
219                        while( loopEle != null ) {
220                                writer.setClassName( superName );
221
222                                // 対象クラスの スーパークラスを取得しておく(メソッド内で使うのと、上位に上がってく時に使う)
223                                final TypeMirror spType = loopEle.getSuperclass();
224
225                                // AbstractObjectPool<java.sql.Connection> の様な型を削除します。
226                                final StringBuilder buf = new StringBuilder().append(spType);
227                                int st = buf.indexOf("<");
228                                int ed = buf.indexOf(">",st);
229                                while( st > 0 ) {
230                                        buf.delete(st,ed+1);
231                                        st = buf.indexOf("<");
232                                        ed = buf.indexOf(">",st);
233                                }
234                                superName = buf.toString();
235                //              superName = String.valueOf( spType );
236
237                                final String extClass   = ( extendFlag == 0 ) ? "" : superName ;
238
239                                // コンストラクタのみフィルタリングして取得する
240                                for( final ExecutableElement exEle : ElementFilter.constructorsIn(loopEle.getEnclosedElements()) ) {
241                                        final DocCommentTree dct = docUtil.getDocCommentTree(exEle);            // ドキュメンテーション・コメントが見つからない場合、null が返る。
242                                        if( dct != null && isAction( exEle,extendFlag ) ) {
243                //                              checkTag( exEle,dct );                                                                                          // 5.5.4.1 (2012/07/06)  チェックを分離
244                                                menberTag( exEle,dct,CONSTRUCTOR,writer,extendFlag,extClass );
245                                        }
246                                }
247
248                                // メソッドのみフィルタリングして取得する
249                                for( final ExecutableElement exEle : ElementFilter.methodsIn(loopEle.getEnclosedElements())) {
250                                        final DocCommentTree dct = docUtil.getDocCommentTree(exEle);            // ドキュメンテーション・コメントが見つからない場合、null が返る。
251                                        if( dct != null && isAction( exEle,extendFlag ) ) {
252                //                              checkTag( exEle,dct );
253                                                menberTag( exEle,dct,METHOD,writer,extendFlag,extClass );
254                                        }
255                                }
256
257                                // 対象クラス(オリジナル)から、上に上がっていく。
258                                if( superName.startsWith( SELECT_PACKAGE ) ) {
259                                        extendFlag = 1;
260                                }
261                                else {
262                                        break;
263                                }
264
265                                loopEle = eleUtil.getTypeElement(superName);
266                        }
267                        writer.printTag( "</classDoc>" );
268                }
269        }
270
271        /**
272         * メンバークラスのXML化を行うかどうか[true/false]を判定します。
273         *
274         * 以下の条件に合致する場合は、処理を行いません。(false を返します。)
275         *
276         * 1.同一クラスを処理中にEXTENDで継承元をさかのぼる場合、すでに同じシグネチャのメソッドが
277         *     存在している。
278         * 2.USE_PRIVATE が true の時の private メソッド
279         * 3.extendFlag が 0以上(1,2)の時の private メソッド
280         * 4.メソッド名におかしな記号(&lt;など)が含まれている場合
281         *
282         * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
283         *
284         * @param       menber ExecutableMemberDocオブジェクト
285         * @param       extendFlag      継承状態 [0:オリジナル/1:org.opengion関連Extend/2:Java関連Extend]
286         *
287         * @return      XML化を行うかどうか[true/false]
288         */
289        private boolean isAction( final ExecutableElement menber,final int extendFlag ) {
290//              final String menberName = String.valueOf( menber.getSimpleName() ) ;
291                final boolean isPrivate   = menber.getModifiers().contains( Modifier.PRIVATE );
292                final boolean isTypeParam = ! menber.getTypeParameters().isEmpty();
293                final boolean rtn = ! mtdClsSet.add( String.valueOf( menber ) )         // 5.5.4.1 (2012/07/06) メソッドの重複処理判定は、クラス名も含めて行う
294                                                ||      USE_PRIVATE    && isPrivate
295                                                ||      extendFlag > 0 && isPrivate
296                                                ||      isTypeParam;                                                                    // PMD Useless parentheses.
297
298                return ! rtn ;
299        }
300
301//      /**
302//       * param,return 等の整合性をチェックします。
303//       *
304//       * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
305//       *
306//       * @param exEle ExecutableElementオブジェクト
307//       * @param menber DocCommentTreeオブジェクト
308//       */
309//      private void checkTag( final ExecutableElement exEle , final DocCommentTree menber ) {
310//              // 未実装
311//      }
312
313//      /**
314//       * PMDで、チェックしている処理のうち、Docletでフォローできる分をチェックします。
315//       *
316//       * ※ このチェックは、警告レベル5 のみ集約していますので、呼出元で、制限します。
317//       *
318//       * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
319//       *
320//       * @param typEle TypeElementオブジェクト
321//       */
322//      private void checkTag2( final TypeElement typEle ) {
323//              String cnstVar = null ;         // 初期値
324//              String seriUID = null ;
325//              final String src = "\tsrc/" + String.valueOf(typEle).replace('.','/') + ".java:100" ;                   // 行が判らないので、100行目 決め打ち
326//
327//              // フィールドのみフィルタリングして取得する
328//              for( final VariableElement varEle : ElementFilter.fieldsIn(typEle.getEnclosedElements())) {             // フィールドだけに絞る
329//                      final String key = String.valueOf(varEle);
330//                      if( "VERSION".equals( key ) ) {
331//                              cnstVar = String.valueOf( varEle.getConstantValue() );
332//                      }
333//                      else if( "serialVersionUID".equals( key ) ) {
334//                              seriUID = String.valueOf( varEle.getConstantValue() ) + "L";                    // 旧JavaDocと違い、"L" まで取ってこれないみたい
335//                      }
336//              }
337//
338//              if( cnstVar == null ) { return; }                               // VERSION が未定義のクラスは処理しない
339//
340//              String maxRev = cnstVar ;                                               // 5.7.1.1 (2013/12/13) 初期値
341//              boolean isChange = false;                                               // max が入れ替わったら、true
342//
343//              // メソッドのみフィルタリングして取得する
344//              for( final ExecutableElement exEle : ElementFilter.methodsIn(typEle.getEnclosedElements())) {
345//                      final DocCommentTree dct = docUtil.getDocCommentTree(exEle);            // ドキュメンテーション・コメントが見つからない場合、null が返る。
346//                      final Map<String,List<String>> blkTagMap = blockTagsMap(dct);
347//                      final List<String> revTags = blkTagMap.get("og.rev");
348//
349//                      if( revTags != null ) {
350//                              for( final String tag :revTags ) {                                                              // 複数存在しているはず
351//                                      final int idx = tag.indexOf( ' ' );                                                     // 最初のスペース
352//                                      if( idx > 0 ) {
353//                                              final String rev = tag.substring( 0,idx ).trim();
354//                                              if( maxRev.compareTo( rev ) < 0 ) {                                             // revTags の og.rev が大きい場合
355//                                                      maxRev = rev ;
356//                                                      isChange = true;
357//                                              }
358//                                      }
359//                              }
360//                      }
361//              }
362//
363//              // VERSION 文字列 の定義があり、かつ、max の入れ替えが発生した場合のみ、警告4:VERSIONが古い
364//              if( isChange ) {                        // 5.7.1.1 (2013/12/13) 入れ替えが発生した場合
365//                      System.err.println( "警告4:VERSIONが古い=\t" + cnstVar + " ⇒ " + maxRev + src );
366//              }
367//
368//              // serialVersionUID の定義がある。
369//              if( seriUID != null ) {
370//                      final StringBuilder buf = new StringBuilder();
371//                      // maxRev は、最大の Revか、初期のVERSION文字列 例:5.6.6.0 (2013/07/05)
372//                      for( int i=0; i<maxRev.length(); i++ ) {        //
373//                              final char ch = maxRev.charAt( i );
374//                              if( ch >= '0' && ch <= '9' ) { buf.append( ch ); }      // 数字だけ取り出す。 例:566020130705
375//                      }
376//                      buf.append( 'L' );      // 強制的に、L を追加する。
377//                      final String maxSeriUID = buf.toString() ;
378//
379//                      // 5.7.1.1 (2013/12/13) 値の取出し。Long型を表す "L" も含まれている。
380//                      if( !maxSeriUID.equals( seriUID ) ) {   // 一致しない
381//                              System.err.println( "警告4:serialVersionUIDが古い=\t" + seriUID + " ⇒ " + maxSeriUID + src );
382//                      }
383//              }
384//      }
385
386//      /**
387//       * PMDで、チェックしている処理のうち、Docletでフォローできる分をチェックします。
388//       *
389//       * ※ このチェックは、警告レベル5 のみ集約していますので、呼出元で、制限します。
390//       *
391//       * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
392//       *
393//       * @param typEle TypeElementオブジェクト
394//       */
395//      private void checkPMD( final TypeElement typEle ) {
396//              // 未実装
397//      }
398
399        /**
400         * メンバークラス(コンストラクタ、メソッド)をXML化します。
401         *
402         * @og.rev 7.3.0.0 (2021/01/06) 新しいJavaDoc対応
403         *
404         * @param       menber          ExecutableElementオブジェクト
405         * @param       docTree         DocCommentTreeオブジェクト
406         * @param       menberType      メンバータイプ(コンストラクタ、メソッド)
407         * @param       writer          Tagを書き出すWriterオブジェクト
408         * @param       extendFlag      継承状態 [0:オリジナル/1::org.opengion関連Extend/2:Java関連Extend]
409         * @param       extClass        継承クラス(オリジナルの場合は、空文字列)
410         */
411        private void menberTag( final ExecutableElement menber,
412                                                        final DocCommentTree docTree ,
413                                                        final String menberType,
414                                                        final DocTreeWriter writer,
415                                                        final int extendFlag ,
416                                                        final String extClass ) {
417
418                final String modifiers ;
419
420                final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE );
421                menber.getModifiers().forEach( modi -> buf.append( modi ).append( ' ' ) );
422                if( ElementKind.METHOD.equals( menber.getKind() ) ) {           // メソッドの処理。コンストラクタの場合は、返り値がないので処理しない。
423                        buf.append( menber.getReturnType() );
424                }
425                modifiers = buf.toString();
426
427                final String menberName = String.valueOf( menber.getSimpleName() );                     // コンストラクタの場合は、<init> が返る。
428
429                final StringBuilder sigBuf = new StringBuilder().append( menberName ).append( '(' ) ;
430                boolean flag = false;
431                for( final VariableElement valEle : menber.getParameters() ) {
432                        flag = true;
433                        final Element ele = typUtil.asElement( valEle.asType() );               // 型が対応する要素を持たない場合はnullを返します。
434                        final String key = ele == null                                                                  // 配列や、プリミティブ型の場合は、null になっている。
435                                                                        ? String.valueOf( valEle.asType() )
436                                                                        : String.valueOf( ele.getSimpleName() );
437
438                        sigBuf.append( key ).append( ' ' )
439                                        .append( valEle.getSimpleName() ).append( ',' );
440                }
441
442                if( flag ) { sigBuf.deleteCharAt( sigBuf.length()-1 ); }
443                sigBuf.append( ')' );
444                final String signature = sigBuf.toString();
445//              final String signature = String.valueOf( menber );
446
447                final List<? extends DocTree> desc      = docTree == null ? EMPTY_LIST : docTree.getFirstSentence();
448                final List<? extends DocTree> cmnt      = docTree == null ? EMPTY_LIST : docTree.getFullBody();
449
450                final Map<String,List<String>> blkTagMap = blockTagsMap(docTree);
451                final String revTags = getBlockTag( OG_REV, blkTagMap, "\n" );
452
453                // tags は、OG_REV 以外のすべてで、かつ、キーワードも含む。
454                final StringBuilder tagBuf = new StringBuilder();
455                if( docTree != null ) {
456                        for( final DocTree dt : docTree.getBlockTags() ) {
457                                final String tag = String.valueOf(dt).trim();
458                                if( !tag.contains( OG_REV ) ) { tagBuf.append( tag ).append( '\n' ); }
459                        }
460                }
461                final String tags = tagBuf.toString().trim();
462
463//              final StringBuilder tagBuf = new StringBuilder();
464//              final StringBuilder revBuf = new StringBuilder();
465//              if( docTree != null ) {
466//                      for( final DocTree dt : docTree.getBlockTags() ) {
467//                              final String tag = String.valueOf(dt).trim();
468//                              if( tag.contains( OG_REV ) ) { revBuf.append( cutTag( tag,OG_REV ) ).append( '\n' ); }
469//                              else { tagBuf.append( tag ).append( '\n' ); }
470//                      }
471//              }
472//              final String revTags = revBuf.toString().trim();
473//              final String tags    = tagBuf.toString().trim();
474
475                final String    extend  = String.valueOf( extendFlag );
476
477        //      final DocSourcePositions srcPos = docUtil.getSourcePositions();
478        //      final String    position = String.valueOf( srcPos.getStartPosition( null,docTree,null ) );
479                final String    position = "";
480
481                writer.printTag( "  <menber>" );
482                writer.printTag( "    <type>"           ,menberType     ,"</type>"                      );
483                writer.printTag( "    <name>"           ,menberName     ,"</name>"                      );
484                writer.printTag( "    <modifiers>"      ,modifiers      ,"</modifiers>"         );
485                writer.printTag( "    <signature>"      ,signature      ,"</signature>"         );
486                writer.printTag( "    <position>"       ,position       ,"</position>"          );
487                writer.printTag( "    <extendClass>",extClass   ,"</extendClass>"       );
488                writer.printTag( "    <extendFlag>"     ,extend         ,"</extendFlag>"        );
489                writer.printTag( "    <description>",desc               ,"</description>"       );
490                writer.printTag( "    <contents>"       ,cmnt           ,"</contents>"          );
491                writer.printTag( "    <tagText>"        ,tags           ,"</tagText>"           );
492                writer.printTag( "    <history>"        ,revTags        ,"</history>"           );
493                writer.printTag( "  </menber>");
494        }
495
496        /**
497         * サポートされているすべてのオプションを返します。
498         *
499         * @return サポートされているすべてのオプションを含むセット、存在しない場合は空のセット
500         */
501        @Override
502        public Set<? extends Option> getSupportedOptions() {
503                final Option[] options = {
504                        new AbstractOption( "-outfile", "-version", "-debugLevel" ) {
505
506                                /**
507                                 * 必要に応じてオプションと引数を処理します。
508                                 *
509                                 * @param  opt オプション名
510                                 * @param  arguments 引数をカプセル化したリスト
511                                 * @return 操作が成功した場合はtrue、そうでない場合はfalse
512                                 */
513                                @Override
514                                public boolean process(final String opt, final List<String> arguments) {
515                                        if( "-outfile".equalsIgnoreCase(opt) ) {
516                                                outfile = arguments.get(0);
517                                        }
518                                        else if( "-version".equalsIgnoreCase(opt) ) {
519                                                version = arguments.get(0);
520                                        }
521                                        else if( "-debugLevel".equalsIgnoreCase(opt) ) {
522                                                debugLevel = Integer.parseInt( arguments.get(0) );
523                                        }
524                                        return true;
525                                }
526                        }
527                };
528                return new HashSet<>(Arrays.asList(options));
529        }
530}