KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > xml > catalog > impl > XCatalog


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.netbeans.modules.xml.catalog.impl;
20
21 import java.awt.*;
22 import java.io.*;
23 import java.beans.*;
24 import java.util.*;
25
26 import org.xml.sax.*;
27 import org.xml.sax.helpers.*;
28
29 import org.openide.util.*;
30 import org.openide.xml.XMLUtil;
31
32 import org.netbeans.modules.xml.catalog.spi.*;
33 import org.netbeans.modules.xml.catalog.lib.*;
34
35 /**
36  * This catalog supports the XCatalog 0.2 and XML Catalog 0.4 proposal as
37  * described at
38  * <a HREF="http://www.ccil.org/~cowan/XML/XCatalog.html">draft</a>.
39  *
40  * @author Petr Kuzel
41  */

42 public final class XCatalog extends AbstractCatalog
43        implements CatalogReader, CatalogDescriptor, Serializable, EntityResolver {
44     
45     /** Serial Version UID MUST NOT change. */
46     private static final long serialVersionUID = 06022001L;
47     
48
49     // ~~~~~~~~~~~~~~~ 0.4 grammar ~~~~~~~~~~~~~~~~~~
50
public static final String JavaDoc DTD_PUBLIC_ID_4 = "-//DTD XMLCatalog//EN"; // NOI18N
51

52     /** XCatalog DTD resource name ("xcatalog.dtd"). */
53     static final String JavaDoc DTD = "xcatalog.dtd"; // NOI18N
54

55     // tag and attribute names
56

57     /** PublicID attribute name ("PublicID"). */
58     static final String JavaDoc PUBLICID_ATT_4 = "PublicId"; // NOI18N
59

60     /** SystemID attribute name ("SystemID"). */
61     static final String JavaDoc SYSTEMID_ATT_4 = "SystemID"; // NOI18N
62

63     // ~~~~~~~~~~~~~~~~~~~~ 0.2 grammar ~~~~~~~~~~~~~~~~~~~
64

65     public static final String JavaDoc DTD_PUBLIC_ID_2 = "-//DTD XCatalog//EN"; // NOI18N
66

67     /** XCatalog element name ("XCatalog"). */
68     static final String JavaDoc XCATALOG_2 = "XMLCatalog"; // NOI18N
69

70     /** XML Catalog version */
71     static final String JavaDoc VERSION_2 = "Version"; // NOI18N
72

73     
74     /** PublicID attribute name ("PublicID"). */
75     static final String JavaDoc PUBLICID_ATT_2 = "PublicID"; // NOI18N
76

77     /** SystemID attribute name ("SystemID"). */
78     static final String JavaDoc SYSTEMID_ATT_2 = "SystemID"; // NOI18N
79

80     // ~~~~~~~~~~~~~~~~~~~ 0.X grammar ~~~~~~~~~~~~~~~~~~~~`
81

82     static final String JavaDoc MAP = "Map"; // NOI18N
83

84     static final String JavaDoc EXTEND = "Extend"; // NOI18N
85

86     static final String JavaDoc BASE = "Base"; // NOI18N
87

88     static final String JavaDoc DELEGATE = "Delegate"; // NOI18N
89

90     static final String JavaDoc REMAP = "Remap"; // NOI18N
91

92     static final String JavaDoc HREF_ATT = "HRef"; // NOI18N
93

94     
95     /**
96      * @serial Only catalog location is serialized.
97      */

98     private String JavaDoc catalogSrc = null;
99     
100     private transient String JavaDoc shortDescription;
101     
102     private transient Image icon;
103     
104     
105     // INIT ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
106

107     
108     /**
109      * Constructs an XCatalog instance.
110      */

111     public XCatalog() {
112     }
113
114     /**
115      * Deserialization constructor.
116      */

117     private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException JavaDoc {
118         in.defaultReadObject();
119         loadCatalog(catalogSrc); //!!! loading catalog during deserialization. Could be deferred
120
}
121
122     
123     // Catalog loading (and error handling) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
124

125     
126     /**
127      * Load catalog from given URL.
128      */

129     public void loadCatalog(String JavaDoc systemID) {
130         try {
131
132             clearAll();
133             
134             if (systemID == null) return; //balk it
135

136             new CatalogParser(new InputSource(systemID));
137             
138             updateShortDescription(catalogSrc);
139             updateIcon(getDefaultIcon(0)); //!!! icon type should be deffered
140

141             
142         } catch (SAXException ex) {
143             handleLoadError(ex);
144         } catch (IOException ex) {
145             handleLoadError(ex);
146         } finally {
147             notifyInvalidate();
148         }
149     }
150     
151     
152     private void handleLoadError(Exception JavaDoc ex) {
153         updateShortDescription(ex.getLocalizedMessage());
154         updateIcon(getDefaultErrorIcon(0)); //!!!
155

156         if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("Can not read: " + shortDescription); // NOI18N
157
}
158
159
160     /** Update and fire. */
161     private void updateShortDescription(String JavaDoc loc) {
162         String JavaDoc old = shortDescription;
163         shortDescription = loc;
164         firePropertyChange(PROP_CATALOG_DESC, old, shortDescription);
165     }
166     
167     /** Update and fire. */
168     private void updateIcon(Image newIcon) {
169         Image old = icon;
170         icon = newIcon;
171         firePropertyChange(PROP_CATALOG_ICON, old, icon);
172     }
173     
174     // Properties (serialized) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
175

176     /**
177      * Set catalog source (URL) to be used. If specified invalid source
178      * then no catalog is loaded update short description and icon.
179      *
180      * @param source should be be URL pointing to valid XML Catalog file
181      */

182     public void setSource(String JavaDoc source) {
183         catalogSrc = source;
184         loadCatalog(source);
185         firePropertyChange(PROP_CATALOG_NAME, null, getDisplayName());
186     }
187     
188     /**
189      * @return currently catalog location URL, the URL may be invalid
190      */

191     public String JavaDoc getSource() {
192         return catalogSrc;
193     }
194
195
196     // Catalog Reader Interface ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
197

198     /**
199      * Reload the catalog from its original location.
200      */

201     public void refresh() {
202         if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("Refreshing catalog...impl..."); // NOI18N
203

204         loadCatalog(getSource());
205     }
206     
207     
208     // Catalog Descriptor Interface ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
209

210     /**
211      * Return display name of this reader (e.g. "XCatalog Reader" or "SOCAT Reader"
212      */

213     public String JavaDoc getDisplayName() {
214         String JavaDoc location = catalogSrc;
215         if (location == null || "".equals(location.trim())) {
216             return Util.THIS.getString("PROP_missing_location");
217         } else {
218             return Util.THIS.getString("PROP_display_name", catalogSrc);
219         }
220     }
221         
222     public Image getIcon(int type) {
223         return icon;
224     }
225     
226     public String JavaDoc getShortDescription() {
227         return shortDescription;
228     }
229     
230
231     public String JavaDoc toString() {
232         return super.toString() + ":" + catalogSrc; // NOI18N
233
}
234
235 /* We must behave like immutable key!
236     public boolean equals(Object obj) {
237         if (obj instanceof XCatalog) {
238             XCatalog cat = (XCatalog) obj;
239             if (catalogSrc == null) return false;
240             return catalogSrc.equals(cat.catalogSrc);
241         }
242         return false;
243     }
244     
245
246     public int hashCode() {
247         return catalogSrc != null ? catalogSrc.hashCode() : 0;
248     }
249     
250 */

251     
252     // Entity resolver interface ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
253

254         
255     /**
256      * Resolves external entities using this catalog.
257      * @see org.xml.sax.EntityResolver#resolveEntity(String,String)
258      */

259     public InputSource resolveEntity(String JavaDoc publicId, String JavaDoc systemId) throws SAXException, IOException {
260         
261         if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("resolveEntity(\""+publicId+"\", \""+systemId+"\")"); // NOI18N
262

263         // public identifier resolution
264
if (publicId != null) {
265             
266             // direct public id mappings
267
String JavaDoc value = getPublicMapping(publicId);
268
269             if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug(" map: \""+publicId+"\" -> \""+value+"\""); // NOI18N
270

271             if (value != null) {
272                 InputSource source = resolveEntity(null, value);
273                 if (source == null) {
274                     source = new InputSource(value);
275                 }
276                 source.setPublicId(publicId);
277                 return source;
278             }
279             
280             // delegates
281
Enumeration delegates = getDelegateCatalogKeys();
282             while (delegates.hasMoreElements()) {
283                 String JavaDoc key = (String JavaDoc)delegates.nextElement();
284
285                 if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug(" delegate: \""+key+"\""); // NOI18N
286

287                 if (publicId.startsWith(key)) {
288                     AbstractCatalog catalog = getDelegateCatalog(key);
289                     InputSource source = catalog.resolveEntity(publicId, systemId);
290                     if (source != null) {
291                         return source;
292                     }
293                 }
294             }
295         }
296         
297         // system identifier resolution
298
String JavaDoc value = getSystemMapping(systemId);
299         if (value != null) {
300             if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug(" remap: \""+systemId+"\" -> \""+value+"\""); // NOI18N
301

302             InputSource source = new InputSource(value);
303             source.setPublicId(publicId);
304             return source;
305         }
306         
307
308         // try extenders
309
Iterator it = extenders.iterator();
310         while (it.hasNext()) {
311             XCatalog cat = (XCatalog) it.next();
312             InputSource mytry = cat.resolveEntity(publicId, systemId);
313             if (mytry != null) return mytry;
314         }
315
316         // use default behavior
317
if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug(" returning null!"); // NOI18N
318

319         return null;
320         
321     }
322     
323
324     
325     // Catalog parsing classes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
326

327     /**
328      * Parser for XML Catalog document instances.
329      * SAX 2 is used only to avoid deprecation messages.
330      */

331     private class CatalogParser extends DefaultHandler {
332                 
333         /** Current base. */
334         private String JavaDoc base;
335                 
336         /** Parses the specified input source. */
337         public CatalogParser(InputSource source) throws SAXException, IOException {
338             
339             XMLReader parser = XMLUtil.createXMLReader(true);
340             
341             // setup parser
342
parser.setEntityResolver(new Resolver()); // overwrite system entity resolver
343
parser.setContentHandler(this);
344             parser.setErrorHandler(this);
345             
346             // set initial base and parse
347
setBase(source.getSystemId());
348             parser.parse(source);
349             
350         }
351         
352         
353         /**
354          * Sets the base from the given system identifier. The base is
355          * the same as the system identifier with the least significant
356          * part (the filename) removed.
357          */

358         private void setBase(String JavaDoc systemId) throws SAXException {
359             
360             // normalize system id
361
if (systemId == null) {
362                 systemId = ""; // NOI18N
363
}
364             
365             // cut off the least significant part
366
int index = systemId.lastIndexOf('/');
367             if (index != -1) {
368                 systemId = systemId.substring(0, index + 1);
369             }
370             
371             // save base
372
base = systemId;
373             
374         }
375         
376
377         //~~~~~~~~~~~~~~~~~ XCATALOG ATTRIBUTES SCANNER ~~~~~~~~~~~~~~~~~~~~~
378

379         /** Stop parsing on fatal error. */
380         public void fatalError(SAXException ex) throws SAXException {
381             throw ex;
382         }
383         
384         /** Stop parsing on error*/
385         public void error(SAXException ex) throws SAXException {
386             throw ex;
387         }
388         
389         /**
390           * The start of an element. Parse attributes.
391           */

392         public void startElement(String JavaDoc ns, String JavaDoc local, String JavaDoc qName, Attributes attrList) throws SAXException {
393             
394             try {
395                 
396                 // <XCatalog Version="...">
397
/* if (qName.equals(XCATALOG)) {
398                     String version = attrList.getValue(VERSION);
399                     if ("1.0".equals(version))
400                         return;
401                     throw new SAXException("Illeagal XML catalog version");
402  
403                     return;
404                 }
405 */

406                 if (qName.equals(MAP)) {
407                     
408                     // <Map PublicID="..." HRef="..."/>
409
String JavaDoc publicId = attrList.getValue(PUBLICID_ATT_4);
410                     if (publicId == null) publicId = attrList.getValue(PUBLICID_ATT_2);
411
412                     String JavaDoc href = attrList.getValue(HREF_ATT);
413
414                     if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("MAP \""+publicId+"\" \""+href+"\""); // NOI18N
415

416                     // create mapping
417
if (Categorizer.isURL(href) == false) {
418                         href = base + href; // seems to be relative
419
}
420                     if (publicId != null)
421                         addPublicMapping(publicId, href);
422                     
423                 } else if (qName.equals(DELEGATE)) {
424                     
425                     // <Delegate PublicId="..." HRef="..."/>
426
String JavaDoc publicId = attrList.getValue(PUBLICID_ATT_4);
427                     if (publicId == null) publicId = attrList.getValue(PUBLICID_ATT_2);
428                     String JavaDoc href = attrList.getValue(HREF_ATT);
429                     
430                     if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("DELEGATE \""+publicId+"\" \""+href+"\""); // NOI18N
431

432                     // expand system id
433
if (Categorizer.isURL(href) == false) {
434                         href = base + href;
435                     }
436                     String JavaDoc systemId = href; //!!!fEntityHandler.expandSystemId(href);
437

438                     // create delegate
439
XCatalog catalog = new XCatalog();
440                     catalog.loadCatalog(systemId);
441                     addDelegateCatalog(publicId, catalog);
442                     
443                 } else if (qName.equals(EXTEND)) {
444                     
445                     // <Extend HRef="..."/>
446
String JavaDoc href = attrList.getValue(HREF_ATT);
447                     
448                     if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("EXTEND \""+href+"\""); // NOI18N
449

450                     // expand system id
451
if (Categorizer.isURL(href) == false) {
452                         href = base + href;
453                     }
454                     String JavaDoc systemId = href; //!!!fEntityHandler.expandSystemId(href);
455

456                     // create "patch/extender" catalog
457
XCatalog extender = new XCatalog();
458                     extender.loadCatalog(systemId);
459                     extenders.add(extender);
460                     
461                 } else if (qName.equals(BASE)) {
462                     
463                     // <Base HRef="..."/>
464
String JavaDoc href = attrList.getValue(HREF_ATT);
465                     
466                     // set new base //!!! new specs replaces it with XBase
467
if (href != null) {
468                         base = href;
469                     }
470                     if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("BASE \""+href+"\" -> \""+base+"\""); // NOI18N
471

472                 } else if (qName.equals(REMAP)) {
473                     
474                     // <Remap SystemID="..." HRef="..."/>
475

476                     String JavaDoc systemId = attrList.getValue(SYSTEMID_ATT_4);
477                     if (systemId == null) systemId=attrList.getValue(SYSTEMID_ATT_2);
478
479                     String JavaDoc href = attrList.getValue(HREF_ATT);
480                     
481                     if ( Util.THIS.isLoggable() ) /* then */ Util.THIS.debug("REMAP \""+systemId+"\" \""+href+"\""); // NOI18N
482

483                     // create mapping
484
if (Categorizer.isURL(href) == false) {
485                         href = base + href;
486                     }
487                     addSystemMapping(systemId, href);
488                     
489                 }
490                 
491             } catch (Exception JavaDoc e) {
492                 throw new SAXException(e);
493             }
494             
495         }
496         
497         
498         private class Resolver implements EntityResolver {
499             
500             /** Resolves the XCatalog DTD entity. */
501             public InputSource resolveEntity(String JavaDoc publicId, String JavaDoc systemId) throws SAXException, IOException {
502                 
503                 // parser does not validate, let skip DTD
504
if (DTD_PUBLIC_ID_2.equals(publicId) || DTD_PUBLIC_ID_4.equals(publicId)) {
505                     InputSource src = new InputSource();
506                     src.setPublicId(publicId);
507                     InputStream is = new ByteArrayInputStream(new byte[0]);
508                     src.setByteStream(is);
509                     src.setCharacterStream(new InputStreamReader(is, "UTF8")); // NOI18N
510
return src;
511                 }
512                 
513                 // no resolution possible
514
return null;
515                 
516             }
517             
518         }
519         
520     }
521     
522 }
523
Popular Tags