KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > org > apache > xml > internal > resolver > Resolver


1 // Resolver.java - Represents an extension of OASIS Open Catalog files.
2

3 /*
4  * Copyright 2001-2004 The Apache Software Foundation or its licensors,
5  * as applicable.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */

19
20 package com.sun.org.apache.xml.internal.resolver;
21
22 import java.io.IOException JavaDoc;
23 import java.io.InputStream JavaDoc;
24 import java.io.FileNotFoundException JavaDoc;
25 import java.util.Enumeration JavaDoc;
26 import java.util.Vector JavaDoc;
27 import java.net.URL JavaDoc;
28 import java.net.URLConnection JavaDoc;
29 import java.net.MalformedURLException JavaDoc;
30 import com.sun.org.apache.xml.internal.resolver.readers.SAXCatalogReader;
31 import com.sun.org.apache.xml.internal.resolver.readers.OASISXMLCatalogReader;
32 import com.sun.org.apache.xml.internal.resolver.readers.TR9401CatalogReader;
33 import javax.xml.parsers.SAXParserFactory JavaDoc;
34
35 /**
36  * An extension to OASIS Open Catalog files, this class supports
37  * suffix-based matching and an external RFC2483 resolver.
38  *
39  * @see Catalog
40  *
41  * @author Norman Walsh
42  * <a HREF="mailto:Norman.Walsh@Sun.COM">Norman.Walsh@Sun.COM</a>
43  *
44  * @version 1.0
45  */

