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 2725 2007-04-01 18:49:29Z taqua $
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    }