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