jp.terasoluna.fw.file.dao.standard.AbstractFileLineWriter.java Source code

Java tutorial

Introduction

Here is the source code for jp.terasoluna.fw.file.dao.standard.AbstractFileLineWriter.java

Source

/*
 * Copyright (c) 2007 NTT DATA Corporation
 *
 * 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 jp.terasoluna.fw.file.dao.standard;

import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jp.terasoluna.fw.file.annotation.FileFormat;
import jp.terasoluna.fw.file.annotation.OutputFileColumn;
import jp.terasoluna.fw.file.annotation.PaddingType;
import jp.terasoluna.fw.file.annotation.StringConverter;
import jp.terasoluna.fw.file.annotation.TrimType;
import jp.terasoluna.fw.file.dao.FileException;
import jp.terasoluna.fw.file.dao.FileLineException;
import jp.terasoluna.fw.file.dao.FileLineWriter;

import org.apache.commons.lang3.StringUtils;

/**
 * ()?
 * <p>
 * ()?3??(CSV???) ??????? ??????<br>
 * ?{@link jp.terasoluna.fw.file.dao.FileLineWriter}????
 * </p>
 * <p>
 * ????????????
 * <ul>
 * <li>??(init()?????)</li>
 * <li>?(printHeaderLine())</li>
 * <li>??(printDataLine())</li>
 * <li>?(printTrailerLine())</li>
 * </ul>
 * ????????<br>
 * </p>
 * @see jp.terasoluna.fw.file.dao.FileLineWriter
 * @see jp.terasoluna.fw.file.dao.standard.CSVFileLineWriter
 * @see jp.terasoluna.fw.file.dao.standard.FixedFileLineWriter
 * @see jp.terasoluna.fw.file.dao.standard.VariableFileLineWriter
 * @see jp.terasoluna.fw.file.dao.standard.PlainFileLineWriter
 * @param <T> 
 */
public abstract class AbstractFileLineWriter<T> implements FileLineWriter<T> {

    /**
     * ????
     */
    private static final int INITIAL_LINE_NO = -1;

    /**
     * ?
     */
    private Writer writer = null;

    /**
     * ???
     */
    private String fileName = null;

    /**
     * 
     */
    private String fileEncoding = System.getProperty("file.encoding");

    /**
     * ?
     */
    private Class<T> clazz = null;

    /**
     * 
     */
    private String lineFeedChar = System.getProperty("line.separator");

    /**
     * ?FieldAnnotation??
     */
    private Field[] fields = null;

    /**
     * ???
     */
    private OutputFileColumn[] outputFileColumns = null;

    /**
     * ????Index??
     */
    private int[] columnIndexs = null;

    /**
     * ???????
     */
    private String[] columnFormats = null;

    /**
     * ???????
     */
    private int[] columnBytes = null;

    /**
     * ??????
     */
    private PaddingType[] paddingTypes = null;

    /**
     * ??????
     */
    private char[] paddingChars = null;

    /**
     * ??????
     */
    private TrimType[] trimTypes;

    /**
     * ????
     */
    private char[] trimChars;

    /**
     * ???????
     */
    private char[] columnEncloseChar;

    /**
     * ????
     */
    private StringConverter[] stringConverters = null;

    /**
     * ????
     */
    @SuppressWarnings("rawtypes")
    private static Map<Class, StringConverter> stringConverterCacheMap = new HashMap<Class, StringConverter>();

    /**
     * 
     */
    private Method[] methods = null;

    /**
     * (??
     */
    private Map<String, ColumnFormatter> columnFormatterMap = null;

    /**
     * ?
     */
    private boolean writeData = false;

    /**
     * ?
     */
    private boolean writeTrailer = false;

    /**
     * ??????
     */
    private int currentLineCount = 0;

    /**
     * ??
     */
    private boolean calledInit = false;

    /**
     * ??
     */
    private boolean enclosed = false;

