1 /*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5 // OASISXMLCatalogReader.java - Read XML Catalog files
6
7 /*
8 * Copyright 2001-2004 The Apache Software Foundation or its licensors,
9 * as applicable.
10 *
11 * Licensed under the Apache License, Version 2.0 (the "License");
12 * you may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
14 *
15 * http://www.apache.org/licenses/LICENSE-2.0
16 *
17 * Unless required by applicable law or agreed to in writing, software
18 * distributed under the License is distributed on an "AS IS" BASIS,
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20 * See the License for the specific language governing permissions and
21 * limitations under the License.
22 */
23
24 package com.sun.org.apache.xml.internal.resolver.readers;
25
26 import java.util.Stack;
27 import java.util.Vector;
28 import java.util.Enumeration;
29 import com.sun.org.apache.xml.internal.resolver.Catalog;
30 import com.sun.org.apache.xml.internal.resolver.CatalogEntry;
31 import com.sun.org.apache.xml.internal.resolver.CatalogException;
32 import com.sun.org.apache.xml.internal.resolver.helpers.PublicId;
33
34 import org.xml.sax;
35 import org.w3c.dom;
36
37 /**
38 * Parse OASIS Entity Resolution Technical Committee
39 * XML Catalog files.
40 *
41 * @see Catalog
42 *
43 * @author Norman Walsh
44 * <a href="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a>
45 *
46 */
47 public class OASISXMLCatalogReader extends SAXCatalogReader implements SAXCatalogParser {
48 /** The catalog object needs to be stored by the object so that
49 * SAX callbacks can use it.
50 */
51 protected Catalog catalog = null;
52
53 /** The namespace name of OASIS ERTC catalogs */
54 public static final String namespaceName = "urn:oasis:names:tc:entity:xmlns:xml:catalog";
55
56 /** The namespace name of OASIS ERTC TR9401 catalog extension */
57 public static final String tr9401NamespaceName = "urn:oasis:names:tc:entity:xmlns:tr9401:catalog";
58
59 protected Stack baseURIStack = new Stack();
60 protected Stack overrideStack = new Stack();
61 protected Stack namespaceStack = new Stack();
62
63 /** Set the current catalog. */
64 public void setCatalog (Catalog catalog) {
65 this.catalog = catalog;
66 debug = catalog.getCatalogManager().debug;
67 }
68
69 /** Get the current catalog. */
70 public Catalog getCatalog () {
71 return catalog;
72 }
73
74 /**
75 * Are we in an extension namespace?
76 *
77 * @return true if the current stack of open namespaces includes
78 * an extension namespace.
79 */
80 protected boolean inExtensionNamespace() {
81 boolean inExtension = false;
82
83 Enumeration elements = namespaceStack.elements();
84 while (!inExtension && elements.hasMoreElements()) {
85 String ns = (String) elements.nextElement();
86 if (ns == null) {
87 inExtension = true;
88 } else {
89 inExtension = (!ns.equals(tr9401NamespaceName)
90 && !ns.equals(namespaceName));
91 }
92 }
93
94 return inExtension;
95 }
96
97 // ----------------------------------------------------------------------
98 // Implement the SAX ContentHandler interface
99
100 /** The SAX <code>setDocumentLocator</code> method does nothing. */
101 public void setDocumentLocator (Locator locator) {
102 return;
103 }
104
105 /** The SAX <code>startDocument</code> method does nothing. */
106 public void startDocument ()
107 throws SAXException {
108 baseURIStack.push(catalog.getCurrentBase());
109 overrideStack.push(catalog.getDefaultOverride());
110 return;
111 }
112
113 /** The SAX <code>endDocument</code> method does nothing. */
114 public void endDocument ()
115 throws SAXException {
116 return;
117 }
118
119 /**
120 * The SAX <code>startElement</code> method recognizes elements
121 * from the plain catalog format and instantiates CatalogEntry
122 * objects for them.
123 *
124 * @param namespaceURI The namespace name of the element.
125 * @param localName The local name of the element.
126 * @param qName The QName of the element.
127 * @param atts The list of attributes on the element.
128 *
129 * @see CatalogEntry
130 */
131 public void startElement (String namespaceURI,
132 String localName,
133 String qName,
134 Attributes atts)
135 throws SAXException {
136
137 int entryType = -1;
138 Vector entryArgs = new Vector();
139
140 namespaceStack.push(namespaceURI);
141
142 boolean inExtension = inExtensionNamespace();
143
144 if (namespaceURI != null && namespaceName.equals(namespaceURI)
145 && !inExtension) {
146 // This is an XML Catalog entry
147
148 if (atts.getValue("xml:base") != null) {
149 String baseURI = atts.getValue("xml:base");
150 entryType = Catalog.BASE;
151 entryArgs.add(baseURI);
152 baseURIStack.push(baseURI);
153
154 debug.message(4, "xml:base", baseURI);
155
156 try {
157 CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
158 catalog.addEntry(ce);
159 } catch (CatalogException cex) {
160 if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
161 debug.message(1, "Invalid catalog entry type", localName);
162 } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
163 debug.message(1, "Invalid catalog entry (base)", localName);
164 }
165 }
166
167 entryType = -1;
168 entryArgs = new Vector();
169
170 } else {
171 baseURIStack.push(baseURIStack.peek());
172 }
173
174 if ((localName.equals("catalog") || localName.equals("group"))
175 && atts.getValue("prefer") != null) {
176 String override = atts.getValue("prefer");
177
178 if (override.equals("public")) {
179 override = "yes";
180 } else if (override.equals("system")) {
181 override = "no";
182 } else {
183 debug.message(1,
184 "Invalid prefer: must be 'system' or 'public'",
185 localName);
186 override = catalog.getDefaultOverride();
187 }
188
189 entryType = Catalog.OVERRIDE;
190 entryArgs.add(override);
191 overrideStack.push(override);
192
193 debug.message(4, "override", override);
194
195 try {
196 CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
197 catalog.addEntry(ce);
198 } catch (CatalogException cex) {
199 if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
200 debug.message(1, "Invalid catalog entry type", localName);
201 } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
202 debug.message(1, "Invalid catalog entry (override)", localName);
203 }
204 }
205
206 entryType = -1;
207 entryArgs = new Vector();
208
209 } else {
210 overrideStack.push(overrideStack.peek());
211 }
212
213 if (localName.equals("delegatePublic")) {
214 if (checkAttributes(atts, "publicIdStartString", "catalog")) {
215 entryType = Catalog.DELEGATE_PUBLIC;
216 entryArgs.add(atts.getValue("publicIdStartString"));
217 entryArgs.add(atts.getValue("catalog"));
218
219 debug.message(4, "delegatePublic",
220 PublicId.normalize(atts.getValue("publicIdStartString")),
221 atts.getValue("catalog"));
222 }
223 } else if (localName.equals("delegateSystem")) {
224 if (checkAttributes(atts, "systemIdStartString", "catalog")) {
225 entryType = Catalog.DELEGATE_SYSTEM;
226 entryArgs.add(atts.getValue("systemIdStartString"));
227 entryArgs.add(atts.getValue("catalog"));
228
229 debug.message(4, "delegateSystem",
230 atts.getValue("systemIdStartString"),
231 atts.getValue("catalog"));
232 }
233 } else if (localName.equals("delegateURI")) {
234 if (checkAttributes(atts, "uriStartString", "catalog")) {
235 entryType = Catalog.DELEGATE_URI;
236 entryArgs.add(atts.getValue("uriStartString"));
237 entryArgs.add(atts.getValue("catalog"));
238
239 debug.message(4, "delegateURI",
240 atts.getValue("uriStartString"),
241 atts.getValue("catalog"));
242 }
243 } else if (localName.equals("rewriteSystem")) {
244 if (checkAttributes(atts, "systemIdStartString", "rewritePrefix")) {
245 entryType = Catalog.REWRITE_SYSTEM;
246 entryArgs.add(atts.getValue("systemIdStartString"));
247 entryArgs.add(atts.getValue("rewritePrefix"));
248
249 debug.message(4, "rewriteSystem",
250 atts.getValue("systemIdStartString"),
251 atts.getValue("rewritePrefix"));
252 }
253 } else if (localName.equals("systemSuffix")) {
254 if (checkAttributes(atts, "systemIdSuffix", "uri")) {
255 entryType = Catalog.SYSTEM_SUFFIX;
256 entryArgs.add(atts.getValue("systemIdSuffix"));
257 entryArgs.add(atts.getValue("uri"));
258
259 debug.message(4, "systemSuffix",
260 atts.getValue("systemIdSuffix"),
261 atts.getValue("uri"));
262 }
263 } else if (localName.equals("rewriteURI")) {
264 if (checkAttributes(atts, "uriStartString", "rewritePrefix")) {
265 entryType = Catalog.REWRITE_URI;
266 entryArgs.add(atts.getValue("uriStartString"));
267 entryArgs.add(atts.getValue("rewritePrefix"));
268
269 debug.message(4, "rewriteURI",
270 atts.getValue("uriStartString"),
271 atts.getValue("rewritePrefix"));
272 }
273 } else if (localName.equals("uriSuffix")) {
274 if (checkAttributes(atts, "uriSuffix", "uri")) {
275 entryType = Catalog.URI_SUFFIX;
276 entryArgs.add(atts.getValue("uriSuffix"));
277 entryArgs.add(atts.getValue("uri"));
278
279 debug.message(4, "uriSuffix",
280 atts.getValue("uriSuffix"),
281 atts.getValue("uri"));
282 }
283 } else if (localName.equals("nextCatalog")) {
284 if (checkAttributes(atts, "catalog")) {
285 entryType = Catalog.CATALOG;
286 entryArgs.add(atts.getValue("catalog"));
287
288 debug.message(4, "nextCatalog", atts.getValue("catalog"));
289 }
290 } else if (localName.equals("public")) {
291 if (checkAttributes(atts, "publicId", "uri")) {
292 entryType = Catalog.PUBLIC;
293 entryArgs.add(atts.getValue("publicId"));
294 entryArgs.add(atts.getValue("uri"));
295
296 debug.message(4, "public",
297 PublicId.normalize(atts.getValue("publicId")),
298 atts.getValue("uri"));
299 }
300 } else if (localName.equals("system")) {
301 if (checkAttributes(atts, "systemId", "uri")) {
302 entryType = Catalog.SYSTEM;
303 entryArgs.add(atts.getValue("systemId"));
304 entryArgs.add(atts.getValue("uri"));
305
306 debug.message(4, "system",
307 atts.getValue("systemId"),
308 atts.getValue("uri"));
309 }
310 } else if (localName.equals("uri")) {
311 if (checkAttributes(atts, "name", "uri")) {
312 entryType = Catalog.URI;
313 entryArgs.add(atts.getValue("name"));
314 entryArgs.add(atts.getValue("uri"));
315
316 debug.message(4, "uri",
317 atts.getValue("name"),
318 atts.getValue("uri"));
319 }
320 } else if (localName.equals("catalog")) {
321 // nop, start of catalog
322 } else if (localName.equals("group")) {
323 // nop, a group
324 } else {
325 // This is equivalent to an invalid catalog entry type
326 debug.message(1, "Invalid catalog entry type", localName);
327 }
328
329 if (entryType >= 0) {
330 try {
331 CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
332 catalog.addEntry(ce);
333 } catch (CatalogException cex) {
334 if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
335 debug.message(1, "Invalid catalog entry type", localName);
336 } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
337 debug.message(1, "Invalid catalog entry", localName);
338 }
339 }
340 }
341 }
342
343 if (namespaceURI != null && tr9401NamespaceName.equals(namespaceURI)
344 && !inExtension) {
345 // This is a TR9401 Catalog entry
346
347 if (atts.getValue("xml:base") != null) {
348 String baseURI = atts.getValue("xml:base");
349 entryType = Catalog.BASE;
350 entryArgs.add(baseURI);
351 baseURIStack.push(baseURI);
352
353 debug.message(4, "xml:base", baseURI);
354
355 try {
356 CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
357 catalog.addEntry(ce);
358 } catch (CatalogException cex) {
359 if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
360 debug.message(1, "Invalid catalog entry type", localName);
361 } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
362 debug.message(1, "Invalid catalog entry (base)", localName);
363 }
364 }
365
366 entryType = -1;
367 entryArgs = new Vector();
368
369 } else {
370 baseURIStack.push(baseURIStack.peek());
371 }
372
373 if (localName.equals("doctype")) {
374 entryType = catalog.DOCTYPE;
375 entryArgs.add(atts.getValue("name"));
376 entryArgs.add(atts.getValue("uri"));
377 } else if (localName.equals("document")) {
378 entryType = catalog.DOCUMENT;
379 entryArgs.add(atts.getValue("uri"));
380 } else if (localName.equals("dtddecl")) {
381 entryType = catalog.DTDDECL;
382 entryArgs.add(atts.getValue("publicId"));
383 entryArgs.add(atts.getValue("uri"));
384 } else if (localName.equals("entity")) {
385 entryType = Catalog.ENTITY;
386 entryArgs.add(atts.getValue("name"));
387 entryArgs.add(atts.getValue("uri"));
388 } else if (localName.equals("linktype")) {
389 entryType = Catalog.LINKTYPE;
390 entryArgs.add(atts.getValue("name"));
391 entryArgs.add(atts.getValue("uri"));
392 } else if (localName.equals("notation")) {
393 entryType = Catalog.NOTATION;
394 entryArgs.add(atts.getValue("name"));
395 entryArgs.add(atts.getValue("uri"));
396 } else if (localName.equals("sgmldecl")) {
397 entryType = Catalog.SGMLDECL;
398 entryArgs.add(atts.getValue("uri"));
399 } else {
400 // This is equivalent to an invalid catalog entry type
401 debug.message(1, "Invalid catalog entry type", localName);
402 }
403
404 if (entryType >= 0) {
405 try {
406 CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
407 catalog.addEntry(ce);
408 } catch (CatalogException cex) {
409 if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
410 debug.message(1, "Invalid catalog entry type", localName);
411 } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
412 debug.message(1, "Invalid catalog entry", localName);
413 }
414 }
415 }
416 }
417 }
418
419 public boolean checkAttributes (Attributes atts, String attName) {
420 if (atts.getValue(attName) == null) {
421 debug.message(1, "Error: required attribute " + attName + " missing.");
422 return false;
423 } else {
424 return true;
425 }
426 }
427
428 public boolean checkAttributes (Attributes atts,
429 String attName1,
430 String attName2) {
431 return checkAttributes(atts, attName1)
432 && checkAttributes(atts, attName2);
433 }
434
435 /** The SAX <code>endElement</code> method does nothing. */
436 public void endElement (String namespaceURI,
437 String localName,
438 String qName)
439 throws SAXException {
440
441 int entryType = -1;
442 Vector entryArgs = new Vector();
443
444 boolean inExtension = inExtensionNamespace();
445
446 if (namespaceURI != null
447 && !inExtension
448 && (namespaceName.equals(namespaceURI)
449 || tr9401NamespaceName.equals(namespaceURI))) {
450
451 String popURI = (String) baseURIStack.pop();
452 String baseURI = (String) baseURIStack.peek();
453
454 if (!baseURI.equals(popURI)) {
455 entryType = catalog.BASE;
456 entryArgs.add(baseURI);
457
458 debug.message(4, "(reset) xml:base", baseURI);
459
460 try {
461 CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
462 catalog.addEntry(ce);
463 } catch (CatalogException cex) {
464 if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
465 debug.message(1, "Invalid catalog entry type", localName);
466 } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
467 debug.message(1, "Invalid catalog entry (rbase)", localName);
468 }
469 }
470 }
471 }
472
473 if (namespaceURI != null && namespaceName.equals(namespaceURI)
474 && !inExtension) {
475 if (localName.equals("catalog") || localName.equals("group")) {
476 String popOverride = (String) overrideStack.pop();
477 String override = (String) overrideStack.peek();
478
479 if (!override.equals(popOverride)) {
480 entryType = catalog.OVERRIDE;
481 entryArgs.add(override);
482 overrideStack.push(override);
483
484 debug.message(4, "(reset) override", override);
485
486 try {
487 CatalogEntry ce = new CatalogEntry(entryType, entryArgs);
488 catalog.addEntry(ce);
489 } catch (CatalogException cex) {
490 if (cex.getExceptionType() == CatalogException.INVALID_ENTRY_TYPE) {
491 debug.message(1, "Invalid catalog entry type", localName);
492 } else if (cex.getExceptionType() == CatalogException.INVALID_ENTRY) {
493 debug.message(1, "Invalid catalog entry (roverride)", localName);
494 }
495 }
496 }
497 }
498 }
499
500 namespaceStack.pop();
501
502 return;
503 }
504
505 /** The SAX <code>characters</code> method does nothing. */
506 public void characters (char ch[], int start, int length)
507 throws SAXException {
508 return;
509 }
510
511 /** The SAX <code>ignorableWhitespace</code> method does nothing. */
512 public void ignorableWhitespace (char ch[], int start, int length)
513 throws SAXException {
514 return;
515 }
516
517 /** The SAX <code>processingInstruction</code> method does nothing. */
518 public void processingInstruction (String target, String data)
519 throws SAXException {
520 return;
521 }
522
523 /** The SAX <code>skippedEntity</code> method does nothing. */
524 public void skippedEntity (String name)
525 throws SAXException {
526 return;
527 }
528
529 /** The SAX <code>startPrefixMapping</code> method does nothing. */
530 public void startPrefixMapping(String prefix, String uri)
531 throws SAXException {
532 return;
533 }
534
535 /** The SAX <code>endPrefixMapping</code> method does nothing. */
536 public void endPrefixMapping(String prefix)
537 throws SAXException {
538 return;
539 }
540
541 }