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.BufferedReader; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.text.ParseException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; import jp.terasoluna.fw.file.annotation.FileFormat; import jp.terasoluna.fw.file.annotation.InputFileColumn; 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.FileLineIterator; import org.apache.commons.lang3.StringUtils; /** * (?)? * <p> * (?)?3??(CSV???) ??????? ??????<br> * ?{@link jp.terasoluna.fw.file.dao.FileLineIterator}???? * </p> * ?????????????????????<br> * <ul> * <li>?(getHeader())</li> * <li>?(skip())</li> * <li>??(hasNext()?readLine())</li> * <li>?(getTrailer())</li> * </ul> * ?????????????? ???????????????<br> * ??????<code>IllegalStateException<code>????<br> * @see jp.terasoluna.fw.file.dao.FileLineIterator * @see jp.terasoluna.fw.file.dao.standard.CSVFileLineIterator * @see jp.terasoluna.fw.file.dao.standard.FixedFileLineIterator * @see jp.terasoluna.fw.file.dao.standard.VariableFileLineIterator * @see jp.terasoluna.fw.file.dao.standard.PlainFileLineIterator * @param <T> */ public abstract class AbstractFileLineIterator<T> implements FileLineIterator<T> { /** * ?????? */ private static final int INITIAL_LINE_NO = -1; /** * ?? */ private String fileName = null; /** * ? */ private Class<T> clazz = null; /** * */ private String lineFeedChar = System.getProperty("line.separator"); /** * */ private String fileEncoding = System.getProperty("file.encoding"); /** * */ private int headerLineCount = 0; /** * */ private int trailerLineCount = 0; /** * ???? */ private int currentLineCount = 0; /** * ? */ private BufferedReader reader = null; /** * ?FieldAnnotation?? */ private Field[] fields = null; /** * ??? */ private InputFileColumn[] inputFileColumns = null; /** * ????Index?? */ private int[] columnIndexs = null; /** * ??????? */ private String[] columnFormats = null; /** * ??????? */ private int[] columnBytes = null; /** * 1???? */ private int totalBytes = 0; /** * ?????? */ 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>(); /** * ?Field??setter?? */ private Method[] methods = null; /** * ?? */ private Map<String, ColumnParser> columnParserMap = null; /** * ? */ private List<String> header = new ArrayList<String>(); /** * ? */ private List<String> trailer = new ArrayList<String>(); /** * ?? */ private boolean readTrailer = false; /** * ??? */ private Queue<String> trailerQueue = null; /** * 1?? */ private LineReader lineReader = null; /** * ??? */ private boolean calledInit = false; /** * ?? */ private boolean enclosed = false; /** * <br> * ??????FileFormat ???<br> * ????????<br> * @param fileName ??? * @param clazz * @param columnParserMap ? * @throws FileException ?????? */ public AbstractFileLineIterator(String fileName, Class<T> clazz, Map<String, ColumnParser> columnParserMap) { // ????? 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 (columnParserMap == null || columnParserMap.isEmpty()) { throw new FileException("columnFormaterMap is required.", new IllegalArgumentException(), fileName); } // ??????? try { clazz.newInstance(); } catch (InstantiationException e) { throw new FileException("Failed in instantiation of clazz.", e, fileName); } catch (IllegalAccessException e) { throw new FileException("clazz's nullary constructor is not accessible", e, fileName); } this.fileName = fileName; this.clazz = clazz; this.columnParserMap = columnParserMap; // FileFormat??? FileFormat fileFormat = clazz.getAnnotation(FileFormat.class); // ?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.lineFeedChar() != null && !"".equals(fileFormat.lineFeedChar())) { this.lineFeedChar = fileFormat.lineFeedChar(); } // ???????? if (fileFormat.fileEncoding() != null && !"".equals(fileFormat.fileEncoding())) { this.fileEncoding = fileFormat.fileEncoding(); } // ? this.headerLineCount = fileFormat.headerLineCount(); // ? this.trailerLineCount = fileFormat.trailerLineCount(); } /** * ??????????<br> * ?????????? true ??? * @return ?????????? <code>true</code> * @throws FileException ?IOException???? */ @Override public boolean hasNext() { try { reader.mark(1); if (reader.read() != -1) { return true; } } catch (IOException e) { throw new FileException("Processing of reader was failed.", e, fileName); } finally { try { reader.reset(); } catch (IOException e) { throw new FileException("Processing of reader#reset was failed.", e, fileName); } } return false; } /** * ?????<br> * <p> * ???????????<br> * ????????<br> * </p> * ?????InputFileColumn?? ?????<br> * ????????? ??????<br> * ???InputFileColumn????????????<br> * ??????????????<br> * <ul> * <li>?</li> * <li>?</li> * <li>??</li> * <li>?()?</li> * </ul> * @return * @throws FileException ?????? * @throws FileLineException ?????? */ @Override public T next() { if (readTrailer) { throw new FileLineException("Data part should be called before trailer part.", new IllegalStateException(), fileName, currentLineCount); } if (!hasNext()) { throw new FileLineException("The data which can be acquired doesn't exist.", new NoSuchElementException(), fileName, currentLineCount + 1); } T fileLineObject = null; // ?hasNext()??????null???? String currentString = readLine(); currentLineCount++; // ????? try { fileLineObject = clazz.newInstance(); } catch (InstantiationException e) { throw new FileException("Failed in an instantiate of a FileLineObject.", e, fileName); } catch (IllegalAccessException e) { throw new FileException("Failed in an instantiate of a FileLineObject.", e, fileName); } // CSV???????? // ???? String[] columns = separateColumns(currentString); // ???????? if (fields.length != columns.length) { throw new FileLineException("Column Count is different from " + "FileLineObject's column counts", new IllegalStateException(), fileName, currentLineCount); } int columnIndex = -1; String columnString = null; for (int i = 0; i < fields.length; i++) { // JavaBean??? columnIndex = columnIndexs[i]; // 1?? columnString = columns[columnIndex]; // ??? if (isCheckByte(columnBytes[i])) { try { if (columnString.getBytes(fileEncoding).length != columnBytes[i]) { throw new FileLineException("Data size is different from a set point " + "of a column.", new IllegalStateException(), fileName, currentLineCount, fields[i].getName(), columnIndex); } } catch (UnsupportedEncodingException e) { throw new FileException("fileEncoding which isn't supported was set.", e, fileName); } } // ? columnString = FileDAOUtility.trim(columnString, fileEncoding, trimChars[i], trimTypes[i]); // ? columnString = FileDAOUtility.padding(columnString, fileEncoding, columnBytes[i], paddingChars[i], paddingTypes[i]); // ??? columnString = stringConverters[i].convert(columnString); // ??? // JavaBean??????????? ColumnParser columnParser = columnParserMap.get(fields[i].getType().getName()); try { columnParser.parse(columnString, fileLineObject, methods[i], columnFormats[i]); } catch (IllegalArgumentException e) { throw new FileLineException("Failed in coluomn data parsing.", e, fileName, currentLineCount, fields[i].getName(), columnIndex); } catch (IllegalAccessException e) { throw new FileLineException("Failed in coluomn data parsing.", e, fileName, currentLineCount, fields[i].getName(), columnIndex); } catch (InvocationTargetException e) { throw new FileLineException("Failed in coluomn data parsing.", e, fileName, currentLineCount, fields[i].getName(), columnIndex); } catch (ParseException e) { throw new FileLineException("Failed in coluomn data parsing.", e, fileName, currentLineCount, fields[i].getName(), columnIndex); } } return fileLineObject; } /** * ????<br> * Iterator????<br> * FileQueryDAO??????????????? UnsupportedOperationException? * @throws UnsupportedOperationException ??????? */ @Override public void remove() { throw new UnsupportedOperationException("remove() isn't supported."); } /** * ???<br> * ???????? * <ul> * <li>?(Field)??</li> * <li>?(stringConverters)??</li> * <li>???(methods)??</li> * <li>????LineReader??</li> * <li>??</li> * <li>??</li> * </ul> * init()?AbstracFileLineIterator??? ???<br> * ??????2?????????? * @throws FileException ?????? * @throws FileLineException ???????? */ protected void init() { if (!calledInit) { // ???? buildFields(); if (isCheckEncloseChar()) { // ??????????? if (isEnclosed()) { throw new FileException("columnEncloseChar can not change.", new IllegalStateException(), fileName); } } if (isCheckColumnAnnotationCount()) { // ?????????? if (fields.length == 0) { throw new FileException("InputFileColumn is not found.", new IllegalStateException(), fileName); } } buildStringConverters(); buildMethods(); try { // ????LineReader?? buildLineReader(); // ????????? buildHeader(); buildTrailerQueue(); } catch (FileException e) { if (this.reader != null) { try { closeFile(); } catch (FileException fe) { // ??????????? // ??FileException????? } } throw e; } calledInit = true; } } /** * ????LineReader??<br> * ?Reader?????????LineReader???<br> * ?0,1,2???????? * @throws FileException LineReader?????? */ private void buildLineReader() { // ??Reader?? try { this.reader = new BufferedReader(new InputStreamReader((new FileInputStream(fileName)), fileEncoding)); if (!reader.markSupported()) { throw new FileException("BufferedReader of this JVM dose not support mark method"); } } catch (UnsupportedEncodingException e) { throw new FileException("Failed in generation of reader.", e, fileName); } catch (FileNotFoundException e) { throw new FileException("Failed in generation of reader.", e, fileName); } // ???????LineReader?? if (lineFeedChar.length() == 2) { // ?2 if (!enclosed) { // ?? lineReader = new LineFeed2LineReader(reader, lineFeedChar); } else { // ?? lineReader = new EncloseCharLineFeed2LineReader(getDelimiter(), getEncloseChar(), columnEncloseChar, reader, lineFeedChar); } } else if (lineFeedChar.length() == 1) { // ?1 if (!enclosed) { // ?? lineReader = new LineFeed1LineReader(reader, lineFeedChar); } else { // ?? lineReader = new EncloseCharLineFeed1LineReader(getDelimiter(), getEncloseChar(), columnEncloseChar, reader, lineFeedChar); } } else if (lineFeedChar.length() == 0) { // ?0 lineReader = new LineFeed0LineReader(reader, fileEncoding, totalBytes); } else { throw new FileException("lineFeedChar length must be 0 or 1 or 2. but: " + lineFeedChar.length(), new IllegalStateException(), fileName); } } /** * InputFileColumn????? ????<br> * ???????<br> * ????{@link InputFileColumn#columnIndex()}????? ?????<br> * ???{@link InputFileColumn#columnIndex()}???????? ???<br> * ???????? InputFileColumn?????????<br> * @throws FileException ????? */ private void buildFields() { // ? List<Field[]> allFields = new ArrayList<Field[]>(); // ? Class<?> tempClass = clazz; Field[] declaredFieldArray = null; int allFieldCount = 0; while (tempClass != null) { declaredFieldArray = tempClass.getDeclaredFields(); allFields.add(declaredFieldArray); allFieldCount += declaredFieldArray.length; tempClass = tempClass.getSuperclass(); } // ????? Field[] dataColumnFields = new Field[allFieldCount]; InputFileColumn inputFileColumn = null; int maxColumnIndex = -1; int columnIndex = -1; int columnCount = 0; for (Field[] fields : allFields) { for (Field field : fields) { inputFileColumn = field.getAnnotation(InputFileColumn.class); if (inputFileColumn != null) { // ???????? if (columnParserMap.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 = inputFileColumn.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); } // InputFileColumn???StringConverter inputFileColumns = new InputFileColumn[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++) { inputFileColumns[i] = fields[i].getAnnotation(InputFileColumn.class); columnIndexs[i] = inputFileColumns[i].columnIndex(); columnFormats[i] = inputFileColumns[i].columnFormat(); columnBytes[i] = inputFileColumns[i].bytes(); totalBytes += columnBytes[i]; paddingTypes[i] = inputFileColumns[i].paddingType(); paddingChars[i] = inputFileColumns[i].paddingChar(); trimTypes[i] = inputFileColumns[i].trimType(); trimChars[i] = inputFileColumns[i].trimChar(); // ?inputFileColumns????? if (inputFileColumns[i].columnEncloseChar() != Character.MIN_VALUE) { columnEncloseChar[i] = inputFileColumns[i].columnEncloseChar(); enclosed = true; } } } /** * ???????<br> * ??????? ??????<br> * ??????????? ??<br> * @throws FileLineException ??????? */ private void buildStringConverters() { // ???? StringConverter[] dataColumnStringConverters = new StringConverter[fields.length]; InputFileColumn inputFileColumn = null; Class<? extends StringConverter> converterKind = null; for (int i = 0; i < fields.length; i++) { // JavaBean???? inputFileColumn = inputFileColumns[i]; // inputFileColumn.stringConverter()???? try { // ???? converterKind = inputFileColumn.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(), inputFileColumn.columnIndex()); } catch (IllegalAccessException e) { throw new FileLineException("Failed in an instantiate of a stringConverter.", e, fileName, INITIAL_LINE_NO, fields[i].getName(), inputFileColumn.columnIndex()); } } this.stringConverters = dataColumnStringConverters; } /** * ??setter?????<br> * ??setter????<br> * <ul> * <li>??????????set?????</li> * </ul> * setter?????????? * @throws FileException setter???????? */ private void buildMethods() { Method[] dataColumnSetMethods = new Method[fields.length]; StringBuilder setterName = new StringBuilder(); String fieldName = null; for (int i = 0; i < fields.length; i++) { // JavaBean?????????? fieldName = fields[i].getName(); // ????setter?????? setterName.setLength(0); setterName.append("set"); setterName.append(StringUtils.upperCase(fieldName.substring(0, 1))); setterName.append(fieldName.substring(1, fieldName.length())); // setter??? // fields[i].getType()????? try { dataColumnSetMethods[i] = clazz.getMethod(setterName.toString(), new Class[] { fields[i].getType() }); } catch (NoSuchMethodException e) { throw new FileException("The setter method of column doesn't exist.", e, fileName); } } this.methods = dataColumnSetMethods; } /** * ???<br> * ??????????<br> * @throws FileException ?????? */ private void buildHeader() { if (0 < headerLineCount) { for (int i = 0; i < headerLineCount; i++) { if (!hasNext()) { throw new FileException("The data which can be acquired doesn't exist.", new NoSuchElementException(), fileName); } try { header.add(lineReader.readLine()); } catch (FileException e) { throw new FileException("Error occurred by reading processing of a File.", e, fileName); } } } } /** * ???<br> * ?????????? ?????????<br> * ????????????????<br> * ??????????<br> * ??????????<br> * @throws FileException ??????? */ private void buildTrailerQueue() { if (0 < trailerLineCount) { // ?? trailerQueue = new ArrayBlockingQueue<String>(trailerLineCount); // ??? for (int i = 0; i < trailerLineCount; i++) { if (!hasNext()) { throw new FileException("The data which can be acquired doesn't exist.", new NoSuchElementException(), fileName); } try { trailerQueue.add(lineReader.readLine()); } catch (FileException e) { throw new FileException("Error occurred by reading processing of a File.", e, fileName); } } } } /** * ??<br> * @throws FileException ????? */ @Override public void closeFile() { try { reader.close(); } catch (IOException e) { throw new FileException("Processing of reader was failed.", e, fileName); } } /** * ???<br> * ???????????????? ? * @return header ? */ @Override public List<String> getHeader() { return header; } /** * ???<br> * ?????????????<br> * ????????????<br> * <b>?</b><br> * ???????????<br> * @return ? * @throws FileException ?????? */ @Override public List<String> getTrailer() { // ??????? if (!readTrailer) { String currentData = null; // ??????? while (hasNext()) { try { currentData = lineReader.readLine(); } catch (FileException e) { throw new FileException("Processing of lineReader was failed.", e, fileName); } if (0 < trailerLineCount) { trailerQueue.poll(); trailerQueue.add(currentData); } } // ???? if (0 < trailerLineCount) { int trailerQueueLength = trailerQueue.size(); for (int i = 0; i < trailerQueueLength; i++) { trailer.add(trailerQueue.poll()); } } readTrailer = true; } return trailer; } /** * ??1?????????<br> * ?????????? ?<br> * ???LineReader?1??????<br> * ??????LineReader????1??? ?<br> * ???1??????null? * @return ?? * @throws FileException ?????? */ protected String readLine() { // ??????null? if (!hasNext()) { return null; } // ?1??? String currentReadLineString = null; try { currentReadLineString = lineReader.readLine(); } catch (FileException e) { throw new FileException("Processing of lineReader was failed.", e, fileName); } // ??????????? // ???1??? if (0 < trailerLineCount) { String pollingLineString = trailerQueue.poll(); trailerQueue.add(currentReadLineString); return pollingLineString; } return currentReadLineString; } /** * ??????<br> * @param skipLines ??? */ @Override public void skip(int skipLines) { for (int i = 0; i < skipLines; i++) { if (!hasNext()) { throw new FileLineException("The data which can be acquired doesn't exist.", new NoSuchElementException(), fileName, currentLineCount + 1); } readLine(); currentLineCount++; } } /** * ?? * @return */ protected abstract char getDelimiter(); /** * ??? * @return ? */ protected abstract char getEncloseChar(); /** * ???? ???<br> * <code>fileLineString</code>?<code>null</code>???? ?????????<code>String</code>????<br> * ??????? * @param fileLineString ? * @return ???? */ protected abstract String[] separateColumns(String fileLineString); /** * ??????? * @param inputFileColumn ?InputFileColumn * @return ?????(1?)??true */ protected boolean isCheckByte(InputFileColumn inputFileColumn) { if (0 < inputFileColumn.bytes()) { return true; } return false; } /** * ??????? * @param columnByte ?? * @return ?????(1?)??true */ protected boolean isCheckByte(int columnByte) { if (0 < columnByte) { return true; } return false; } /** * ?? * @return */ protected String getLineFeedChar() { return lineFeedChar; } /** * ? * @param */ protected void setLineFeedChar(String lineFeedChar) { this.lineFeedChar = lineFeedChar; } /** * ?? * @return */ protected String getFileEncoding() { return fileEncoding; } /** * ?? * @return */ protected int getHeaderLineCount() { return headerLineCount; } /** * ?? * @return */ protected int getTrailerLineCount() { return trailerLineCount; } /** * ??????? * @return ???? */ public int getCurrentLineCount() { return currentLineCount; } /** * ?FieldAnnotation???? * @return ?FieldAnnotation?? */ protected Field[] getFields() { return fields; } /** * ???? * @return fileName ?? */ protected String getFileName() { return fileName; } /** * ???? * @return columnEncloseChar ? */ protected char[] getColumnEncloseChar() { return columnEncloseChar; } /** * ??????? * @return enclosed ? */ protected boolean isEnclosed() { return enclosed; } /** * ????? * @return columnBytes ??? */ protected int[] getColumnBytes() { return columnBytes; } /** * 1???? * @return totalBytes 1?? */ protected int getTotalBytes() { return totalBytes; } /** * ?????????????? * @return ????true */ protected boolean isCheckEncloseChar() { return false; } /** * ???????????? * @return ????true */ protected boolean isCheckColumnAnnotationCount() { return true; } }