Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4864117

RFE: Make XMLDecoder API more reusable

    XMLWordPrintable

    Details

    • Subcomponent:
    • Resolved In Build:
      b46
    • CPU:
      x86
    • OS:
      linux
    • Verification:
      Verified

      Description

      Name: rmT116609 Date: 05/14/2003


      A DESCRIPTION OF THE REQUEST :
      The XMLDecoder is a very useful unit of code. It is unfortunate that it is very inflexible in its use and API. The main problem lies with the use of an InputStream as the primary source of XML for parsing. In addition, the package protected nature of the XMLDecoders internals makes it impossible to customize without rewriting the entire beans package (which is bad because so much is static).

      JUSTIFICATION :
      If XML persisted beans reside in another XML document, in order to decode them one must either:
      a) store them in a CDATA block, buffer the characters, turn them into bytes and then create an XMLDecoder using a ByteArrayInputStream
      b) store them as normal elements in the document (taking care to remove the redundant <?xml version> element that the XMLEncoder places everytime it writes), reconstruct elements from the handler, buffer the elements, turn them into bytes, then create an XMLDecoder using ByteArrayInputStream

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      1) Make XMLEncoder flexible so that it does not always output the ever-so-useful <?xml version> element
      2) Make XMLDecoder take a Reader as an argument so that if the data is ALREADY in character form, I don't have to waste time and memory converting it to bytes...
      3) Restructure XMLDecoder so that sub-classes can have access to some internals. I understand the desire to keep JAXP classes out of the public XMLDecoder API, but for subclasses, access to a ContentHandler would make dealing with embedded bean documents much easier.
      ACTUAL -
      If one wants to embed a XMLEncoded bean in another document, one must take care to either
      a) write the persisted bean within a CDATA section
      b) remove the <?xml version> element the XMLEncoder produces

      If one wants to decode an element which represents an encoded bean, one must buffer all the content, turn it into bytes, and pass the bytes to the XMLDecoder.

      ---------- BEGIN SOURCE ----------
      //Encoder for embedding XML persisted beans in document
      import java.beans.XMLEncoder;
      import java.io.*;
      public class MixedEncoder {
        
        PrintStream writer;
        TrickyOutputStream tricky;
        
        static final class TrickyOutputStream extends FilterOutputStream {
          boolean ignore = false;
          public TrickyOutputStream(OutputStream out) {
            super(out);
          }
          
          public void write(byte[] b) throws IOException {
            if (ignore) return;
            out.write(b);
          }
          
          public void write(byte[] b,int off,int len) throws IOException {
            if (ignore) return;
            out.write(b,off,len);
          }
          
          public void write(int i) throws IOException {
            if (ignore) return;
            out.write(i);
          }
          
          public void close() throws IOException {
             
          }
        }
        
        /** Creates a new instance of MixedEncoder */
        public MixedEncoder(OutputStream out) throws Exception {
          writer = new PrintStream(out,false,"UTF-8");
          tricky = new TrickyOutputStream(writer);
          writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
          writer.println("<specialDocument>");
        }
        
        public void writeObject(Object o,String extra) {
          writer.println("<special extra=\"" + extra + "\">");
          writer.println("<java>");
          XMLEncoder encoder = new XMLEncoder(tricky);
          tricky.ignore = true;
          encoder.flush();
          tricky.ignore = false;
          encoder.writeObject(o);
          encoder.flush();
          encoder.close();
          writer.println("</special>");
        }
        
        public void flush() {
          writer.flush();
        }
        
        public void close() {
          writer.println("</specialDocument>");
          writer.flush();
          writer.close();
        }
      }

      // Class for decoding from mixed document
      import java.beans.*;
      import java.io.*;
      import javax.xml.parsers.*;
      import org.xml.sax.*;
      import org.xml.sax.helpers.*;
      public class MixedDecoder {
        
        /** Creates a new instance of MixedDecoder */
        public MixedDecoder(InputStream in) throws Exception {
          SAXParserFactory factory = SAXParserFactory.newInstance();
          SAXParser parser = factory.newSAXParser();
          try {
            MixedHandler mh = new MixedHandler();
            parser.parse(in,mh);
          } catch (SAXParseException spe) {
            spe.getException().printStackTrace();
          }
        }
        
        static class MixedHandler extends DefaultHandler {
          
          String message;
          StringBuffer buffer;
          boolean special = false;
          
          public void startElement(String namespaceURI, String localName, String qName, org.xml.sax.Attributes atts) throws org.xml.sax.SAXException {
            if (special)
              appendStart(qName,atts);
            else if (qName.equals("special")) {
              message = atts.getValue("extra");
              special = true;
              buffer = new StringBuffer();
            }
          }
          
          private void appendStart(String qn,Attributes atts) {
            buffer.append('<').append(qn);
            if (atts.getLength() > 0)
              buffer.append(' ');
            for (int i = 0, ii = atts.getLength(); i < ii; i++) {
              buffer.append(atts.getQName(i)).append("=\"").append(atts.getValue(i)).append("\"");
            }
            buffer.append('>');
          }
          
          public void characters(char[] ch, int start, int length) throws org.xml.sax.SAXException {
            if (special) {
              while (start < ch.length && Character.isWhitespace(ch[start])) {start++; length--;}
              buffer.append(ch,start,length);
            }
          }
          
          public void endElement(String namespaceURI, String localName, String qName) throws org.xml.sax.SAXException {
            
            if (qName.equals("special")) {
              XMLDecoder decoder = new XMLDecoder(new ByteArrayInputStream(buffer.toString().getBytes()));
              if (decoder.readObject() == null)
                System.out.println("bogus");
              buffer = null;
              special = false;
            }
            else if (special)
              appendEnd(qName);
          }
          
          private void appendEnd(String qn) {
            buffer.append("</").append(qn).append(">\n");
          }
        }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      The hacks presented in the two classes provide a work around for this functionality.
      (Review ID: 185723)
      ======================================================================

        Attachments

          Issue Links

            Activity

              People

              Assignee:
              malenkov Sergey Malenkov (Inactive)
              Reporter:
              rmandalasunw Ranjith Mandala (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

                Dates

                Created:
                Updated:
                Resolved:
                Imported:
                Indexed: