001/* 002 * Copyright 2006-2007 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.batch.item.file.separator; 018 019import org.springframework.util.StringUtils; 020 021/** 022 * A {@link RecordSeparatorPolicy} that treats all lines as record endings, as 023 * long as they do not have unterminated quotes, and do not end in a 024 * continuation marker. 025 * 026 * @author Dave Syer 027 * 028 */ 029public class DefaultRecordSeparatorPolicy extends SimpleRecordSeparatorPolicy { 030 031 private static final String QUOTE = "\""; 032 033 private static final String CONTINUATION = "\\"; 034 035 private String quoteCharacter = QUOTE; 036 037 private String continuation = CONTINUATION; 038 039 /** 040 * Default constructor. 041 */ 042 public DefaultRecordSeparatorPolicy() { 043 this(QUOTE, CONTINUATION); 044 } 045 046 /** 047 * Convenient constructor with quote character as parameter. 048 * 049 * @param quoteCharacter value used to indicate a quoted string 050 */ 051 public DefaultRecordSeparatorPolicy(String quoteCharacter) { 052 this(quoteCharacter, CONTINUATION); 053 } 054 055 /** 056 * Convenient constructor with quote character and continuation marker as 057 * parameters. 058 * 059 * @param quoteCharacter value used to indicate a quoted string 060 * @param continuation value used to indicate a line continuation 061 */ 062 public DefaultRecordSeparatorPolicy(String quoteCharacter, String continuation) { 063 super(); 064 this.continuation = continuation; 065 this.quoteCharacter = quoteCharacter; 066 } 067 068 /** 069 * Public setter for the quoteCharacter. Defaults to double quote mark. 070 * 071 * @param quoteCharacter the quoteCharacter to set 072 */ 073 public void setQuoteCharacter(String quoteCharacter) { 074 this.quoteCharacter = quoteCharacter; 075 } 076 077 /** 078 * Public setter for the continuation. Defaults to back slash. 079 * 080 * @param continuation the continuation to set 081 */ 082 public void setContinuation(String continuation) { 083 this.continuation = continuation; 084 } 085 086 /** 087 * Return true if the line does not have unterminated quotes (delimited by 088 * "), and does not end with a continuation marker ('\'). The test for the 089 * continuation marker ignores whitespace at the end of the line. 090 * 091 * @see org.springframework.batch.item.file.separator.RecordSeparatorPolicy#isEndOfRecord(java.lang.String) 092 */ 093 @Override 094 public boolean isEndOfRecord(String line) { 095 return !isQuoteUnterminated(line) && !isContinued(line); 096 } 097 098 /** 099 * If we are in an unterminated quote, add a line separator. Otherwise 100 * remove the continuation marker (plus whitespace at the end) if it is 101 * there. 102 * 103 * @see org.springframework.batch.item.file.separator.SimpleRecordSeparatorPolicy#preProcess(java.lang.String) 104 */ 105 @Override 106 public String preProcess(String line) { 107 if (isQuoteUnterminated(line)) { 108 return line + "\n"; 109 } 110 if (isContinued(line)) { 111 return line.substring(0, line.lastIndexOf(continuation)); 112 } 113 return line; 114 } 115 116 /** 117 * Determine if the current line (or buffered concatenation of lines) 118 * contains an unterminated quote, indicating that the record is continuing 119 * onto the next line. 120 * 121 * @param line 122 * @return 123 */ 124 private boolean isQuoteUnterminated(String line) { 125 return StringUtils.countOccurrencesOf(line, quoteCharacter) % 2 != 0; 126 } 127 128 /** 129 * Determine if the current line (or buffered concatenation of lines) ends 130 * with the continuation marker, indicating that the record is continuing 131 * onto the next line. 132 * 133 * @param line 134 * @return 135 */ 136 private boolean isContinued(String line) { 137 if (line == null) { 138 return false; 139 } 140 return line.trim().endsWith(continuation); 141 } 142}