001: // Resolver.java - Represents an extension of OASIS Open Catalog files.
002:
003: /*
004: * Copyright 2001-2004 The Apache Software Foundation or its licensors,
005: * as applicable.
006: *
007: * Licensed under the Apache License, Version 2.0 (the "License");
008: * you may not use this file except in compliance with the License.
009: * You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: */
019:
020: package com.sun.org.apache.xml.internal.resolver;
021:
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.io.FileNotFoundException;
025: import java.util.Enumeration;
026: import java.util.Vector;
027: import java.net.URL;
028: import java.net.URLConnection;
029: import java.net.MalformedURLException;
030: import com.sun.org.apache.xml.internal.resolver.readers.SAXCatalogReader;
031: import com.sun.org.apache.xml.internal.resolver.readers.OASISXMLCatalogReader;
032: import com.sun.org.apache.xml.internal.resolver.readers.TR9401CatalogReader;
033: import javax.xml.parsers.SAXParserFactory;
034:
035: /**
036: * An extension to OASIS Open Catalog files, this class supports
037: * suffix-based matching and an external RFC2483 resolver.
038: *
039: * @see Catalog
040: *
041: * @author Norman Walsh
042: * <a href="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a>
043: *
044: * @version 1.0
045: */
046: public class Resolver extends Catalog {
047: /**
048: * The URISUFFIX Catalog Entry type.
049: *
050: * <p>URI suffix entries match URIs that end in a specified suffix.</p>
051: */
052: public static final int URISUFFIX = CatalogEntry.addEntryType(
053: "URISUFFIX", 2);
054:
055: /**
056: * The SYSTEMSUFFIX Catalog Entry type.
057: *
058: * <p>System suffix entries match system identifiers that end in a
059: * specified suffix.</p>
060: */
061: public static final int SYSTEMSUFFIX = CatalogEntry.addEntryType(
062: "SYSTEMSUFFIX", 2);
063:
064: /**
065: * The RESOLVER Catalog Entry type.
066: *
067: * <p>A hook for providing support for web-based backup resolvers.</p>
068: */
069: public static final int RESOLVER = CatalogEntry.addEntryType(
070: "RESOLVER", 1);
071:
072: /**
073: * The SYSTEMREVERSE Catalog Entry type.
074: *
075: * <p>This is a bit of a hack. There's no actual SYSTEMREVERSE entry,
076: * but this entry type is used to indicate that a reverse lookup is
077: * being performed. (This allows the Resolver to implement
078: * RFC2483 I2N and I2NS.)
079: */
080: public static final int SYSTEMREVERSE = CatalogEntry.addEntryType(
081: "SYSTEMREVERSE", 1);
082:
083: /**
084: * Setup readers.
085: */
086: public void setupReaders() {
087: SAXParserFactory spf = SAXParserFactory.newInstance();
088: spf.setNamespaceAware(true);
089: spf.setValidating(false);
090:
091: SAXCatalogReader saxReader = new SAXCatalogReader(spf);
092:
093: saxReader
094: .setCatalogParser(null, "XMLCatalog",
095: "com.sun.org.apache.xml.internal.resolver.readers.XCatalogReader");
096:
097: saxReader
098: .setCatalogParser(OASISXMLCatalogReader.namespaceName,
099: "catalog",
100: "com.sun.org.apache.xml.internal.resolver.readers.ExtendedXMLCatalogReader");
101:
102: addReader("application/xml", saxReader);
103:
104: TR9401CatalogReader textReader = new TR9401CatalogReader();
105: addReader("text/plain", textReader);
106: }
107:
108: /**
109: * Cleanup and process a Catalog entry.
110: *
111: * <p>This method processes each Catalog entry, changing mapped
112: * relative system identifiers into absolute ones (based on the current
113: * base URI), and maintaining other information about the current
114: * catalog.</p>
115: *
116: * @param entry The CatalogEntry to process.
117: */
118: public void addEntry(CatalogEntry entry) {
119: int type = entry.getEntryType();
120:
121: if (type == URISUFFIX) {
122: String suffix = normalizeURI(entry.getEntryArg(0));
123: String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
124:
125: entry.setEntryArg(1, fsi);
126:
127: catalogManager.debug.message(4, "URISUFFIX", suffix, fsi);
128: } else if (type == SYSTEMSUFFIX) {
129: String suffix = normalizeURI(entry.getEntryArg(0));
130: String fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
131:
132: entry.setEntryArg(1, fsi);
133:
134: catalogManager.debug
135: .message(4, "SYSTEMSUFFIX", suffix, fsi);
136: }
137:
138: super .addEntry(entry);
139: }
140:
141: /**
142: * Return the applicable URI.
143: *
144: * <p>If a URI entry exists in the Catalog
145: * for the URI specified, return the mapped value.</p>
146: *
147: * <p>In the Resolver (as opposed to the Catalog) class, if the
148: * URI isn't found by the usual algorithm, URISUFFIX entries are
149: * considered.</p>
150: *
151: * <p>URI comparison is case sensitive.</p>
152: *
153: * @param uri The URI to locate in the catalog.
154: *
155: * @return The resolved URI.
156: *
157: * @throws MalformedURLException The system identifier of a
158: * subordinate catalog cannot be turned into a valid URL.
159: * @throws IOException Error reading subordinate catalog file.
160: */
161: public String resolveURI(String uri) throws MalformedURLException,
162: IOException {
163:
164: String resolved = super .resolveURI(uri);
165: if (resolved != null) {
166: return resolved;
167: }
168:
169: Enumeration en = catalogEntries.elements();
170: while (en.hasMoreElements()) {
171: CatalogEntry e = (CatalogEntry) en.nextElement();
172: if (e.getEntryType() == RESOLVER) {
173: resolved = resolveExternalSystem(uri, e.getEntryArg(0));
174: if (resolved != null) {
175: return resolved;
176: }
177: } else if (e.getEntryType() == URISUFFIX) {
178: String suffix = e.getEntryArg(0);
179: String result = e.getEntryArg(1);
180:
181: if (suffix.length() <= uri.length()
182: && uri
183: .substring(
184: uri.length() - suffix.length())
185: .equals(suffix)) {
186: return result;
187: }
188: }
189: }
190:
191: // Otherwise, look in the subordinate catalogs
192: return resolveSubordinateCatalogs(Catalog.URI, null, null, uri);
193: }
194:
195: /**
196: * Return the applicable SYSTEM system identifier, resorting
197: * to external RESOLVERs if necessary.
198: *
199: * <p>If a SYSTEM entry exists in the Catalog
200: * for the system ID specified, return the mapped value.</p>
201: *
202: * <p>In the Resolver (as opposed to the Catalog) class, if the
203: * URI isn't found by the usual algorithm, SYSTEMSUFFIX entries are
204: * considered.</p>
205: *
206: * <p>On Windows-based operating systems, the comparison between
207: * the system identifier provided and the SYSTEM entries in the
208: * Catalog is case-insensitive.</p>
209: *
210: * @param systemId The system ID to locate in the catalog.
211: *
212: * @return The system identifier to use for systemId.
213: *
214: * @throws MalformedURLException The formal system identifier of a
215: * subordinate catalog cannot be turned into a valid URL.
216: * @throws IOException Error reading subordinate catalog file.
217: */
218: public String resolveSystem(String systemId)
219: throws MalformedURLException, IOException {
220:
221: String resolved = super .resolveSystem(systemId);
222: if (resolved != null) {
223: return resolved;
224: }
225:
226: Enumeration en = catalogEntries.elements();
227: while (en.hasMoreElements()) {
228: CatalogEntry e = (CatalogEntry) en.nextElement();
229: if (e.getEntryType() == RESOLVER) {
230: resolved = resolveExternalSystem(systemId, e
231: .getEntryArg(0));
232: if (resolved != null) {
233: return resolved;
234: }
235: } else if (e.getEntryType() == SYSTEMSUFFIX) {
236: String suffix = e.getEntryArg(0);
237: String result = e.getEntryArg(1);
238:
239: if (suffix.length() <= systemId.length()
240: && systemId.substring(
241: systemId.length() - suffix.length())
242: .equals(suffix)) {
243: return result;
244: }
245: }
246: }
247:
248: return resolveSubordinateCatalogs(Catalog.SYSTEM, null, null,
249: systemId);
250: }
251:
252: /**
253: * Return the applicable PUBLIC or SYSTEM identifier, resorting
254: * to external resolvers if necessary.
255: *
256: * <p>This method searches the Catalog and returns the system
257: * identifier specified for the given system or
258: * public identifiers. If
259: * no appropriate PUBLIC or SYSTEM entry is found in the Catalog,
260: * null is returned.</p>
261: *
262: * <p>Note that a system or public identifier in the current catalog
263: * (or subordinate catalogs) will be used in preference to an
264: * external resolver. Further, if a systemId is present, the external
265: * resolver(s) will be queried for that before the publicId.</p>
266: *
267: * @param publicId The public identifier to locate in the catalog.
268: * Public identifiers are normalized before comparison.
269: * @param systemId The nominal system identifier for the entity
270: * in question (as provided in the source document).
271: *
272: * @throws MalformedURLException The formal system identifier of a
273: * subordinate catalog cannot be turned into a valid URL.
274: * @throws IOException Error reading subordinate catalog file.
275: *
276: * @return The system identifier to use.
277: * Note that the nominal system identifier is not returned if a
278: * match is not found in the catalog, instead null is returned
279: * to indicate that no match was found.
280: */
281: public String resolvePublic(String publicId, String systemId)
282: throws MalformedURLException, IOException {
283:
284: String resolved = super .resolvePublic(publicId, systemId);
285: if (resolved != null) {
286: return resolved;
287: }
288:
289: Enumeration en = catalogEntries.elements();
290: while (en.hasMoreElements()) {
291: CatalogEntry e = (CatalogEntry) en.nextElement();
292: if (e.getEntryType() == RESOLVER) {
293: if (systemId != null) {
294: resolved = resolveExternalSystem(systemId, e
295: .getEntryArg(0));
296: if (resolved != null) {
297: return resolved;
298: }
299: }
300: resolved = resolveExternalPublic(publicId, e
301: .getEntryArg(0));
302: if (resolved != null) {
303: return resolved;
304: }
305: }
306: }
307:
308: return resolveSubordinateCatalogs(Catalog.PUBLIC, null,
309: publicId, systemId);
310: }
311:
312: /**
313: * Query an external RFC2483 resolver for a system identifier.
314: *
315: * @param systemId The system ID to locate.
316: * @param resolver The name of the resolver to use.
317: *
318: * @return The system identifier to use for the systemId.
319: */
320: protected String resolveExternalSystem(String systemId,
321: String resolver) throws MalformedURLException, IOException {
322: Resolver r = queryResolver(resolver, "i2l", systemId, null);
323: if (r != null) {
324: return r.resolveSystem(systemId);
325: } else {
326: return null;
327: }
328: }
329:
330: /**
331: * Query an external RFC2483 resolver for a public identifier.
332: *
333: * @param publicId The system ID to locate.
334: * @param resolver The name of the resolver to use.
335: *
336: * @return The system identifier to use for the systemId.
337: */
338: protected String resolveExternalPublic(String publicId,
339: String resolver) throws MalformedURLException, IOException {
340: Resolver r = queryResolver(resolver, "fpi2l", publicId, null);
341: if (r != null) {
342: return r.resolvePublic(publicId, null);
343: } else {
344: return null;
345: }
346: }
347:
348: /**
349: * Query an external RFC2483 resolver.
350: *
351: * @param resolver The URL of the RFC2483 resolver.
352: * @param command The command to send the resolver.
353: * @param arg1 The first argument to the resolver.
354: * @param arg2 The second argument to the resolver, usually null.
355: *
356: * @return The Resolver constructed.
357: */
358: protected Resolver queryResolver(String resolver, String command,
359: String arg1, String arg2) {
360: InputStream iStream = null;
361: String RFC2483 = resolver + "?command=" + command
362: + "&format=tr9401&uri=" + arg1 + "&uri2=" + arg2;
363: String line = null;
364:
365: try {
366: URL url = new URL(RFC2483);
367:
368: URLConnection urlCon = url.openConnection();
369:
370: urlCon.setUseCaches(false);
371:
372: Resolver r = (Resolver) newCatalog();
373:
374: String cType = urlCon.getContentType();
375:
376: // I don't care about the character set or subtype
377: if (cType.indexOf(";") > 0) {
378: cType = cType.substring(0, cType.indexOf(";"));
379: }
380:
381: r.parseCatalog(cType, urlCon.getInputStream());
382:
383: return r;
384: } catch (CatalogException cex) {
385: if (cex.getExceptionType() == CatalogException.UNPARSEABLE) {
386: catalogManager.debug.message(1, "Unparseable catalog: "
387: + RFC2483);
388: } else if (cex.getExceptionType() == CatalogException.UNKNOWN_FORMAT) {
389: catalogManager.debug.message(1,
390: "Unknown catalog format: " + RFC2483);
391: }
392: return null;
393: } catch (MalformedURLException mue) {
394: catalogManager.debug.message(1, "Malformed resolver URL: "
395: + RFC2483);
396: return null;
397: } catch (IOException ie) {
398: catalogManager.debug.message(1,
399: "I/O Exception opening resolver: " + RFC2483);
400: return null;
401: }
402: }
403:
404: /**
405: * Append two vectors, returning the result.
406: *
407: * @param vec The first vector
408: * @param appvec The vector to be appended
409: * @return The vector vec, with appvec's elements appended to it
410: */
411: private Vector appendVector(Vector vec, Vector appvec) {
412: if (appvec != null) {
413: for (int count = 0; count < appvec.size(); count++) {
414: vec.addElement(appvec.elementAt(count));
415: }
416: }
417: return vec;
418: }
419:
420: /**
421: * Find the URNs for a given system identifier in all catalogs.
422: *
423: * @param systemId The system ID to locate.
424: *
425: * @return A vector of URNs that map to the systemId.
426: */
427: public Vector resolveAllSystemReverse(String systemId)
428: throws MalformedURLException, IOException {
429: Vector resolved = new Vector();
430:
431: // If there's a SYSTEM entry in this catalog, use it
432: if (systemId != null) {
433: Vector localResolved = resolveLocalSystemReverse(systemId);
434: resolved = appendVector(resolved, localResolved);
435: }
436:
437: // Otherwise, look in the subordinate catalogs
438: Vector subResolved = resolveAllSubordinateCatalogs(
439: SYSTEMREVERSE, null, null, systemId);
440:
441: return appendVector(resolved, subResolved);
442: }
443:
444: /**
445: * Find the URN for a given system identifier.
446: *
447: * @param systemId The system ID to locate.
448: *
449: * @return A (single) URN that maps to the systemId.
450: */
451: public String resolveSystemReverse(String systemId)
452: throws MalformedURLException, IOException {
453: Vector resolved = resolveAllSystemReverse(systemId);
454: if (resolved != null && resolved.size() > 0) {
455: return (String) resolved.elementAt(0);
456: } else {
457: return null;
458: }
459: }
460:
461: /**
462: * Return the applicable SYSTEM system identifiers.
463: *
464: * <p>If one or more SYSTEM entries exists in the Catalog
465: * for the system ID specified, return the mapped values.</p>
466: *
467: * <p>The caller is responsible for doing any necessary
468: * normalization of the system identifier before calling
469: * this method. For example, a relative system identifier in
470: * a document might be converted to an absolute system identifier
471: * before attempting to resolve it.</p>
472: *
473: * <p>Note that this function will force all subordinate catalogs
474: * to be loaded.</p>
475: *
476: * <p>On Windows-based operating systems, the comparison between
477: * the system identifier provided and the SYSTEM entries in the
478: * Catalog is case-insensitive.</p>
479: *
480: * @param systemId The system ID to locate in the catalog.
481: *
482: * @return The system identifier to use for the notation.
483: *
484: * @throws MalformedURLException The formal system identifier of a
485: * subordinate catalog cannot be turned into a valid URL.
486: * @throws IOException Error reading subordinate catalog file.
487: */
488: public Vector resolveAllSystem(String systemId)
489: throws MalformedURLException, IOException {
490: Vector resolutions = new Vector();
491:
492: // If there are SYSTEM entries in this catalog, start with them
493: if (systemId != null) {
494: Vector localResolutions = resolveAllLocalSystem(systemId);
495: resolutions = appendVector(resolutions, localResolutions);
496: }
497:
498: // Then look in the subordinate catalogs
499: Vector subResolutions = resolveAllSubordinateCatalogs(SYSTEM,
500: null, null, systemId);
501: resolutions = appendVector(resolutions, subResolutions);
502:
503: if (resolutions.size() > 0) {
504: return resolutions;
505: } else {
506: return null;
507: }
508: }
509:
510: /**
511: * Return all applicable SYSTEM system identifiers in this
512: * catalog.
513: *
514: * <p>If one or more SYSTEM entries exists in the catalog file
515: * for the system ID specified, return the mapped values.</p>
516: *
517: * @param systemId The system ID to locate in the catalog
518: *
519: * @return A vector of the mapped system identifiers or null
520: */
521: private Vector resolveAllLocalSystem(String systemId) {
522: Vector map = new Vector();
523: String osname = System.getProperty("os.name");
524: boolean windows = (osname.indexOf("Windows") >= 0);
525: Enumeration en = catalogEntries.elements();
526: while (en.hasMoreElements()) {
527: CatalogEntry e = (CatalogEntry) en.nextElement();
528: if (e.getEntryType() == SYSTEM
529: && (e.getEntryArg(0).equals(systemId) || (windows && e
530: .getEntryArg(0).equalsIgnoreCase(systemId)))) {
531: map.addElement(e.getEntryArg(1));
532: }
533: }
534: if (map.size() == 0) {
535: return null;
536: } else {
537: return map;
538: }
539: }
540:
541: /**
542: * Find the URNs for a given system identifier in the current catalog.
543: *
544: * @param systemId The system ID to locate.
545: *
546: * @return A vector of URNs that map to the systemId.
547: */
548: private Vector resolveLocalSystemReverse(String systemId) {
549: Vector map = new Vector();
550: String osname = System.getProperty("os.name");
551: boolean windows = (osname.indexOf("Windows") >= 0);
552: Enumeration en = catalogEntries.elements();
553: while (en.hasMoreElements()) {
554: CatalogEntry e = (CatalogEntry) en.nextElement();
555: if (e.getEntryType() == SYSTEM
556: && (e.getEntryArg(1).equals(systemId) || (windows && e
557: .getEntryArg(1).equalsIgnoreCase(systemId)))) {
558: map.addElement(e.getEntryArg(0));
559: }
560: }
561: if (map.size() == 0) {
562: return null;
563: } else {
564: return map;
565: }
566: }
567:
568: /**
569: * Search the subordinate catalogs, in order, looking for all
570: * match.
571: *
572: * <p>This method searches the Catalog and returns all of the system
573: * identifiers specified for the given entity type with the given
574: * name, public, and system identifiers. In some contexts, these
575: * may be null.</p>
576: *
577: * @param entityType The CatalogEntry type for which this query is
578: * being conducted. This is necessary in order to do the approprate
579: * query on a subordinate catalog.
580: * @param entityName The name of the entity being searched for, if
581: * appropriate.
582: * @param publicId The public identifier of the entity in question
583: * (as provided in the source document).
584: * @param systemId The nominal system identifier for the entity
585: * in question (as provided in the source document).
586: *
587: * @throws MalformedURLException The formal system identifier of a
588: * delegated catalog cannot be turned into a valid URL.
589: * @throws IOException Error reading delegated catalog file.
590: *
591: * @return The system identifier to use.
592: * Note that the nominal system identifier is not returned if a
593: * match is not found in the catalog, instead null is returned
594: * to indicate that no match was found.
595: */
596: private synchronized Vector resolveAllSubordinateCatalogs(
597: int entityType, String entityName, String publicId,
598: String systemId) throws MalformedURLException, IOException {
599:
600: Vector resolutions = new Vector();
601:
602: for (int catPos = 0; catPos < catalogs.size(); catPos++) {
603: Resolver c = null;
604:
605: try {
606: c = (Resolver) catalogs.elementAt(catPos);
607: } catch (ClassCastException e) {
608: String catfile = (String) catalogs.elementAt(catPos);
609: c = (Resolver) newCatalog();
610:
611: try {
612: c.parseCatalog(catfile);
613: } catch (MalformedURLException mue) {
614: catalogManager.debug.message(1,
615: "Malformed Catalog URL", catfile);
616: } catch (FileNotFoundException fnfe) {
617: catalogManager.debug.message(1,
618: "Failed to load catalog, file not found",
619: catfile);
620: } catch (IOException ioe) {
621: catalogManager.debug.message(1,
622: "Failed to load catalog, I/O error",
623: catfile);
624: }
625:
626: catalogs.setElementAt(c, catPos);
627: }
628:
629: String resolved = null;
630:
631: // Ok, now what are we supposed to call here?
632: if (entityType == DOCTYPE) {
633: resolved = c.resolveDoctype(entityName, publicId,
634: systemId);
635: if (resolved != null) {
636: // Only find one DOCTYPE resolution
637: resolutions.addElement(resolved);
638: return resolutions;
639: }
640: } else if (entityType == DOCUMENT) {
641: resolved = c.resolveDocument();
642: if (resolved != null) {
643: // Only find one DOCUMENT resolution
644: resolutions.addElement(resolved);
645: return resolutions;
646: }
647: } else if (entityType == ENTITY) {
648: resolved = c.resolveEntity(entityName, publicId,
649: systemId);
650: if (resolved != null) {
651: // Only find one ENTITY resolution
652: resolutions.addElement(resolved);
653: return resolutions;
654: }
655: } else if (entityType == NOTATION) {
656: resolved = c.resolveNotation(entityName, publicId,
657: systemId);
658: if (resolved != null) {
659: // Only find one NOTATION resolution
660: resolutions.addElement(resolved);
661: return resolutions;
662: }
663: } else if (entityType == PUBLIC) {
664: resolved = c.resolvePublic(publicId, systemId);
665: if (resolved != null) {
666: // Only find one PUBLIC resolution
667: resolutions.addElement(resolved);
668: return resolutions;
669: }
670: } else if (entityType == SYSTEM) {
671: Vector localResolutions = c.resolveAllSystem(systemId);
672: resolutions = appendVector(resolutions,
673: localResolutions);
674: break;
675: } else if (entityType == SYSTEMREVERSE) {
676: Vector localResolutions = c
677: .resolveAllSystemReverse(systemId);
678: resolutions = appendVector(resolutions,
679: localResolutions);
680: }
681: }
682:
683: if (resolutions != null) {
684: return resolutions;
685: } else {
686: return null;
687: }
688: }
689: }
|