KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > xml > tools > generator > GenerateDTDSupport


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
20 package org.netbeans.modules.xml.tools.generator;
21
22 import java.io.IOException JavaDoc;
23 import java.io.OutputStream JavaDoc;
24 import java.io.OutputStreamWriter JavaDoc;
25 import java.io.PrintWriter JavaDoc;
26 import java.io.UnsupportedEncodingException JavaDoc;
27 import java.io.Writer JavaDoc;
28 import java.net.URL JavaDoc;
29 import java.util.Collection JavaDoc;
30 import java.util.HashSet JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.LinkedHashMap JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.Set JavaDoc;
35 import java.util.Stack JavaDoc;
36 import java.util.Vector JavaDoc;
37 import org.netbeans.api.xml.services.UserCatalog;
38 import org.netbeans.modules.xml.core.XMLDataObject;
39 import org.netbeans.modules.xml.core.lib.GuiUtil;
40 import org.netbeans.tax.TreeUtilities;
41 import org.openide.cookies.SaveCookie;
42 import org.openide.filesystems.FileLock;
43 import org.openide.filesystems.FileObject;
44 import org.openide.filesystems.FileStateInvalidException;
45 import org.openide.loaders.DataObject;
46 import org.openide.util.UserCancelException;
47 import org.openide.xml.XMLUtil;
48 import org.xml.sax.Attributes JavaDoc;
49 import org.xml.sax.ContentHandler JavaDoc;
50 import org.xml.sax.EntityResolver JavaDoc;
51 import org.xml.sax.ErrorHandler JavaDoc;
52 import org.xml.sax.InputSource JavaDoc;
53 import org.xml.sax.Locator JavaDoc;
54 import org.xml.sax.SAXException JavaDoc;
55 import org.xml.sax.SAXParseException JavaDoc;
56 import org.xml.sax.XMLReader JavaDoc;
57
58 /**
59  * GenerateDTDSupport class generate a DTD by guessing it from
60  * XML document.
61  * <p>
62  * It's already prepared for plugging in XMLSchema generator.
63  *
64  * @author Libor Kramolis
65  * @author Petr Kuzel, rewritten to SAX
66  */

