001    /*
002     * CDDL HEADER START
003     *
004     * The contents of this file are subject to the terms of the
005     * Common Development and Distribution License, Version 1.0 only
006     * (the "License").  You may not use this file except in compliance
007     * with the License.
008     *
009     * You can obtain a copy of the license at
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.util.table;
028    
029    
030    
031    import java.io.BufferedWriter;
032    import java.io.OutputStream;
033    import java.io.OutputStreamWriter;
034    import java.io.PrintWriter;
035    import java.io.Writer;
036    
037    
038    
039    /**
040     * An interface for creating a CSV formatted table.
041     */
042    public final class CSVTablePrinter extends TablePrinter {
043    
044      /**
045       * Table serializer implementation.
046       */
047      private final class Serializer extends TableSerializer {
048    
049        // The current column being output.
050        private int column = 0;
051    
052        // Counts the number of separators that should be output the next
053        // time a non-empty cell is displayed. The comma separators are
054        // not displayed immediately so that we can avoid displaying
055        // unnecessary trailing separators.
056        private int requiredSeparators = 0;
057    
058    
059    
060        // Private constructor.
061        private Serializer() {
062          // No implementation required.
063        }
064    
065    
066    
067        /**
068         * {@inheritDoc}
069         */
070        @Override
071        public void addCell(String s) {
072          // Avoid printing comma separators for trailing empty cells.
073          if (s.length() == 0) {
074            requiredSeparators++;
075          } else {
076            for (int i = 0; i < requiredSeparators; i++) {
077              writer.print(',');
078            }
079            requiredSeparators = 1;
080          }
081    
082          boolean needsQuoting = false;
083    
084          if (s.contains(",")) {
085            needsQuoting = true;
086          }
087    
088          if (s.contains("\n")) {
089            needsQuoting = true;
090          }
091    
092          if (s.contains("\r")) {
093            needsQuoting = true;
094          }
095    
096          if (s.contains("\"")) {
097            needsQuoting = true;
098            s = s.replace("\"", "\"\"");
099          }
100    
101          if (s.startsWith(" ")) {
102            needsQuoting = true;
103          }
104    
105          if (s.endsWith(" ")) {
106            needsQuoting = true;
107          }
108    
109          StringBuilder builder = new StringBuilder();
110          if (needsQuoting) {
111            builder.append("\"");
112          }
113    
114          builder.append(s);
115    
116          if (needsQuoting) {
117            builder.append("\"");
118          }
119    
120          writer.print(builder.toString());
121          column++;
122        }
123    
124    
125    
126        /**
127         * {@inheritDoc}
128         */
129        @Override
130        public void addHeading(String s) {
131          if (displayHeadings) {
132            addCell(s);
133          }
134        }
135    
136    
137    
138        /**
139         * {@inheritDoc}
140         */
141        @Override
142        public void endHeader() {
143          if (displayHeadings) {
144            writer.println();
145          }
146        }
147    
148    
149    
150        /**
151         * {@inheritDoc}
152         */
153        @Override
154        public void endRow() {
155          writer.println();
156        }
157    
158    
159    
160        /**
161         * {@inheritDoc}
162         */
163        @Override
164        public void endTable() {
165          writer.flush();
166        }
167    
168    
169    
170        /**
171         * {@inheritDoc}
172         */
173        @Override
174        public void startHeader() {
175          column = 0;
176          requiredSeparators = 0;
177        }
178    
179    
180    
181        /**
182         * {@inheritDoc}
183         */
184        @Override
185        public void startRow() {
186          column = 0;
187          requiredSeparators = 0;
188        }
189      }
190    
191      // Indicates whether or not the headings should be output.
192      private boolean displayHeadings = false;
193    
194      // The output destination.
195      private PrintWriter writer = null;
196    
197    
198    
199      /**
200       * Creates a new CSV table printer for the specified output stream.
201       * Headings will not be displayed by default.
202       *
203       * @param stream
204       *          The stream to output tables to.
205       */
206      public CSVTablePrinter(OutputStream stream) {
207        this(new BufferedWriter(new OutputStreamWriter(stream)));
208      }
209    
210    
211    
212      /**
213       * Creates a new CSV table printer for the specified writer.
214       * Headings will not be displayed by default.
215       *
216       * @param writer
217       *          The writer to output tables to.
218       */
219      public CSVTablePrinter(Writer writer) {
220        this.writer = new PrintWriter(writer);
221      }
222    
223    
224    
225      /**
226       * Specify whether or not table headings should be displayed.
227       *
228       * @param displayHeadings
229       *          <code>true</code> if table headings should be
230       *          displayed.
231       */
232      public void setDisplayHeadings(boolean displayHeadings) {
233        this.displayHeadings = displayHeadings;
234      }
235    
236    
237    
238      /**
239       * {@inheritDoc}
240       */
241      @Override
242      protected TableSerializer getSerializer() {
243        return new Serializer();
244      }
245    
246    }