1 7 package org.jboss.xml.binding; 8 9 import com.wutka.dtd.DTD; 10 import com.wutka.dtd.DTDAttribute; 11 import com.wutka.dtd.DTDContainer; 12 import com.wutka.dtd.DTDElement; 13 import com.wutka.dtd.DTDEmpty; 14 import com.wutka.dtd.DTDItem; 15 import com.wutka.dtd.DTDMixed; 16 import com.wutka.dtd.DTDName; 17 import com.wutka.dtd.DTDPCData; 18 import com.wutka.dtd.DTDParser; 19 import com.wutka.dtd.DTDCardinal; 20 import org.jboss.logging.Logger; 21 import org.xml.sax.Attributes ; 22 import org.xml.sax.SAXException ; 23 24 import java.io.IOException ; 25 import java.io.Reader ; 26 import java.io.Writer ; 27 import java.util.Collection ; 28 import java.util.Collections ; 29 import java.util.Enumeration ; 30 import java.util.Hashtable ; 31 import java.util.Iterator ; 32 import java.util.LinkedList ; 33 34 35 41 public class DtdMarshaller 42 extends AbstractMarshaller 43 { 44 private static final Logger log = Logger.getLogger(DtdMarshaller.class); 45 46 private String publicId; 47 private String systemId; 48 49 private final Stack stack = new StackImpl(); 50 private DTD dtd; 51 private GenericObjectModelProvider provider; 52 private Content content = new Content(); 53 54 private final LinkedList elementStack = new LinkedList (); 55 56 public void mapPublicIdToSystemId(String publicId, String systemId) 57 { 58 this.publicId = publicId; 59 this.systemId = systemId; 60 } 61 62 public void marshal(Reader dtdReader, ObjectModelProvider provider, Object document, Writer writer) 63 throws IOException , SAXException 64 { 65 DTDParser parser = new DTDParser(dtdReader); 66 dtd = parser.parse(true); 67 68 this.provider = provider instanceof GenericObjectModelProvider ? 69 (GenericObjectModelProvider)provider : new DelegatingObjectModelProvider(provider); 70 72 DTDElement[] roots = null; 73 if(dtd.rootElement != null) 74 { 75 handleRootElement(document, dtd.rootElement); 76 } 77 else 78 { 79 roots = getRootList(dtd); 80 for(int i = 0; i < roots.length; ++i) 81 { 82 handleRootElement(document, roots[i]); 83 } 84 } 85 86 88 writeXmlVersion(writer); 90 91 writer.write("<!DOCTYPE "); 93 94 if(dtd.rootElement != null) 95 { 96 writer.write(dtd.rootElement.getName()); 97 } 98 else 99 { 100 for(int i = 0; i < roots.length; ++i) 101 { 102 writer.write(", "); 103 writer.write(roots[i].getName()); 104 } 105 } 106 107 writer.write(" PUBLIC \""); 108 writer.write(publicId); 109 writer.write("\" \""); 110 writer.write(systemId); 111 writer.write("\">\n"); 112 113 ContentWriter contentWriter = new ContentWriter(writer, propertyIsTrueOrNotSet(Marshaller.PROP_OUTPUT_INDENTATION)); 114 content.handleContent(contentWriter); 115 } 116 117 private void handleRootElement(Object o, final DTDElement dtdRoot) 118 { 119 Element el = new Element(dtdRoot, true); 120 elementStack.addLast(el); 121 content.startDocument(); 122 123 Object root = provider.getRoot(o, systemId, dtdRoot.getName()); 124 if(root == null) 125 { 126 return; 127 } 128 stack.push(root); 129 130 Attributes attrs = provideAttributes(dtdRoot, root); 131 content.startElement("", dtdRoot.getName(), dtdRoot.getName(), attrs); 132 handleElement(dtd, dtdRoot, attrs); 133 content.endElement("", dtdRoot.getName(), dtdRoot.getName()); 134 135 stack.pop(); 136 content.endDocument(); 137 elementStack.removeLast(); 138 } 139 140 private final void handleElement(DTD dtd, DTDElement element, Attributes attrs) 141 { 142 DTDItem item = element.content; 143 if(item instanceof DTDMixed) 144 { 145 handleMixedElement((DTDMixed)item, element.getName(), attrs); 146 } 147 else if(item instanceof DTDEmpty) 148 { 149 final Object value = provider.getElementValue(stack.peek(), systemId, element.getName()); 150 if(Boolean.TRUE.equals(value)) 151 { 152 writeSkippedElements(); 153 content.startElement("", element.getName(), element.getName(), attrs); 154 content.endElement("", element.getName(), element.getName()); 155 } 156 } 157 else if(item instanceof DTDContainer) 158 { 159 processContainer(dtd, (DTDContainer)item); 160 } 161 else 162 { 163 throw new IllegalStateException ("Unexpected element: " + element.getName()); 164 } 165 } 166 167 private final void handleMixedElement(DTDMixed mixed, String elementName, Attributes attrs) 168 { 169 Object parent = stack.peek(); 170 DTDItem[] items = mixed.getItems(); 171 for(int i = 0; i < items.length; ++i) 172 { 173 DTDItem item = items[i]; 174 if(item instanceof DTDPCData) 175 { 176 Object value = provider.getElementValue(parent, systemId, elementName); 177 if(value != null) 178 { 179 writeSkippedElements(); 180 181 char[] ch = value.toString().toCharArray(); 182 content.startElement("", elementName, elementName, attrs); 183 content.characters(ch, 0, ch.length); 184 content.endElement("", elementName, elementName); 185 } 186 } 187 } 188 } 189 190 private final void handleChildren(DTD dtd, DTDElement element, DTDCardinal elementCardinal) 191 { 192 Object parent = stack.peek(); 193 Object children = provider.getChildren(parent, systemId, element.getName()); 194 195 if(children != null) 196 { 197 Iterator iter; 198 if(children instanceof Iterator ) 199 { 200 iter = (Iterator )children; 201 } 202 else if(children instanceof Collection ) 203 { 204 iter = ((Collection )children).iterator(); 205 } 206 else 207 { 208 iter = Collections.singletonList(children).iterator(); 209 } 210 211 writeSkippedElements(); 212 213 Element el = new Element(element, true); 214 elementStack.addLast(el); 215 216 final boolean singleValued = elementCardinal == DTDCardinal.NONE || elementCardinal == DTDCardinal.OPTIONAL; 217 if(singleValued) 218 { 219 content.startElement("", element.getName(), element.getName(), null); 221 } 222 223 while(iter.hasNext()) 224 { 225 Object child = iter.next(); 226 stack.push(child); 227 228 AttributesImpl attrs = (element.attributes.isEmpty() ? null : provideAttributes(element, child)); 229 if(!singleValued) 230 { 231 content.startElement("", element.getName(), element.getName(), null); 232 } 233 234 handleElement(dtd, element, attrs); 235 236 if(!singleValued) 237 { 238 content.endElement(systemId, element.getName(), element.getName()); 239 } 240 241 stack.pop(); 242 } 243 244 if(singleValued) 245 { 246 content.endElement(systemId, element.getName(), element.getName()); 247 } 248 249 elementStack.removeLast(); 250 } 251 else 252 { 253 boolean removeLast = false; 254 if(!(element.getContent() instanceof DTDMixed || element.getContent() instanceof DTDEmpty)) 255 { 256 Element el = new Element(element); 257 elementStack.addLast(el); 258 removeLast = true; 259 } 260 261 AttributesImpl attrs = (element.attributes.isEmpty() ? null : provideAttributes(element, parent)); 262 handleElement(dtd, element, attrs); 263 264 if(removeLast) 265 { 266 Element el = (Element)elementStack.removeLast(); 267 if(el.started) 268 { 269 DTDElement started = el.element; 270 content.endElement("", started.getName(), started.getName()); 271 } 272 } 273 } 274 } 275 276 private final void processContainer(DTD dtd, DTDContainer container) 277 { 278 DTDItem[] items = container.getItems(); 279 for(int i = 0; i < items.length; ++i) 280 { 281 DTDItem item = items[i]; 282 if(item instanceof DTDContainer) 283 { 284 processContainer(dtd, (DTDContainer)item); 285 } 286 else if(item instanceof DTDName) 287 { 288 DTDName name = (DTDName)item; 289 DTDElement element = (DTDElement)dtd.elements.get(name.value); 290 handleChildren(dtd, element, name.getCardinal()); 291 } 292 } 293 } 294 295 private void writeSkippedElements() 296 { 297 Element el = (Element)elementStack.getLast(); 298 if(!el.started) 299 { 300 int firstNotStarted = elementStack.size() - 1; 301 do 302 { 303 el = (Element)elementStack.get(--firstNotStarted); 304 } 305 while(!el.started); 306 307 ++firstNotStarted; 308 309 while(firstNotStarted < elementStack.size()) 310 { 311 el = (Element)elementStack.get(firstNotStarted++); 312 DTDElement notStarted = el.element; 313 314 if(log.isTraceEnabled()) 315 { 316 log.trace("starting skipped> " + notStarted.getName()); 317 } 318 319 content.startElement("", notStarted.getName(), notStarted.getName(), null); 320 el.started = true; 321 } 322 } 323 } 324 325 private AttributesImpl provideAttributes(DTDElement element, Object container) 326 { 327 final Hashtable attributes = element.attributes; 328 AttributesImpl attrs = new AttributesImpl(attributes.size()); 329 330 for(Iterator attrIter = attributes.values().iterator(); attrIter.hasNext();) 331 { 332 DTDAttribute attr = (DTDAttribute)attrIter.next(); 333 final Object attrValue = provider.getAttributeValue(container, systemId, attr.getName()); 334 335 if(attrValue != null) 336 { 337 attrs.add(systemId, 338 attr.getName(), 339 attr.getName(), 340 attr.getType().toString(), 341 attrValue.toString() 342 ); 343 } 344 } 345 346 return attrs; 347 } 348 349 353 protected static DTDElement[] getRootList(DTD dtd) 354 { 355 Hashtable roots = new Hashtable (); 356 Enumeration e = dtd.elements.elements(); 357 while(e.hasMoreElements()) 358 { 359 DTDElement element = (DTDElement)e.nextElement(); 360 roots.put(element.name, element); 361 } 362 363 e = dtd.elements.elements(); 364 while(e.hasMoreElements()) 365 { 366 DTDElement element = (DTDElement)e.nextElement(); 367 if(!(element.content instanceof DTDContainer)) 368 { 369 continue; 370 } 371 372 Enumeration items = ((DTDContainer)element.content).getItemsVec().elements(); 373 while(items.hasMoreElements()) 374 { 375 removeElements(roots, dtd, (DTDItem)items.nextElement()); 376 } 377 } 378 379 final Collection rootCol = roots.values(); 380 return (DTDElement[])rootCol.toArray(new DTDElement[rootCol.size()]); 381 } 382 383 protected static void removeElements(Hashtable h, DTD dtd, DTDItem item) 384 { 385 if(item instanceof DTDName) 386 { 387 h.remove(((DTDName)item).value); 388 } 389 else if(item instanceof DTDContainer) 390 { 391 Enumeration e = ((DTDContainer)item).getItemsVec().elements(); 392 while(e.hasMoreElements()) 393 { 394 removeElements(h, dtd, (DTDItem)e.nextElement()); 395 } 396 } 397 } 398 399 401 private static final class Element 402 { 403 public final DTDElement element; 404 public boolean started; 405 406 public Element(DTDElement element, boolean started) 407 { 408 this.element = element; 409 this.started = started; 410 } 411 412 public Element(DTDElement element) 413 { 414 this.element = element; 415 } 416 417 public String toString() 418 { 419 return "[element=" + element.getName() + ", started=" + started + "]"; 420 } 421 } 422 } 423 | Popular Tags |