001 /**
002 * ========================================
003 * JFreeReport : a free Java report library
004 * ========================================
005 *
006 * Project Info: http://reporting.pentaho.org/
007 *
008 * (C) Copyright 2000-2007, by Object Refinery Limited, Pentaho Corporation and Contributors.
009 *
010 * This library is free software; you can redistribute it and/or modify it under the terms
011 * of the GNU Lesser General Public License as published by the Free Software Foundation;
012 * either version 2.1 of the License, or (at your option) any later version.
013 *
014 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016 * See the GNU Lesser General Public License for more details.
017 *
018 * You should have received a copy of the GNU Lesser General Public License along with this
019 * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020 * Boston, MA 02111-1307, USA.
021 *
022 * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023 * in the United States and other countries.]
024 *
025 * ------------
026 * $Id: CSVQuoter.java,v 1.5 2007/04/01 18:49:33 taqua Exp $
027 * ------------
028 * (C) Copyright 2000-2005, by Object Refinery Limited.
029 * (C) Copyright 2005-2007, by Pentaho Corporation.
030 */
031
032 package org.jfree.report.util;
033
034 /**
035 * The <code>CSVQuoter</code> is a helper class to encode a string for the CSV file
036 * format.
037 *
038 * @author Thomas Morgner.
039 */
040 public class CSVQuoter
041 {
042 /**
043 * The separator used in the CSV file.
044 */
045 private char separator;
046 /**
047 * The quoting character or a single quote.
048 */
049 private char quate;
050 /**
051 * The double quote. This is a string containing the quate two times.
052 */
053 private String doubleQuate;
054
055 /**
056 * Creates a new CSVQuoter, which uses a comma as the default separator.
057 */
058 public CSVQuoter ()
059 {
060 this(',', '"');
061 }
062
063 /**
064 * Creates a new <code>CSVQuoter</code>, which uses the defined separator.
065 *
066 * @param separator the separator.
067 * @throws NullPointerException if the given separator is <code>null</code>.
068 */
069 public CSVQuoter (final char separator)
070 {
071 this(separator, '"');
072 }
073
074 /**
075 * Creates a new CSVQuoter with the given separator and quoting character.
076 *
077 * @param separator the separator
078 * @param quate the quoting character
079 */
080 public CSVQuoter (final char separator, final char quate)
081 {
082 this.separator = separator;
083 this.quate = quate;
084 this.doubleQuate = "" + quate + quate;
085 }
086
087 /**
088 * Encodes the string, so that the string can safely be used in CSV files. If the string
089 * does not need quoting, the original string is returned unchanged.
090 *
091 * @param original the unquoted string.
092 * @return The quoted string
093 */
094 public String doQuoting (final String original)
095 {
096 if (isQuotingNeeded(original))
097 {
098 final StringBuffer retval = new StringBuffer();
099 retval.append(quate);
100 applyQuote(retval, original);
101 retval.append(quate);
102 return retval.toString();
103 }
104 else
105 {
106 return original;
107 }
108 }
109
110 /**
111 * Decodes the string, so that all escape sequences get removed. If the string was not
112 * quoted, then the string is returned unchanged.
113 *
114 * @param nativeString the quoted string.
115 * @return The unquoted string.
116 */
117 public String undoQuoting (final String nativeString)
118 {
119 if (isQuotingNeeded(nativeString))
120 {
121 final StringBuffer b = new StringBuffer(nativeString.length());
122 final int length = nativeString.length() - 1;
123 int start = 1;
124
125 int pos = start;
126 while (pos != -1)
127 {
128 pos = nativeString.indexOf(doubleQuate, start);
129 if (pos == -1)
130 {
131 b.append(nativeString.substring(start, length));
132 }
133 else
134 {
135 b.append(nativeString.substring(start, pos));
136 start = pos + 1;
137 }
138 }
139 return b.toString();
140 }
141 else
142 {
143 return nativeString;
144 }
145 }
146
147 /**
148 * Tests, whether this string needs to be quoted. A string is encoded if the string
149 * contains a newline character, a quote character or the defined separator.
150 *
151 * @param str the string that should be tested.
152 * @return true, if quoting needs to be applied, false otherwise.
153 */
154 private boolean isQuotingNeeded (final String str)
155 {
156 if (str.indexOf(separator) != -1)
157 {
158 return true;
159 }
160 if (str.indexOf('\n') != -1)
161 {
162 return true;
163 }
164 if (str.indexOf(quate, 1) != -1)
165 {
166 return true;
167 }
168 return false;
169 }
170
171 /**
172 * Applies the quoting to a given string, and stores the result in the StringBuffer
173 * <code>b</code>.
174 *
175 * @param b the result buffer
176 * @param original the string, that should be quoted.
177 */
178 private void applyQuote (final StringBuffer b, final String original)
179 {
180 // This solution needs improvements. Copy blocks instead of single
181 // characters.
182 final int length = original.length();
183
184 for (int i = 0; i < length; i++)
185 {
186 final char c = original.charAt(i);
187 if (c == quate)
188 {
189 b.append(doubleQuate);
190 }
191 else
192 {
193 b.append(c);
194 }
195 }
196 }
197
198 /**
199 * Gets the separator used in this quoter and the CSV file.
200 *
201 * @return the separator (never <code>null</code>).
202 */
203 public char getSeparator ()
204 {
205 return separator;
206 }
207
208 /**
209 * Returns the quoting character.
210 *
211 * @return the quote character.
212 */
213 public char getQuate ()
214 {
215 return quate;
216 }
217 }