    /**
     * <br>
     * ?<code>@FileFormat</code>??? <code>@FileFormat</code>??????????<br>
     * ?????????????<br>
     * ???????<br>
     * @param fileName ??
     * @param clazz 
     * @param columnFormatterMap ?
     */
    public AbstractFileLineWriter(String fileName, Class<T> clazz,
            Map<String, ColumnFormatter> columnFormatterMap) {

        if (fileName == null || "".equals(fileName)) {
            throw new FileException("fileName is required.", new IllegalArgumentException(), fileName);
        }

        if (clazz == null) {
            throw new FileException("clazz is required.", new IllegalArgumentException(), fileName);
        }

        if (columnFormatterMap == null || columnFormatterMap.isEmpty()) {
            throw new FileException("columnFormatterMap is required.", new IllegalArgumentException(), fileName);
        }

        this.fileName = fileName;
        this.clazz = clazz;
        this.columnFormatterMap = columnFormatterMap;

        // FileFormat????

        // ??
        FileFormat fileFormat = clazz.getAnnotation(FileFormat.class);

        // @FileFormat?????
        if (fileFormat == null) {
            throw new FileException("FileFormat annotation is not found.", new IllegalStateException(), fileName);
        }

        // ????????
        if (fileFormat.delimiter() == fileFormat.encloseChar()) {
            throw new FileException("Delimiter is the same as EncloseChar and is no use.",
                    new IllegalStateException(), fileName);
        }

        // ??
        if (fileFormat.fileEncoding() != null && !"".equals(fileFormat.fileEncoding())) {
            this.fileEncoding = fileFormat.fileEncoding();
        }

        // ????????
        if (fileFormat.lineFeedChar() != null && !"".equals(fileFormat.lineFeedChar())) {
            this.lineFeedChar = fileFormat.lineFeedChar();
        }

        // ?3????
        if (lineFeedChar.length() > 2) {
            throw new FileException("lineFeedChar length must be 1 or 2. but: " + lineFeedChar.length(),
                    new IllegalStateException(), fileName);
        }
    }

    /**
     * ???<br>
     * ????????
     * <ul>
     * <li>?(Field)??</li>
     * <li>?(stringConverters)??</li>
     * <li>???(methods)??</li>
     * <li>?????</li>
     * <li>????</li>
     * </ul>
     * init()?AbstracFileLineWriter??? ???<br>
     * ??????2??????????
     */
    protected void init() {
        if (!calledInit) {
            // ??
            FileFormat fileFormat = clazz.getAnnotation(FileFormat.class);

            buildFields();

            if (isCheckEncloseChar()) {
                // ???????????
                if (enclosed) {
                    throw new FileException("columnEncloseChar can not change.", new IllegalStateException(),
                            fileName);
                }
            }

            if (isCheckColumnAnnotationCount()) {
                // ??????????
                if (fields.length == 0) {
                    throw new FileException("OutputFileColumn is not found.", new IllegalStateException(),
                            fileName);
                }
            }

            buildStringConverters();
            buildMethods();

            // 
            try {
                boolean append = !fileFormat.overWriteFlg();
                writer = new BufferedWriter(
                        new OutputStreamWriter((new FileOutputStream(fileName, append)), fileEncoding));
            } catch (UnsupportedEncodingException e) {
                throw new FileException("Failed in generation of writer.", e, fileName);
            } catch (FileNotFoundException e) {
                throw new FileException("Failed in generation of writer.", e, fileName);
            }
            calledInit = true;
        }
    }

