/*
 * 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 java.lang.reflect.Field;

import com.sun.javadoc.Tag;
import com.sun.javadoc.Doc;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.ProgramElementDoc;

/**
 * Doclet を処理するプログラムで共通して使用される簡易メソッド群(ユーティリティクラス)です。
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public final class DocletUtil {
	/** リターンコード  System.getProperty("line.separator")  */
	public static final String CR = System.getProperty("line.separator");

	private static final String CLS = "org.opengion.hayabusa.common.BuildNumber";		// package.class
	private static final String FLD = "VERSION_NO";										// field
	private static final String versionNo = getStaticField( CLS , FLD );

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

	/**
	 * target 文字列に含まれる from 文字列を to 文字列に置き換えます。
	 *
	 * @param	target	元の文字列
	 * @param	from	置換元FROM
	 * @param	to		置換先TO
	 *
	 * @return	変換後文字列
	 */
	public static String replace( final String target,final String from,final String to ) {
		if( target == null || from == null || to == null ) { return target; }
		StringBuilder strBuf = new StringBuilder( 200 );

		int start = 0;
		int end   = target.indexOf( from,start );
		while( end >= 0  ) {
			strBuf.append( target.substring( start,end ) );
			strBuf.append( to );
			start = end + from.length();
			end   = target.indexOf( from,start );
		}
		strBuf.append( target.substring( start ) );

		return strBuf.toString();
	}

	/**
	 * セッターメソッドの setXXXX の set を削除し、次の文字を小文字化します。
	 * つまり、セッターメソッドから属性値を推測します。
	 * (超特殊処理)セッターメソッドのset以下２文字目が大文字の場合は、
	 * １文字目も大文字と考えて小文字化を行いません。
	 * 例えば、setSYS や setUSER など、RequestValueTag.javaに使用するケースです。
	 *
	 * @param target 処理対象となる文字列
	 *
	 * @return オプション文字列
	 */
	public static String removeSetter( final String target ) {
		if( target != null && target.startsWith( "set" ) ) {
			char[] chs = target.toCharArray();
			if( chs.length > 4 && !( chs[4] >= 'A' && chs[4] <= 'Z' ) ) {
				chs[3] = Character.toLowerCase( chs[3] ) ;
			}
			return new String( chs,3,chs.length-3 );
		}
		return target;
	}

	/**
	 * オプション配列文字列より、指定のキーに対応するオプション値を返します。
	 *
	 * @param	key		キー
	 * @param	options	オプション配列文字列
	 *
	 * @return	オプション文字列
	 */
	public static String getOption( final String key , final String[][] options ) {
		String rtn = "";
		if( key == null || options == null ) { return rtn; }

		for( int i=0; i<options.length; i++ ) {
			if( key.equalsIgnoreCase( options[i][0] ) ) {
				rtn = options[i][1];
				break ;
			}
		}
		return rtn ;
	}

	/**
	 * {&#064;og.value package.class#field} 形式のvalueタグを文字列に置き換えます。
	 *
	 * 処理的には、リフレクションで、値を取得します。値は、staticフィールドのみ取得可能です。
	 *
	 * @og.rev 5.5.4.1 (2012/07/06) 新規追加
	 * @og.rev 5.5.5.6 (2012/08/31) クラス名の取得で、ProgramElementDoc で処理するように変更
	 *
	 * @param tag Tagオブジェクト
	 *
	 * @return valueタグの解析結果の文字列
	 */
	public static String valueTag( final Tag tag ) {
		String txt = tag.text();
		if( txt != null ) {
			String cls = null;		// package.class
			String fld = null;		// field

			// package.class#field 形式の解析。
			int adrs = txt.indexOf('#') ;
			if( adrs > 0 ) {
				cls = txt.substring( 0,adrs );		// package.class
				fld = txt.substring( adrs+1 );		// field
			}
			else if( adrs == 0 ) {
				fld = txt.substring( 1 );			// #field
			}
			else {
				String errMsg = "警告:{@value package.class#field} 形式の フィールド名 #field がありません。" + CR
								+ tag.position() + " : " + txt + CR ;
				System.err.println( errMsg );
				// # を付け忘れたと考え、自分自身のクラスを利用
				fld = txt;							// field
			}

			// package.class をきちんと作成する。
			Doc doc = tag.holder();

			// 5.5.5.6 (2012/08/31) ProgramElementDoc で処理するように変更
			if( doc instanceof ProgramElementDoc ) {
				ProgramElementDoc pdoc = (ProgramElementDoc)doc;
				ClassDoc cdoc = pdoc.containingClass();
				if( cdoc != null ) {
					if( cls == null ) {					// package.class が登録されていない場合。
						cls = cdoc.qualifiedName() ;
					}
					else if( cls.indexOf('.') < 0 ) {	// class のみが登録されている場合。findClass で、package 込の正式クラス名を検索する。
						ClassDoc fdoc = cdoc.findClass( cls );
						if( fdoc != null ) {
							cls = fdoc.qualifiedName() ;
						}
					}
				}
				else {
					if( cls == null ) {					// package.class が登録されていない場合。
						cls = pdoc.qualifiedName() ;
					}
				}
			}

			//  5.6.3.3 (2013/04/19) メソッド化で共有します。
			txt = getStaticField( cls,fld );
		}
		return txt ;
	}

	/**
	 * {&#064;og.doc03Link queryType Query_**** クラス} 形式のdoc03Linkタグをリンク文字列に置き換えます。
	 *
	 * <a href="/gf/jsp/DOC03/index.jsp?command=NEW&GAMENID=DOC03&VERNO=X.X.X.X&VALUENAME=queryType" target="CONTENTS">Query_**** クラス</a>
	 * のようなリンクを作成します。
	 * 第一引数は、VALUENAME の引数です。
	 * それ以降のテキストは、リンク文字列のドキュメントになります。
	 * DOC03 画面へのリンクを作成するに当たり、バージョンが必要です。org.opengion.hayabusa.common.BuildNumber#VERSION_NO から取得しますが、
	 * パッケージの優先順の関係で、リフレクションを使用します。
	 *
	 * @og.rev 5.6.3.3 (2013/04/19) 新規作成
	 *
	 * @param tag Tagオブジェクト
	 *
	 * @return valueタグの解析結果の文字列
	 */
	public static String doc03LinkTag( final Tag tag ) {
		String txt = tag.text();
		if( txt != null ) {
			String valnm = null;			// VALUENAME
			String body  = null;			// ドキュメント

			int adrs = txt.indexOf(' ') ;	// 最初のスペースで分離します。
			if( adrs > 0 ) {
				valnm = txt.substring( 0,adrs );		// VALUENAME
				body  = txt.substring( adrs+1 );		// ドキュメント
			}
			else {
				valnm = txt;							// VALUENAME
				body  = txt;							// ドキュメント
			}

			txt = "&lt;a href=\"/gf/jsp/DOC03/index.jsp?command=NEW&amp;GAMENID=DOC03"
					+ "&amp;VERNO="     + versionNo
					+ "&amp;VALUENAME=" + valnm
					+ "\" target=\"CONTENTS\"&gt;"
					+ body
					+ "&lt;/a&gt;" ;
		}
		return txt ;
	}

	/**
	 * パッケージ.クラス名 と、フィールド名 から、staticフィールドの値を取得します。
	 *
	 * Field fldObj = Class.forName( cls ).getDeclaredField( fld ); で、Fieldオブジェクトを呼出し、
	 * String.valueOf( fldObj.get( null ) ); で、値を取得しています。
	 * static フィールドは、引数 null で値を取得できます。
	 *
	 * 例；
     *      String cls = "org.opengion.hayabusa.common.BuildNumber";        // package.class
     *      String fld = "VERSION_NO";                                      // field
	 *
	 * @og.rev 5.6.3.3 (2013/04/19) 新規作成
	 *
	 * @param cls パッケージ.クラス名
	 * @param fld フィールド名
	 * @return 取得値
	 */
	public static String getStaticField( final String cls , final String fld ) {

		String txt = null;
		try {
			Field fldObj = Class.forName( cls ).getDeclaredField( fld );
			// privateフィールドへのアクセス。(セキュリティーマネージャーによってアクセス制限がかけられていない場合)
			if( !fldObj.isAccessible() ) { fldObj.setAccessible( true ); }
			txt = String.valueOf( fldObj.get( null ) );		// static フィールドは、引数 null で値を取得
		}
		catch( Exception ex ) {
			String errMsg = "package.class = " + cls + " field = " + fld + " の取得に失敗しました。"
							+ ex.getMessage() + CR;
			System.err.println( errMsg );
		}

		return txt ;
	}
}
