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

import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.File;
import java.io.IOException;

/**
 * CommentLineParser.java は、ファイルを行単位に処理して、コメントを除去するクラスです。
 * １行分の文字列を読み取って、コメント部分を削除した文字列を返します。
 *
 * ブロックコメントの状態や、コメント除外の状態を管理しています。
 * オブジェクト作成後、line( String ) メソッドに、ファイルから読み取った１行分の文字列を渡せば、
 * コメントが除外された形で返されます。
 * 行として存在しない場合は、null を返します。
 *
 * @og.rev 5.7.4.0 (2014/03/07) 新規追加
 * @og.group ユーティリティ
 *
 * @version  6.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK7.0,
 */
public class CommentLineParser {
	private String LINE_CMNT   = "//" ;		// ラインコメント
	private String BLOCK_CMNT1 = "/*" ;		// ブロックコメントの開始
	private String BLOCK_CMNT2 = "*/" ;		// ブロックコメントの終了
	private char   ESC_CHAR    = '"'  ;		// コメント除外(プログラム等で使用)

	private boolean escIn   = false;		// コメント除外中かどうか
	private boolean blockIn = false;		// ブロックコメントが継続しているかどうか
	private boolean rtnOnly = false;		// 前の行が改行のみだったかどうか。

	/**
	 * コメントの種類を指定します。何も指定しない場合は、Javaコメントを初期値で設定します。
	 *
	 * Javaの場合は、順番に、&#47;&#47; , /&#042; , &#042;/ , &#034;(二重引用符) になります。
	 * JavaScriptなら、&#47;&#47; , &lt;!-- , --&gt; , &#034;
	 * ORACLEなら、-- , /&#042; , &#042;/ , &#034;(一重引用符) になります。
	 *
	 * ※ サブクラスで分けてもよかったのですが、とりあえず引数私にしました。
	 *
	 * @og.rev 5.7.4.0 (2014/03/07) 新規追加
	 *
	 * @param	lineCmnt	ラインコメント
	 * @param	blockCmnt1	ブロックコメントの開始
	 * @param	blockCmnt2	ブロックコメントの終了
	 * @param	escChar		コメント除外(プログラム等で使用)
	 */
	public void init( final String lineCmnt,final String blockCmnt1,final String blockCmnt2,final char escChar ) {
		LINE_CMNT   = lineCmnt ;		// ラインコメント
		BLOCK_CMNT1 = blockCmnt1 ;		// ブロックコメントの開始
		BLOCK_CMNT2 = blockCmnt2 ;		// ブロックコメントの終了
		ESC_CHAR    = escChar  ;		// コメント除外(プログラム等で使用)
	}

	/**
	 * １行分の文字列を読み取って、コメント部分を削除した文字列を返します。
	 * 行として存在しない場合は、null を返します。
	 *
	 * @og.rev 5.7.4.0 (2014/03/07) 新規追加
	 *
	 * @param	inLine １行の文字列
	 * @return	コメント削除後の１行の文字列
	 */
	public String line( final String inLine ) {
		if( inLine == null ) { return null; }

		int size = inLine.length();

		StringBuilder buf = new StringBuilder( size );

		for( int st=0; st<size; st++ ) {
			char ch = inLine.charAt(st);

			// ブロック外で、エスケープ文字の場合は、内外反転
			if( !blockIn && ESC_CHAR == ch ) { escIn = !escIn ; }

			if( !escIn ) {										// エスケープ外ら、処理を進める
				// ブロックコメント継続中
				if( blockIn ) {
					int ed = inLine.indexOf( BLOCK_CMNT2,st ) ;		// 終了を見つける
					if( ed >= 0 ) {									// 終了があれば、そこまで進める。
						blockIn = false;
						st = ed+BLOCK_CMNT2.length();
						continue;									// ブロックコメント脱出。再読み込み
					}
					break;											// ブロックコメント未発見。次の行へ
				}

				// ラインコメント発見。次の行へ
				if( inLine.startsWith( LINE_CMNT,st ) ) { break; }	

				// ブロックコメントを見つける
				if( inLine.startsWith( BLOCK_CMNT1,st ) ) {
					int ed = inLine.indexOf( BLOCK_CMNT2,st ) ;		// 終了を見つける
					if( ed >= 0 ) {
						st = ed+BLOCK_CMNT2.length();
						continue;									// ブロックコメント脱出。再読み込み
					}
					else {
						blockIn = true;
					}
					break;											// ブロックコメント未発見。次の行へ
				}
			}

			// 通常の文字なので、追加する。
			buf.append( ch );
		}

		// rTrim() と同等の処理
		int len = buf.length();
		while( 0 < len && buf.charAt(len-1) <= ' ' ) {
			len--;
		}
		buf.setLength( len );

		String rtn = null;
		// 長さが 0 の行は、連続で現れないようにします。
		if( len == 0 ) {
			if( !rtnOnly ) {
				rtnOnly = true;
				rtn = "";
			}
		}
		else {
			rtnOnly = false;
			rtn = buf.toString();
		}

		return rtn ;
	}

	/**
	 * このクラスの動作確認用の、main メソッドです。
	 *
	 * Usage: java org.opengion.fukurou.util.CommentLineParser inFile outFile [encode]
	 *
	 * @param	args	コマンド引数配列
	 */
	public static void main( final String[] args ) {
		if( args.length < 2 ) {
			System.out.println( "Usage: java org.opengion.fukurou.util.CommentLineParser inFile outFile [encode]" );
		}

		File inFile  = new File( args[0] );
		File outFile = new File( args[1] );
		String encode  = (args.length >= 3 ) ? args[3] : "UTF-8" ;

		BufferedReader reader = FileUtil.getBufferedReader( inFile ,encode );
		PrintWriter    writer = FileUtil.getPrintWriter(   outFile ,encode );

		CommentLineParser clp = new CommentLineParser();

		try {
			String line1;
			while((line1 = reader.readLine()) != null) {
				line1 = clp.line( line1 );
				if( line1 != null ) {
					writer.println( line1 );
				}
			}
		}
		catch( IOException ex ) {
			String errMsg = "ファイルコピー中に例外が発生しました。\n"
						+ " inFile=[" + inFile + "] , outFile=[" + outFile + "]\n" ;
			throw new RuntimeException( errMsg,ex );
		}
		finally {
			Closer.ioClose( reader ) ;
			Closer.ioClose( writer ) ;
		}
	}
}