    /**
     * ??????
     */
    private void buildFields() {
        List<Field[]> fieldList = new ArrayList<Field[]>();

        // ?
        Class<?> tempClass = clazz;
        Field[] declaredFieldArray = null;
        int allFieldCount = 0;
        while (tempClass != null) {
            declaredFieldArray = tempClass.getDeclaredFields();
            fieldList.add(declaredFieldArray);
            allFieldCount += declaredFieldArray.length;
            tempClass = tempClass.getSuperclass();
        }

        // ?????
        Field[] dataColumnFields = new Field[allFieldCount];

        OutputFileColumn outputFileColumn = null;
        int maxColumnIndex = -1;
        int columnIndex = -1;
        int columnCount = 0;

        for (Field[] fields : fieldList) {
            for (Field field : fields) {
                outputFileColumn = field.getAnnotation(OutputFileColumn.class);
                if (outputFileColumn != null) {
                    // ????????
                    if (columnFormatterMap.get(field.getType().getName()) == null) {
                        throw new FileException(
                                "There is a type which isn't supported in a "
                                        + "mapping target field in FileLineObject.",
                                new IllegalStateException(), fileName);
                    }

                    columnIndex = outputFileColumn.columnIndex();
                    // Index??????
                    if (columnIndex < 0) {
                        throw new FileException("Column Index in FileLineObject is the minus " + "number.",
                                new IllegalStateException(), fileName);
                    }
                    // Index?????????
                    if (dataColumnFields.length <= columnIndex) {
                        throw new FileException(
                                "Column Index in FileLineObject is bigger than " + "the total number of the field.",
                                new IllegalStateException(), fileName);
                    }
                    // Index??????????
                    if (dataColumnFields[columnIndex] == null) {
                        dataColumnFields[columnIndex] = field;
                        if (maxColumnIndex < columnIndex) {
                            maxColumnIndex = columnIndex;
                        }
                        columnCount++;
                    } else {
                        throw new FileException("Column Index is duplicate : " + columnIndex, fileName);
                    }
                }
            }
        }
        // columnIndex????????
        if (columnCount != (maxColumnIndex + 1)) {
            throw new FileException("columnIndex in FileLineObject is not sequential order.",
                    new IllegalStateException(), fileName);
        }

        // (null?)
        if (dataColumnFields.length == columnCount) {
            this.fields = dataColumnFields;
        } else {
            this.fields = new Field[columnCount];
            System.arraycopy(dataColumnFields, 0, this.fields, 0, columnCount);
        }

        // OutputFileColumnList??
        outputFileColumns = new OutputFileColumn[fields.length];
        columnIndexs = new int[fields.length];
        columnFormats = new String[fields.length];
        columnBytes = new int[fields.length];
        paddingTypes = new PaddingType[fields.length];
        paddingChars = new char[fields.length];
        trimTypes = new TrimType[fields.length];
        trimChars = new char[fields.length];

        // ???FileFormat???
        columnEncloseChar = new char[fields.length];
        if (getEncloseChar() != Character.MIN_VALUE) {
            enclosed = true;
            for (int index = 0; index < fields.length; index++) {
                columnEncloseChar[index] = getEncloseChar();
            }
        }

        for (int i = 0; i < fields.length; i++) {
            outputFileColumns[i] = fields[i].getAnnotation(OutputFileColumn.class);
            columnIndexs[i] = outputFileColumns[i].columnIndex();
            columnFormats[i] = outputFileColumns[i].columnFormat();
            columnBytes[i] = outputFileColumns[i].bytes();
            paddingTypes[i] = outputFileColumns[i].paddingType();
            paddingChars[i] = outputFileColumns[i].paddingChar();
            trimTypes[i] = outputFileColumns[i].trimType();
            trimChars[i] = outputFileColumns[i].trimChar();
            // ?inputFileColumns?????
            if (outputFileColumns[i].columnEncloseChar() != Character.MIN_VALUE) {
                columnEncloseChar[i] = outputFileColumns[i].columnEncloseChar();
                enclosed = true;
            }
        }
    }

    /**
     * ???????<br>
     */
    private void buildStringConverters() {

        // ????
        StringConverter[] dataColumnStringConverters = new StringConverter[fields.length];

        OutputFileColumn outputFileColumn = null;
        Class<? extends StringConverter> converterKind = null;

        for (int i = 0; i < fields.length; i++) {
            // JavaBean????
            // outputFileColumn = fields[i].getAnnotation(OutputFileColumn.class);
            outputFileColumn = outputFileColumns[i];

            // OutputFileColumn.stringConverter()????
            try {
                // ????
                converterKind = outputFileColumn.stringConverter();

                // ???????????
                if (stringConverterCacheMap.containsKey(converterKind)) {
                    // ?????????
                    dataColumnStringConverters[i] = stringConverterCacheMap.get(converterKind);
                } else {
                    // ????????
                    dataColumnStringConverters[i] = converterKind.newInstance();
                    stringConverterCacheMap.put(converterKind, dataColumnStringConverters[i]);
                }

            } catch (InstantiationException e) {
                throw new FileLineException("Failed in an instantiate of a stringConverter.", e, fileName,
                        INITIAL_LINE_NO, fields[i].getName(), outputFileColumn.columnIndex());

            } catch (IllegalAccessException e) {
                throw new FileLineException("Failed in an instantiate of a stringConverter.", e, fileName,
                        INITIAL_LINE_NO, fields[i].getName(), outputFileColumn.columnIndex());
            }
        }
        this.stringConverters = dataColumnStringConverters;
    }

