1 19 20 package org.netbeans.modules.xml.tools.generator; 21 22 import java.io.IOException ; 23 import java.io.OutputStream ; 24 import java.io.OutputStreamWriter ; 25 import java.io.PrintWriter ; 26 import java.io.UnsupportedEncodingException ; 27 import java.io.Writer ; 28 import java.net.URL ; 29 import java.util.Collection ; 30 import java.util.HashSet ; 31 import java.util.Iterator ; 32 import java.util.LinkedHashMap ; 33 import java.util.Map ; 34 import java.util.Set ; 35 import java.util.Stack ; 36 import java.util.Vector ; 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 ; 49 import org.xml.sax.ContentHandler ; 50 import org.xml.sax.EntityResolver ; 51 import org.xml.sax.ErrorHandler ; 52 import org.xml.sax.InputSource ; 53 import org.xml.sax.Locator ; 54 import org.xml.sax.SAXException ; 55 import org.xml.sax.SAXParseException ; 56 import org.xml.sax.XMLReader ; 57 58 67 public final class GenerateDTDSupport implements XMLGenerateCookie { 68 static final String DTD_EXT = "dtd"; 70 73 private final DataObject template; 74 private ElementInfo current; 75 private Stack elementStack; 76 77 private Map elementInfos; 78 private String warning; 79 private String rootQName; 80 81 82 85 public GenerateDTDSupport(XMLDataObject template) { 86 this.template = template; 87 rootQName = null; 88 warning = null; 89 current = null; 90 } 91 92 95 public void generate() { 96 97 try { 98 SaveCookie save = (SaveCookie)template.getCookie(SaveCookie.class); 100 if (save!=null) save.save(); 101 102 FileObject primFile = template.getPrimaryFile(); 103 String 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 String encoding = "UTF-8"; 111 String dtd = xml2dtd(name, encoding); 112 if (dtd == null) { 113 String msg = Util.THIS.getString("BK0009"); 114 GuiUtil.notifyWarning(msg + "\n" + warning); return; 116 } 117 118 FileLock lock = null; 120 Writer writer = null; 121 try { 122 lock = generFile.lock(); 123 encoding = TreeUtilities.iana2java(encoding == null ? "UTF-8" : encoding); OutputStream output = generFile.getOutputStream(lock); 125 try { 126 writer = new OutputStreamWriter (output, encoding); 127 } catch (UnsupportedEncodingException e) { 128 writer = new OutputStreamWriter (output); 129 } 130 writer = new PrintWriter (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 143 GuiUtil.performDefaultAction(generFile); 144 145 } catch (UserCancelException e) { 146 } catch (Exception exc) { 150 GuiUtil.notifyException(exc); 151 } 152 } 153 154 155 172 173 178 String xml2dtd(String name, String encoding) { 179 StringBuffer sb = new StringBuffer (); 180 elementStack = new Stack (); 181 elementInfos = new LinkedHashMap (101); 182 183 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"); } 191 192 String todo = Util.THIS.getString("TODO", name + "." + DTD_EXT); 193 sb.append("<!--\n ").append(todo).append("\n\n-->"); 194 195 String usage = Util.THIS.getString("BK0010"); 196 sb.append("<!--\n").append(" " + usage + "\n\n").append(" <?xml version=\"1.0\"?>\n\n"). append(" <!DOCTYPE ").append(rootQName).append(" SYSTEM \""). append(name).append(".").append(DTD_EXT).append("\">\n\n"). append(" <").append(rootQName).append(">\n ...\n").append(" </").append(rootQName).append(">\n"). append("-->\n"); 202 205 Iterator it = elementInfos.values().iterator(); 206 ElementInfo elem; 207 while (it.hasNext()) { 208 sb.append("\n"); elem = (ElementInfo) it.next(); 210 sb.append("<!--- " + Util.THIS.getString("FMT_DTDDoc") + " -->\n"); 212 sb.append("<!ELEMENT ").append(elem.name.qName).append(" "); 216 if (elem.empty) { 217 sb.append("EMPTY"); } else { 219 Collection collect = elem.children; 220 if ((elem.pcdata == true) || 221 (collect.size() == 0)) { 222 Vector vect = new Vector (collect); 223 vect.insertElementAt(new XName("","","#PCDATA"), 0); collect = vect; 225 } 226 Iterator itc = collect.iterator(); 227 XName elemName; 228 elemName = (XName) itc.next(); 229 sb.append("(").append(elemName.qName); while (itc.hasNext()) { 231 elemName = (XName) itc.next(); 232 sb.append("|").append(elemName.qName); } 234 235 if (false == sb.toString().endsWith("#PCDATA")) { sb.append(")*"); } else { 239 sb.append(")"); } 241 } 242 sb.append(">\n"); 244 if (elem.attributes.size() != 0) { 246 sb.append("<!ATTLIST ").append(elem.name.qName).append("\n"); Iterator ita = elem.attributes.iterator(); 248 while (ita.hasNext()) { 249 XName attName = (XName) ita.next(); 250 sb.append(" ").append(attName.qName).append(" CDATA #IMPLIED\n"); } 252 sb.append(" >\n"); } 254 } 255 256 return sb.toString(); 257 } 258 259 263 private boolean scanTemplate() { 264 URL url = null; 265 XMLReader parser = null; 266 try { 267 url = template.getPrimaryFile().getURL(); 268 } catch (FileStateInvalidException e) { 269 warning = e.getLocalizedMessage(); 270 return false; 271 } 272 273 String 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); UserCatalog catalog = UserCatalog.getDefault(); 281 if (catalog != null) { 282 EntityResolver resolver = catalog.getEntityResolver(); 283 if (resolver != null) { 284 parser.setEntityResolver(resolver); 285 } 286 } 287 } catch (SAXException e) { 288 warning = e.getLocalizedMessage(); 289 return false; 290 } 291 292 InputSource input = new InputSource (system); 293 try { 294 parser.parse(input); 295 return true; 296 } catch (IOException e) { 297 warning = e.getLocalizedMessage(); 298 return false; 299 } catch (SAXException e) { 300 warning = e.getLocalizedMessage(); 301 return false; 302 } 303 } 304 305 306 307 310 private boolean wsOnly(String 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 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 338 private class Impl implements ContentHandler , ErrorHandler { 339 public void characters(char[] chars, int i, int i1) throws SAXException { 340 if (false == wsOnly(chars, i, i1)) { 341 if (current != null) { 342 current.hasPCDATA(); 343 } 344 } 345 } 346 347 public void endDocument() throws SAXException { 348 } 349 350 public void endElement(String s, String s1, String s2) throws SAXException { 351 current = (ElementInfo) elementStack.pop(); 352 } 353 354 public void endPrefixMapping(String s) throws SAXException { 355 } 356 357 public void ignorableWhitespace(char[] chars, int i, int i1) throws SAXException { 358 } 359 360 public void processingInstruction(String s, String s1) throws SAXException { 361 } 362 363 public void setDocumentLocator(Locator locator) { 364 } 365 366 public void skippedEntity(String s) throws SAXException { 367 } 368 369 public void startDocument() throws SAXException { 370 } 371 372 public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { 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 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 s, String s1) throws SAXException { 397 } 398 399 public void error(SAXParseException e) throws SAXException { 400 throw e; 401 } 402 403 public void fatalError(SAXParseException e) throws SAXException { 404 throw e; 405 } 406 407 public void warning(SAXParseException e) throws SAXException { 408 } 409 } 410 411 412 415 private class ElementInfo { 416 XName name; 417 Set children; 418 Set attributes; 419 boolean pcdata; 420 boolean empty; 421 422 public ElementInfo(String uri, String localName, String qName) { 423 name = new XName(uri, localName, qName); 424 children = new HashSet (); 425 attributes = new HashSet (); 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 uri, String localName, String qName) { 445 attributes.add(new XName(uri, localName, qName)); 446 } 447 448 public boolean equals(Object 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 } 462 468 private static class XName { 469 private String uri, localName, qName; 470 public XName(String uri, String localName, String qName) { 471 this.uri = uri; 472 this.localName = localName; 473 this.qName = qName; 474 } 475 476 public boolean equals(Object 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 |