/*
* Copyright 2004-2015 the Seasar Foundation and the Others.
*
* 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.seasar.extension.jdbc.gen.internal.data;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.seasar.extension.jdbc.gen.desc.ColumnDesc;
import org.seasar.extension.jdbc.gen.desc.TableDesc;
import org.seasar.extension.jdbc.gen.dialect.GenDialect;
import org.seasar.extension.jdbc.gen.internal.util.CloseableUtil;
import org.seasar.extension.jdbc.gen.internal.util.DumpUtil;
import org.seasar.extension.jdbc.gen.sqltype.SqlType;
import org.seasar.framework.exception.IORuntimeException;
import org.seasar.framework.log.Logger;
import org.seasar.framework.util.CaseInsensitiveMap;
import org.seasar.framework.util.FileOutputStreamUtil;
/**
* ダンプファイルのライタです。
*
* @author taedium
*/
public class DumpFileWriter {
/** ロガー */
protected static Logger logger = Logger.getLogger(DumpFileWriter.class);
/** ダンプファイル */
protected File dumpFile;
/** 方言 */
protected GenDialect dialect;
/** テーブル記述 */
protected TableDesc tableDesc;
/** エンコーディング */
protected String encoding;
/** カラム名をキー、カラム記述を値とするマップ */
@SuppressWarnings("unchecked")
protected Map<String, ColumnDesc> columnDescMap = new CaseInsensitiveMap();
/** 区切り文字 */
protected char delimiter;
/** ライタ */
protected BufferedWriter writer;
/** 行番号 */
protected int lineNumber;
/**
* インスタンスを構築します。
*
* @param dumpFile
* ダンプファイル
* @param tableDesc
* テーブル記述
* @param dialect
* 方言
* @param encoding
* エンコーディング
* @param delimiter
* 区切り文字
*/
public DumpFileWriter(File dumpFile, TableDesc tableDesc,
GenDialect dialect, String encoding, char delimiter) {
if (dumpFile == null) {
throw new NullPointerException("dumpFile");
}
if (tableDesc == null) {
throw new NullPointerException("tableDesc");
}
if (dialect == null) {
throw new NullPointerException("dialect");
}
if (encoding == null) {
throw new NullPointerException("encoding");
}
this.dumpFile = dumpFile;
this.dialect = dialect;
this.tableDesc = tableDesc;
this.encoding = encoding;
this.delimiter = delimiter;
setupColumnDescMap();
}
/**
* カラム記述のマップを用意します。
*/
protected void setupColumnDescMap() {
for (ColumnDesc columnDesc : tableDesc.getColumnDescList()) {
if (isIgnoreColumn(columnDesc)) {
continue;
}
columnDescMap.put(columnDesc.getName(), columnDesc);
}
}
/**
* ヘッダーのみを書き込みます。
*/
public void writeHeaderOnly() {
int size = tableDesc.getColumnDescList().size();
StringBuilder buf = new StringBuilder(size * 10);
for (ColumnDesc columnDesc : tableDesc.getColumnDescList()) {
if (isIgnoreColumn(columnDesc)) {
continue;
}
String columnName = columnDesc.getName();
buf.append(DumpUtil.quote(columnName));
buf.append(delimiter);
}
if (buf.length() > 0) {
buf.setLength(buf.length() - 1);
}
writeLine(buf.toString());
}
/**
* 無視するカラムの場合{@code true}を返します。
*
* @param columnDesc
* カラム記述
* @return 無視するカラムの場合{@code true}
*/
protected boolean isIgnoreColumn(ColumnDesc columnDesc) {
return columnDesc.isIdentity() && !dialect.supportsIdentityInsert();
}
/**
* ヘッダーとデータ行を書き込みます。
*
* @param rs
* 結果セット
* @throws SQLException
* SQL例外が発生した場合
*/
public void writeRows(ResultSet rs) throws SQLException {
ResultSetMetaData metaData = rs.getMetaData();
Header header = createHeader(metaData);
writeHeader(header);
while (rs.next()) {
writeRowData(rs, header);
}
}
/**
* ヘッダーを書き込みます。
*
* @param header
* ヘッダー
*/
protected void writeHeader(Header header) {
StringBuilder buf = new StringBuilder(header.columnList.size() * 10);
for (HeaderColumn headerColumn : header.columnList) {
String columnName = headerColumn.columnDesc.getName();
buf.append(DumpUtil.quote(columnName));
buf.append(delimiter);
}
if (buf.length() > 0) {
buf.setLength(buf.length() - 1);
}
writeLine(buf.toString());
}
/**
* データ行を書き込みます。
*
* @param resultSet
* 結果セット
* @param header
* ヘッダー
* @throws SQLException
* SQL例外が発生した場合
*/
protected void writeRowData(ResultSet resultSet, Header header)
throws SQLException {
StringBuilder buf = new StringBuilder(header.columnList.size() * 10);
for (HeaderColumn headerColumn : header.columnList) {
String value = null;
if (headerColumn.present) {
SqlType sqlType = headerColumn.columnDesc.getSqlType();
value = sqlType.getValue(resultSet, headerColumn.index);
}
buf.append(DumpUtil.encode(value));
buf.append(delimiter);
}
if (buf.length() > 0) {
buf.setLength(buf.length() - 1);
}
writeLine(buf.toString());
}
/**
* 行を書き込みます。
*
* @param line
* 行
*/
protected void writeLine(String line) {
if (writer == null) {
writer = createBufferdWriter();
}
try {
writer.write(line);
writer.newLine();
} catch (IOException e) {
throw new IORuntimeException(e);
}
lineNumber++;
}
/**
* {@link BufferedWriter}を作成します。
*
* @return {@link BufferedWriter}
*/
protected BufferedWriter createBufferdWriter() {
dumpFile.getParentFile().mkdirs();
Charset charset = Charset.forName(encoding);
FileOutputStream fos = FileOutputStreamUtil.create(dumpFile);
OutputStreamWriter osw = new OutputStreamWriter(fos, charset);
return new BufferedWriter(osw);
}
/**
* クローズします。
*/
public void close() {
CloseableUtil.close(writer);
}
/**
* 行番号を返します。
*
* @return 行番号
*/
public int getLineNumber() {
return lineNumber;
}
/**
* ヘッダーを作成します。
*
* @param metaData
* 結果セットのメタデータ
* @return ヘッダー
* @throws SQLException
* SQL例外が発生した場合
*/
protected Header createHeader(ResultSetMetaData metaData)
throws SQLException {
Header header = new Header();
@SuppressWarnings("unchecked")
Map<String, Integer> indexMap = new CaseInsensitiveMap();
for (int i = 0; i < metaData.getColumnCount(); i++) {
int index = i + 1;
String columnLabel = metaData.getColumnLabel(index);
indexMap.put(columnLabel, index);
}
for (Map.Entry<String, ColumnDesc> entry : columnDescMap.entrySet()) {
String columnLabel = entry.getKey();
if (indexMap.containsKey(columnLabel)) {
int index = indexMap.get(columnLabel);
HeaderColumn headerColumn = new HeaderColumn();
headerColumn.columnDesc = entry.getValue();
headerColumn.index = index;
headerColumn.present = true;
header.columnList.add(headerColumn);
} else {
HeaderColumn headerColumn = new HeaderColumn();
headerColumn.columnDesc = entry.getValue();
header.columnList.add(headerColumn);
}
}
return header;
}
/**
* ダンプファイルのヘッダーです。
*
* @author taedium
*/
protected static class Header {
/** ダンプファイルのヘッダーカラムのリスト */
protected List<HeaderColumn> columnList = new ArrayList<HeaderColumn>();
}
/**
* ダンプファイルのヘッダーのカラムです。
*
* @author taedium
*/
protected static class HeaderColumn {
/** カラム記述 */
protected ColumnDesc columnDesc;
/** 対応するカラムがデータベースに存在する場合{@code true} */
protected boolean present;
/** データベースのカラムのインデックス */
protected int index;
}
}