1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package javax.jdo.schema;
18
19 import java.io.BufferedReader;
20 import java.io.File;
21 import java.io.FileReader;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.io.IOException;
25 import java.io.FilenameFilter;
26
27 import java.security.AccessController;
28 import java.security.PrivilegedAction;
29 import java.util.ArrayList;
30
31 import javax.jdo.JDOFatalException;
32 import javax.jdo.util.AbstractTest;
33 import javax.jdo.util.BatchTestRunner;
34
35 import javax.xml.parsers.*;
36 import org.w3c.dom.Document;
37 import org.xml.sax.*;
38 import org.xml.sax.helpers.*;
39
40 /***
41 * Tests schema files.
42 * <p>
43 */
44 public class XMLTest extends AbstractTest {
45
46 /*** */
47 protected static String BASEDIR = System.getProperty("basedir", ".");
48
49 /*** "http://www.w3.org/2001/XMLSchema" target="alexandria_uri">http://www.w3.org/2001/XMLSchema" */
50 protected static final String XSD_TYPE =
51 "http://www.w3.org/2001/XMLSchema";
52
53 /*** */
54 protected static final String SCHEMA_LANGUAGE_PROP =
55 "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
56
57 /*** */
58 protected static final String SCHEMA_LOCATION_PROP =
59 "http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation";
60
61 /*** */
62 protected static final File JDO_XSD_FILE =
63 new File(BASEDIR + "/target/classes/javax/jdo/jdo.xsd");
64
65 /*** */
66 protected static final File ORM_XSD_FILE =
67 new File(BASEDIR + "/target/classes/javax/jdo/orm.xsd");
68
69 /*** */
70 protected static final File JDOQL_XSD_FILE =
71 new File(BASEDIR + "/target/classes/javax/jdo/jdoquery.xsd");
72
73 /*** File prefix */
74 protected static final String FILE_PREFIX = BASEDIR + "/test/schema/";
75
76 /*** Entity resolver */
77 protected static final EntityResolver resolver = new JDOEntityResolver();
78
79 /*** Error handler */
80 protected static final Handler handler = new Handler();
81
82 /*** .xsd files */
83 protected static final File[] XSD_FILES = {
84 JDO_XSD_FILE, ORM_XSD_FILE, JDOQL_XSD_FILE
85 };
86
87 /*** XSD metadata files. */
88 protected static File[] positiveXSDJDO = getFiles("Positive", "-xsd.jdo");
89 protected static File[] negativeXSDJDO = getFiles("Negative", "-xsd.jdo");
90 protected static File[] positiveXSDORM = getFiles("Positive", "-xsd.orm");
91 protected static File[] negativeXSDORM = getFiles("Negative", "-xsd.orm");
92 protected static File[] positiveXSDJDOQL = getFiles("Positive", "-xsd.jdoquery");
93 protected static File[] negativeXSDJDOQL = getFiles("Negative", "-xsd.jdoquery");
94
95 /*** DTD metadata files. */
96 protected static File[] positiveDTDJDO = getFiles("Positive", "-dtd.jdo");
97 protected static File[] negativeDTDJDO = getFiles("Negative", "-dtd.jdo");
98 protected static File[] positiveDTDORM = getFiles("Positive", "-dtd.orm");
99 protected static File[] negativeDTDORM = getFiles("Negative", "-dtd.orm");
100 protected static File[] positiveDTDJDOQL = getFiles("Positive", "-dtd.jdoquery");
101 protected static File[] negativeDTDJDOQL = getFiles("Negative", "-dtd.jdoquery");
102
103 /*** Returns array of files of matching file names. */
104 protected static File[] getFiles(final String prefix, final String suffix) {
105 FilenameFilter filter = new FilenameFilter () {
106 public boolean accept(File file, String name) {
107 return (name.startsWith(prefix) && name.endsWith(suffix));
108 }
109 };
110 File dir = new File(FILE_PREFIX);
111 return dir.listFiles(filter);
112 }
113
114 /*** */
115 public static void main(String args[]) {
116 BatchTestRunner.run(XMLTest.class);
117 }
118
119 /*** Test XSD files jdo.xsd, orm.xsd, and jdoquery.xsd. */
120 public void testXSD() throws SAXException, IOException {
121 DocumentBuilder builder = null;
122 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
123 factory.setNamespaceAware(true);
124 builder = getParser(factory);
125 checkXML(builder, XSD_FILES, true);
126 String messages = retrieveMessages();
127 if (messages != null) {
128 fail(messages);
129 }
130 }
131
132 /*** Test XSD based .jdo, .orm and .jdoquery files. */
133 public void testXSDBased() {
134
135 DocumentBuilder builder = null;
136 builder = createBuilder(JDO_XSD_FILE.toURI().toString());
137 checkXML(builder, positiveXSDJDO, true);
138 checkXML(builder, negativeXSDJDO, false);
139 builder = createBuilder(ORM_XSD_FILE.toURI().toString());
140 checkXML(builder, positiveXSDORM, true);
141 checkXML(builder, negativeXSDORM, false);
142 builder = createBuilder(JDOQL_XSD_FILE.toURI().toString());
143 checkXML(builder, positiveXSDJDOQL, true);
144 checkXML(builder, negativeXSDJDOQL, false);
145 String messages = retrieveMessages();
146 if (messages != null) {
147 fail(messages);
148 }
149 }
150
151 /*** Test DTD based .jdo, .orm and .jdoquery files. */
152 public void testDTDBased() {
153
154 DocumentBuilder builder = createBuilder();
155 checkXML(builder, positiveDTDJDO, true);
156 checkXML(builder, negativeDTDJDO, false);
157 checkXML(builder, positiveDTDORM, true);
158 checkXML(builder, negativeDTDORM, false);
159 checkXML(builder, positiveDTDJDOQL, true);
160 checkXML(builder, negativeDTDJDOQL, false);
161 String messages = retrieveMessages();
162 if (messages != null) {
163 fail(messages);
164 }
165 }
166
167 /*** Create XSD builder.
168 */
169 private DocumentBuilder createBuilder(String location) {
170 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
171 factory.setValidating(true);
172 factory.setNamespaceAware(true);
173 factory.setAttribute(SCHEMA_LANGUAGE_PROP, XSD_TYPE);
174 factory.setAttribute(SCHEMA_LOCATION_PROP, location);
175 return getParser(factory);
176 }
177
178 /*** Create DTD builder.
179 */
180 private DocumentBuilder createBuilder() {
181 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
182 factory.setValidating(true);
183 factory.setNamespaceAware(true);
184 return getParser(factory);
185 }
186
187 /*** Returns a parser obtained from specified factroy. */
188 private DocumentBuilder getParser(DocumentBuilderFactory factory) {
189 try {
190 DocumentBuilder builder = factory.newDocumentBuilder();
191 builder.setEntityResolver(resolver);
192 builder.setErrorHandler(handler);
193 return builder;
194 } catch (ParserConfigurationException ex) {
195 throw new JDOFatalException("Cannot create XML parser", ex);
196 }
197 }
198
199 /*** Parse the specified files using the specified builder. The valid
200 * parameter determines whether the specified files are valid JDO metadata
201 * files. The method does not throw an exception on an error, instead it
202 * appends any error message to the global message handler.
203 */
204 private void checkXML(DocumentBuilder builder, File[] files, boolean valid) {
205 for (int i = 0; i < files.length; i++) {
206 File file = files[i];
207 handler.init(file);
208 try {
209 builder.parse(file);
210 } catch (SAXParseException ex) {
211 handler.error(ex);
212 } catch (Exception ex) {
213 throw new JDOFatalException("Fatal error", ex);
214 }
215 String messages = handler.getMessages();
216 if (valid && (messages != null)) {
217 appendMessage(messages);
218 } else if (!valid && (messages == null)) {
219 appendMessage(file.getName() + " is not valid, " +
220 "but the parser did not catch the error.");
221 }
222 }
223 }
224
225 /*** ErrorHandler implementation. */
226 private static class Handler implements ErrorHandler {
227
228 private File fileUnderTest;
229 private String[] lines;
230 private StringBuffer messages;
231
232 public void error(SAXParseException ex) {
233 append("Handler.error: ", ex);
234 }
235
236 public void fatalError(SAXParseException ex) {
237 append("Handler.fatalError: ", ex);
238 }
239
240 public void warning(SAXParseException ex) {
241 append("Handler.warning: ", ex);
242 }
243
244 public void init(File file) {
245 this.fileUnderTest = file;
246 this.messages = new StringBuffer();
247 this.lines = null;
248 }
249
250 public String getMessages() {
251 return (messages.length() == 0) ? null : messages.toString();
252 }
253
254 private void append(String prefix, SAXParseException ex) {
255 int lineNumber = ex.getLineNumber();
256 int columnNumber = ex.getColumnNumber();
257 messages.append("------------------------").append(NL);
258 messages.append(prefix).append(fileUnderTest.getName());
259 messages.append(" [line=").append(lineNumber);
260 messages.append(", col=").append(columnNumber).append("]: ");
261 messages.append(ex.getMessage()).append(NL);
262 messages.append(getErrorLocation(lineNumber, columnNumber));
263 }
264
265 private String[] getLines() {
266 if (lines == null) {
267 try {
268 BufferedReader bufferedReader =
269 new BufferedReader(new FileReader(fileUnderTest));
270 ArrayList tmp = new ArrayList();
271 while (bufferedReader.ready()) {
272 tmp.add(bufferedReader.readLine());
273 }
274 lines = (String[])tmp.toArray(new String[tmp.size()]);
275 } catch (IOException ex) {
276 throw new JDOFatalException("getLines: caught IOException", ex);
277 }
278 }
279 return lines;
280 }
281
282 /*** Return the error location for the file under test.
283 */
284 private String getErrorLocation(int lineNumber, int columnNumber) {
285 String[] lines = getLines();
286 int length = lines.length;
287 if (lineNumber > length) {
288 return "Line number " + lineNumber +
289 " exceeds the number of lines in the file (" +
290 lines.length + ")";
291 } else if (lineNumber < 1) {
292 return "Line number " + lineNumber +
293 " does not allow retriving the error location.";
294 }
295 StringBuffer buf = new StringBuffer();
296 if (lineNumber > 2) {
297 buf.append(lines[lineNumber-3]);
298 buf.append(NL);
299 buf.append(lines[lineNumber-2]);
300 buf.append(NL);
301 }
302 buf.append(lines[lineNumber-1]);
303 buf.append(NL);
304 for (int i = 1; i < columnNumber; ++i) {
305 buf.append(' ');
306 }
307 buf.append("^\n");
308 if (lineNumber + 1 < length) {
309 buf.append(lines[lineNumber]);
310 buf.append(NL);
311 buf.append(lines[lineNumber+1]);
312 buf.append(NL);
313 }
314 return buf.toString();
315 }
316 }
317
318 /*** Implementation of EntityResolver interface to check the jdo.dtd location
319 **/
320 private static class JDOEntityResolver
321 implements EntityResolver {
322
323 private static final String RECOGNIZED_PUBLIC_ID =
324 "-//Sun Microsystems, Inc.//DTD Java Data Objects Metadata 2.0//EN";
325 private static final String RECOGNIZED_SYSTEM_ID =
326 "file:/javax/jdo/jdo.dtd";
327
328 public InputSource resolveEntity(String publicId, final String systemId)
329 throws SAXException, IOException
330 {
331
332 if (((publicId != null) && RECOGNIZED_PUBLIC_ID.equals(publicId)) ||
333 ((publicId == null) && (systemId != null) &&
334 RECOGNIZED_SYSTEM_ID.equals(systemId))) {
335
336
337
338
339 InputStream stream = (InputStream) AccessController.doPrivileged (
340 new PrivilegedAction () {
341 public Object run () {
342 return getClass().getClassLoader().
343 getResourceAsStream("javax/jdo/jdo.dtd");
344 }
345 }
346 );
347 if (stream == null) {
348
349 throw new JDOFatalException("Cannot load javax/jdo/jdo.dtd, " +
350 "because the file does not exist in the jdo.jar file, " +
351 "or the JDOParser class is not granted permission to read this file. " +
352 "The metadata .xml file contained PUBLIC=" + publicId +
353 " SYSTEM=" + systemId + ".");
354 }
355 return new InputSource(new InputStreamReader(stream));
356 }
357 return null;
358 }
359 }
360 }
361