67 public final class GenerateDTDSupport implements XMLGenerateCookie {
68     static final String JavaDoc DTD_EXT = "dtd"; // NOI18N
69

70     /**
71      * XML document data object actiang as a "template".
72      */

73     private final DataObject template;
74     private ElementInfo current;
75     private Stack JavaDoc elementStack;
76
77     private Map JavaDoc elementInfos;
78     private String JavaDoc warning;
79     private String JavaDoc rootQName;
80
81
82     /**
83      * @param template data object that is a "template" for created DTD
84      */

85     public GenerateDTDSupport(XMLDataObject template) {
86         this.template = template;
87         rootQName = null;
88         warning = null;
89         current = null;
90     }
91
92     /**
93      * Performs a dialog with a user and generates the DTD
94      */

95     public void generate() {
96
97         try {
98             // saving file before DTD generation
99
SaveCookie save = (SaveCookie)template.getCookie(SaveCookie.class);
100             if (save!=null) save.save();
101             
102             FileObject primFile = template.getPrimaryFile();
103             String JavaDoc name = primFile.getName();
104             FileObject folder = primFile.getParent();
105
106             FileObject generFile = (new SelectFileDialog(folder, name, DTD_EXT, Util.NONEMPTY_CHECK)).getFileObject();
107             name = generFile.getName();
108
109             // IANA encoding name
110
String JavaDoc encoding = "UTF-8";
111             String JavaDoc dtd = xml2dtd(name, encoding);
112             if (dtd == null) {
113                 String JavaDoc msg = Util.THIS.getString("BK0009");
114                 GuiUtil.notifyWarning(msg + "\n" + warning); // NOI18N
115
return;
116             }
117
118             // write to file
119
FileLock lock = null;
120             Writer JavaDoc writer = null;
121             try {
122                 lock = generFile.lock();
123                 encoding = TreeUtilities.iana2java(encoding == null ? "UTF-8" : encoding); // NOI18N
124
OutputStream JavaDoc output = generFile.getOutputStream(lock);
125                 try {
126                     writer = new OutputStreamWriter JavaDoc(output, encoding);
127                 } catch (UnsupportedEncodingException JavaDoc e) {
128                     writer = new OutputStreamWriter JavaDoc(output);
129                 }
130                 writer = new PrintWriter JavaDoc(writer);
131                 writer.write(dtd.toString());
132                 lock.releaseLock();
133             } finally {
134                 if (writer != null)
135                     writer.close();
136                 if (lock != null)
137                     lock.releaseLock();
138             }
139
140 // disabled until in-memory XML model is not performance problem
141
// trySetDocumentType(name);
142

143             GuiUtil.performDefaultAction(generFile);
144
145         } catch (UserCancelException e) {
146 // } catch (FileStateInvalidException e) {
147
// } catch (TreeException e) {
148
// } catch (IOException e) {
149
} catch (Exception JavaDoc exc) {
150             GuiUtil.notifyException(exc);
151         }
152     }
153
154
155 // /**
156
// * Update template's DOCTYPE to point to generated DTD.
157
// */
158
// private void trySetDocumentType(String fileName) {
159
// if (templateRoot.getParentNode() instanceof TreeDocument) { // try to set only when element is root document element
160
// TreeDocument document = (TreeDocument) templateRoot.getParentNode();
161
// if (GuiUtil.confirmAction(Util.THIS.getString("MSG_use_dtd_as_document_type?"))) {
162
// try {
163
// TreeDocumentType newDoctype = new TreeDocumentType(templateRoot.getQName(), null, fileName + "." + DTD_EXT); // NOI18N
164
// document.setDocumentType(newDoctype);
165
// } catch (TreeException exc) {
166
// GuiUtil.notifyWarning(exc.getLocalizedMessage());
167
// }
168
// }
169
// }
170
// }
171

172
173     /**
174      * Generate the DTD into temporary string.
175      * @return null if problems leaved in <code>warning</code> field occured
176      * otherwise the DTD.
177      */

178     String JavaDoc xml2dtd(String JavaDoc name, String JavaDoc encoding) {
179         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
180         elementStack = new Stack JavaDoc();
181         elementInfos = new LinkedHashMap JavaDoc(101);
182         
183         // fill table of dtd declarations
184
if (false == scanTemplate()) {
185             return null;
186         }
187
188         if (encoding != null) {
189             sb.append("<?xml version='1.0' encoding='").append(encoding).append("'?>\n\n"); // NOI18N
190
}
191
192         String JavaDoc todo = Util.THIS.getString("TODO", name + "." + DTD_EXT);
193         sb.append("<!--\n ").append(todo).append("\n\n-->");
194
195         String JavaDoc usage = Util.THIS.getString("BK0010");
196         sb.append("<!--\n").append(" " + usage + "\n\n").append(" <?xml version=\"1.0\"?>\n\n").// NOI18N
197
append(" <!DOCTYPE ").append(rootQName).append(" SYSTEM \"").// NOI18N
198
append(name).append(".").append(DTD_EXT).append("\">\n\n").// NOI18N
199
append(" <").append(rootQName).append(">\n ...\n").append(" </").append(rootQName).append(">\n").// NOI18N
200
append("-->\n"); // NOI18N
201

202         // generate DTD contaent by the table
203
//??? we could easily plug here XML Schema generator
204

205         Iterator JavaDoc it = elementInfos.values().iterator();
206         ElementInfo elem;
207         while (it.hasNext()) {
208             sb.append("\n"); // NOI18N
209
elem = (ElementInfo) it.next();
210             // <!ELEMENT ...
211
sb.append("<!--- " + Util.THIS.getString("FMT_DTDDoc") + " -->\n");
212             //!!! there may by clash if the doccument happens to map several
213
// URI into one prefix
214
sb.append("<!ELEMENT ").append(elem.name.qName).append(" "); // NOI18N
215

216             if (elem.empty) {
217                 sb.append("EMPTY"); // NOI18N
218
} else {
219                 Collection JavaDoc collect = elem.children;
220                 if ((elem.pcdata == true) ||
221                         (collect.size() == 0)) {
222                     Vector JavaDoc vect = new Vector JavaDoc(collect);
223                     vect.insertElementAt(new XName("","","#PCDATA"), 0); // NOI18N
224
collect = vect;
225                 }
226                 Iterator JavaDoc itc = collect.iterator();
227                 XName elemName;
228                 elemName = (XName) itc.next();
229                 sb.append("(").append(elemName.qName); // NOI18N
230
while (itc.hasNext()) {
231                     elemName = (XName) itc.next();
232                     sb.append("|").append(elemName.qName); // NOI18N
233
}
234
235                 //!!!HACK #6928
236
if (false == sb.toString().endsWith("#PCDATA")) { // NOI18N
237
sb.append(")*"); // NOI18N
238
} else {
239                     sb.append(")"); // NOI18N
240
}
241             }
242             sb.append(">\n"); // NOI18N
243

244             // <!ATTLIST ...
245
if (elem.attributes.size() != 0) {
246                 sb.append("<!ATTLIST ").append(elem.name.qName).append("\n"); // NOI18N
247
Iterator JavaDoc ita = elem.attributes.iterator();
248                 while (ita.hasNext()) {
249                     XName attName = (XName) ita.next();
250                     sb.append(" ").append(attName.qName).append(" CDATA #IMPLIED\n"); // NOI18N
251
}
252                 sb.append(" >\n"); // NOI18N
253
}
254         }
255
256         return sb.toString();
257     }
258
259     /**
260      * Using SAX events fill elementsInfo map.
261      * @return false in parsing errors have occured.
262      */

263     private boolean scanTemplate() {
264         URL JavaDoc url = null;
265         XMLReader JavaDoc parser = null;
266         try {
267             url = template.getPrimaryFile().getURL();
268         } catch (FileStateInvalidException e) {
269             warning = e.getLocalizedMessage();
270             return false;
271         }
272
273         String JavaDoc system = url.toExternalForm();
274         try {
275             parser = XMLUtil.createXMLReader(false, true);
276             Impl impl = new Impl();
277             parser.setContentHandler(impl);
278             parser.setErrorHandler(impl);
279             parser.setFeature("http://xml.org/sax/features/namespace-prefixes", true); // NOI18N
280
UserCatalog catalog = UserCatalog.getDefault();
281             if (catalog != null) {
282                 EntityResolver JavaDoc resolver = catalog.getEntityResolver();
283                 if (resolver != null) {
284                     parser.setEntityResolver(resolver);
285                 }
286             }
287         } catch (SAXException JavaDoc e) {
288             warning = e.getLocalizedMessage();
289             return false;
290         }
291
292         InputSource JavaDoc input = new InputSource JavaDoc(system);
293         try {
294             parser.parse(input);
295             return true;
296         } catch (IOException JavaDoc e) {
297             warning = e.getLocalizedMessage();
298             return false;
299         } catch (SAXException JavaDoc e) {
300             warning = e.getLocalizedMessage();
301             return false;
302         }
303     }
304
305
306
307     /**
308      * Return true if parameter contains just white spaces.
309      */

310     private boolean wsOnly(String JavaDoc s) {
311         if (s == null) return true;
312
313         char[] data = s.toCharArray();
314         for (int i = 0; i < data.length; i++) {
315             if (Character.isWhitespace(data[i]) == false) {
316                 return false;
317             }
318         }
319
320         return true;
321     }
322
323     /**
324      * Return true if parameter contains just white spaces.
325      */

326     private boolean wsOnly(char[] data, int from, int length) {
327         for (int i = from; i < from + length; i++) {
328             if (Character.isWhitespace(data[i]) == false) {
329                 return false;
330             }
331         }
332
333         return true;
334     }
335
336     // SAX2 Content handler methods
337

338     private class Impl implements ContentHandler JavaDoc, ErrorHandler JavaDoc {
339         public void characters(char[] chars, int i, int i1) throws SAXException JavaDoc {
340             if (false == wsOnly(chars, i, i1)) {
341                 if (current != null) {
342                     current.hasPCDATA();
343                 }
344             }
345         }
346
347         public void endDocument() throws SAXException JavaDoc {
348         }
349
350         public void endElement(String JavaDoc s, String JavaDoc s1, String JavaDoc s2) throws SAXException JavaDoc {
351             current = (ElementInfo) elementStack.pop();
352         }
353
354         public void endPrefixMapping(String JavaDoc s) throws SAXException JavaDoc {
355         }
356
357         public void ignorableWhitespace(char[] chars, int i, int i1) throws SAXException JavaDoc {
358         }
359
360         public void processingInstruction(String JavaDoc s, String JavaDoc s1) throws SAXException JavaDoc {
361         }
362
363         public void setDocumentLocator(Locator JavaDoc locator) {
364         }
365
366         public void skippedEntity(String JavaDoc s) throws SAXException JavaDoc {
367         }
368
369         public void startDocument() throws SAXException JavaDoc {
370         }
371
372         public void startElement(String JavaDoc uri, String JavaDoc localName, String JavaDoc qName, Attributes JavaDoc attributes) throws SAXException JavaDoc {
373
374             if (rootQName == null) {
375                 rootQName = qName;
376             }
377
378             XName xName = new XName(uri, localName, qName);
379             ElementInfo info = (ElementInfo) elementInfos.get(xName);
380             if (info == null) {
381                 info = new ElementInfo(uri, localName, qName);
382                 elementInfos.put(xName, info);
383             }
384             for (int i = 0; i<attributes.getLength(); i++) {
385                 // what is URI of the "xmlns:" prefix?
386
info.addAttribute(attributes.getURI(i), attributes.getLocalName(i), attributes.getQName(i));
387             }
388
389             if (current != null) {
390                 current.addChild(info);
391             }
392             elementStack.push(current);
393             current = info;
394         }
395
396         public void startPrefixMapping(String JavaDoc s, String JavaDoc s1) throws SAXException JavaDoc {
397         }
398
399         public void error(SAXParseException JavaDoc e) throws SAXException JavaDoc {
400             throw e;
401         }
402
403         public void fatalError(SAXParseException JavaDoc e) throws SAXException JavaDoc {
404             throw e;
405         }
406
407         public void warning(SAXParseException JavaDoc e) throws SAXException JavaDoc {
408         }
409     }
410
411
412     /**
413      * Holds all info gathered while scanning template document.
414      */

415     private class ElementInfo {
416         XName name;
417         Set JavaDoc children;
418         Set JavaDoc attributes;
419         boolean pcdata;
420         boolean empty;
421
422         public ElementInfo(String JavaDoc uri, String JavaDoc localName, String JavaDoc qName) {
423             name = new XName(uri, localName, qName);
424             children = new HashSet JavaDoc();
425             attributes = new HashSet JavaDoc();
426             pcdata = false;
427             empty = true;
428         }
429
430         public void hasPCDATA() {
431             pcdata = true;
432             empty = false;
433         }
434
435         public boolean isTextAllowed() {
436             return pcdata;
437         }
438
439         public void addChild(ElementInfo info) {
440             empty = false;
441             children.add(info.name);
442         }
443
444         public void addAttribute(String JavaDoc uri, String JavaDoc localName, String JavaDoc qName) {
445             attributes.add(new XName(uri, localName, qName));
446         }
447
448         public boolean equals(Object JavaDoc obj) {
449             if (this == obj) return true;
450             if (obj instanceof ElementInfo) {
451                 ElementInfo info = (ElementInfo) obj;
452                 return name.equals(info.name);
453             }
454             return false;
455         }
456
457         public int hashCode() {
458             return name.hashCode();
459         }
460     } // end of inner class ElementInfo
461

462     /**
463      * Structured XML name with value based identity.
464      * The identity onors all URI, localName, qName. It's usefull
465      * for DTD generators. XML Schema generators must remove
466      * {URI}localName duplicities caused by qNames.
467      */

468     private static class XName {
469         private String JavaDoc uri, localName, qName;
470         public XName(String JavaDoc uri, String JavaDoc localName, String JavaDoc qName) {
471             this.uri = uri;
472             this.localName = localName;
473             this.qName = qName;
474         }
475
476         public boolean equals(Object JavaDoc peer) {
477             if (peer == this) return true;
478             if (peer instanceof XName) {
479                 XName id = (XName) peer;
480                 return uri.equals(id.uri)
481                     && localName.equals(id.localName)
482                     && qName.equals(id.qName);
483             }
484             return false;
485         }
486
487         public int hashCode() {
488             return uri.hashCode() ^ localName.hashCode() ^ qName.hashCode();
489         }
490     }
491 }
492
Popular Tags