/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.opengion.fukurou.taglet;

import org.opengion.fukurou.system.LogWriter;
import org.opengion.fukurou.util.StringUtil;

import com.sun.javadoc.RootDoc;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.FieldDoc;
import com.sun.javadoc.Tag;
import java.io.IOException;

/**
 * ソースコメントから、パラメータ情報を取り出す Doclet クラスです。
 * og.paramLevel タグと og.cryptography タグを切り出します。
 * これらは、ｼｽﾃﾑパラメータとしてGE12ﾃｰﾌﾞﾙに設定される値をクラスより抽出する
 * のに使用します。
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public final class DocletParam {
	private static final String OG_PARAM_LVL    = "og.paramLevel";
	private static final String OG_CRYPTOGRAPHY = "og.cryptography";
	private static final String ENCODE = "UTF-8";
	private static final int    CNST   = 1000;

	/**
	 * すべてが staticメソッドなので、コンストラクタを呼び出さなくしておきます。
	 *
	 */
	private DocletParam() {}

	/**
	 * Doclet のエントリポイントメソッドです。
	 *
	 * @og.rev 5.5.2.0 (2012/05/01) systemIdのbuild.xmlの引数が、** の場合にエラーになるための対応
	 * @og.rev 5.7.1.1 (2013/12/13) タグのインデントを止める。
	 *
	 * @param root ドキュメントルートオブジェクト
	 *
	 * @return 正常実行時 true
	 */
	public static boolean start( final RootDoc root ) {
		String systemId = DocletUtil.getOption( "-systemId" , root.options() );
		final String file     = DocletUtil.getOption( "-outfile"  , root.options() );

		if( systemId == null || systemId.isEmpty() ) { systemId = "**"; }	// 5.5.2.0 (2012/05/01)

		DocletTagWriter writer = null;
		try {
			writer = new DocletTagWriter( file,ENCODE );

			// 5.7.1.1 (2013/12/13) タグのインデントを止める。
			writer.printTag( "<?xml version=\"1.0\" encoding=\"", ENCODE, "\" ?>" );
			writer.printTag( "<javadoc>" );
			writer.printTag( "  <systemId>",systemId,"</systemId>" );
			writeContents( root.classes(),writer );
			writer.printTag( "</javadoc>" );
		}
		catch( IOException ex ) {
			LogWriter.log( ex );
		}
		finally {
			if( writer != null ) { writer.close(); }
		}
		return true;
	}

	/**
	 * ClassDoc 配列よりコンテンツを作成します。
	 *
	 * @og.rev 5.5.4.1 (2012/07/06) コメントは文字列でなく、Tag配列として処理させる。
	 * @og.rev 5.5.4.1 (2012/07/06) DocletUtil.htmlFilter → StringUtil.htmlFilter に変更
	 * @og.rev 5.7.1.1 (2013/12/13) タグのインデントを止める。
	 *
	 * @param classes	ClassDoc配列
	 * @param writer	DocletTagWriterオブジェクト
	 */
	private static void writeContents( final ClassDoc[] classes,final DocletTagWriter writer ) {
		for( int i=0; i< classes.length; i++ ) {
			final ClassDoc classDoc      = classes[i] ;
			final FieldDoc[] fields = classDoc.fields();

			for( int j=0; j<fields.length; j++ ) {
				final FieldDoc field = fields[j];
				String param = field.constantValueExpression() ;
				if( param != null && param.length() >=2 &&
					param.charAt(0) == '"' && param.charAt( param.length()-1 ) == '"' ) {
						param = param.substring( 1,param.length()-1 );
				}
				param = StringUtil.htmlFilter( param );		// 5.5.4.1 (2012/07/06) DocletUtil → StringUtil に変更

				final String paramId	= field.name();
				final String seq	= String.valueOf(j*10 + CNST);
				final Tag[] title	= field.firstSentenceTags();
				final Tag[] cmnt	= field.inlineTags();								// 5.5.4.1 (2012/07/06)
				final Tag[] paramLvl	= field.tags(OG_PARAM_LVL);
				final Tag[] fgcrypt	= field.tags(OG_CRYPTOGRAPHY);

				// 5.7.1.1 (2013/12/13) タグのインデントを止める。
				writer.printTag( " <fieldDoc>" );
				writer.printTag( "   <paramId>"		,paramId	,"</paramId>"		);
				writer.printTag( "   <seq>"			,seq		,"</seq>"			);
				writer.printTag( "   <param>"		,param		,"</param>"			);
				writer.printTag( "   <title>"		,title		,"</title>"			);
				writer.printTag( "   <contents>"	,cmnt		,"</contents>"		);
				writer.printChar("   <paramLevel>"	,paramLvl	,"</paramLevel>"	);
				writer.printChar("   <fgcrypt>"		,fgcrypt	,"</fgcrypt>"		);
				writer.printTag( " </fieldDoc>" );
			}

		}
	}

	/**
	 * カスタムオプションを使用するドックレットの必須メソッド optionLength(String) です。
	 *
	 * ドックレットに認識させる各カスタムオプションに、 optionLength がその
	 * オプションを構成する要素 (トークン) の数を返さなければなりません。
	 * このカスタムオプションでは、 -tag オプションそのものと
	 * その値の 2 つの要素で構成されるので、作成するドックレットの
	 * optionLengthメソッドは、 -tag オプションに対して 2 を返さなくては
	 * なりません。また、認識できないオプションに対しては、0 を返します。
	 *
	 * @param option オプション文字列
	 *
	 * @return 要素 (トークン) の数
	 */
	public static int optionLength( final String option ) {
		if( "-outfile".equalsIgnoreCase(option) ) {
			return 2;
		}
		else if( "-systemId".equalsIgnoreCase(option) ) {
			return 2;
		}
		return 0;
	}
}
