/*
 * 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.xml;

import java.io.File;
import java.io.IOException;
// import java.io.Reader;
// import java.io.StringReader;
import java.io.BufferedReader;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

import org.opengion.fukurou.util.FileUtil;
import org.opengion.fukurou.util.StringUtil;
import org.opengion.fukurou.util.Closer;
import org.opengion.fukurou.util.LogWriter;

/**
 * このクラスは、jspファイルのXSLT変換に特化した、Readerオブジェクトを作成するクラスです。
 * jspファイル に記述される、jsp:directive.include を見つけて、そのファイル属性に
 * 記述されているファイルを、インクルードします。
 * Tomcat の特性上、インクルード時のファイルは、＆等のエスケープを処理しておく
 * 必要があります。
 * （つまり、オリジナルは、エスケープ処理なしで、インクルードされるファイルは、
 * 属性部分のみ、エスケープ処理が必要です。
 *
 * タグのBODY部に記述されている ＆ なども、パースの対象になりますが、
 * 擬似的に処理しています。
 * それは、『&nbsp;』『< 』『<=』について、処理しています。
 * この形以外の処理は、今は入れていません。
 *
 * JSP では、og:head タグで、<html> を出力したり、htmlend.jsp インクルードで
 * </body></html> を出力していますが、フレームや、フォワードなど、整合性が取れない
 * ケースがありますので、XML処理用として、<html> を出力していません。
 * 変換結果を、正式な HTML ファイルとして再利用される場合は、ご注意ください。
 *
 * @og.rev 4.0.0.2 (2007/12/10) 新規追加
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class JspIncludeReader {
	private static final String CR = System.getProperty("line.separator");

	// タグの属性の値のみを抜き出しています。特に、<>& を含む場合。
	private static final Pattern ptn = Pattern.compile( "=[ \t]*\"([^\"]*[<>&].[^\"]*)\"" );

	/**
	 *	JSP のインクルードを考慮した、Reader オブジェクトを返します。
	 *
	 * @param file File
	 * @param encode String
	 * @return Reader
	 */
//	public Reader getReader( final File file,final String encode ) {
//		return new StringReader( getString( file,encode,false ) );
//	}

	/**
	 * JSP のインクルードを考慮した、JSPファイルを、String で返します。
	 * このメソッドは、内部で再帰定義されています。つまり、jsp:directive.include
	 * 文字列が見つかった場合は、その代わりに、ファイル名を取出して、もう一度
	 * このメソッドを呼び出します。
	 * Tomcat の特性上、インクルード時のファイルは、＆等のエスケープを処理しておく
	 * 必要がありますが、オリジナルは、エスケープ処理しません。
	 * その切り替えを、第３引数の isEscape で制御しています。
	 *
	 * @param file File
	 * @param encode String
	 * @param isEscape boolean	エスケープするかどうか(true:する/false:しない)
	 * @return String
	 */
	public String getString( final File file,final String encode,final boolean isEscape ) {
		StringBuilder buf = new StringBuilder() ;
		BufferedReader reader = FileUtil.getBufferedReader( file,encode );

		// ファイルが、jsp 直下かどうかを判断します。
		String parentFile = file.getParent() ;
		boolean isUnder = parentFile.endsWith( "\\jsp" );

		try {
			String line ;
			while((line = reader.readLine()) != null) {
				// 特殊処理：og:head で html タグを出力している。
	//			if( line.indexOf( "<og:head" ) >= 0 ) {
	//				buf.append( "<html>" );
	//			}

				if( isEscape ) {
					line = line.replace( "&nbsp;","&amp;nbsp;" );	// ちょっと小細工
					line = line.replace( "< ","&lt; " );			// ちょっと小細工
					line = line.replace( "<=","&lt;=" );			// ちょっと小細工

					Matcher mtch = ptn.matcher( line );
					int adrs = 0;
					StringBuilder buf2 = new StringBuilder();
					while( mtch.find(adrs) ) {
						String grp = mtch.group(1);
	// System.out.println( grp );
						String htm = StringUtil.htmlFilter( grp );
						int in = mtch.start(1);
						buf2.append( line.substring( adrs,in ) ).append( htm );
						adrs = mtch.end(1);
					}
					buf2.append( line.substring( adrs ) );
					line = buf2.toString();
				}

				int st = line.indexOf( "<jsp:directive.include" );
				if( st < 0 ) { buf.append( line ); }	// include が無ければ、そのまま追加
				else {
					buf.append( line.substring( 0,st ) );
					int fin = line.indexOf( '\"',st );		// ファイルの最初
					int fout= line.indexOf( '\"',fin+1 );	// ファイルの最後
					String fname = line.substring( fin+1,fout );	// ファイル名

					// htmlend.jsp の インクルードは行わない。
					if( fname.endsWith( "htmlend.jsp" ) ) {
						if( buf.indexOf( "<body" ) >= 0 && buf.indexOf( "</body>" ) < 0 ) {
							buf.append( "</body>" );
						}

	//					if( buf.indexOf( "<html" ) >= 0 ) {
	//						buf.append( "</html>" );
	//					}
					}
					else {
						// ちょっと小細工
						// include するファイルは、/jsp/ からの絶対パス。
						// jsp 直下の場合は、./ 、それ以外は、../ と置き換えます。
						if( isUnder ) { fname = fname.replace( "/jsp/","./" ); }
						else 		  { fname = fname.replace( "/jsp/","../" ); }
						File newfile = new File( parentFile,fname );
						buf.append( getString( newfile,encode,true ) );
					}
					int tagout = line.indexOf( "/>",fout+1 );	// タグの最後

					buf.append( line.substring( tagout+2 ) );
				}

				// og:commonForward を見つけた場合は、最後に html タグを出力する。
	//			if( line.indexOf( "<og:commonForward" ) >= 0 ) {
	//				buf.append( "</html>" );
	//			}

				buf.append( CR );
			}
		}
		catch( IOException ex ) {
			LogWriter.log( ex );
		}
		finally {
			Closer.ioClose( reader );
		}

		return buf.toString();
	}

	/**
	 * テスト用の main メソッド
	 *
	 * @param args String[]
	 */
	public static void main( final String[] args ) {
		JspIncludeReader reader = new JspIncludeReader();
		String xml = reader.getString( new File( args[0] ),"UTF-8",false );

		if( args.length > 1 ) {
			java.io.PrintWriter writer = FileUtil.getPrintWriter( new File( args[1] ),"UTF-8" );
			writer.print( xml );
			Closer.ioClose( writer );
		}
		else {
			System.out.println( xml );
		}
	}
}