    /**
     * ??getter?????<br>
     * ??getter????<br>
     * <ul>
     * <li>??????????get?????</li>
     * <li>is()?has()???getter???</li>
     * </ul>
     * getter??????????
     * @throws FileException getter????????
     */
    private void buildMethods() {
        Method[] dataColumnGetMethods = new Method[fields.length];
        StringBuilder getterName = new StringBuilder();
        String fieldName = null;

        for (int i = 0; i < fields.length; i++) {
            // JavaBean??????????
            fieldName = fields[i].getName();

            // ????getter??????
            getterName.setLength(0);
            getterName.append("get");
            getterName.append(StringUtils.upperCase(fieldName.substring(0, 1)));
            getterName.append(fieldName.substring(1, fieldName.length()));

            // getter???
            try {
                dataColumnGetMethods[i] = clazz.getMethod(getterName.toString());
            } catch (NoSuchMethodException e) {
                throw new FileException("The getter method of column doesn't exist.", e, fileName);
            }
        }
        this.methods = dataColumnGetMethods;
    }

    /**
     * ????
     * @param headerLine ????
     */
    @Override
    public void printHeaderLine(List<String> headerLine) {
        if (writeData || writeTrailer) {
            throw new FileException("Header part should be called before " + "data part or trailer part.",
                    new IllegalStateException(), fileName);
        }
        printList(headerLine);
    }

    /**
     * ??????
     * @param t ???
     */
    @Override
    public void printDataLine(T t) {
        checkWriteTrailer();
        // ?????
        StringBuilder fileLineBuilder = new StringBuilder();

        // ??
        // (?????????)
        if (getDelimiter() == Character.MIN_VALUE && getEncloseChar() == Character.MIN_VALUE) {
            for (int i = 0; i < fields.length; i++) {
                fileLineBuilder.append(getColumn(t, i));
            }
        } else {
            for (int i = 0; i < fields.length; i++) {
                // ????
                if (columnEncloseChar[i] != Character.MIN_VALUE) {
                    fileLineBuilder.append(columnEncloseChar[i]);
                    fileLineBuilder.append(getColumn(t, i));
                    fileLineBuilder.append(columnEncloseChar[i]);
                } else {
                    fileLineBuilder.append(getColumn(t, i));
                }
                fileLineBuilder.append(getDelimiter());
            }
            // ??
            if (fileLineBuilder.length() > 0) {
                fileLineBuilder.deleteCharAt(fileLineBuilder.length() - 1);
            }
        }

        // ?
        fileLineBuilder.append(getLineFeedChar());

        // ??????
        try {
            getWriter().write(fileLineBuilder.toString());
        } catch (IOException e) {
            throw new FileException("Processing of writer was failed.", e, fileName);
        }
        currentLineCount++;
        setWriteData(true);
    }

    /**
     * ????
     * @param trailerLine ????
     */
    @Override
    public void printTrailerLine(List<String> trailerLine) {
        printList(trailerLine);
        writeTrailer = true;
    }

    /**
     * ??????
     * @param stringList ?
     */
    private void printList(List<String> stringList) {
        for (String stringData : stringList) {
            try {
                getWriter().write(stringData);
                getWriter().write(lineFeedChar);
            } catch (IOException e) {
                throw new FileException("Processing of writer was failed.", e, fileName);
            }
        }
    }

    /**
     * ?
     */
    @Override
    public void closeFile() {
        try {
            getWriter().flush();
            getWriter().close();
        } catch (IOException e) {
            throw new FileException("Closing of writer was failed.", e, fileName);
        }
    }

