1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.dbunit.dataset.xml;
22
23 import java.io.IOException;
24 import java.io.StringReader;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.LinkedList;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.StringTokenizer;
31
32 import javax.xml.parsers.ParserConfigurationException;
33 import javax.xml.parsers.SAXParser;
34 import javax.xml.parsers.SAXParserFactory;
35
36 import org.dbunit.dataset.Column;
37 import org.dbunit.dataset.DataSetException;
38 import org.dbunit.dataset.DefaultTableMetaData;
39 import org.dbunit.dataset.datatype.DataType;
40 import org.dbunit.dataset.stream.DefaultConsumer;
41 import org.dbunit.dataset.stream.IDataSetConsumer;
42 import org.dbunit.dataset.stream.IDataSetProducer;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45 import org.xml.sax.EntityResolver;
46 import org.xml.sax.InputSource;
47 import org.xml.sax.SAXException;
48 import org.xml.sax.SAXNotRecognizedException;
49 import org.xml.sax.SAXNotSupportedException;
50 import org.xml.sax.XMLReader;
51 import org.xml.sax.ext.DeclHandler;
52 import org.xml.sax.ext.LexicalHandler;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 public class FlatDtdProducer implements IDataSetProducer, EntityResolver, DeclHandler, LexicalHandler
73 {
74
75
76
77 public static final String REQUIRED = "#REQUIRED";
78
79
80
81
82 public static final String IMPLIED = "#IMPLIED";
83
84
85
86
87 public static final String ANY = "ANY";
88
89
90
91
92 private static final Logger logger = LoggerFactory.getLogger(FlatDtdProducer.class);
93
94 private static final IDataSetConsumer EMPTY_CONSUMER = new DefaultConsumer();
95
96 private static final String XML_CONTENT =
97 "<?xml version=\"1.0\"?>" +
98 "<!DOCTYPE dataset SYSTEM \"urn:/dummy.dtd\">" +
99 "<dataset/>";
100 private static final String DECL_HANDLER_PROPERTY_NAME =
101 "http://xml.org/sax/properties/declaration-handler";
102 private static final String LEXICAL_HANDLER_PROPERTY_NAME =
103 "http://xml.org/sax/properties/lexical-handler";
104
105 private InputSource _inputSource;
106 private IDataSetConsumer _consumer = EMPTY_CONSUMER;
107
108 private String _rootName;
109 private String _rootModel;
110 private final Map _columnListMap = new HashMap();
111
112 public FlatDtdProducer()
113 {
114 }
115
116 public FlatDtdProducer(final InputSource inputSource)
117 {
118 _inputSource = inputSource;
119 }
120
121 public static void setDeclHandler(final XMLReader xmlReader, final DeclHandler handler)
122 throws SAXNotRecognizedException, SAXNotSupportedException
123 {
124 logger.debug("setDeclHandler(xmlReader={}, handler={}) - start", xmlReader, handler);
125 xmlReader.setProperty(DECL_HANDLER_PROPERTY_NAME, handler);
126 }
127
128 public static void setLexicalHandler(final XMLReader xmlReader, final LexicalHandler handler)
129 throws SAXNotRecognizedException, SAXNotSupportedException
130 {
131 logger.debug("setLexicalHandler(xmlReader={}, handler={}) - start", xmlReader, handler);
132 xmlReader.setProperty(LEXICAL_HANDLER_PROPERTY_NAME, handler);
133 }
134
135 private List createColumnList()
136 {
137 return new LinkedList();
138 }
139
140
141
142
143 @Override
144 public void setConsumer(final IDataSetConsumer consumer) throws DataSetException
145 {
146 _consumer = consumer;
147 }
148
149 @Override
150 public void produce() throws DataSetException
151 {
152 logger.debug("produce() - start");
153
154 try
155 {
156
157 final SAXParser saxParser = SAXParserFactory.newInstance("com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl", null).newSAXParser();
158 final XMLReader xmlReader = saxParser.getXMLReader();
159
160 setDeclHandler(xmlReader, this);
161 setLexicalHandler(xmlReader, this);
162 xmlReader.setEntityResolver(this);
163 xmlReader.parse(new InputSource(new StringReader(XML_CONTENT)));
164 }
165 catch (final ParserConfigurationException e)
166 {
167 throw new DataSetException(e);
168 }
169 catch (final SAXException e)
170 {
171 final Exception exception = e.getException() == null ? e : e.getException();
172 if(exception instanceof DataSetException)
173 {
174 throw (DataSetException)exception;
175 }
176 else
177 {
178 throw new DataSetException(exception);
179 }
180 }
181 catch (final IOException e)
182 {
183 throw new DataSetException(e);
184 }
185 }
186
187
188
189
190 @Override
191 public InputSource resolveEntity(final String publicId, final String systemId)
192 throws SAXException
193 {
194 return _inputSource;
195 }
196
197
198
199
200 @Override
201 public void elementDecl(final String name, final String model) throws SAXException
202 {
203 logger.debug("elementDecl(name={}, model={}) - start", name, model);
204
205
206 if (name.equals(_rootName))
207 {
208
209 _rootModel = model;
210 }
211 else if (!_columnListMap.containsKey(name))
212 {
213 _columnListMap.put(name, createColumnList());
214 }
215 }
216
217 @Override
218 public void attributeDecl(final String elementName, final String attributeName,
219 final String type, final String mode, final String value) throws SAXException
220 {
221 if (logger.isDebugEnabled())
222 {
223 logger.debug("attributeDecl(elementName={}, attributeName={}, type={}, mode={}, value={}) - start",
224 new Object[]{ elementName, attributeName, type, mode, value });
225 }
226
227
228 final Column.Nullable nullable = (REQUIRED.equals(mode)) ?
229 Column.NO_NULLS : Column.NULLABLE;
230 final Column column = new Column(attributeName, DataType.UNKNOWN, nullable);
231
232 if (!_columnListMap.containsKey(elementName))
233 {
234 _columnListMap.put(elementName, createColumnList());
235 }
236 final List columnList = (List)_columnListMap.get(elementName);
237 columnList.add(column);
238 }
239
240 @Override
241 public void internalEntityDecl(final String name, final String value) throws SAXException
242 {
243
244 }
245
246 @Override
247 public void externalEntityDecl(final String name, final String publicId,
248 final String systemId) throws SAXException
249 {
250
251 }
252
253
254
255
256 @Override
257 public void startDTD(final String name, final String publicId, final String systemId)
258 throws SAXException
259 {
260 if (logger.isDebugEnabled())
261 {
262 logger.debug("startDTD(name={}, publicId={}, systemId={}) - start",
263 new Object[]{ name, publicId, systemId });
264 }
265
266 try
267 {
268 _rootName = name;
269 _consumer.startDataSet();
270 }
271 catch (final DataSetException e)
272 {
273 throw new SAXException(e);
274 }
275 }
276
277 @Override
278 public void endDTD() throws SAXException
279 {
280 logger.debug("endDTD() - start");
281
282 try
283 {
284 if(_rootModel == null)
285 {
286 logger.info("The rootModel is null. Cannot add tables.");
287 }
288 else
289 {
290 if (ANY.equalsIgnoreCase(_rootModel))
291 {
292 final Iterator i = _columnListMap.keySet().iterator();
293 while (i.hasNext()) {
294 final String tableName = (String) i.next();
295 addTable(tableName);
296 }
297 }
298 else {
299
300 final String rootModel = _rootModel.substring(1, _rootModel.length() - 1);
301
302
303
304 final String delim = (rootModel.indexOf(",") != -1) ? "," : "|";
305 final StringTokenizer tokenizer = new StringTokenizer(rootModel, delim);
306 while (tokenizer.hasMoreTokens()) {
307 String tableName = tokenizer.nextToken();
308 tableName = cleanupTableName(tableName);
309 addTable(tableName);
310 }
311 }
312 }
313
314 _consumer.endDataSet();
315 }
316 catch (final DataSetException e)
317 {
318 throw new SAXException(e);
319 }
320 }
321
322 private void addTable(final String tableName) throws DataSetException
323 {
324 final Column[] columns = getColumns(tableName);
325 _consumer.startTable(new DefaultTableMetaData(tableName, columns));
326 _consumer.endTable();
327 }
328
329 private Column[] getColumns(final String tableName) throws DataSetException
330 {
331 final List columnList = (List)_columnListMap.get(tableName);
332 if(columnList==null){
333 throw new DataSetException("ELEMENT/ATTRIBUTE declaration for '" + tableName + "' is missing. " +
334 "Every table must have an element describing the table.");
335 }
336 final Column[] columns = (Column[])columnList.toArray(new Column[0]);
337 return columns;
338 }
339
340 protected String cleanupTableName(final String tableName)
341 {
342 String cleaned = tableName;
343
344 while (cleaned.startsWith("(")) {
345 cleaned = cleaned.substring(1);
346 }
347
348 while (cleaned.endsWith(")")
349 || cleaned.endsWith("*")
350 || cleaned.endsWith("?")
351 || cleaned.endsWith("+")) {
352 cleaned = cleaned.substring(0, cleaned.length() - 1);
353 }
354 return cleaned;
355 }
356
357 @Override
358 public void startEntity(final String name) throws SAXException
359 {
360
361 }
362
363 @Override
364 public void endEntity(final String name) throws SAXException
365 {
366
367 }
368
369 @Override
370 public void startCDATA() throws SAXException
371 {
372
373 }
374
375 @Override
376 public void endCDATA() throws SAXException
377 {
378
379 }
380
381 @Override
382 public void comment(final char ch[], final int start, final int length) throws SAXException
383 {
384
385 }
386 }