1 22 package org.jboss.xb.binding; 23 24 import com.wutka.dtd.DTD; 25 import com.wutka.dtd.DTDAttribute; 26 import com.wutka.dtd.DTDContainer; 27 import com.wutka.dtd.DTDElement; 28 import com.wutka.dtd.DTDEmpty; 29 import com.wutka.dtd.DTDItem; 30 import com.wutka.dtd.DTDMixed; 31 import com.wutka.dtd.DTDName; 32 import com.wutka.dtd.DTDPCData; 33 import com.wutka.dtd.DTDParser; 34 import com.wutka.dtd.DTDCardinal; 35 import org.jboss.logging.Logger; 36 import org.xml.sax.Attributes ; 37 import org.xml.sax.SAXException ; 38 39 import java.io.IOException ; 40 import java.io.Reader ; 41 import java.io.Writer ; 42 import java.io.InputStream ; 43 import java.io.InputStreamReader ; 44 import java.util.Collection ; 45 import java.util.Collections ; 46 import java.util.Enumeration ; 47 import java.util.Hashtable ; 48 import java.util.Iterator ; 49 import java.util.Map ; 50 import java.util.HashMap ; 51 import java.util.List ; 52 import java.util.ArrayList ; 53 import java.net.URL ; 54 import java.net.MalformedURLException ; 55 import javax.xml.parsers.ParserConfigurationException ; 56 57 58 64 public class DtdMarshaller 65 extends AbstractMarshaller 66 { 67 private static final Logger log = Logger.getLogger(DtdMarshaller.class); 68 69 private String publicId; 70 private String systemId; 71 72 private final Stack stack = new StackImpl(); 73 private DTD dtd; 74 private GenericObjectModelProvider provider; 75 private Content content = new Content(); 76 77 private final List elementStack = new ArrayList (); 78 79 private final Map simpleTypeBindings = new HashMap (); 80 81 public void addBinding(String elementName, TypeBinding binding) 82 { 83 simpleTypeBindings.put(elementName, binding); 84 } 85 86 public void mapPublicIdToSystemId(String publicId, String systemId) 87 { 88 this.publicId = publicId; 89 this.systemId = systemId; 90 } 91 92 public void declareNamespace(String prefix, String uri) 93 { 94 throw new UnsupportedOperationException ("declareNamespace is not implemented."); 95 } 96 97 public void addAttribute(String prefix, String localName, String type, String value) 98 { 99 throw new UnsupportedOperationException ("addAttribute is not implemented."); 100 } 101 102 public void marshal(String schemaUri, ObjectModelProvider provider, Object root, Writer writer) throws IOException , 103 ParserConfigurationException , 104 SAXException 105 { 106 URL url; 107 try 108 { 109 url = new URL (schemaUri); 110 } 111 catch(MalformedURLException e) 112 { 113 throw new IllegalArgumentException ("Malformed schema URI " + schemaUri + ": " + e.getMessage()); 114 } 115 116 InputStream is; 117 try 118 { 119 is = url.openStream(); 120 } 121 catch(IOException e) 122 { 123 throw new IllegalStateException ("Failed to open input stream for schema " + schemaUri + ": " + e.getMessage()); 124 } 125 126 try 127 { 128 InputStreamReader reader = new InputStreamReader (is); 129 marshal(reader, provider, root, writer); 130 } 131 finally 132 { 133 is.close(); 134 } 135 } 136 137 public void marshal(Reader dtdReader, ObjectModelProvider provider, Object document, Writer writer) 138 throws IOException , SAXException 139 { 140 DTDParser parser = new DTDParser(dtdReader); 141 dtd = parser.parse(true); 142 143 this.provider = provider instanceof GenericObjectModelProvider ? 144 (GenericObjectModelProvider)provider : new DelegatingObjectModelProvider(provider); 145 147 DTDElement[] roots = null; 148 if(dtd.rootElement != null) 149 { 150 handleRootElement(document, dtd.rootElement); 151 } 152 else 153 { 154 roots = getRootList(dtd); 155 for(int i = 0; i < roots.length; ++i) 156 { 157 handleRootElement(document, roots[i]); 158 } 159 } 160 161 163 writeXmlVersion(writer); 165 166 writer.write("<!DOCTYPE "); 168 169 if(dtd.rootElement != null) 170 { 171 writer.write(dtd.rootElement.getName()); 172 } 173 else 174 { 175 for(int i = 0; i < roots.length; ++i) 176 { 177 writer.write(", "); 178 writer.write(roots[i].getName()); 179 } 180 } 181 182 writer.write(" PUBLIC \""); 183 writer.write(publicId); 184 writer.write("\" \""); 185 writer.write(systemId); 186 writer.write("\">\n"); 187 188 ContentWriter contentWriter = new ContentWriter(writer, propertyIsTrueOrNotSet(Marshaller.PROP_OUTPUT_INDENTATION)); 189 content.handleContent(contentWriter); 190 } 191 192 private void handleRootElement(Object o, final DTDElement dtdRoot) 193 { 194 Element el = new Element(dtdRoot, true); 195 elementStack.add(el); 196 content.startDocument(); 197 198 Object root = provider.getRoot(o, null, systemId, dtdRoot.getName()); 199 if(root == null) 200 { 201 return; 202 } 203 stack.push(root); 204 205 Attributes attrs = provideAttributes(dtdRoot, root); 206 content.startElement("", dtdRoot.getName(), dtdRoot.getName(), attrs); 207 handleElement(dtd, dtdRoot, attrs); 208 content.endElement("", dtdRoot.getName(), dtdRoot.getName()); 209 210 stack.pop(); 211 content.endDocument(); 212 elementStack.remove(elementStack.size() - 1); 213 } 214 215 private final void handleElement(DTD dtd, DTDElement element, Attributes attrs) 216 { 217 DTDItem item = element.content; 218 if(item instanceof DTDMixed) 219 { 220 handleMixedElement(element, attrs); 221 } 222 else if(item instanceof DTDEmpty) 223 { 224 final Object value = provider.getElementValue(stack.peek(), null, systemId, element.getName()); 225 if(Boolean.TRUE.equals(value)) 226 { 227 writeSkippedElements(); 228 content.startElement("", element.getName(), element.getName(), attrs); 229 content.endElement("", element.getName(), element.getName()); 230 } 231 } 232 else if(item instanceof DTDContainer) 233 { 234 processContainer(dtd, (DTDContainer)item); 235 } 236 else 237 { 238 throw new IllegalStateException ("Unexpected element: " + element.getName()); 239 } 240 } 241 242 private final void handleMixedElement(DTDElement element, Attributes attrs) 243 { 244 boolean startElement = false; 245 if(!elementStack.isEmpty()) 246 { 247 Element e = (Element) elementStack.get(elementStack.size() - 1); 248 startElement = element != e.element; 249 } 250 251 DTDMixed mixed = (DTDMixed) element.content; 252 String elementName = element.getName(); 253 Object parent = stack.peek(); 254 DTDItem[] items = mixed.getItems(); 255 for(int i = 0; i < items.length; ++i) 256 { 257 DTDItem item = items[i]; 258 if(item instanceof DTDPCData) 259 { 260 Object value = provider.getElementValue(parent, null, systemId, elementName); 261 if(value != null) 262 { 263 writeSkippedElements(); 264 265 String marshalled; 266 TypeBinding binding = (TypeBinding)simpleTypeBindings.get(elementName); 267 if(binding != null) 268 { 269 marshalled = binding.marshal(value); 270 } 271 else 272 { 273 marshalled = value.toString(); 274 } 275 276 char[] ch = marshalled.toCharArray(); 277 if(startElement) 278 { 279 content.startElement("", elementName, elementName, attrs); 280 } 281 282 content.characters(ch, 0, ch.length); 283 284 if(startElement) 285 { 286 content.endElement("", elementName, elementName); 287 } 288 } 289 } 290 } 291 } 292 293 private final void handleChildren(DTD dtd, DTDElement element, DTDCardinal elementCardinal) 294 { 295 Object parent = stack.peek(); 296 Object children = provider.getChildren(parent, null, systemId, element.getName()); 297 298 if(children != null) 299 { 300 Iterator iter; 301 if(children instanceof Iterator ) 302 { 303 iter = (Iterator )children; 304 } 305 else if(children instanceof Collection ) 306 { 307 iter = ((Collection )children).iterator(); 308 } 309 else 310 { 311 iter = Collections.singletonList(children).iterator(); 312 } 313 314 writeSkippedElements(); 315 316 Element el = new Element(element, true); 317 elementStack.add(el); 318 319 final boolean singleValued = elementCardinal == DTDCardinal.NONE || elementCardinal == DTDCardinal.OPTIONAL; 320 if(singleValued) 321 { 322 content.startElement("", element.getName(), element.getName(), null); 324 } 325 326 while(iter.hasNext()) 327 { 328 Object child = iter.next(); 329 stack.push(child); 330 331 AttributesImpl attrs = (element.attributes.isEmpty() ? null : provideAttributes(element, child)); 332 if(!singleValued) 333 { 334 content.startElement("", element.getName(), element.getName(), attrs); 335 } 336 337 handleElement(dtd, element, attrs); 338 339 if(!singleValued) 340 { 341 content.endElement(systemId, element.getName(), element.getName()); 342 } 343 344 stack.pop(); 345 } 346 347 if(singleValued) 348 { 349 content.endElement(systemId, element.getName(), element.getName()); 350 } 351 352 elementStack.remove(elementStack.size() - 1); 353 } 354 else 355 { 356 boolean removeLast = false; 357 if(!(element.getContent() instanceof DTDMixed || element.getContent() instanceof DTDEmpty)) 358 { 359 Element el = new Element(element); 360 elementStack.add(el); 361 removeLast = true; 362 } 363 364 AttributesImpl attrs = (element.attributes.isEmpty() ? null : provideAttributes(element, parent)); 365 handleElement(dtd, element, attrs); 366 367 if(removeLast) 368 { 369 Element el = (Element)elementStack.remove(elementStack.size() - 1); 370 if(el.started) 371 { 372 DTDElement started = el.element; 373 content.endElement("", started.getName(), started.getName()); 374 } 375 } 376 } 377 } 378 379 private final void processContainer(DTD dtd, DTDContainer container) 380 { 381 DTDItem[] items = container.getItems(); 382 for(int i = 0; i < items.length; ++i) 383 { 384 DTDItem item = items[i]; 385 if(item instanceof DTDContainer) 386 { 387 processContainer(dtd, (DTDContainer)item); 388 } 389 else if(item instanceof DTDName) 390 { 391 DTDName name = (DTDName)item; 392 DTDElement element = (DTDElement)dtd.elements.get(name.value); 393 handleChildren(dtd, element, name.getCardinal()); 394 } 395 } 396 } 397 398 private void writeSkippedElements() 399 { 400 Element el = (Element)elementStack.get(elementStack.size() - 1); 401 if(!el.started) 402 { 403 int firstNotStarted = elementStack.size() - 1; 404 do 405 { 406 el = (Element)elementStack.get(--firstNotStarted); 407 } 408 while(!el.started); 409 410 ++firstNotStarted; 411 412 while(firstNotStarted < elementStack.size()) 413 { 414 el = (Element)elementStack.get(firstNotStarted++); 415 DTDElement notStarted = el.element; 416 417 if(log.isTraceEnabled()) 418 { 419 log.trace("starting skipped> " + notStarted.getName()); 420 } 421 422 content.startElement("", notStarted.getName(), notStarted.getName(), null); 423 el.started = true; 424 } 425 } 426 } 427 428 private AttributesImpl provideAttributes(DTDElement element, Object container) 429 { 430 final Hashtable attributes = element.attributes; 431 AttributesImpl attrs = new AttributesImpl(attributes.size()); 432 433 for(Iterator attrIter = attributes.values().iterator(); attrIter.hasNext();) 434 { 435 DTDAttribute attr = (DTDAttribute)attrIter.next(); 436 final Object attrValue = provider.getAttributeValue(container, null, systemId, attr.getName()); 437 438 if(attrValue != null) 439 { 440 attrs.add(systemId, 441 attr.getName(), 442 attr.getName(), 443 attr.getType().toString(), 444 attrValue.toString() 445 ); 446 } 447 } 448 449 return attrs; 450 } 451 452 456 protected static DTDElement[] getRootList(DTD dtd) 457 { 458 Hashtable roots = new Hashtable (); 459 Enumeration e = dtd.elements.elements(); 460 while(e.hasMoreElements()) 461 { 462 DTDElement element = (DTDElement)e.nextElement(); 463 roots.put(element.name, element); 464 } 465 466 e = dtd.elements.elements(); 467 while(e.hasMoreElements()) 468 { 469 DTDElement element = (DTDElement)e.nextElement(); 470 if(!(element.content instanceof DTDContainer)) 471 { 472 continue; 473 } 474 475 Enumeration items = ((DTDContainer)element.content).getItemsVec().elements(); 476 while(items.hasMoreElements()) 477 { 478 removeElements(roots, dtd, (DTDItem)items.nextElement()); 479 } 480 } 481 482 final Collection rootCol = roots.values(); 483 return (DTDElement[])rootCol.toArray(new DTDElement[rootCol.size()]); 484 } 485 486 protected static void removeElements(Hashtable h, DTD dtd, DTDItem item) 487 { 488 if(item instanceof DTDName) 489 { 490 h.remove(((DTDName)item).value); 491 } 492 else if(item instanceof DTDContainer) 493 { 494 Enumeration e = ((DTDContainer)item).getItemsVec().elements(); 495 while(e.hasMoreElements()) 496 { 497 removeElements(h, dtd, (DTDItem)e.nextElement()); 498 } 499 } 500 } 501 502 504 private static final class Element 505 { 506 public final DTDElement element; 507 public boolean started; 508 509 public Element(DTDElement element, boolean started) 510 { 511 this.element = element; 512 this.started = started; 513 } 514 515 public Element(DTDElement element) 516 { 517 this.element = element; 518 } 519 520 public String toString() 521 { 522 return "[element=" + element.getName() + ", started=" + started + "]"; 523 } 524 } 525 } 526 | Popular Tags |