Java tutorial
/* * 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; } }