Asakusa Framework0.7.1でテキストファイルを入出力する方法のメモ。
|
|
Direct I/O(やWindGate)でCSVファイル(カンマ区切り)やTSVファイル(タブ区切り)を扱うことが出来るが、ベタのテキストファイルを扱うことは出来ない。
(0.7.5で出来るようになった。→テキストファイル連携 [2015-12-05])
ベタのテキストファイルを読み込めると便利な場面があるのだが。
しかしこれは、AsakusaFWがそういったフォーマットを扱うImporter/Exporterの雛形を生成しない、というだけであって、自分でクラスを作成すれば、ベタのテキストファイルを扱うことが出来る。
Direct I/OではFormatクラスを自作することになる。
Formatクラスの中では、ファイルを読み込むModelInputと、ファイルへ書き出すModelOutputを用意する。
仮にファイル読み込みしか必要ない場合でも、フローのテスト実行時にテストデータをファイルに書き出す必要があるので、ModelOutputも必ず用意しておかなければならない。
データモデルに@directio.csvを付けてDMDLをコンパイルするとFormatクラスが生成されるので、自作する場合は参考になる。
(0.7.5で@directio.lineが用意された。→テキストファイル連携 [2015-12-05])
Direct I/Oでベタのテキストファイルを読み込むImporterを作ってみる。
まず、テキストファイルの1行を保持するデータモデルを用意する。
"テキストファイルの一行"
line_model = {
"テキスト"
text : TEXT;
};
※ここでは特に「@directio.csv」等の属性は付けない。この属性は、あくまでFormatクラス等を自動生成する為のものであり、今回はそれを自作するので。
次に、Formatクラスを作成する。
Direct I/Oの場合、BinaryStreamFormatを継承したクラスを作成する。
(→完成したDirectioLineFormatのソース)
import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.nio.charset.StandardCharsets; import com.asakusafw.runtime.directio.BinaryStreamFormat; import com.asakusafw.runtime.directio.util.DelimiterRangeInputStream; import com.asakusafw.runtime.io.ModelInput; import com.asakusafw.runtime.io.ModelOutput; import com.example.modelgen.dmdl.model.LineModel;
/**
* format for {@link LineModel}.
*/
public final class DirectioLineFormat extends BinaryStreamFormat<LineModel> {
@Override
public Class<LineModel> getSupportedType() {
return LineModel.class;
}
@Override
public long getPreferredFragmentSize() {
return -1L;
}
@Override
public long getMinimumFragmentSize() {
return Long.MAX_VALUE;
}
まずは、お決まりのようなメソッドから実装する。
今回はLineModelにファイルの一行分のデータを書き込むので、対象データモデルはLineModelを指定する。
getPreferredFragmentSizeメソッドはブロックの推奨サイズを返すらしい。
getMinimumFragmentSizeメソッドは、入力ファイルをブロックに分割することに関係しているらしい。分割しない(つまり並列で処理されない)ようにする場合は、-1を返すようにする。
@Override
public ModelInput<LineModel> createInput(Class<? extends LineModel> dataType, String path, InputStream stream, long offset, long fragmentSize) throws IOException {
InputStream fragmentInput = new DelimiterRangeInputStream(stream, '\n', fragmentSize, offset > 0L);
return new Reader(fragmentInput);
}
createInputメソッドで、ファイルを読み込んでデータモデルオブジェクトに書き込むModelInputを返す。
DelimiterRangeInputStreamは、入力のテキストファイルを分割して読み込む為のInputStreamらしい。
分割する必要が無い場合は、Readerに直接stream(createInputメソッドの引数)を渡せばいい。
Readerクラスは自分で用意する。後述。
@Override
public ModelOutput<LineModel> createOutput(Class<? extends LineModel> dataType, String path, OutputStream stream) throws IOException {
return new Writer(stream);
}
createOutputメソッドは、データモデルオブジェクトの内容をファイルに書き出すModelOutputを返す。
Writerクラスは自分で用意する。後述。
Readerクラスは、ModelInputインターフェースの実装。
private static final class Reader implements ModelInput<LineModel> {
private final BufferedReader reader;
Reader(InputStream stream) {
this.reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
}
@Override
public boolean readTo(LineModel object) throws IOException {
String line = reader.readLine();
if (line == null) {
return false;
}
object.setTextAsString(line);
return true;
}
@Override
public void close() throws IOException {
reader.close();
}
}
readToメソッドで、データモデルオブジェクトに読み込んだデータを書き込む。
データが無かった場合はfalseを返す。
Writerクラスは、ModelOutputインターフェースの実装。
private static final class Writer implements ModelOutput<LineModel> {
private final BufferedWriter writer;
Writer(OutputStream stream) {
this.writer = new BufferedWriter(new OutputStreamWriter(stream, StandardCharsets.UTF_8));
}
@Override
public void write(LineModel object) throws IOException {
if (!object.getTextOption().isNull()) {
writer.write(object.getTextAsString());
}
writer.write('\n');
}
@Override
public void close() throws IOException {
writer.close();
}
}
}
writeメソッドで、データモデルオブジェクトのデータをファイルに出力する。
上記のFormatを使ったImporterクラスは、以下のようになる。
import com.asakusafw.runtime.directio.DataFormat; import com.asakusafw.vocabulary.directio.DirectFileInputDescription; import com.example.modelgen.dmdl.model.LineModel;
public class TextFromFile extends DirectFileInputDescription {
@Override
public Class<?> getModelType() {
return LineModel.class;
}
@Override
public Class<? extends DataFormat<?>> getFormat() {
return DirectioLineFormat.class;
}
@Override
public String getBasePath() {
return "input/file";
}
@Override
public String getResourcePattern() {
return "*.txt";
}
}
getModelTypeメソッドは、データモデルのクラスを返す。
getFormatメソッドで、自作したFormatクラスを返す。
getBasePathおよびgetResourcePatternメソッドは、Direct I/OのCSVファイルと同様。
→ベースパスとリソースパターンの使い分け