View Javadoc
1   /*
2    * (c) Copyright 2006-2020 by rapiddweller GmbH & Volker Bergmann. All rights reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, is permitted under the terms of the
6    * GNU General Public License.
7    *
8    * For redistributing this software or a derivative work under a license other
9    * than the GPL-compatible Free Software License as defined by the Free
10   * Software Foundation or approved by OSI, you must first obtain a commercial
11   * license to this software product from rapiddweller GmbH & Volker Bergmann.
12   *
13   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14   * WITHOUT A WARRANTY OF ANY KIND. ALL EXPRESS OR IMPLIED CONDITIONS,
15   * REPRESENTATIONS AND WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF
16   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE
17   * HEREBY EXCLUDED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24   * POSSIBILITY OF SUCH DAMAGE.
25   */
26  
27  package com.rapiddweller.platform.csv;
28  
29  import com.rapiddweller.benerator.consumer.TextFileExporter;
30  import com.rapiddweller.benerator.engine.DefaultBeneratorContext;
31  import com.rapiddweller.common.ArrayFormat;
32  import com.rapiddweller.common.ArrayUtil;
33  import com.rapiddweller.common.BeanUtil;
34  import com.rapiddweller.common.CollectionUtil;
35  import com.rapiddweller.common.StringUtil;
36  import com.rapiddweller.model.data.ComplexTypeDescriptor;
37  import com.rapiddweller.model.data.ComponentDescriptor;
38  import com.rapiddweller.model.data.Entity;
39  import org.apache.logging.log4j.LogManager;
40  import org.apache.logging.log4j.Logger;
41  
42  import java.util.Collection;
43  import java.util.List;
44  
45  /**
46   * Exports Entities to a CSV file.
47   * The default line separator is CR LF according to RFC 4180.
48   * It can be set explicitly by <code>setLineSeparator()</code>.<br/>
49   * <br/>
50   * Created: 21.08.2007 21:16:59
51   *
52   * @author Volker Bergmann
53   */
54  public class CSVEntityExporter extends TextFileExporter {
55  
56    private static final Logger logger =
57        LogManager.getLogger(CSVEntityExporter.class);
58  
59    // defaults --------------------------------------------------------------------------------------------------------
60  
61    private static final String DEFAULT_LINE_SEPARATOR = "\r\n";
62    // as defined by RFC 4180
63    private static final String DEFAULT_URI = "export.csv";
64  
65    // configuration attributes ----------------------------------------------------------------------------------------
66  
67    private String[] columns;
68    private boolean headless;
69    private boolean endWithNewLine;
70    private char separator;
71    private boolean quoteEmpty;
72  
73    // state attributes ------------------------------------------------------------------------------------------------
74  
75    private boolean lfRequired;
76  
77    // constructors ----------------------------------------------------------------------------------------------------
78  
79    /**
80     * Instantiates a new Csv entity exporter.
81     */
82    public CSVEntityExporter() {
83      this(DEFAULT_URI);
84    }
85  
86    /**
87     * Instantiates a new Csv entity exporter.
88     *
89     * @param uri the uri
90     */
91    public CSVEntityExporter(String uri) {
92      this(uri, (String) null);
93    }
94  
95    /**
96     * Instantiates a new Csv entity exporter.
97     *
98     * @param uri         the uri
99     * @param columnsSpec the columns spec
100    */
101   public CSVEntityExporter(String uri, String columnsSpec) {
102     this(uri, columnsSpec,
103         DefaultBeneratorContext.getDefaultCellSeparator(), null,
104         DEFAULT_LINE_SEPARATOR);
105   }
106 
107   /**
108    * Instantiates a new Csv entity exporter.
109    *
110    * @param uri           the uri
111    * @param columnsSpec   the columns spec
112    * @param separator     the separator
113    * @param encoding      the encoding
114    * @param lineSeparator the line separator
115    */
116   public CSVEntityExporter(String uri, String columnsSpec, char separator,
117                            String encoding, String lineSeparator) {
118     super(uri, encoding, lineSeparator);
119     if (columnsSpec != null) {
120       setColumns(ArrayFormat.parse(columnsSpec, ",", String.class));
121     }
122     this.separator = separator;
123     this.quoteEmpty = true;
124   }
125 
126   /**
127    * Instantiates a new Csv entity exporter.
128    *
129    * @param descriptor the descriptor
130    */
131   public CSVEntityExporter(ComplexTypeDescriptor descriptor) {
132     this(descriptor.getName() + ".csv", descriptor);
133   }
134 
135   /**
136    * Instantiates a new Csv entity exporter.
137    *
138    * @param uri        the uri
139    * @param descriptor the descriptor
140    */
141   public CSVEntityExporter(String uri, ComplexTypeDescriptor descriptor) {
142     this(uri, descriptor, DefaultBeneratorContext.getDefaultCellSeparator(),
143         null, DEFAULT_LINE_SEPARATOR);
144   }
145 
146   /**
147    * Instantiates a new Csv entity exporter.
148    *
149    * @param uri           the uri
150    * @param descriptor    the descriptor
151    * @param separator     the separator
152    * @param encoding      the encoding
153    * @param lineSeparator the line separator
154    */
155   public CSVEntityExporter(String uri, ComplexTypeDescriptor descriptor,
156                            char separator, String encoding,
157                            String lineSeparator) {
158     super(uri, encoding, lineSeparator);
159     Collection<ComponentDescriptor> componentDescriptors =
160         descriptor.getComponents();
161     List<String> componentNames =
162         BeanUtil.extractProperties(componentDescriptors, "name");
163     this.columns = CollectionUtil.toArray(componentNames, String.class);
164     this.endWithNewLine = false;
165     this.separator = separator;
166   }
167 
168 
169   // properties ------------------------------------------------------------------------------------------------------
170 
171   /**
172    * Sets columns.
173    *
174    * @param columns the columns
175    */
176   public void setColumns(String[] columns) {
177     if (ArrayUtil.isEmpty(columns)) {
178       this.columns = null;
179     } else {
180       this.columns = columns.clone();
181       StringUtil.trimAll(this.columns);
182     }
183   }
184 
185   /**
186    * Sets separator.
187    *
188    * @param separator the separator
189    */
190   public void setSeparator(char separator) {
191     this.separator = separator;
192   }
193 
194   /**
195    * Is headless boolean.
196    *
197    * @return the boolean
198    */
199   public boolean isHeadless() {
200     return headless;
201   }
202 
203   /**
204    * Sets headless.
205    *
206    * @param headless the headless
207    */
208   public void setHeadless(boolean headless) {
209     this.headless = headless;
210   }
211 
212   /**
213    * Is end with new line boolean.
214    *
215    * @return the boolean
216    */
217   public boolean isEndWithNewLine() {
218     return endWithNewLine;
219   }
220 
221   /**
222    * Sets end with new line.
223    *
224    * @param endWithNewLine the end with new line
225    */
226   public void setEndWithNewLine(boolean endWithNewLine) {
227     this.endWithNewLine = endWithNewLine;
228   }
229 
230   /**
231    * Is quote empty boolean.
232    *
233    * @return the boolean
234    */
235   public boolean isQuoteEmpty() {
236     return quoteEmpty;
237   }
238 
239   /**
240    * Sets quote empty.
241    *
242    * @param quoteEmpty the quote empty
243    */
244   public void setQuoteEmpty(boolean quoteEmpty) {
245     this.quoteEmpty = quoteEmpty;
246   }
247 
248   // Callback methods for parent class functionality -----------------------------------------------------------------
249 
250   @Override
251   protected void startConsumingImpl(Object object) {
252     logger.debug("exporting {}", object);
253     if (!(object instanceof Entity)) {
254       throw new IllegalArgumentException("Expecting entity");
255     }
256     Entity../../../../com/rapiddweller/model/data/Entity.html#Entity">Entity entity = (Entity) object;
257     if (lfRequired) {
258       println();
259     } else {
260       lfRequired = true;
261     }
262     for (int i = 0; i < columns.length; i++) {
263       if (i > 0) {
264         printer.print(separator);
265       }
266       Object value = entity.getComponent(columns[i]);
267       String out;
268       if (value == null) {
269         out = getNullString();
270       } else {
271         out = plainConverter.convert(value);
272         if (out.length() == 0 && quoteEmpty) {
273           out = "\"\"";
274         } else if (out.indexOf(separator) >= 0) {
275           out = '"' + out + '"';
276         }
277       }
278       printer.print(out);
279     }
280   }
281 
282   @Override
283   protected void postInitPrinter(Object object) {
284     Entity../../../../com/rapiddweller/model/data/Entity.html#Entity">Entity entity = (Entity) object;
285     // determine columns from entity, if they have not been predefined
286     if (columns == null && entity != null) {
287       columns = CollectionUtil.toArray(entity.getComponents().keySet());
288     }
289     printHeaderRow();
290   }
291 
292   @Override
293   protected void preClosePrinter() {
294     if (endWithNewLine) {
295       println();
296     }
297   }
298 
299 
300   // private helpers -------------------------------------------------------------------------------------------------
301 
302   private void printHeaderRow() {
303     if (!wasAppended && !headless && columns != null) {
304       if (wasAppended && !endWithNewLine) {
305         println();
306       }
307       for (int i = 0; i < columns.length; i++) {
308         if (i > 0) {
309           printer.print(separator);
310         }
311         printer.print(columns[i]);
312       }
313       lfRequired = true;
314     } else {
315       lfRequired = (wasAppended && !endWithNewLine);
316     }
317   }
318 
319 
320 }