View Javadoc

    1   package org.codehaus.xfire.util;
    2
    3   import java.io.InputStream;
    4   import java.io.OutputStream;
    5   import java.io.Reader;
    6
    7   import javax.xml.parsers.DocumentBuilder;
    8   import javax.xml.stream.XMLInputFactory;
    9   import javax.xml.stream.XMLOutputFactory;
    10  import javax.xml.stream.XMLStreamConstants;
    11  import javax.xml.stream.XMLStreamException;
    12  import javax.xml.stream.XMLStreamReader;
    13  import javax.xml.stream.XMLStreamWriter;
    14
    15  import org.codehaus.xfire.XFireRuntimeException;
    16  import org.w3c.dom.Attr;
    17  import org.w3c.dom.Document;
    18  import org.w3c.dom.Element;
    19  import org.w3c.dom.NamedNodeMap;
    20  import org.w3c.dom.Node;
    21  import org.w3c.dom.NodeList;
    22
    23  /***
    24   * Common StAX utilities.
    25   *
    26   * @author <a href="mailto:dan@envoisolutions.com">Dan Diephouse</a>
    27   * @since Oct 26, 2004
    28   */
    29  public class STAXUtils
    30  {
    31      /***
    32       * Returns true if currently at the start of an element, otherwise move forwards to the next
    33       * element start and return true, otherwise false is returned if the end of the stream is reached.
    34       */
    35      public static boolean skipToStartOfElement(XMLStreamReader in)
    36          throws XMLStreamException
    37      {
    38          for (int code = in.getEventType(); code != XMLStreamReader.END_DOCUMENT; code = in.next())
    39          {
    40              if (code == XMLStreamReader.START_ELEMENT)
    41              {
    42                  return true;
    43              }
    44          }
    45          return false;
    46      }
    47
    48      public static boolean toNextElement(DepthXMLStreamReader dr)
    49      {
    50          if (dr.getEventType() == XMLStreamReader.START_ELEMENT)
    51              return true;
    52
    53          if (dr.getEventType() == XMLStreamReader.END_ELEMENT)
    54              return false;
    55
    56          try
    57          {
    58              int depth = dr.getDepth();
    59
    60              for (int event = dr.getEventType(); dr.getDepth() >= depth; event = dr.next())
    61              {
    62                  if (event == XMLStreamReader.START_ELEMENT && dr.getDepth() == depth + 1)
    63                  {
    64                      return true;
    65                  }
    66                  else if (event == XMLStreamReader.END_ELEMENT)
    67                  {
    68                      depth--;
    69                  }
    70              }
    71
    72              return false;
    73          }
    74          catch (XMLStreamException e)
    75          {
    76              throw new XFireRuntimeException("Couldn't parse stream.", e);
    77          }
    78      }
    79
    80      public static boolean skipToStartOfElement(DepthXMLStreamReader in)
    81          throws XMLStreamException
    82      {
    83          int depth = in.getDepth();
    84          for (int code = in.getEventType(); code != XMLStreamReader.END_DOCUMENT; code = in.next())
    85          {
    86              if (code == XMLStreamReader.START_ELEMENT)
    87              {
    88                  return true;
    89              }
    90          }
    91          return false;
    92      }
    93
    94      /***
    95       * Copies the reader to the writer.  The start and end document
    96       * methods must be handled on the writer manually.
    97       *
    98       * TODO: if the namespace on the reader has been declared previously
    99       * to where we are in the stream, this probably won't work.
    100      *
    101      * @param reader
    102      * @param writer
    103      * @throws XMLStreamException
    104      */
    105     public static void copy( XMLStreamReader reader, XMLStreamWriter writer )
    106         throws XMLStreamException
    107     {
    108         int read = 0; // number of elements read in
    109         int event = reader.getEventType();
    110
    111         while ( true )
    112         {
    113             switch( event )
    114             {
    115                 case XMLStreamConstants.START_ELEMENT:
    116                     read++;
    117                     writeStartElement( reader, writer );
    118                     break;
    119                 case XMLStreamConstants.END_ELEMENT:
    120                     writer.writeEndElement();
    121                     read--;
    122                     if ( read <= 0 )
    123                         return;
    124                     break;
    125                 case XMLStreamConstants.CHARACTERS:
    126                     writer.writeCharacters( reader.getText() );
    127                     break;
    128                 case XMLStreamConstants.START_DOCUMENT:
    129                 case XMLStreamConstants.END_DOCUMENT:
    130                 case XMLStreamConstants.ATTRIBUTE:
    131                 case XMLStreamConstants.NAMESPACE:
    132                     break;
    133                 default:
    134                     break;
    135             }
    136             event = reader.next();
    137         }
    138     }
    139
    140     private static void writeStartElement(XMLStreamReader reader, XMLStreamWriter writer)
    141         throws XMLStreamException
    142     {
    143         String local = reader.getLocalName();
    144         String uri = reader.getNamespaceURI();
    145         String prefix = reader.getPrefix();
    146         if (prefix == null)
    147         {
    148             prefix = "";
    149         }
    150
    151         String boundPrefix = writer.getPrefix(uri);
    152         boolean writeElementNS = false;
    153         if ( boundPrefix == null || !prefix.equals(boundPrefix) )
    154         {
    155             writeElementNS = true;
    156         }
    157
    158         // Write out the element name and possible the default namespace
    159         if (uri != null)
    160         {
    161             if (prefix.length() == 0)
    162             {
    163                 writer.writeStartElement(local);
    164                 writer.setDefaultNamespace(uri);
    165             }
    166             else
    167             {
    168                 writer.writeStartElement(prefix, local, uri);
    169                 writer.setPrefix(prefix, uri);
    170             }
    171         }
    172         else
    173         {
    174             writer.writeStartElement( reader.getLocalName() );
    175         }
    176
    177         // Write out the namespaces
    178         for ( int i = 0; i < reader.getNamespaceCount(); i++ )
    179         {
    180             String nsURI = reader.getNamespaceURI(i);
    181             String nsPrefix = reader.getNamespacePrefix(i);
    182             if (nsPrefix == null) nsPrefix = "";
    183
    184             if ( nsPrefix.length() ==  0 )
    185             {
    186                 writer.writeDefaultNamespace(nsURI);
    187             }
    188             else
    189             {
    190                 writer.writeNamespace(nsPrefix, nsURI);
    191             }
    192
    193             if (nsURI.equals(uri) && nsPrefix.equals(prefix))
    194             {
    195                 writeElementNS = false;
    196             }
    197         }
    198
    199         // Check if the namespace still needs to be written.
    200         // We need this check because namespace writing works
    201         // different on Woodstox and the RI.
    202         if (writeElementNS)
    203         {
    204             if ( prefix == null || prefix.length() ==  0 )
    205             {
    206                 writer.writeDefaultNamespace(uri);
    207             }
    208             else
    209             {
    210                 writer.writeNamespace(prefix, uri);
    211             }
    212         }
    213
    214         // Write out attributes
    215         for ( int i = 0; i < reader.getAttributeCount(); i++ )
    216         {
    217             String ns = reader.getAttributeNamespace(i);
    218             if ( ns == null || ns.length() == 0 )
    219             {
    220                 writer.writeAttribute(
    221                         reader.getAttributeLocalName(i),
    222                         reader.getAttributeValue(i));
    223             }
    224             else
    225             {
    226                 writer.writeAttribute(
    227                     reader.getAttributeNamespace(i),
    228                     reader.getAttributeLocalName(i),
    229                     reader.getAttributeValue(i));
    230             }
    231         }
    232     }
    233
    234     /***
    235      * Writes an Element to an XMLStreamWriter.  The writer must already
    236      * have started the doucment (via writeStartDocument()). Also, this probably
    237      * won't work with just a fragment of a document. The Element should be
    238      * the root element of the document.
    239      *
    240      * @param e
    241      * @param writer
    242      * @throws XMLStreamException
    243      */
    244     public static void writeElement( Element e, XMLStreamWriter writer )
    245         throws XMLStreamException
    246     {
    247         String prefix = e.getPrefix();
    248         String ns = e.getNamespaceURI();
    249         String localName = e.getLocalName();
    250
    251         if ( prefix == null )
    252         {
    253             if ( ns == null )
    254             {
    255                 writer.writeStartElement( localName );
    256             }
    257             else
    258             {
    259                 prefix = "";
    260                 writer.setDefaultNamespace(ns);
    261                 writer.writeStartElement( ns, localName );
    262
    263                 String curUri = writer.getNamespaceContext().getNamespaceURI(prefix);
    264                 if ( curUri == null || curUri.length() != ns.length() )
    265                 {
    266                     writer.writeDefaultNamespace(ns);
    267                 }
    268             }
    269         }
    270         else
    271         {
    272             writer.writeStartElement(prefix, localName, ns);
    273
    274             String curUri = writer.getNamespaceContext().getNamespaceURI(prefix);
    275             if ( curUri == null || curUri.length() != ns.length() || !curUri.equals(ns) )
    276             {
    277                 writer.writeNamespace(prefix, ns);
    278             }
    279         }
    280
    281         NamedNodeMap attrs = e.getAttributes();
    282         for ( int i = 0; i < attrs.getLength(); i++ )
    283         {
    284             Node attr = attrs.item(i);
    285
    286             String attrPrefix = writer.getNamespaceContext().getPrefix(attr.getNamespaceURI());
    287             if ( attrPrefix == null )
    288             {
    289                 writer.writeAttribute(attr.getNamespaceURI(), attr.getNodeName(), attr.getNodeValue());
    290             }
    291             else
    292             {
    293                 writer.writeAttribute(attrPrefix, attr.getNamespaceURI(), attr.getNodeName(), attr.getNodeValue());
    294             }
    295         }
    296
    297         String value = DOMUtils.getContent(e);
    298
    299         if ( value != null && value.length() > 0)
    300             writer.writeCharacters( value );
    301
    302         NodeList nodes = e.getChildNodes();
    303         for ( int i = 0; i < nodes.getLength(); i++ )
    304         {
    305             Node n = nodes.item(i);
    306             if ( n instanceof Element )
    307             {
    308                 writeElement((Element)n, writer);
    309             }
    310         }
    311
    312         writer.writeEndElement();
    313     }
    314
    315     /***
    316      * @param e
    317      * @return
    318      */
    319     private static Element getNamespaceDeclarer(Element e)
    320     {
    321         while( true )
    322         {
    323             Node n = e.getParentNode();
    324             if ( n.equals(e) )
    325                 return null;
    326             if ( n.getNamespaceURI() != null )
    327                 return (Element) n;
    328         }
    329     }
    330
    331     public static Document read(DocumentBuilder builder, XMLStreamReader reader)
    332         throws XMLStreamException
    333     {
    334         Element rootEl = null;
    335         Document doc = builder.newDocument();
    336
    337         int event = reader.getEventType();
    338         while ( reader.hasNext() )
    339         {
    340             switch( event )
    341             {
    342             case XMLStreamConstants.START_ELEMENT:
    343                 rootEl = doc.createElementNS(reader.getNamespaceURI(), reader.getLocalName());
    344
    345                 doc.appendChild(rootEl);
    346
    347                 declareNamespaces(reader, rootEl);
    348
    349                 for ( int i = 0; i < reader.getAttributeCount(); i++ )
    350                 {
    351                     Attr attr = doc.createAttributeNS(reader.getAttributeNamespace(i),
    352                                                       reader.getAttributeLocalName(i));
    353                     attr.setValue(reader.getAttributeValue(i));
    354                     rootEl.setAttributeNode(attr);
    355                 }
    356
    357                 reader.next();
    358
    359                 readElements(rootEl, reader);
    360
    361                 reader.next();
    362
    363                 return doc;
    364             case XMLStreamConstants.END_ELEMENT:
    365                 return doc;
    366             case XMLStreamConstants.CHARACTERS:
    367                 if (rootEl != null)
    368                 {
    369                     rootEl.appendChild(doc.createTextNode(reader.getText()));
    370                 }
    371                 break;
    372             default:
    373                 break;
    374             }
    375
    376             event = reader.next();
    377         }
    378
    379         return doc;
    380     }
    381
    382     public static void readElements(Element parent, XMLStreamReader reader)
    383         throws XMLStreamException
    384     {
    385         Element e = null;
    386         Document doc = parent.getOwnerDocument();
    387
    388         int event = reader.getEventType();
    389         while ( reader.hasNext() )
    390         {
    391             switch( event )
    392             {
    393             case XMLStreamConstants.START_ELEMENT:
    394                 e = doc.createElementNS(reader.getNamespaceURI(), reader.getLocalName());
    395
    396                 declareNamespaces(reader, e);
    397
    398                 for ( int i = 0; i < reader.getAttributeCount(); i++ )
    399                 {
    400                     Attr attr = doc.createAttributeNS(reader.getAttributeNamespace(i),
    401                                                       reader.getAttributeLocalName(i));
    402                     attr.setValue(reader.getAttributeValue(i));
    403                     e.setAttributeNode(attr);
    404                 }
    405
    406                 parent.appendChild(e);
    407
    408                 reader.next();
    409
    410                 readElements(e, reader);
    411
    412                 reader.next();
    413                 break;
    414             case XMLStreamConstants.END_ELEMENT:
    415                 return;
    416             case XMLStreamConstants.CHARACTERS:
    417                 parent.appendChild(doc.createTextNode(reader.getText()));
    418
    419                 break;
    420             case XMLStreamConstants.NAMESPACE:
    421             case XMLStreamConstants.END_DOCUMENT:
    422             case XMLStreamConstants.CDATA:
    423             case XMLStreamConstants.START_DOCUMENT:
    424             case XMLStreamConstants.ATTRIBUTE:
    425             default:
    426                 break;
    427             }
    428             event = reader.next();
    429         }
    430     }
    431
    432     private static void declareNamespaces(XMLStreamReader reader, Element node)
    433     {
    434         for (int i = 0; i < reader.getNamespaceCount(); i++)
    435         {
    436             String uri = reader.getNamespaceURI(i);
    437             String prefix = reader.getNamespacePrefix(i);
    438
    439             if (prefix != null && !uri.equals(node.getNamespaceURI()))
    440             {
    441                 node.setAttribute("xmlns:" + prefix, uri);
    442             }
    443         }
    444     }
    445
    446     public static XMLStreamWriter createXMLStreamWriter(OutputStream out, String encoding)
    447     {
    448         XMLOutputFactory factory = XMLOutputFactory.newInstance();
    449
    450         if (encoding == null) encoding = "UTF-8";
    451
    452         try
    453         {
    454             XMLStreamWriter writer = factory.createXMLStreamWriter(out, encoding);
    455
    456             return writer;
    457         }
    458         catch (XMLStreamException e)
    459         {
    460             throw new XFireRuntimeException("Couldn't parse stream.", e);
    461         }
    462     }
    463
    464     public static XMLStreamReader createXMLStreamReader(InputStream in, String encoding)
    465     {
    466         XMLInputFactory factory = XMLInputFactory.newInstance();
    467
    468         if (encoding == null) encoding = "UTF-8";
    469
    470         try
    471         {
    472             return factory.createXMLStreamReader(in, encoding);
    473         }
    474         catch (XMLStreamException e)
    475         {
    476             throw new XFireRuntimeException("Couldn't parse stream.", e);
    477         }
    478     }
    479
    480     public static XMLStreamReader createXMLStreamReader(Reader reader)
    481     {
    482         XMLInputFactory factory = XMLInputFactory.newInstance();
    483
    484         try
    485         {
    486             return factory.createXMLStreamReader(reader);
    487         }
    488         catch (XMLStreamException e)
    489         {
    490             throw new XFireRuntimeException("Couldn't parse stream.", e);
    491         }
    492     }
    493 }