/*
 * 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 javax.xml.transform.TransformerException ;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.SourceLocator;

import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXParseException;

import static org.opengion.fukurou.system.HybsConst.CR;
import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE;		// 6.4.2.1 (2016/02/05) refactoring

/**
 * TransformerFactory、Transformer 等で発生するエラーや警告を補足する ErrorListener の実装クラスです。
 * さらに、XMLReader 等で発生するエラーや警告を補足する ErrorHandler の実装クラスも兼ねています。
 *
 * ErrorListener や、ErrorHandler で捕らえたエラーを、内部の StringBuilder に書き出して、
 * エラー発生時にその内容を、読み出します。
 * 
 * TransformerFactory と Transformer に、同じオブジェクトを渡すことで、エラー発生時の
 * 状況に応じて、メッセージが設定されていきます。
 * 取り出しは、セットしたオブジェクトを、取り出して、toString() するだけにしています。
 *
 * @og.rev 6.4.0.2 (2015/12/11) 新規作成
 * @og.rev 6.4.3.2 (2016/02/19) 新規作成
 *
 * @version  6.4
 * @author   Kazuhiko Hasegawa
 * @since    JDK8.0,
 */
public class HybsErrorListener implements ErrorListener , ErrorHandler {
	// 6.4.3.2 (2016/02/19) 最初から用意しておきます。
	private final StringBuilder errBuf = new StringBuilder( BUFFER_MIDDLE ) ;

	/**
	 * 引数なしのコンストラクタ。
	 * 
	 * @og.rev 6.4.3.2 (2016/02/19) 内部の StringBuilder は、最初から用意しておきます。
	 */
	public HybsErrorListener() { super(); }		// これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。

	/**
	 * TransformerFactory,Transformer処理の、警告の通知を受け取ります。
	 *
	 * @param	ex Transformer例外にカプセル化されたエラー情報
	 * @see		javax.xml.transform.ErrorListener#warning( TransformerException )
	 */
	@Override
	public void warning( final TransformerException ex ) { errMsg( "Warning:" , ex ); }

	/**
	 * TransformerFactory,Transformer処理の、回復可能なエラーの通知を受け取ります。
	 *
	 * @param	ex Transformer例外にカプセル化されたエラー情報
	 * @see		javax.xml.transform.ErrorListener#error( TransformerException )
	 */
	@Override
	public void error( final TransformerException ex ) { errMsg( "Error:" , ex ); }

	/**
	 * TransformerFactory,Transformer処理の、回復できないエラーの通知を受け取ります。
	 *
	 * @param	ex Transformer例外にカプセル化されたエラー情報
	 * @see		javax.xml.transform.ErrorListener#fatalError( TransformerException )
	 */
	@Override
	public void fatalError( final TransformerException ex ) { errMsg( "Fatal:" , ex ); }

	/**
	 * XML構文解析エラーまたは警告の、警告の通知を受け取ります。
	 *
	 * @param	ex SAXParseException例外にカプセル化されたエラー情報
	 * @see		org.xml.sax.ErrorHandler#warning( SAXParseException )
	 */
	@Override
	public void warning( final SAXParseException ex ) { errMsg( "Warning:" , ex ); }

	/**
	 * XML構文解析エラーまたは警告の、回復可能なエラーの通知を受け取ります。
	 *
	 * @param	ex SAXParseException例外にカプセル化されたエラー情報
	 * @see		org.xml.sax.ErrorHandler#error( SAXParseException )
	 */
	@Override
	public void error( final SAXParseException ex ) { errMsg( "Error:" , ex ); }

	/**
	 * XML構文解析エラーまたは警告の、回復できないエラーの通知を受け取ります。
	 *
	 * @param	ex SAXParseException例外にカプセル化されたエラー情報
	 * @see		org.xml.sax.ErrorHandler#fatalError( SAXParseException )
	 */
	@Override
	public void fatalError( final SAXParseException ex ) { errMsg( "Fatal:" , ex ); }

	/**
	 * warning,error,fatalErrorの通知を受け取ります。
	 *
	 * @param	type 例外の発生元( "Warning:","Error:","Fatal:" )
	 * @param	ex	 Transformer例外にカプセル化されたエラー情報
	 */
	private void errMsg( final String type , final TransformerException ex ) {
		final SourceLocator locator = ex.getLocator();		// 例外発生元情報の取得
		if( locator != null ) {
			errBuf.append( " (" ).append( locator.getLineNumber() ).append( "): " );
		}

		final String errMsg = ex.getMessage();
		// null という文字列が取得されているようです。
		if( errMsg != null && !"null".equals( errMsg ) ) {
			errBuf.append( type ).append( errMsg ).append( CR );
		}

		Throwable cause = ex.getException();			// この例外がラップする例外を取得
		if( cause instanceof TransformerException ) {
			errMsg( "ﾗｯﾌﾟ:" , (TransformerException)cause );
		}

		cause = ex.getCause();							// この例外の原因となる例外を取得
		if( cause instanceof TransformerException ) {
			errMsg( "原因:" , (TransformerException)cause );
		}
	}

	/**
	 * warning,error,fatalErrorの通知を受け取ります。
	 *
	 * @param	type 例外の発生元( "Warning:","Error:","Fatal:" )
	 * @param	ex	 SAXParseException例外にカプセル化されたエラー情報
	 */
	private void errMsg( final String type , final SAXParseException ex ) {
		final int lineNo = ex.getLineNumber();		// 発生元テキストの終わりの行番号(最初は１、使用できない場合は、-1
		if( lineNo >= 0 ) {
			errBuf.append( " (" ).append( lineNo ).append( "): " );
		}

		final String errMsg = ex.getMessage();
		// null という文字列が取得されるかどうかは、未確認です。
		if( errMsg != null && !"null".equals( errMsg ) ) {
			errBuf.append( type ).append( errMsg ).append( CR );
		}

		Throwable cause = ex.getException();			// この例外がラップする例外を取得
		if( cause instanceof SAXParseException ) {
			errMsg( "ﾗｯﾌﾟ:" , (SAXParseException)cause );
		}

		cause = ex.getCause();							// この例外の原因となる例外を取得
		if( cause instanceof SAXParseException ) {
			errMsg( "原因:" , (SAXParseException)cause );
		}
	}

	/**
	 * 内部で保管しているエラーメッセージを返します。
	 *
	 * メソッド的には、getMessage() がよいのですが、ErrorListener のまま、
	 * オブジェクトとして文字列(メッセージ)を取り出すには、Objectから継承している
	 * toString() メソッドが、便利なので、こちらにしておきます。
	 *
	 * @return	エラーメッセー
	 */
	@Override
	public String toString() { return errBuf.toString(); }
}
