/*
 * 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.Writer ;
import java.io.IOException ;

import javax.xml.transform.dom.DOMSource ;
import javax.xml.transform.stream.StreamResult ;
import javax.xml.transform.Transformer ;
import javax.xml.transform.TransformerFactory ;
import javax.xml.transform.TransformerException ;
import javax.xml.transform.TransformerConfigurationException ;
import javax.xml.transform.OutputKeys ;

import javax.xml.parsers.DocumentBuilder ;
import javax.xml.parsers.DocumentBuilderFactory ;
import javax.xml.parsers.ParserConfigurationException ;
import org.xml.sax.SAXException ;
import org.w3c.dom.Document ;
import org.opengion.fukurou.system.Closer;
import org.opengion.fukurou.util.FileUtil;							// 6.3.8.0 (2015/09/11)
import org.opengion.fukurou.system.ThrowUtil;							// 6.4.2.0 (2016/01/29)

/**
 * XMLファイルを読み取って、Document オブジェクトを取得する、ユーティリティークラスです。
 *
 * javax.xml.parsers および、org.w3c.dom の簡易処理を行います。
 * read で、Document を読み込み、write で、ファイルに書き出します。
 * なお、書き出しに関しては、UTF-8 固定で、かつ、Transformer で行いますので、
 * 属性の並び順は、保障されません。(つまり、簡易的な書き出し機能です。)
 *
 * @og.rev 5.1.7.0 (2010/06/01) 新規作成
 *
 * @version  5.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK6.0,
 */

public final class DomParser {

	/**
	 * プライベート コンストラクター
	 *
	 * オブジェクトの作成を拒否します。
	 */
	private DomParser() {}

	/**
	 * XMLファイルを読み込み、org.w3c.dom.Documentを返す。
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) ex.printStackTrace() を、ThrowUtil#ogStackTrace(Throwable) に置き換え。
	 *
	 * @param	aFile	XMLファイル
	 *
	 * @return	構築した Document( nullは読み込み失敗 )
	 */
	public static Document read( final File aFile ) {

		Document document = null;
		try {
			//----------------------------------------------------
			// step1. DocumentBuilderを構築する
			//----------------------------------------------------
			final DocumentBuilderFactory bfactory = DocumentBuilderFactory.newInstance();
			final DocumentBuilder builder = bfactory.newDocumentBuilder();

			//----------------------------------------------------
			// step2. XMLファイルを読み込んで、Documentを構築する
			//----------------------------------------------------
			document = builder.parse( aFile );

		} catch( final ParserConfigurationException ex) {
			System.err.println( ex.getMessage() );
			System.err.println( ThrowUtil.ogStackTrace( ex ) );				// 6.4.2.0 (2016/01/29)
		} catch( final SAXException ex) {
			// 文法エラーが発生した場合
			System.err.println( ex.getMessage() );
			System.err.println( ThrowUtil.ogStackTrace( ex ) );				// 6.4.2.0 (2016/01/29)
		} catch( final IOException ex) {
			// ファイルが読み込めなかった場合
			System.err.println( ex.getMessage() );
			System.err.println( ThrowUtil.ogStackTrace( ex ) );				// 6.4.2.0 (2016/01/29)
		}

		// 完了
		return document;
	}

	/**
	 * Documentを指定ファイルに保存する。
	 *
	 * @param aFile 保存先ファイル
	 * @param aDocument Documentインスタンス
	 *
	 * @og.rev 5.1.9.0 (2010/08/01) Closeされないバグを修正
	 * @og.rev 5.6.6.0 (2013/07/05) 若干のインデックス（らしきもの）を入れます
	 * @og.rev 6.3.8.0 (2015/09/11) FileUtil#getPrintWriter( File,String ) を使用。
	 * @og.rev 6.4.0.2 (2015/12/11) Transformer のエラーを、より詳細に出力します。
	 * @og.rev 6.4.2.0 (2016/01/29) ex.printStackTrace() を、ThrowUtil#ogStackTrace(Throwable) に置き換え。
	 */
	public static void write( final File aFile, final Document aDocument ){
		final HybsErrorListener errListener = new HybsErrorListener();
		Writer out = null;
		try {
			//---------------------------------
			// step1. Transformerの準備
			//---------------------------------
			final TransformerFactory tFactory = TransformerFactory.newInstance();
			// 6.4.0.2 (2015/12/11) TransformerFactory のエラーを、より詳細に出力します。
			tFactory.setErrorListener( errListener );

			final Transformer transformer = tFactory.newTransformer();
			// 6.4.0.2 (2015/12/11) Transformer のエラーを、より詳細に出力します。
			transformer.setErrorListener( errListener );

			//---------------------------------
			// step2. Transformerの動作設定
			//---------------------------------
			transformer.setOutputProperty( OutputKeys.INDENT, "yes" );
			transformer.setOutputProperty( OutputKeys.METHOD, "xml" );			// 5.6.6.0 (2013/07/05)

			// 5.6.6.0 (2013/07/05) インデントを入れるには、OutputKeys.INDENT だけではだめ。
			// Xalan の メインの xalan.jarじゃなくて、serializer.jar に入っている OutputPropertiesFactory が必要。
			// transformer.setOutputPropert( org.apache.xml.serializer.OutputPropertiesFactory.S_KEY_INDENT_AMOUNT, "2" );

			// この為だけに、また、java の JAVA_HOME/jre/lib/ext を増やすのは嫌なので、OutputPropertiesFactory.S_KEY_INDENT_AMOUNT
			// で作成している文字列を直接記述しちゃいます。良い子はマネしないでね。
			transformer.setOutputProperty( "{http://xml.apache.org/xalan}indent-amount", "2" );

			transformer.setOutputProperty( OutputKeys.ENCODING, "UTF-8" );

			//---------------------------------
			// step3. Writerの準備
			//---------------------------------
			// 6.3.8.0 (2015/09/11) FileUtil#getPrintWriter( File,String ) を使用。
			out = FileUtil.getPrintWriter( aFile,"UTF-8" ) ;		// 6.3.8.0 (2015/09/11)

			//---------------------------------
			// step4. 書き出し
			//---------------------------------
			transformer.transform( new DOMSource( aDocument ), new StreamResult( out ) );

		}
		catch( final TransformerConfigurationException ex ) {
	//		System.err.println( ex.getMessageAndLocation() );	// 6.4.0.2 (2015/12/11) 行番号がうまく取り出せない。
			System.err.println( errListener.toString() );		// 6.4.0.2 (2015/12/11) エラー内容が重複するかも。
			System.err.println( ThrowUtil.ogStackTrace( ex ) );				// 6.4.2.0 (2016/01/29)
		}
		catch( final TransformerException ex ) {
			// 書き出しエラー発生
	//		System.err.println( ex.getMessageAndLocation() );	// 6.4.0.2 (2015/12/11) 行番号がうまく取り出せない。
			System.err.println( errListener.toString() );		// 6.4.0.2 (2015/12/11) エラー内容が重複するかも。
			System.err.println( ThrowUtil.ogStackTrace( ex ) );				// 6.4.2.0 (2016/01/29)
		}
		// 5.1.9.0 (2010/08/01) Closeされないバグを修正
		finally {
			Closer.ioClose( out );
		}
	}
}