46 public class Resolver extends Catalog {
47   /**
48    * The URISUFFIX Catalog Entry type.
49    *
50    * <p>URI suffix entries match URIs that end in a specified suffix.</p>
51    */

52   public static final int URISUFFIX = CatalogEntry.addEntryType("URISUFFIX", 2);
53
54   /**
55    * The SYSTEMSUFFIX Catalog Entry type.
56    *
57    * <p>System suffix entries match system identifiers that end in a
58    * specified suffix.</p>
59    */

60   public static final int SYSTEMSUFFIX = CatalogEntry.addEntryType("SYSTEMSUFFIX", 2);
61
62   /**
63    * The RESOLVER Catalog Entry type.
64    *
65    * <p>A hook for providing support for web-based backup resolvers.</p>
66    */

67   public static final int RESOLVER = CatalogEntry.addEntryType("RESOLVER", 1);
68
69   /**
70    * The SYSTEMREVERSE Catalog Entry type.
71    *
72    * <p>This is a bit of a hack. There's no actual SYSTEMREVERSE entry,
73    * but this entry type is used to indicate that a reverse lookup is
74    * being performed. (This allows the Resolver to implement
75    * RFC2483 I2N and I2NS.)
76    */

77   public static final int SYSTEMREVERSE
78     = CatalogEntry.addEntryType("SYSTEMREVERSE", 1);
79
80   /**
81    * Setup readers.
82    */

83   public void setupReaders() {
84     SAXParserFactory JavaDoc spf = SAXParserFactory.newInstance();
85     spf.setNamespaceAware(true);
86     spf.setValidating(false);
87
88     SAXCatalogReader saxReader = new SAXCatalogReader(spf);
89
90     saxReader.setCatalogParser(null, "XMLCatalog",
91                    "com.sun.org.apache.xml.internal.resolver.readers.XCatalogReader");
92
93     saxReader.setCatalogParser(OASISXMLCatalogReader.namespaceName,
94                    "catalog",
95                    "com.sun.org.apache.xml.internal.resolver.readers.ExtendedXMLCatalogReader");
96
97     addReader("application/xml", saxReader);
98
99     TR9401CatalogReader textReader = new TR9401CatalogReader();
100     addReader("text/plain", textReader);
101   }
102
103   /**
104    * Cleanup and process a Catalog entry.
105    *
106    * <p>This method processes each Catalog entry, changing mapped
107    * relative system identifiers into absolute ones (based on the current
108    * base URI), and maintaining other information about the current
109    * catalog.</p>
110    *
111    * @param entry The CatalogEntry to process.
112    */

113   public void addEntry(CatalogEntry entry) {
114     int type = entry.getEntryType();
115
116     if (type == URISUFFIX) {
117       String JavaDoc suffix = normalizeURI(entry.getEntryArg(0));
118       String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
119
120       entry.setEntryArg(1, fsi);
121
122       catalogManager.debug.message(4, "URISUFFIX", suffix, fsi);
123     } else if (type == SYSTEMSUFFIX) {
124       String JavaDoc suffix = normalizeURI(entry.getEntryArg(0));
125       String JavaDoc fsi = makeAbsolute(normalizeURI(entry.getEntryArg(1)));
126
127       entry.setEntryArg(1, fsi);
128
129       catalogManager.debug.message(4, "SYSTEMSUFFIX", suffix, fsi);
130     }
131
132     super.addEntry(entry);
133   }
134
135   /**
136    * Return the applicable URI.
137    *
138    * <p>If a URI entry exists in the Catalog
139    * for the URI specified, return the mapped value.</p>
140    *
141    * <p>In the Resolver (as opposed to the Catalog) class, if the
142    * URI isn't found by the usual algorithm, URISUFFIX entries are
143    * considered.</p>
144    *
145    * <p>URI comparison is case sensitive.</p>
146    *
147    * @param uri The URI to locate in the catalog.
148    *
149    * @return The resolved URI.
150    *
151    * @throws MalformedURLException The system identifier of a
152    * subordinate catalog cannot be turned into a valid URL.
153    * @throws IOException Error reading subordinate catalog file.
154    */

155   public String JavaDoc resolveURI(String JavaDoc uri)
156     throws MalformedURLException JavaDoc, IOException JavaDoc {
157
158     String JavaDoc resolved = super.resolveURI(uri);
159     if (resolved != null) {
160       return resolved;
161     }
162
163     Enumeration JavaDoc en = catalogEntries.elements();
164     while (en.hasMoreElements()) {
165       CatalogEntry e = (CatalogEntry) en.nextElement();
166       if (e.getEntryType() == RESOLVER) {
167     resolved = resolveExternalSystem(uri, e.getEntryArg(0));
168     if (resolved != null) {
169       return resolved;
170     }
171       } else if (e.getEntryType() == URISUFFIX) {
172     String JavaDoc suffix = e.getEntryArg(0);
173     String JavaDoc result = e.getEntryArg(1);
174
175     if (suffix.length() <= uri.length()
176         && uri.substring(uri.length()-suffix.length()).equals(suffix)) {
177       return result;
178     }
179       }
180     }
181
182     // Otherwise, look in the subordinate catalogs
183
return resolveSubordinateCatalogs(Catalog.URI,
184                       null,
185                       null,
186                       uri);
187   }
188
189   /**
190    * Return the applicable SYSTEM system identifier, resorting
191    * to external RESOLVERs if necessary.
192    *
193    * <p>If a SYSTEM entry exists in the Catalog
194    * for the system ID specified, return the mapped value.</p>
195    *
196    * <p>In the Resolver (as opposed to the Catalog) class, if the
197    * URI isn't found by the usual algorithm, SYSTEMSUFFIX entries are
198    * considered.</p>
199    *
200    * <p>On Windows-based operating systems, the comparison between
201    * the system identifier provided and the SYSTEM entries in the
202    * Catalog is case-insensitive.</p>
203    *
204    * @param systemId The system ID to locate in the catalog.
205    *
206    * @return The system identifier to use for systemId.
207    *
208    * @throws MalformedURLException The formal system identifier of a
209    * subordinate catalog cannot be turned into a valid URL.
210    * @throws IOException Error reading subordinate catalog file.
211    */

212   public String JavaDoc resolveSystem(String JavaDoc systemId)
213     throws MalformedURLException JavaDoc, IOException JavaDoc {
214
215     String JavaDoc resolved = super.resolveSystem(systemId);
216     if (resolved != null) {
217       return resolved;
218     }
219
220     Enumeration JavaDoc en = catalogEntries.elements();
221     while (en.hasMoreElements()) {
222       CatalogEntry e = (CatalogEntry) en.nextElement();
223       if (e.getEntryType() == RESOLVER) {
224     resolved = resolveExternalSystem(systemId, e.getEntryArg(0));
225     if (resolved != null) {
226       return resolved;
227     }
228       } else if (e.getEntryType() == SYSTEMSUFFIX) {
229     String JavaDoc suffix = e.getEntryArg(0);
230     String JavaDoc result = e.getEntryArg(1);
231
232     if (suffix.length() <= systemId.length()
233         && systemId.substring(systemId.length()-suffix.length()).equals(suffix)) {
234       return result;
235     }
236       }
237     }
238
239     return resolveSubordinateCatalogs(Catalog.SYSTEM,
240                       null,
241                       null,
242                       systemId);
243   }
244
245   /**
246    * Return the applicable PUBLIC or SYSTEM identifier, resorting
247    * to external resolvers if necessary.
248    *
249    * <p>This method searches the Catalog and returns the system
250    * identifier specified for the given system or
251    * public identifiers. If
252    * no appropriate PUBLIC or SYSTEM entry is found in the Catalog,
253    * null is returned.</p>
254    *
255    * <p>Note that a system or public identifier in the current catalog
256    * (or subordinate catalogs) will be used in preference to an
257    * external resolver. Further, if a systemId is present, the external
258    * resolver(s) will be queried for that before the publicId.</p>
259    *
260    * @param publicId The public identifier to locate in the catalog.
261    * Public identifiers are normalized before comparison.
262    * @param systemId The nominal system identifier for the entity
263    * in question (as provided in the source document).
264    *
265    * @throws MalformedURLException The formal system identifier of a
266    * subordinate catalog cannot be turned into a valid URL.
267    * @throws IOException Error reading subordinate catalog file.
268    *
269    * @return The system identifier to use.
270    * Note that the nominal system identifier is not returned if a
271    * match is not found in the catalog, instead null is returned
272    * to indicate that no match was found.
273    */

274   public String JavaDoc resolvePublic(String JavaDoc publicId, String JavaDoc systemId)
275     throws MalformedURLException JavaDoc, IOException JavaDoc {
276
277     String JavaDoc resolved = super.resolvePublic(publicId, systemId);
278     if (resolved != null) {
279       return resolved;
280     }
281
282     Enumeration JavaDoc en = catalogEntries.elements();
283     while (en.hasMoreElements()) {
284       CatalogEntry e = (CatalogEntry) en.nextElement();
285       if (e.getEntryType() == RESOLVER) {
286     if (systemId != null) {
287       resolved = resolveExternalSystem(systemId,
288                        e.getEntryArg(0));
289       if (resolved != null) {
290         return resolved;
291       }
292     }
293     resolved = resolveExternalPublic(publicId, e.getEntryArg(0));
294     if (resolved != null) {
295       return resolved;
296     }
297       }
298     }
299
300     return resolveSubordinateCatalogs(Catalog.PUBLIC,
301                       null,
302                       publicId,
303                       systemId);
304   }
305
306     /**
307      * Query an external RFC2483 resolver for a system identifier.
308      *
309      * @param systemId The system ID to locate.
310      * @param resolver The name of the resolver to use.
311      *
312      * @return The system identifier to use for the systemId.
313      */

314     protected String JavaDoc resolveExternalSystem(String JavaDoc systemId, String JavaDoc resolver)
315     throws MalformedURLException JavaDoc, IOException JavaDoc {
316     Resolver r = queryResolver(resolver, "i2l", systemId, null);
317     if (r != null) {
318         return r.resolveSystem(systemId);
319     } else {
320         return null;
321     }
322     }
323
324     /**
325      * Query an external RFC2483 resolver for a public identifier.
326      *
327      * @param publicId The system ID to locate.
328      * @param resolver The name of the resolver to use.
329      *
330      * @return The system identifier to use for the systemId.
331      */

332     protected String JavaDoc resolveExternalPublic(String JavaDoc publicId, String JavaDoc resolver)
333     throws MalformedURLException JavaDoc, IOException JavaDoc {
334     Resolver r = queryResolver(resolver, "fpi2l", publicId, null);
335     if (r != null) {
336         return r.resolvePublic(publicId, null);
337     } else {
338         return null;
339     }
340     }
341
342     /**
343      * Query an external RFC2483 resolver.
344      *
345      * @param resolver The URL of the RFC2483 resolver.
346      * @param command The command to send the resolver.
347      * @param arg1 The first argument to the resolver.
348      * @param arg2 The second argument to the resolver, usually null.
349      *
350      * @return The Resolver constructed.
351      */

352     protected Resolver queryResolver(String JavaDoc resolver,
353                      String JavaDoc command,
354                      String JavaDoc arg1,
355                      String JavaDoc arg2) {
356     InputStream JavaDoc iStream = null;
357     String JavaDoc RFC2483 = resolver + "?command=" + command
358         + "&format=tr9401&uri=" + arg1
359         + "&uri2=" + arg2;
360     String JavaDoc line = null;
361
362     try {
363         URL JavaDoc url = new URL JavaDoc(RFC2483);
364
365         URLConnection JavaDoc urlCon = url.openConnection();
366
367         urlCon.setUseCaches(false);
368
369         Resolver r = (Resolver) newCatalog();
370
371         String JavaDoc cType = urlCon.getContentType();
372
373         // I don't care about the character set or subtype
374
if (cType.indexOf(";") > 0) {
375         cType = cType.substring(0, cType.indexOf(";"));
376         }
377
378         r.parseCatalog(cType, urlCon.getInputStream());
379
380         return r;
381     } catch (CatalogException cex) {
382       if (cex.getExceptionType() == CatalogException.UNPARSEABLE) {
383         catalogManager.debug.message(1, "Unparseable catalog: " + RFC2483);
384       } else if (cex.getExceptionType()
385              == CatalogException.UNKNOWN_FORMAT) {
386         catalogManager.debug.message(1, "Unknown catalog format: " + RFC2483);
387       }
388       return null;
389     } catch (MalformedURLException JavaDoc mue) {
390         catalogManager.debug.message(1, "Malformed resolver URL: " + RFC2483);
391         return null;
392     } catch (IOException JavaDoc ie) {
393         catalogManager.debug.message(1, "I/O Exception opening resolver: " + RFC2483);
394         return null;
395     }
396     }
397
398     /**
399      * Append two vectors, returning the result.
400      *
401      * @param vec The first vector
402      * @param appvec The vector to be appended
403      * @return The vector vec, with appvec's elements appended to it
404      */

405     private Vector JavaDoc appendVector(Vector JavaDoc vec, Vector JavaDoc appvec) {
406     if (appvec != null) {
407         for (int count = 0; count < appvec.size(); count++) {
408         vec.addElement(appvec.elementAt(count));
409         }
410     }
411     return vec;
412     }
413
414     /**
415      * Find the URNs for a given system identifier in all catalogs.
416      *
417      * @param systemId The system ID to locate.
418      *
419      * @return A vector of URNs that map to the systemId.
420      */

421     public Vector JavaDoc resolveAllSystemReverse(String JavaDoc systemId)
422     throws MalformedURLException JavaDoc, IOException JavaDoc {
423     Vector JavaDoc resolved = new Vector JavaDoc();
424
425     // If there's a SYSTEM entry in this catalog, use it
426
if (systemId != null) {
427         Vector JavaDoc localResolved = resolveLocalSystemReverse(systemId);
428         resolved = appendVector(resolved, localResolved);
429     }
430
431     // Otherwise, look in the subordinate catalogs
432
Vector JavaDoc subResolved = resolveAllSubordinateCatalogs(SYSTEMREVERSE,
433                                null,
434                                null,
435                                systemId);
436
437     return appendVector(resolved, subResolved);
438     }
439
440     /**
441      * Find the URN for a given system identifier.
442      *
443      * @param systemId The system ID to locate.
444      *
445      * @return A (single) URN that maps to the systemId.
446      */

447     public String JavaDoc resolveSystemReverse(String JavaDoc systemId)
448     throws MalformedURLException JavaDoc, IOException JavaDoc {
449     Vector JavaDoc resolved = resolveAllSystemReverse(systemId);
450     if (resolved != null && resolved.size() > 0) {
451         return (String JavaDoc) resolved.elementAt(0);
452     } else {
453         return null;
454     }
455     }
456
457     /**
458      * Return the applicable SYSTEM system identifiers.
459      *
460      * <p>If one or more SYSTEM entries exists in the Catalog
461      * for the system ID specified, return the mapped values.</p>
462      *
463      * <p>The caller is responsible for doing any necessary
464      * normalization of the system identifier before calling
465      * this method. For example, a relative system identifier in
466      * a document might be converted to an absolute system identifier
467      * before attempting to resolve it.</p>
468      *
469      * <p>Note that this function will force all subordinate catalogs
470      * to be loaded.</p>
471      *
472      * <p>On Windows-based operating systems, the comparison between
473      * the system identifier provided and the SYSTEM entries in the
474      * Catalog is case-insensitive.</p>
475      *
476      * @param systemId The system ID to locate in the catalog.
477      *
478      * @return The system identifier to use for the notation.
479      *
480      * @throws MalformedURLException The formal system identifier of a
481      * subordinate catalog cannot be turned into a valid URL.
482      * @throws IOException Error reading subordinate catalog file.
483      */

484     public Vector JavaDoc resolveAllSystem(String JavaDoc systemId)
485     throws MalformedURLException JavaDoc, IOException JavaDoc {
486     Vector JavaDoc resolutions = new Vector JavaDoc();
487
488     // If there are SYSTEM entries in this catalog, start with them
489
if (systemId != null) {
490         Vector JavaDoc localResolutions = resolveAllLocalSystem(systemId);
491         resolutions = appendVector(resolutions, localResolutions);
492     }
493
494     // Then look in the subordinate catalogs
495
Vector JavaDoc subResolutions = resolveAllSubordinateCatalogs(SYSTEM,
496                                   null,
497                                   null,
498                                   systemId);
499     resolutions = appendVector(resolutions, subResolutions);
500
501     if (resolutions.size() > 0) {
502         return resolutions;
503     } else {
504         return null;
505     }
506     }
507
508     /**
509      * Return all applicable SYSTEM system identifiers in this
510      * catalog.
511      *
512      * <p>If one or more SYSTEM entries exists in the catalog file
513      * for the system ID specified, return the mapped values.</p>
514      *
515      * @param systemId The system ID to locate in the catalog
516      *
517      * @return A vector of the mapped system identifiers or null
518      */

519     private Vector JavaDoc resolveAllLocalSystem(String JavaDoc systemId) {
520     Vector JavaDoc map = new Vector JavaDoc();
521     String JavaDoc osname = System.getProperty("os.name");
522     boolean windows = (osname.indexOf("Windows") >= 0);
523     Enumeration JavaDoc en = catalogEntries.elements();
524     while (en.hasMoreElements()) {
525         CatalogEntry e = (CatalogEntry) en.nextElement();
526         if (e.getEntryType() == SYSTEM
527         && (e.getEntryArg(0).equals(systemId)
528             || (windows
529             && e.getEntryArg(0).equalsIgnoreCase(systemId)))) {
530         map.addElement(e.getEntryArg(1));
531         }
532     }
533     if (map.size() == 0) {
534         return null;
535     } else {
536         return map;
537     }
538     }
539
540     /**
541      * Find the URNs for a given system identifier in the current catalog.
542      *
543      * @param systemId The system ID to locate.
544      *
545      * @return A vector of URNs that map to the systemId.
546      */

547     private Vector JavaDoc resolveLocalSystemReverse(String JavaDoc systemId) {
548     Vector JavaDoc map = new Vector JavaDoc();
549     String JavaDoc osname = System.getProperty("os.name");
550     boolean windows = (osname.indexOf("Windows") >= 0);
551     Enumeration JavaDoc en = catalogEntries.elements();
552     while (en.hasMoreElements()) {
553         CatalogEntry e = (CatalogEntry) en.nextElement();
554         if (e.getEntryType() == SYSTEM
555         && (e.getEntryArg(1).equals(systemId)
556             || (windows
557             && e.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 JavaDoc resolveAllSubordinateCatalogs(int entityType,
597                           String JavaDoc entityName,
598                           String JavaDoc publicId,
599                           String JavaDoc systemId)
600     throws MalformedURLException JavaDoc, IOException JavaDoc {
601
602     Vector JavaDoc resolutions = new Vector JavaDoc();
603
604     for (int catPos = 0; catPos < catalogs.size(); catPos++) {
605         Resolver c = null;
606
607         try {
608         c = (Resolver) catalogs.elementAt(catPos);
609         } catch (ClassCastException JavaDoc e) {
610         String JavaDoc catfile = (String JavaDoc) catalogs.elementAt(catPos);
611         c = (Resolver) newCatalog();
612
613         try {
614             c.parseCatalog(catfile);
615         } catch (MalformedURLException JavaDoc mue) {
616             catalogManager.debug.message(1, "Malformed Catalog URL", catfile);
617         } catch (FileNotFoundException JavaDoc fnfe) {
618             catalogManager.debug.message(1, "Failed to load catalog, file not found",
619               catfile);
620         } catch (IOException JavaDoc ioe) {
621             catalogManager.debug.message(1, "Failed to load catalog, I/O error", catfile);
622         }
623
624         catalogs.setElementAt(c, catPos);
625         }
626
627         String JavaDoc resolved = null;
628
629         // Ok, now what are we supposed to call here?
630
if (entityType == DOCTYPE) {
631         resolved = c.resolveDoctype(entityName,
632                         publicId,
633                         systemId);
634         if (resolved != null) {
635             // Only find one DOCTYPE resolution
636
resolutions.addElement(resolved);
637             return resolutions;
638         }
639         } else if (entityType == DOCUMENT) {
640         resolved = c.resolveDocument();
641         if (resolved != null) {
642             // Only find one DOCUMENT resolution
643
resolutions.addElement(resolved);
644             return resolutions;
645         }
646         } else if (entityType == ENTITY) {
647         resolved = c.resolveEntity(entityName,
648                        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,
657                          publicId,
658                          systemId);
659         if (resolved != null) {
660             // Only find one NOTATION resolution
661
resolutions.addElement(resolved);
662             return resolutions;
663         }
664         } else if (entityType == PUBLIC) {
665         resolved = c.resolvePublic(publicId, systemId);
666         if (resolved != null) {
667             // Only find one PUBLIC resolution
668
resolutions.addElement(resolved);
669             return resolutions;
670         }
671         } else if (entityType == SYSTEM) {
672         Vector JavaDoc localResolutions = c.resolveAllSystem(systemId);
673         resolutions = appendVector(resolutions, localResolutions);
674         break;
675         } else if (entityType == SYSTEMREVERSE) {
676         Vector JavaDoc localResolutions = c.resolveAllSystemReverse(systemId);
677         resolutions = appendVector(resolutions, localResolutions);
678         }
679     }
680
681     if (resolutions != null) {
682         return resolutions;
683     } else {
684         return null;
685     }
686     }
687 }
688
689
690
691
692
Popular Tags