src/share/jaxws_classes/com/sun/xml/internal/ws/encoding/MtomCodec.java

Wed, 12 Jun 2013 14:47:09 +0100

author
mkos
date
Wed, 12 Jun 2013 14:47:09 +0100
changeset 384
8f2986ff0235
parent 368
0989ad8c0860
child 397
b99d7e355d4b
permissions
-rw-r--r--

8013021: Rebase 8005432 & 8003542 against the latest jdk8/jaxws
8003542: Improve processing of MTOM attachments
8005432: Update access to JAX-WS
Reviewed-by: mullan

     1 /*
     2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package com.sun.xml.internal.ws.encoding;
    28 import com.sun.istack.internal.NotNull;
    29 import com.sun.xml.internal.bind.DatatypeConverterImpl;
    30 import com.sun.xml.internal.ws.api.SOAPVersion;
    31 import com.sun.xml.internal.ws.api.WSFeatureList;
    32 import com.sun.xml.internal.ws.api.message.Attachment;
    33 import com.sun.xml.internal.ws.api.message.AttachmentSet;
    34 import com.sun.xml.internal.ws.api.message.Packet;
    35 import com.sun.xml.internal.ws.api.pipe.ContentType;
    36 import com.sun.xml.internal.ws.api.pipe.StreamSOAPCodec;
    37 import com.sun.xml.internal.ws.api.streaming.XMLStreamReaderFactory;
    38 import com.sun.xml.internal.ws.api.streaming.XMLStreamWriterFactory;
    39 import com.sun.xml.internal.ws.developer.SerializationFeature;
    40 import com.sun.xml.internal.ws.developer.StreamingDataHandler;
    41 import com.sun.xml.internal.ws.message.MimeAttachmentSet;
    42 import com.sun.xml.internal.ws.streaming.XMLStreamWriterUtil;
    43 import com.sun.xml.internal.ws.util.ByteArrayDataSource;
    44 import com.sun.xml.internal.ws.util.xml.XMLStreamReaderFilter;
    45 import com.sun.xml.internal.ws.util.xml.XMLStreamWriterFilter;
    46 import com.sun.xml.internal.ws.streaming.MtomStreamWriter;
    47 import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil;
    48 import com.sun.xml.internal.ws.server.UnsupportedMediaException;
    49 import com.sun.xml.internal.org.jvnet.staxex.Base64Data;
    50 import com.sun.xml.internal.org.jvnet.staxex.NamespaceContextEx;
    51 import com.sun.xml.internal.org.jvnet.staxex.XMLStreamReaderEx;
    52 import com.sun.xml.internal.org.jvnet.staxex.XMLStreamWriterEx;
    54 import javax.activation.DataHandler;
    55 import javax.xml.namespace.NamespaceContext;
    56 import javax.xml.stream.XMLStreamConstants;
    57 import javax.xml.stream.XMLStreamException;
    58 import javax.xml.stream.XMLStreamReader;
    59 import javax.xml.stream.XMLStreamWriter;
    60 import javax.xml.ws.WebServiceException;
    61 import javax.xml.ws.soap.MTOMFeature;
    62 import javax.xml.bind.attachment.AttachmentMarshaller;
    63 import java.io.IOException;
    64 import java.io.OutputStream;
    65 import java.io.UnsupportedEncodingException;
    66 import java.net.URLDecoder;
    67 import java.nio.channels.WritableByteChannel;
    68 import java.nio.charset.Charset;
    69 import java.util.ArrayList;
    70 import java.util.Iterator;
    71 import java.util.List;
    72 import java.util.Map;
    73 import java.util.UUID;
    75 /**
    76  * Mtom message Codec. It can be used even for non-soap message's mtom encoding.
    77  *
    78  * @author Vivek Pandey
    79  * @author Jitendra Kotamraju
    80  */
    81 public class MtomCodec extends MimeCodec {
    83     public static final String XOP_XML_MIME_TYPE = "application/xop+xml";
    84     public static final String XOP_LOCALNAME = "Include";
    85     public static final String XOP_NAMESPACEURI = "http://www.w3.org/2004/08/xop/include";
    87     private final StreamSOAPCodec codec;
    88     private final MTOMFeature mtomFeature;
    89     private final SerializationFeature sf;
    90     private final static String DECODED_MESSAGE_CHARSET = "decodedMessageCharset";
    92     MtomCodec(SOAPVersion version, StreamSOAPCodec codec, WSFeatureList features){
    93         super(version, features);
    94         this.codec = codec;
    95         sf = features.get(SerializationFeature.class);
    96         MTOMFeature mtom = features.get(MTOMFeature.class);
    97         if(mtom == null)
    98             this.mtomFeature = new MTOMFeature();
    99         else
   100             this.mtomFeature = mtom;
   101     }
   103     /**
   104      * Return the soap 1.1 and soap 1.2 specific XOP packaged ContentType
   105      *
   106      * @return A non-null content type for soap11 or soap 1.2 content type
   107      */
   108     @Override
   109     public ContentType getStaticContentType(Packet packet) {
   110         return getStaticContentTypeStatic(packet, version);
   111     }
   113     public static ContentType getStaticContentTypeStatic(Packet packet, SOAPVersion version) {
   114         ContentType ct = (ContentType) packet.getInternalContentType();
   115         if ( ct != null ) return ct;
   117         String uuid = UUID.randomUUID().toString();
   118         String boundary = "uuid:" + uuid;
   119         String rootId = "<rootpart*"+uuid+"@example.jaxws.sun.com>";
   120         String soapActionParameter = SOAPVersion.SOAP_11.equals(version) ?  null : createActionParameter(packet);
   122         String boundaryParameter = "boundary=\"" + boundary +"\"";
   123         String messageContentType = MULTIPART_RELATED_MIME_TYPE +
   124                 ";start=\""+rootId +"\"" +
   125                 ";type=\"" + XOP_XML_MIME_TYPE + "\";" +
   126                 boundaryParameter +
   127                 ";start-info=\"" + version.contentType +
   128                 (soapActionParameter == null? "" : soapActionParameter) +
   129                 "\"";
   131         ContentTypeImpl ctImpl = SOAPVersion.SOAP_11.equals(version) ?
   132                 new ContentTypeImpl(messageContentType, (packet.soapAction == null)?"":packet.soapAction, null) :
   133                 new ContentTypeImpl(messageContentType, null, null);
   134         ctImpl.setBoundary(boundary);
   135         ctImpl.setRootId(rootId);
   136         packet.setContentType(ctImpl);
   137         return ctImpl;
   138     }
   140     private static String createActionParameter(Packet packet) {
   141         return packet.soapAction != null? ";action=\\\""+packet.soapAction+"\\\"" : "";
   142     }
   144     @Override
   145     public ContentType encode(Packet packet, OutputStream out) throws IOException {
   146         ContentTypeImpl ctImpl = (ContentTypeImpl) this.getStaticContentType(packet);
   147         String boundary = ctImpl.getBoundary();
   148         String rootId = ctImpl.getRootId();
   150         if(packet.getMessage() != null){
   151             try {
   152                 String encoding = getPacketEncoding(packet);
   153                 packet.invocationProperties.remove(DECODED_MESSAGE_CHARSET);
   155                 String actionParameter = getActionParameter(packet, version);
   156                 String soapXopContentType = getSOAPXopContentType(encoding, version, actionParameter);
   158                 writeln("--"+boundary, out);
   159                 writeMimeHeaders(soapXopContentType, rootId, out);
   161                 //mtom attachments that need to be written after the root part
   162                 List<ByteArrayBuffer> mtomAttachments = new ArrayList<ByteArrayBuffer>();
   163                 MtomStreamWriterImpl writer = new MtomStreamWriterImpl(
   164                         XMLStreamWriterFactory.create(out, encoding), mtomAttachments, boundary, mtomFeature);
   166                 packet.getMessage().writeTo(writer);
   167                 XMLStreamWriterFactory.recycle(writer);
   168                 writeln(out);
   170                 for(ByteArrayBuffer bos : mtomAttachments){
   171                     bos.write(out);
   172                 }
   174                 // now write out the attachments in the message that weren't
   175                 // previously written
   176                 writeNonMtomAttachments(packet.getMessage().getAttachments(),
   177                         out, boundary);
   179                 //write out the end boundary
   180                 writeAsAscii("--"+boundary, out);
   181                 writeAsAscii("--", out);
   183             } catch (XMLStreamException e) {
   184                 throw new WebServiceException(e);
   185             }
   186         }
   187         //now create the boundary for next encode() call
   188 //        createConteTypeHeader();
   189         return ctImpl;
   190     }
   192     public static String getSOAPXopContentType(String encoding, SOAPVersion version,
   193             String actionParameter) {
   194         return XOP_XML_MIME_TYPE +";charset="+encoding+";type=\""+version.contentType+ actionParameter + "\"";
   195     }
   197     public static String getActionParameter(Packet packet, SOAPVersion version) {
   198         return (version == SOAPVersion.SOAP_11) ? "" : createActionParameter(packet);
   199     }
   201     public static class ByteArrayBuffer{
   202         final String contentId;
   204         private final DataHandler dh;
   205         private final String boundary;
   207         ByteArrayBuffer(@NotNull DataHandler dh, String b) {
   208             this.dh = dh;
   209             String cid = null;
   210             if (dh instanceof StreamingDataHandler) {
   211                 StreamingDataHandler sdh = (StreamingDataHandler) dh;
   212                 if (sdh.getHrefCid() != null)
   213                     cid = sdh.getHrefCid();
   214             }
   215             this.contentId = cid != null ? cid : encodeCid();
   216             boundary = b;
   217         }
   219         public void write(OutputStream os) throws IOException {
   220             //build attachment frame
   221             writeln("--"+boundary, os);
   222             writeMimeHeaders(dh.getContentType(), contentId, os);
   223             dh.writeTo(os);
   224             writeln(os);
   225         }
   226     }
   228     public static void writeMimeHeaders(String contentType, String contentId, OutputStream out) throws IOException {
   229         String cid = contentId;
   230         if(cid != null && cid.length() >0 && cid.charAt(0) != '<')
   231             cid = '<' + cid + '>';
   232         writeln("Content-Id: " + cid, out);
   233         writeln("Content-Type: " + contentType, out);
   234         writeln("Content-Transfer-Encoding: binary", out);
   235         writeln(out);
   236     }
   238     // Compiler warning for not calling close, but cannot call close,
   239     // will consume attachment bytes.
   240         @SuppressWarnings("resource")
   241     private void writeNonMtomAttachments(AttachmentSet attachments,
   242             OutputStream out, String boundary) throws IOException {
   244         for (Attachment att : attachments) {
   246             DataHandler dh = att.asDataHandler();
   247             if (dh instanceof StreamingDataHandler) {
   248                 StreamingDataHandler sdh = (StreamingDataHandler) dh;
   249                 // If DataHandler has href Content-ID, it is MTOM, so skip.
   250                 if (sdh.getHrefCid() != null)
   251                     continue;
   252             }
   254             // build attachment frame
   255             writeln("--" + boundary, out);
   256             writeMimeHeaders(att.getContentType(), att.getContentId(), out);
   257             att.writeTo(out);
   258             writeln(out); // write \r\n
   259         }
   260     }
   262     @Override
   263     public ContentType encode(Packet packet, WritableByteChannel buffer) {
   264         throw new UnsupportedOperationException();
   265     }
   267     @Override
   268     public MtomCodec copy() {
   269         return new MtomCodec(version, (StreamSOAPCodec)codec.copy(), features);
   270     }
   272     private static String encodeCid(){
   273         String cid="example.jaxws.sun.com";
   274         String name = UUID.randomUUID()+"@";
   275         return name + cid;
   276     }
   278     @Override
   279     protected void decode(MimeMultipartParser mpp, Packet packet) throws IOException {
   280         //TODO shouldn't we check for SOAP1.1/SOAP1.2 and throw
   281         //TODO UnsupportedMediaException like StreamSOAPCodec
   282         String charset = null;
   283         String ct = mpp.getRootPart().getContentType();
   284         if (ct != null) {
   285             charset = new ContentTypeImpl(ct).getCharSet();
   286         }
   287         if (charset != null && !Charset.isSupported(charset)) {
   288             throw new UnsupportedMediaException(charset);
   289         }
   291         if (charset != null) {
   292             packet.invocationProperties.put(DECODED_MESSAGE_CHARSET, charset);
   293         } else {
   294             packet.invocationProperties.remove(DECODED_MESSAGE_CHARSET);
   295         }
   297         // we'd like to reuse those reader objects but unfortunately decoder may be reused
   298         // before the decoded message is completely used.
   299         XMLStreamReader mtomReader = new MtomXMLStreamReaderEx( mpp,
   300             XMLStreamReaderFactory.create(null, mpp.getRootPart().asInputStream(), charset, true)
   301         );
   303         packet.setMessage(codec.decode(mtomReader, new MimeAttachmentSet(mpp)));
   304         packet.setMtomFeature(mtomFeature);
   305         packet.setContentType(mpp.getContentType());
   306     }
   308     private String getPacketEncoding(Packet packet) {
   309         // If SerializationFeature is set, just use that encoding
   310         if (sf != null && sf.getEncoding() != null) {
   311             return sf.getEncoding().equals("") ? SOAPBindingCodec.DEFAULT_ENCODING : sf.getEncoding();
   312         }
   313         return determinePacketEncoding(packet);
   314     }
   316     public static String determinePacketEncoding(Packet packet) {
   317         if (packet != null && packet.endpoint != null) {
   318             // Use request message's encoding for Server-side response messages
   319             String charset = (String)packet.invocationProperties.get(DECODED_MESSAGE_CHARSET);
   320             return charset == null
   321                     ? SOAPBindingCodec.DEFAULT_ENCODING : charset;
   322         }
   324         // Use default encoding for client-side request messages
   325         return SOAPBindingCodec.DEFAULT_ENCODING;
   326     }
   328     public static class MtomStreamWriterImpl extends XMLStreamWriterFilter implements XMLStreamWriterEx,
   329             MtomStreamWriter, HasEncoding {
   330         private final List<ByteArrayBuffer> mtomAttachments;
   331         private final String boundary;
   332         private final MTOMFeature myMtomFeature;
   333         public MtomStreamWriterImpl(XMLStreamWriter w, List<ByteArrayBuffer> mtomAttachments, String b, MTOMFeature myMtomFeature) {
   334             super(w);
   335             this.mtomAttachments = mtomAttachments;
   336             this.boundary = b;
   337             this.myMtomFeature = myMtomFeature;
   338         }
   340         @Override
   341         public void writeBinary(byte[] data, int start, int len, String contentType) throws XMLStreamException {
   342             //check threshold and if less write as base64encoded value
   343             if(myMtomFeature.getThreshold() > len){
   344                 writeCharacters(DatatypeConverterImpl._printBase64Binary(data, start, len));
   345                 return;
   346             }
   347             ByteArrayBuffer bab = new ByteArrayBuffer(new DataHandler(new ByteArrayDataSource(data, start, len, contentType)), boundary);
   348             writeBinary(bab);
   349         }
   351         @Override
   352         public void writeBinary(DataHandler dataHandler) throws XMLStreamException {
   353             // TODO how do we check threshold and if less inline the data
   354             writeBinary(new ByteArrayBuffer(dataHandler, boundary));
   355         }
   357         @Override
   358         public OutputStream writeBinary(String contentType) throws XMLStreamException {
   359             throw new UnsupportedOperationException();
   360         }
   362         @Override
   363         public void writePCDATA(CharSequence data) throws XMLStreamException {
   364             if(data == null)
   365                 return;
   366             if(data instanceof Base64Data){
   367                 Base64Data binaryData = (Base64Data)data;
   368                 writeBinary(binaryData.getDataHandler());
   369                 return;
   370             }
   371             writeCharacters(data.toString());
   372         }
   374         private void writeBinary(ByteArrayBuffer bab) {
   375             try {
   376                 mtomAttachments.add(bab);
   377                 writer.setPrefix("xop", XOP_NAMESPACEURI);
   378                 writer.writeNamespace("xop", XOP_NAMESPACEURI);
   379                 writer.writeStartElement(XOP_NAMESPACEURI, XOP_LOCALNAME);
   380                 writer.writeAttribute("href", "cid:"+bab.contentId);
   381                 writer.writeEndElement();
   382                 writer.flush();
   383             } catch (XMLStreamException e) {
   384                 throw new WebServiceException(e);
   385             }
   386         }
   388         @Override
   389         public Object getProperty(String name) throws IllegalArgumentException {
   390             // Hack for JDK6's SJSXP
   391             if (name.equals("sjsxp-outputstream") && writer instanceof Map) {
   392                 Object obj = ((Map) writer).get("sjsxp-outputstream");
   393                 if (obj != null) {
   394                     return obj;
   395                 }
   396             }
   397             return super.getProperty(name);
   398         }
   400         /**
   401          * JAXBMessage writes envelope directly to the OutputStream(for SJSXP, woodstox).
   402          * While writing, it calls the AttachmentMarshaller methods for adding attachments.
   403          * JAXB writes xop:Include in this case.
   404          */
   405         @Override
   406         public AttachmentMarshaller getAttachmentMarshaller() {
   407             return new AttachmentMarshaller() {
   409                 @Override
   410                 public String addMtomAttachment(DataHandler data, String elementNamespace, String elementLocalName) {
   411                     // Should we do the threshold processing on DataHandler ? But that would be
   412                     // expensive as DataHolder need to read the data again from its source
   413                     ByteArrayBuffer bab = new ByteArrayBuffer(data, boundary);
   414                     mtomAttachments.add(bab);
   415                     return "cid:"+bab.contentId;
   416                 }
   418                 @Override
   419                 public String addMtomAttachment(byte[] data, int offset, int length, String mimeType, String elementNamespace, String elementLocalName) {
   420                     // inline the data based on the threshold
   421                     if (myMtomFeature.getThreshold() > length) {
   422                         return null;                // JAXB inlines the attachment data
   423                     }
   424                     ByteArrayBuffer bab = new ByteArrayBuffer(new DataHandler(new ByteArrayDataSource(data, offset, length, mimeType)), boundary);
   425                     mtomAttachments.add(bab);
   426                     return "cid:"+bab.contentId;
   427                 }
   429                 @Override
   430                 public String addSwaRefAttachment(DataHandler data) {
   431                     ByteArrayBuffer bab = new ByteArrayBuffer(data, boundary);
   432                     mtomAttachments.add(bab);
   433                     return "cid:"+bab.contentId;
   434                 }
   436                 @Override
   437                 public boolean isXOPPackage() {
   438                     return true;
   439                 }
   440             };
   441         }
   443         public List<ByteArrayBuffer> getMtomAttachments() {
   444             return this.mtomAttachments;
   445         }
   447         @Override
   448         public String getEncoding() {
   449             return XMLStreamWriterUtil.getEncoding(writer);
   450         }
   452         private static class MtomNamespaceContextEx implements NamespaceContextEx {
   453             private final NamespaceContext nsContext;
   455             public MtomNamespaceContextEx(NamespaceContext nsContext) {
   456                 this.nsContext = nsContext;
   457             }
   459             @Override
   460             public Iterator<Binding> iterator() {
   461                 throw new UnsupportedOperationException();
   462             }
   464             @Override
   465             public String getNamespaceURI(String prefix) {
   466                 return nsContext.getNamespaceURI(prefix);
   467             }
   469             @Override
   470             public String getPrefix(String namespaceURI) {
   471                 return nsContext.getPrefix(namespaceURI);
   472             }
   474             @Override
   475             public Iterator getPrefixes(String namespaceURI) {
   476                 return nsContext.getPrefixes(namespaceURI);
   477             }
   478         }
   480         @Override
   481         public NamespaceContextEx getNamespaceContext() {
   482             NamespaceContext nsContext = writer.getNamespaceContext();
   483             return new MtomNamespaceContextEx(nsContext);
   484         }
   485     }
   487     public static class MtomXMLStreamReaderEx extends XMLStreamReaderFilter implements XMLStreamReaderEx {
   488         /**
   489          * The parser for the outer MIME 'shell'.
   490          */
   491         private final MimeMultipartParser mimeMP;
   493         private boolean xopReferencePresent = false;
   494         private Base64Data base64AttData;
   496         //To be used with #getTextCharacters
   497         private char[] base64EncodedText;
   499         private String xopHref;
   501         public MtomXMLStreamReaderEx(MimeMultipartParser mimeMP, XMLStreamReader reader) {
   502             super(reader);
   503             this.mimeMP = mimeMP;
   504         }
   506         @Override
   507         public CharSequence getPCDATA() throws XMLStreamException {
   508             if(xopReferencePresent){
   509                 return base64AttData;
   510             }
   511             return reader.getText();
   512         }
   514         @Override
   515         public NamespaceContextEx getNamespaceContext() {
   516             NamespaceContext nsContext = reader.getNamespaceContext();
   517             return new MtomNamespaceContextEx(nsContext);
   518         }
   520         @Override
   521         public String getElementTextTrim() throws XMLStreamException {
   522             throw new UnsupportedOperationException();
   523         }
   525         private static class MtomNamespaceContextEx implements NamespaceContextEx {
   526             private final NamespaceContext nsContext;
   528             public MtomNamespaceContextEx(NamespaceContext nsContext) {
   529                 this.nsContext = nsContext;
   530             }
   532             @Override
   533             public Iterator<Binding> iterator() {
   534                 throw new UnsupportedOperationException();
   535             }
   537             @Override
   538             public String getNamespaceURI(String prefix) {
   539                 return nsContext.getNamespaceURI(prefix);
   540             }
   542             @Override
   543             public String getPrefix(String namespaceURI) {
   544                 return nsContext.getPrefix(namespaceURI);
   545             }
   547             @Override
   548             public Iterator getPrefixes(String namespaceURI) {
   549                 return nsContext.getPrefixes(namespaceURI);
   550             }
   552         }
   554         @Override
   555         public int getTextLength() {
   556             if (xopReferencePresent) {
   557                 return base64AttData.length();
   558             }
   559             return reader.getTextLength();
   560         }
   562         @Override
   563         public int getTextStart() {
   564             if (xopReferencePresent) {
   565                 return 0;
   566             }
   567             return reader.getTextStart();
   568         }
   570         @Override
   571         public int getEventType() {
   572             if(xopReferencePresent)
   573                 return XMLStreamConstants.CHARACTERS;
   574             return super.getEventType();
   575         }
   577         @Override
   578         public int next() throws XMLStreamException {
   579             int event = reader.next();
   580             if (event == XMLStreamConstants.START_ELEMENT && reader.getLocalName().equals(XOP_LOCALNAME) && reader.getNamespaceURI().equals(XOP_NAMESPACEURI)) {
   581                 //its xop reference, take the URI reference
   582                 String href = reader.getAttributeValue(null, "href");
   583                 try {
   584                     xopHref = href;
   585                     Attachment att = getAttachment(href);
   586                     if(att != null){
   587                         DataHandler dh = att.asDataHandler();
   588                         if (dh instanceof StreamingDataHandler) {
   589                             ((StreamingDataHandler)dh).setHrefCid(att.getContentId());
   590                         }
   591                         base64AttData = new Base64Data();
   592                         base64AttData.set(dh);
   593                     }
   594                     xopReferencePresent = true;
   595                 } catch (IOException e) {
   596                     throw new WebServiceException(e);
   597                 }
   598                 //move to the </xop:Include>
   599                 XMLStreamReaderUtil.nextElementContent(reader);
   600                 return XMLStreamConstants.CHARACTERS;
   601             }
   602             if(xopReferencePresent){
   603                 xopReferencePresent = false;
   604                 base64EncodedText = null;
   605                 xopHref = null;
   606             }
   607             return event;
   608         }
   610         private String decodeCid(String cid) {
   611             try {
   612                 cid = URLDecoder.decode(cid, "utf-8");
   613             } catch (UnsupportedEncodingException e) {
   614                 //on recceiving side lets not fail now, try to look for it
   615             }
   616             return cid;
   617         }
   619         private Attachment getAttachment(String cid) throws IOException {
   620             if (cid.startsWith("cid:"))
   621                 cid = cid.substring(4, cid.length());
   622             if (cid.indexOf('%') != -1) {
   623                 cid = decodeCid(cid);
   624                 return mimeMP.getAttachmentPart(cid);
   625             }
   626             return mimeMP.getAttachmentPart(cid);
   627         }
   629         @Override
   630         public char[] getTextCharacters() {
   631             if (xopReferencePresent) {
   632                 char[] chars = new char[base64AttData.length()];
   633                 base64AttData.writeTo(chars, 0);
   634                 return chars;
   635             }
   636             return reader.getTextCharacters();
   637         }
   639         @Override
   640         public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length) throws XMLStreamException {
   641             if(xopReferencePresent){
   642                 if(target == null){
   643                     throw new NullPointerException("target char array can't be null") ;
   644                 }
   646                 if(targetStart < 0 || length < 0 || sourceStart < 0 || targetStart >= target.length ||
   647                         (targetStart + length ) > target.length) {
   648                     throw new IndexOutOfBoundsException();
   649                 }
   651                 int textLength = base64AttData.length();
   652                 if(sourceStart > textLength)
   653                     throw new IndexOutOfBoundsException();
   655                 if(base64EncodedText == null){
   656                     base64EncodedText = new char[base64AttData.length()];
   657                     base64AttData.writeTo(base64EncodedText, 0);
   658                 }
   660                 int copiedLength = Math.min(textLength - sourceStart, length);
   661                 System.arraycopy(base64EncodedText, sourceStart , target, targetStart, copiedLength);
   662                 return copiedLength;
   663             }
   664             return reader.getTextCharacters(sourceStart, target, targetStart, length);
   665         }
   667         @Override
   668         public String getText() {
   669             if (xopReferencePresent) {
   670                 return base64AttData.toString();
   671             }
   672             return reader.getText();
   673         }
   675         protected boolean isXopReference() throws XMLStreamException {
   676             return xopReferencePresent;
   677         }
   679         protected String getXopHref() {
   680             return xopHref;
   681         }
   683         public MimeMultipartParser getMimeMultipartParser() {
   684             return mimeMP;
   685         }
   686     }
   688 }

mercurial