View Javadoc
1   /*
2    *
3    * The DbUnit Database Testing Framework
4    * Copyright (C)2002-2006, DbUnit.org
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 2.1 of the License, or (at your option) any later version.
10   *
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, write to the Free Software
18   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   *
20   */
21  package org.dbunit.dataset.xml;
22  
23  import java.io.IOException;
24  import java.io.OutputStream;
25  import java.io.UnsupportedEncodingException;
26  import java.io.Writer;
27  
28  import org.dbunit.dataset.Column;
29  import org.dbunit.dataset.DataSetException;
30  import org.dbunit.dataset.IDataSet;
31  import org.dbunit.dataset.ITable;
32  import org.dbunit.dataset.ITableMetaData;
33  import org.dbunit.dataset.datatype.DataType;
34  import org.dbunit.dataset.datatype.TypeCastException;
35  import org.dbunit.dataset.stream.DataSetProducerAdapter;
36  import org.dbunit.dataset.stream.IDataSetConsumer;
37  import org.dbunit.util.xml.XmlWriter;
38  import org.slf4j.Logger;
39  import org.slf4j.LoggerFactory;
40  
41  /**
42   * @author Manuel Laflamme
43   * @author Last changed by: $Author$
44   * @version $Revision$ $Date$
45   * @since 1.5.5 (Jun 13, 2003)
46   */
47  public class XmlDataSetWriter implements IDataSetConsumer
48  {
49  
50      /**
51       * Logger for this class
52       */
53      private static final Logger logger = LoggerFactory.getLogger(XmlDataSetWriter.class);
54  
55      private static final String DATASET = "dataset";
56      private static final String TABLE = "table";
57      private static final String NAME = "name";
58      private static final String COLUMN = "column";
59      private static final String ROW = "row";
60      private static final String VALUE = "value";
61      private static final String NULL = "null";
62      private static final String NONE = "none";
63  
64      static char[] CDATA_DETECTION_CHARS = new char[] {
65          0x20, '\n', '\r', '\t',     // whitespace
66          '&', '<',                   // forbidden char
67      };
68  
69      private XmlWriter _xmlWriter;
70      private ITableMetaData _activeMetaData;
71      private boolean includeColumnComments = false;
72  
73  
74      /**
75       * @param outputStream The stream to which the XML will be written.
76       * @param encoding The encoding to be used for the {@link XmlWriter}.
77       * Can be null. See {@link XmlWriter#XmlWriter(OutputStream, String)}.
78       * @throws UnsupportedEncodingException
79       */
80      public XmlDataSetWriter(OutputStream outputStream, String encoding) 
81      throws UnsupportedEncodingException
82      {
83          _xmlWriter = new XmlWriter(outputStream, encoding);
84          _xmlWriter.enablePrettyPrint(true);
85      }
86  
87      public XmlDataSetWriter(Writer writer)
88      {
89          _xmlWriter = new XmlWriter(writer);
90          _xmlWriter.enablePrettyPrint(true);
91      }
92  
93      public XmlDataSetWriter(Writer writer, String encoding)
94      {
95          _xmlWriter = new XmlWriter(writer, encoding);
96          _xmlWriter.enablePrettyPrint(true);
97      }
98  
99      /**
100      * Enable or disable pretty print of the XML.
101      * @param enabled <code>true</code> to enable pretty print (which is the default). 
102      * <code>false</code> otherwise.
103      * @since 2.4
104      */
105     public void setPrettyPrint(boolean enabled)
106     {
107         _xmlWriter.enablePrettyPrint(enabled);
108     }
109 
110     /**
111      * Whether or not to write the column name as comment into the XML
112      * @param includeColumnComments Whether or not to write the column name as comment into the XML
113      */
114     public void setIncludeColumnComments(boolean includeColumnComments)
115     {
116       this.includeColumnComments = includeColumnComments;
117     }
118 
119     /**
120      * Writes the given {@link IDataSet} using this writer.
121      * @param dataSet The {@link IDataSet} to be written
122      * @throws DataSetException
123      */
124     public void write(IDataSet dataSet) throws DataSetException
125     {
126         logger.trace("write(dataSet{}) - start", dataSet);
127 
128         DataSetProducerAdapter provider = new DataSetProducerAdapter(dataSet);
129         provider.setConsumer(this);
130         provider.produce();
131     }
132 
133     boolean needsCData(String text)
134     {
135         logger.trace("needsCData(text={}) - start", text);
136 
137         if (text == null)
138         {
139             return false;
140         }
141 
142         for (int i = 0; i < text.length(); i++)
143         {
144             char c = text.charAt(i);
145             for (int j = 0; j < CDATA_DETECTION_CHARS.length; j++)
146             {
147                 if (CDATA_DETECTION_CHARS[j] == c)
148                 {
149                     return true;
150                 }
151             }
152         }
153         return false;
154     }
155 
156     ////////////////////////////////////////////////////////////////////////////
157     // IDataSetConsumer interface
158 
159     public void startDataSet() throws DataSetException
160     {
161         logger.trace("startDataSet() - start");
162 
163         try
164         {
165             _xmlWriter.writeDeclaration();
166             _xmlWriter.writeElement(DATASET);
167         }
168         catch (IOException e)
169         {
170             throw new DataSetException(e);
171         }
172     }
173 
174     public void endDataSet() throws DataSetException
175     {
176         logger.trace("endDataSet() - start");
177 
178         try
179         {
180             _xmlWriter.endElement();
181             _xmlWriter.close();
182         }
183         catch (IOException e)
184         {
185             throw new DataSetException(e);
186         }
187     }
188 
189     public void startTable(ITableMetaData metaData) throws DataSetException
190     {
191         logger.trace("startTable(metaData={}) - start", metaData);
192 
193         try
194         {
195             _activeMetaData = metaData;
196 
197             String tableName = _activeMetaData.getTableName();
198             _xmlWriter.writeElement(TABLE);
199             _xmlWriter.writeAttribute(NAME, tableName);
200 
201             Column[] columns = _activeMetaData.getColumns();
202             for (int i = 0; i < columns.length; i++)
203             {
204                 String columnName = columns[i].getColumnName();
205                 _xmlWriter.writeElementWithText(COLUMN, columnName);
206             }
207         }
208         catch (IOException e)
209         {
210             throw new DataSetException(e);
211         }
212 
213     }
214 
215     public void endTable() throws DataSetException
216     {
217         logger.trace("endTable() - start");
218 
219         try
220         {
221             _xmlWriter.endElement();
222             _activeMetaData = null;
223         }
224         catch (IOException e)
225         {
226             throw new DataSetException(e);
227         }
228     }
229 
230     public void row(Object[] values) throws DataSetException
231     {
232         logger.trace("row(values={}) - start", values);
233 
234         try
235         {
236             _xmlWriter.writeElement(ROW);
237 
238             Column[] columns = _activeMetaData.getColumns();
239             for (int i = 0; i < columns.length; i++)
240             {
241                 String columnName = columns[i].getColumnName();
242                 Object value = values[i];
243                 
244                 // null
245                 if (value == null)
246                 {
247                     _xmlWriter.writeEmptyElement(NULL);
248                 }
249                 // none
250                 else if (value == ITable.NO_VALUE)
251                 {
252                     _xmlWriter.writeEmptyElement(NONE);
253                 }
254                 // values
255                 else
256                 {
257                     try
258                     {
259                         String stringValue = DataType.asString(value);
260 
261                         _xmlWriter.writeElement(VALUE);
262                         if (needsCData(stringValue))
263                         {
264                             writeValueCData(stringValue);
265                         }
266                         else if (stringValue.length() > 0)
267                         {
268                             writeValue(stringValue);
269                         }
270                         _xmlWriter.endElement();
271                     }
272                     catch (TypeCastException e)
273                     {
274                         throw new DataSetException("table=" +
275                                 _activeMetaData.getTableName() + ", row=" + i +
276                                 ", column=" + columnName +
277                                 ", value=" + value, e);
278                     }
279                 }
280                 if ( this.includeColumnComments ) {
281                   _xmlWriter.writeComment( columnName );
282                 }
283             }
284             _xmlWriter.endElement();
285         }
286         catch (IOException e)
287         {
288             throw new DataSetException(e);
289         }
290     }
291 
292     /**
293      * Writes the given String as CDATA using the {@link XmlWriter}.
294      * Can be overridden to add custom behavior.
295      * This implementation just invokes {@link XmlWriter#writeCData(String)}
296      * @param stringValue The value to be written
297      * @throws IOException
298      * @since 2.4.4
299      */
300     protected void writeValueCData(String stringValue) throws IOException
301     {
302         logger.trace("writeValueCData(stringValue={}) - start", stringValue);
303         _xmlWriter.writeCData(stringValue);
304     }
305     
306     /**
307      * Writes the given String as normal text using the {@link XmlWriter}.
308      * Can be overridden to add custom behavior.
309      * This implementation just invokes {@link XmlWriter#writeText(String)}.
310      * @param stringValue The value to be written
311      * @throws IOException
312      * @since 2.4.4
313      */
314     protected void writeValue(String stringValue) throws IOException
315     {
316         logger.trace("writeValue(stringValue={}) - start", stringValue);
317         _xmlWriter.writeText(stringValue);
318     }
319 
320     /**
321      * @return The {@link XmlWriter} that is used for writing out XML.
322      * @since 2.4.4
323      */
324     protected final XmlWriter getXmlWriter()
325     {
326         return _xmlWriter;
327     }
328 }