    /**
     * <p>
     * ??????
     * </p>
     * <p>
     * ?????? ???<br>
     * <li>?<br>
     * <li><br>
     * <li>??<br>
     * <br>
     * ??????????<br>
     * ???????????
     * </p>
     * @param t 
     * @param index ?
     * @return ?
     */
    protected String getColumn(T t, int index) {
        // ????
        String columnString = null;

        // (t)??????
        ColumnFormatter columnFormatter = columnFormatterMap.get(methods[index].getReturnType().getName());
        try {
            columnString = columnFormatter.format(t, methods[index], columnFormats[index]);
        } catch (IllegalArgumentException e) {
            throw new FileLineException("Failed in column data formatting.", e, fileName, currentLineCount + 1,
                    fields[index].getName(), columnIndexs[index]);
        } catch (IllegalAccessException e) {
            throw new FileLineException("Failed in column data formatting.", e, fileName, currentLineCount + 1,
                    fields[index].getName(), columnIndexs[index]);
        } catch (InvocationTargetException e) {
            throw new FileLineException("Failed in column data formatting.", e, fileName, currentLineCount + 1,
                    fields[index].getName(), columnIndexs[index]);
        }

        if (columnString == null) {
            columnString = "";
        }

        // ?
        columnString = FileDAOUtility.trim(columnString, fileEncoding, trimChars[index], trimTypes[index]);

        // ?
        columnString = FileDAOUtility.padding(columnString, fileEncoding, columnBytes[index], paddingChars[index],
                paddingTypes[index]);

        // ??
        // OutputFileColumn.stringConverter()????
        columnString = stringConverters[index].convert(columnString);

        // ???
        if (isCheckByte(columnBytes[index])) {
            try {
                // ?Bytes???
                if (columnBytes[index] <= 0) {
                    throw new FileLineException("bytes is not set " + "or a number equal to or less than 0 is set.",
                            new IllegalStateException(), getFileName(), currentLineCount + 1,
                            fields[index].getName(), columnIndexs[index]);
                }
                // ??Bytes???????
                if (columnString.getBytes(fileEncoding).length != columnBytes[index]) {
                    throw new FileLineException(
                            "The data size is different from bytes value of " + "the set value of the column .",
                            new IllegalStateException(), fileName, currentLineCount + 1, fields[index].getName(),
                            columnIndexs[index]);
                }
            } catch (UnsupportedEncodingException e) {
                throw new FileException("fileEncoding which isn't supported was set.", e, fileName);
            }
        }
        return columnString;
    }

    /**
     * ????
     * @return fileName ??
     */
    public String getFileName() {
        return fileName;
    }

    /**
     * ?
     * @return lineFeedChar 
     */
    protected String getLineFeedChar() {
        return lineFeedChar;
    }

    /**
     * ?
     * @param lineFeedChar 
     */
    protected void setLineFeedChar(String lineFeedChar) {
        this.lineFeedChar = lineFeedChar;
    }

    /**
     * (?????
     * @param columnFormatterMap (??
     */
    public void setColumnFormatterMap(Map<String, ColumnFormatter> columnFormatterMap) {
        this.columnFormatterMap = columnFormatterMap;
    }

    /**
     * ???
     * @return bufferedWriter ?
     */
    protected Writer getWriter() {
        return writer;
    }

    /**
     * ?FieldAnnotation????
     * @return fields ?FieldAnnotation??
     */
    protected Field[] getFields() {
        return fields;
    }

    /**
     * ?Field??getter????
     * @return methods ?Field??getter??
     */
    protected Method[] getMethods() {
        return methods;
    }

    /**
     * ??????????
     * @param writeData 
     */
    protected void setWriteData(boolean writeData) {
        this.writeData = writeData;
    }

    /**
     * ????????????<br>
     * ????????
     */
    protected void checkWriteTrailer() {
        if (writeTrailer) {
            throw new FileException("Header part or data part should be " + "called before TrailerPart",
                    new IllegalStateException(), fileName);
        }
    }

    /**
     * ??
     * @return 
     */
    public abstract char getDelimiter();

    /**
     * ???
     * @return ?
     */
    public abstract char getEncloseChar();

    /**
     * ???????
     * <p>
     * ?????<code>true</code>??????<br>
     * ????<code>bytes</code>????????? true?????
     * </p>
     * @param outputFileColumn ?OutputFileColumn
     * @return ?????(1?)??true
     */
    protected boolean isCheckByte(OutputFileColumn outputFileColumn) {

        if (0 < outputFileColumn.bytes()) {
            return true;
        }

        return false;
    }

    /**
     * ???????
     * @param columnByte ??
     * @return ?????(1?)??true
     */
    protected boolean isCheckByte(int columnByte) {
        if (0 < columnByte) {
            return true;
        }
        return false;
    }

    /**
     * ??????????????
     * @return ????true
     */
    protected boolean isCheckEncloseChar() {
        return false;
    }

    /**
     * ????????????
     * @return ????true
     */
    protected boolean isCheckColumnAnnotationCount() {
        return true;
    }

    /**
     * ????
     * @return columnEncloseChar ?
     */
    protected char[] getColumnEncloseChar() {
        return columnEncloseChar;
    }
}