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(InputSource inputSource)
117 {
118 _inputSource = inputSource;
119 }
120
121 public static void setDeclHandler(XMLReader xmlReader, 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(XMLReader xmlReader, 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 public void setConsumer(IDataSetConsumer consumer) throws DataSetException
144 {
145 _consumer = consumer;
146 }
147
148 public void produce() throws DataSetException
149 {
150 logger.debug("produce() - start");
151
152 try
153 {
154 SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
155 XMLReader xmlReader = saxParser.getXMLReader();
156
157 setDeclHandler(xmlReader, this);
158 setLexicalHandler(xmlReader, this);
159 xmlReader.setEntityResolver(this);
160 xmlReader.parse(new InputSource(new StringReader(XML_CONTENT)));
161 }
162 catch (ParserConfigurationException e)
163 {
164 throw new DataSetException(e);
165 }
166 catch (SAXException e)
167 {
168 Exception exception = e.getException() == null ? e : e.getException();
169 if(exception instanceof DataSetException)
170 {
171 throw (DataSetException)exception;
172 }
173 else
174 {
175 throw new DataSetException(exception);
176 }
177 }
178 catch (IOException e)
179 {
180 throw new DataSetException(e);
181 }
182 }
183
184
185
186
187 public InputSource resolveEntity(String publicId, String systemId)
188 throws SAXException
189 {
190 return _inputSource;
191 }
192
193
194
195
196 public void elementDecl(String name, String model) throws SAXException
197 {
198 logger.debug("elementDecl(name={}, model={}) - start", name, model);
199
200
201 if (name.equals(_rootName))
202 {
203
204 _rootModel = model;
205 }
206 else if (!_columnListMap.containsKey(name))
207 {
208 _columnListMap.put(name, createColumnList());
209 }
210 }
211
212 public void attributeDecl(String elementName, String attributeName,
213 String type, String mode, String value) throws SAXException
214 {
215 if (logger.isDebugEnabled())
216 {
217 logger.debug("attributeDecl(elementName={}, attributeName={}, type={}, mode={}, value={}) - start",
218 new Object[]{ elementName, attributeName, type, mode, value });
219 }
220
221
222 Column.Nullable nullable = (REQUIRED.equals(mode)) ?
223 Column.NO_NULLS : Column.NULLABLE;
224 Column column = new Column(attributeName, DataType.UNKNOWN, nullable);
225
226 if (!_columnListMap.containsKey(elementName))
227 {
228 _columnListMap.put(elementName, createColumnList());
229 }
230 List columnList = (List)_columnListMap.get(elementName);
231 columnList.add(column);
232 }
233
234 public void internalEntityDecl(String name, String value) throws SAXException
235 {
236
237 }
238
239 public void externalEntityDecl(String name, String publicId,
240 String systemId) throws SAXException
241 {
242
243 }
244
245
246
247
248 public void startDTD(String name, String publicId, String systemId)
249 throws SAXException
250 {
251 if (logger.isDebugEnabled())
252 {
253 logger.debug("startDTD(name={}, publicId={}, systemId={}) - start",
254 new Object[]{ name, publicId, systemId });
255 }
256
257 try
258 {
259 _rootName = name;
260 _consumer.startDataSet();
261 }
262 catch (DataSetException e)
263 {
264 throw new SAXException(e);
265 }
266 }
267
268 public void endDTD() throws SAXException
269 {
270 logger.debug("endDTD() - start");
271
272 try
273 {
274 if(_rootModel == null)
275 {
276 logger.info("The rootModel is null. Cannot add tables.");
277 }
278 else
279 {
280 if (ANY.equalsIgnoreCase(_rootModel))
281 {
282 Iterator i = _columnListMap.keySet().iterator();
283 while (i.hasNext()) {
284 String tableName = (String) i.next();
285 addTable(tableName);
286 }
287 }
288 else {
289
290 String rootModel = _rootModel.substring(1, _rootModel.length() - 1);
291
292
293
294 String delim = (rootModel.indexOf(",") != -1) ? "," : "|";
295 StringTokenizer tokenizer = new StringTokenizer(rootModel, delim);
296 while (tokenizer.hasMoreTokens()) {
297 String tableName = tokenizer.nextToken();
298 tableName = cleanupTableName(tableName);
299 addTable(tableName);
300 }
301 }
302 }
303
304 _consumer.endDataSet();
305 }
306 catch (DataSetException e)
307 {
308 throw new SAXException(e);
309 }
310 }
311
312 private void addTable(String tableName) throws DataSetException
313 {
314 Column[] columns = getColumns(tableName);
315 _consumer.startTable(new DefaultTableMetaData(tableName, columns));
316 _consumer.endTable();
317 }
318
319 private Column[] getColumns(String tableName) throws DataSetException
320 {
321 List columnList = (List)_columnListMap.get(tableName);
322 if(columnList==null){
323 throw new DataSetException("ELEMENT/ATTRIBUTE declaration for '" + tableName + "' is missing. " +
324 "Every table must have an element describing the table.");
325 }
326 Column[] columns = (Column[])columnList.toArray(new Column[0]);
327 return columns;
328 }
329
330 protected String cleanupTableName(String tableName)
331 {
332 String cleaned = tableName;
333
334 while (cleaned.startsWith("(")) {
335 cleaned = cleaned.substring(1);
336 }
337
338 while (cleaned.endsWith(")")
339 || cleaned.endsWith("*")
340 || cleaned.endsWith("?")
341 || cleaned.endsWith("+")) {
342 cleaned = cleaned.substring(0, cleaned.length() - 1);
343 }
344 return cleaned;
345 }
346
347 public void startEntity(String name) throws SAXException
348 {
349
350 }
351
352 public void endEntity(String name) throws SAXException
353 {
354
355 }
356
357 public void startCDATA() throws SAXException
358 {
359
360 }
361
362 public void endCDATA() throws SAXException
363 {
364
365 }
366
367 public void comment(char ch[], int start, int length) throws SAXException
368 {
369
370 }
371 }