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

Fri, 23 Aug 2013 09:57:21 +0100

author
mkos
date
Fri, 23 Aug 2013 09:57:21 +0100
changeset 397
b99d7e355d4b
parent 384
8f2986ff0235
child 637
9c07ef4934dd
permissions
-rw-r--r--

8022885: Update JAX-WS RI integration to 2.2.9-b14140
8013016: Rebase 8009009 against the latest jdk8/jaxws
Reviewed-by: alanb, chegar

     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.NamespaceContextExAdaper;
    45 import com.sun.xml.internal.ws.util.xml.XMLStreamReaderFilter;
    46 import com.sun.xml.internal.ws.util.xml.XMLStreamWriterFilter;
    47 import com.sun.xml.internal.ws.streaming.MtomStreamWriter;
    48 import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil;
    49 import com.sun.xml.internal.ws.server.UnsupportedMediaException;
    50 import com.sun.xml.internal.org.jvnet.staxex.Base64Data;
    51 import com.sun.xml.internal.org.jvnet.staxex.NamespaceContextEx;
    52 import com.sun.xml.internal.org.jvnet.staxex.XMLStreamReaderEx;
    53 import com.sun.xml.internal.org.jvnet.staxex.XMLStreamWriterEx;
    55 import javax.activation.DataHandler;
    56 import javax.xml.namespace.NamespaceContext;
    57 import javax.xml.stream.XMLStreamConstants;
    58 import javax.xml.stream.XMLStreamException;
    59 import javax.xml.stream.XMLStreamReader;
    60 import javax.xml.stream.XMLStreamWriter;
    61 import javax.xml.ws.WebServiceException;
    62 import javax.xml.ws.soap.MTOMFeature;
    63 import javax.xml.bind.attachment.AttachmentMarshaller;
    64 import java.io.IOException;
    65 import java.io.OutputStream;
    66 import java.io.UnsupportedEncodingException;
    67 import java.net.URLDecoder;
    68 import java.nio.channels.WritableByteChannel;
    69 import java.nio.charset.Charset;
    70 import java.util.ArrayList;
    71 import java.util.Iterator;
    72 import java.util.List;
    73 import java.util.Map;
    74 import java.util.UUID;
    76 /**
    77  * Mtom message Codec. It can be used even for non-soap message's mtom encoding.
    78  *
    79  * @author Vivek Pandey
    80  * @author Jitendra Kotamraju
    81  */
    82 public class MtomCodec extends MimeCodec {
    84     public static final String XOP_XML_MIME_TYPE = "application/xop+xml";
    85     public static final String XOP_LOCALNAME = "Include";
    86     public static final String XOP_NAMESPACEURI = "http://www.w3.org/2004/08/xop/include";
    88     private final StreamSOAPCodec codec;
    89     private final MTOMFeature mtomFeature;
    90     private final SerializationFeature sf;
    91     private final static String DECODED_MESSAGE_CHARSET = "decodedMessageCharset";
    93     MtomCodec(SOAPVersion version, StreamSOAPCodec codec, WSFeatureList features){
    94         super(version, features);
    95         this.codec = codec;
    96         sf = features.get(SerializationFeature.class);
    97         MTOMFeature mtom = features.get(MTOMFeature.class);
    98         if(mtom == null)
    99             this.mtomFeature = new MTOMFeature();
   100         else
   101             this.mtomFeature = mtom;
   102     }
   104     /**
   105      * Return the soap 1.1 and soap 1.2 specific XOP packaged ContentType
   106      *
   107      * @return A non-null content type for soap11 or soap 1.2 content type
   108      */
   109     @Override
   110     public ContentType getStaticContentType(Packet packet) {
   111         return getStaticContentTypeStatic(packet, version);
   112     }
   114     public static ContentType getStaticContentTypeStatic(Packet packet, SOAPVersion version) {
   115         ContentType ct = (ContentType) packet.getInternalContentType();
   116         if ( ct != null ) return ct;
   118         String uuid = UUID.randomUUID().toString();
   119         String boundary = "uuid:" + uuid;
   120         String rootId = "<rootpart*"+uuid+"@example.jaxws.sun.com>";
   121         String soapActionParameter = SOAPVersion.SOAP_11.equals(version) ?  null : createActionParameter(packet);
   123         String boundaryParameter = "boundary=\"" + boundary +"\"";
   124         String messageContentType = MULTIPART_RELATED_MIME_TYPE +
   125                 ";start=\""+rootId +"\"" +
   126                 ";type=\"" + XOP_XML_MIME_TYPE + "\";" +
   127                 boundaryParameter +
   128                 ";start-info=\"" + version.contentType +
   129                 (soapActionParameter == null? "" : soapActionParameter) +
   130                 "\"";
   132         ContentTypeImpl ctImpl = SOAPVersion.SOAP_11.equals(version) ?
   133                 new ContentTypeImpl(messageContentType, (packet.soapAction == null)?"":packet.soapAction, null) :
   134                 new ContentTypeImpl(messageContentType, null, null);
   135         ctImpl.setBoundary(boundary);
   136         ctImpl.setRootId(rootId);
   137         packet.setContentType(ctImpl);
   138         return ctImpl;
   139     }
   141     private static String createActionParameter(Packet packet) {
   142         return packet.soapAction != null? ";action=\\\""+packet.soapAction+"\\\"" : "";
   143     }
   145     @Override
   146     public ContentType encode(Packet packet, OutputStream out) throws IOException {
   147         ContentTypeImpl ctImpl = (ContentTypeImpl) this.getStaticContentType(packet);
   148         String boundary = ctImpl.getBoundary();
   149         String rootId = ctImpl.getRootId();
   151         if(packet.getMessage() != null){
   152             try {
   153                 String encoding = getPacketEncoding(packet);
   154                 packet.invocationProperties.remove(DECODED_MESSAGE_CHARSET);
   156                 String actionParameter = getActionParameter(packet, version);
   157                 String soapXopContentType = getSOAPXopContentType(encoding, version, actionParameter);
   159                 writeln("--"+boundary, out);
   160                 writeMimeHeaders(soapXopContentType, rootId, out);
   162                 //mtom attachments that need to be written after the root part
   163                 List<ByteArrayBuffer> mtomAttachments = new ArrayList<ByteArrayBuffer>();
   164                 MtomStreamWriterImpl writer = new MtomStreamWriterImpl(
   165                         XMLStreamWriterFactory.create(out, encoding), mtomAttachments, boundary, mtomFeature);
   167                 packet.getMessage().writeTo(writer);
   168                 XMLStreamWriterFactory.recycle(writer);
   169                 writeln(out);
   171                 for(ByteArrayBuffer bos : mtomAttachments){
   172                     bos.write(out);
   173                 }
   175                 // now write out the attachments in the message that weren't
   176                 // previously written
   177                 writeNonMtomAttachments(packet.getMessage().getAttachments(),
   178                         out, boundary);
   180                 //write out the end boundary
   181                 writeAsAscii("--"+boundary, out);
   182                 writeAsAscii("--", out);
   184             } catch (XMLStreamException e) {
   185                 throw new WebServiceException(e);
   186             }
   187         }
   188         //now create the boundary for next encode() call
   189 //        createConteTypeHeader();
   190         return ctImpl;
   191     }
   193     public static String getSOAPXopContentType(String encoding, SOAPVersion version,
   194             String actionParameter) {
   195         return XOP_XML_MIME_TYPE +";charset="+encoding+";type=\""+version.contentType+ actionParameter + "\"";
   196     }
   198     public static String getActionParameter(Packet packet, SOAPVersion version) {
   199         return (version == SOAPVersion.SOAP_11) ? "" : createActionParameter(packet);
   200     }
   202     public static class ByteArrayBuffer{
   203         final String contentId;
   205         private final DataHandler dh;
   206         private final String boundary;
   208         ByteArrayBuffer(@NotNull DataHandler dh, String b) {
   209             this.dh = dh;
   210             String cid = null;
   211             if (dh instanceof StreamingDataHandler) {
   212                 StreamingDataHandler sdh = (StreamingDataHandler) dh;
   213                 if (sdh.getHrefCid() != null)
   214                     cid = sdh.getHrefCid();
   215             }
   216             this.contentId = cid != null ? cid : encodeCid();
   217             boundary = b;
   218         }
   220         public void write(OutputStream os) throws IOException {
   221             //build attachment frame
   222             writeln("--"+boundary, os);
   223             writeMimeHeaders(dh.getContentType(), contentId, os);
   224             dh.writeTo(os);
   225             writeln(os);
   226         }
   227     }
   229     public static void writeMimeHeaders(String contentType, String contentId, OutputStream out) throws IOException {
   230         String cid = contentId;
   231         if(cid != null && cid.length() >0 && cid.charAt(0) != '<')
   232             cid = '<' + cid + '>';
   233         writeln("Content-Id: " + cid, out);
   234         writeln("Content-Type: " + contentType, out);
   235         writeln("Content-Transfer-Encoding: binary", out);
   236         writeln(out);
   237     }
   239     // Compiler warning for not calling close, but cannot call close,
   240     // will consume attachment bytes.
   241         @SuppressWarnings("resource")
   242     private void writeNonMtomAttachments(AttachmentSet attachments,
   243             OutputStream out, String boundary) throws IOException {
   245         for (Attachment att : attachments) {
   247             DataHandler dh = att.asDataHandler();
   248             if (dh instanceof StreamingDataHandler) {
   249                 StreamingDataHandler sdh = (StreamingDataHandler) dh;
   250                 // If DataHandler has href Content-ID, it is MTOM, so skip.
   251                 if (sdh.getHrefCid() != null)
   252                     continue;
   253             }
   255             // build attachment frame
   256             writeln("--" + boundary, out);
   257             writeMimeHeaders(att.getContentType(), att.getContentId(), out);
   258             att.writeTo(out);
   259             writeln(out); // write \r\n
   260         }
   261     }
   263     @Override
   264     public ContentType encode(Packet packet, WritableByteChannel buffer) {
   265         throw new UnsupportedOperationException();
   266     }
   268     @Override
   269     public MtomCodec copy() {
   270         return new MtomCodec(version, (StreamSOAPCodec)codec.copy(), features);
   271     }
   273     private static String encodeCid(){
   274         String cid="example.jaxws.sun.com";
   275         String name = UUID.randomUUID()+"@";
   276         return name + cid;
   277     }
   279     @Override
   280     protected void decode(MimeMultipartParser mpp, Packet packet) throws IOException {
   281         //TODO shouldn't we check for SOAP1.1/SOAP1.2 and throw
   282         //TODO UnsupportedMediaException like StreamSOAPCodec
   283         String charset = null;
   284         String ct = mpp.getRootPart().getContentType();
   285         if (ct != null) {
   286             charset = new ContentTypeImpl(ct).getCharSet();
   287         }
   288         if (charset != null && !Charset.isSupported(charset)) {
   289             throw new UnsupportedMediaException(charset);
   290         }
   292         if (charset != null) {
   293             packet.invocationProperties.put(DECODED_MESSAGE_CHARSET, charset);
   294         } else {
   295             packet.invocationProperties.remove(DECODED_MESSAGE_CHARSET);
   296         }
   298         // we'd like to reuse those reader objects but unfortunately decoder may be reused
   299         // before the decoded message is completely used.
   300         XMLStreamReader mtomReader = new MtomXMLStreamReaderEx( mpp,
   301             XMLStreamReaderFactory.create(null, mpp.getRootPart().asInputStream(), charset, true)
   302         );
   304         packet.setMessage(codec.decode(mtomReader, new MimeAttachmentSet(mpp)));
   305         packet.setMtomFeature(mtomFeature);
   306         packet.setContentType(mpp.getContentType());
   307     }
   309     private String getPacketEncoding(Packet packet) {
   310         // If SerializationFeature is set, just use that encoding
   311         if (sf != null && sf.getEncoding() != null) {
   312             return sf.getEncoding().equals("") ? SOAPBindingCodec.DEFAULT_ENCODING : sf.getEncoding();
   313         }
   314         return determinePacketEncoding(packet);
   315     }
   317     public static String determinePacketEncoding(Packet packet) {
   318         if (packet != null && packet.endpoint != null) {
   319             // Use request message's encoding for Server-side response messages
   320             String charset = (String)packet.invocationProperties.get(DECODED_MESSAGE_CHARSET);
   321             return charset == null
   322                     ? SOAPBindingCodec.DEFAULT_ENCODING : charset;
   323         }
   325         // Use default encoding for client-side request messages
   326         return SOAPBindingCodec.DEFAULT_ENCODING;
   327     }
   329     public static class MtomStreamWriterImpl extends XMLStreamWriterFilter implements XMLStreamWriterEx,
   330             MtomStreamWriter, HasEncoding {
   331         private final List<ByteArrayBuffer> mtomAttachments;
   332         private final String boundary;
   333         private final MTOMFeature myMtomFeature;
   334         public MtomStreamWriterImpl(XMLStreamWriter w, List<ByteArrayBuffer> mtomAttachments, String b, MTOMFeature myMtomFeature) {
   335             super(w);
   336             this.mtomAttachments = mtomAttachments;
   337             this.boundary = b;
   338             this.myMtomFeature = myMtomFeature;
   339         }
   341         @Override
   342         public void writeBinary(byte[] data, int start, int len, String contentType) throws XMLStreamException {
   343             //check threshold and if less write as base64encoded value
   344             if(myMtomFeature.getThreshold() > len){
   345                 writeCharacters(DatatypeConverterImpl._printBase64Binary(data, start, len));
   346                 return;
   347             }
   348             ByteArrayBuffer bab = new ByteArrayBuffer(new DataHandler(new ByteArrayDataSource(data, start, len, contentType)), boundary);
   349             writeBinary(bab);
   350         }
   352         @Override
   353         public void writeBinary(DataHandler dataHandler) throws XMLStreamException {
   354             // TODO how do we check threshold and if less inline the data
   355             writeBinary(new ByteArrayBuffer(dataHandler, boundary));
   356         }
   358         @Override
   359         public OutputStream writeBinary(String contentType) throws XMLStreamException {
   360             throw new UnsupportedOperationException();
   361         }
   363         @Override
   364         public void writePCDATA(CharSequence data) throws XMLStreamException {
   365             if(data == null)
   366                 return;
   367             if(data instanceof Base64Data){
   368                 Base64Data binaryData = (Base64Data)data;
   369                 writeBinary(binaryData.getDataHandler());
   370                 return;
   371             }
   372             writeCharacters(data.toString());
   373         }
   375         private void writeBinary(ByteArrayBuffer bab) {
   376             try {
   377                 mtomAttachments.add(bab);
   378                 String prefix = writer.getPrefix(XOP_NAMESPACEURI);
   379                 if (prefix == null || !prefix.equals("xop")) {
   380                     writer.setPrefix("xop", XOP_NAMESPACEURI);
   381                     writer.writeNamespace("xop", XOP_NAMESPACEURI);
   382                 }
   383                 writer.writeStartElement(XOP_NAMESPACEURI, XOP_LOCALNAME);
   384                 writer.writeAttribute("href", "cid:"+bab.contentId);
   385                 writer.writeEndElement();
   386                 writer.flush();
   387             } catch (XMLStreamException e) {
   388                 throw new WebServiceException(e);
   389             }
   390         }
   392         @Override
   393         public Object getProperty(String name) throws IllegalArgumentException {
   394             // Hack for JDK6's SJSXP
   395             if (name.equals("sjsxp-outputstream") && writer instanceof Map) {
   396                 Object obj = ((Map) writer).get("sjsxp-outputstream");
   397                 if (obj != null) {
   398                     return obj;
   399                 }
   400             }
   401             return super.getProperty(name);
   402         }
   404         /**
   405          * JAXBMessage writes envelope directly to the OutputStream(for SJSXP, woodstox).
   406          * While writing, it calls the AttachmentMarshaller methods for adding attachments.
   407          * JAXB writes xop:Include in this case.
   408          */
   409         @Override
   410         public AttachmentMarshaller getAttachmentMarshaller() {
   411             return new AttachmentMarshaller() {
   413                 @Override
   414                 public String addMtomAttachment(DataHandler data, String elementNamespace, String elementLocalName) {
   415                     // Should we do the threshold processing on DataHandler ? But that would be
   416                     // expensive as DataHolder need to read the data again from its source
   417                     ByteArrayBuffer bab = new ByteArrayBuffer(data, boundary);
   418                     mtomAttachments.add(bab);
   419                     return "cid:"+bab.contentId;
   420                 }
   422                 @Override
   423                 public String addMtomAttachment(byte[] data, int offset, int length, String mimeType, String elementNamespace, String elementLocalName) {
   424                     // inline the data based on the threshold
   425                     if (myMtomFeature.getThreshold() > length) {
   426                         return null;                // JAXB inlines the attachment data
   427                     }
   428                     ByteArrayBuffer bab = new ByteArrayBuffer(new DataHandler(new ByteArrayDataSource(data, offset, length, mimeType)), boundary);
   429                     mtomAttachments.add(bab);
   430                     return "cid:"+bab.contentId;
   431                 }
   433                 @Override
   434                 public String addSwaRefAttachment(DataHandler data) {
   435                     ByteArrayBuffer bab = new ByteArrayBuffer(data, boundary);
   436                     mtomAttachments.add(bab);
   437                     return "cid:"+bab.contentId;
   438                 }
   440                 @Override
   441                 public boolean isXOPPackage() {
   442                     return true;
   443                 }
   444             };
   445         }
   447         public List<ByteArrayBuffer> getMtomAttachments() {
   448             return this.mtomAttachments;
   449         }
   451         @Override
   452         public String getEncoding() {
   453             return XMLStreamWriterUtil.getEncoding(writer);
   454         }
   456         private static class MtomNamespaceContextEx implements NamespaceContextEx {
   457             private final NamespaceContext nsContext;
   459             public MtomNamespaceContextEx(NamespaceContext nsContext) {
   460                 this.nsContext = nsContext;
   461             }
   463             @Override
   464             public Iterator<Binding> iterator() {
   465                 throw new UnsupportedOperationException();
   466             }
   468             @Override
   469             public String getNamespaceURI(String prefix) {
   470                 return nsContext.getNamespaceURI(prefix);
   471             }
   473             @Override
   474             public String getPrefix(String namespaceURI) {
   475                 return nsContext.getPrefix(namespaceURI);
   476             }
   478             @Override
   479             public Iterator getPrefixes(String namespaceURI) {
   480                 return nsContext.getPrefixes(namespaceURI);
   481             }
   482         }
   484         @Override
   485         public NamespaceContextEx getNamespaceContext() {
   486             NamespaceContext nsContext = writer.getNamespaceContext();
   487             return new MtomNamespaceContextEx(nsContext);
   488         }
   489     }
   491     public static class MtomXMLStreamReaderEx extends XMLStreamReaderFilter implements XMLStreamReaderEx {
   492         /**
   493          * The parser for the outer MIME 'shell'.
   494          */
   495         private final MimeMultipartParser mimeMP;
   497         private boolean xopReferencePresent = false;
   498         private Base64Data base64AttData;
   500         //To be used with #getTextCharacters
   501         private char[] base64EncodedText;
   503         private String xopHref;
   505         public MtomXMLStreamReaderEx(MimeMultipartParser mimeMP, XMLStreamReader reader) {
   506             super(reader);
   507             this.mimeMP = mimeMP;
   508         }
   510         @Override
   511         public CharSequence getPCDATA() throws XMLStreamException {
   512             if(xopReferencePresent){
   513                 return base64AttData;
   514             }
   515             return reader.getText();
   516         }
   518         @Override
   519         public NamespaceContextEx getNamespaceContext() {
   520             return new NamespaceContextExAdaper(reader.getNamespaceContext());
   521         }
   523         @Override
   524         public String getElementTextTrim() throws XMLStreamException {
   525             throw new UnsupportedOperationException();
   526         }
   528         @Override
   529         public int getTextLength() {
   530             if (xopReferencePresent) {
   531                 return base64AttData.length();
   532             }
   533             return reader.getTextLength();
   534         }
   536         @Override
   537         public int getTextStart() {
   538             if (xopReferencePresent) {
   539                 return 0;
   540             }
   541             return reader.getTextStart();
   542         }
   544         @Override
   545         public int getEventType() {
   546             if(xopReferencePresent)
   547                 return XMLStreamConstants.CHARACTERS;
   548             return super.getEventType();
   549         }
   551         @Override
   552         public int next() throws XMLStreamException {
   553             int event = reader.next();
   554             if (event == XMLStreamConstants.START_ELEMENT && reader.getLocalName().equals(XOP_LOCALNAME) && reader.getNamespaceURI().equals(XOP_NAMESPACEURI)) {
   555                 //its xop reference, take the URI reference
   556                 String href = reader.getAttributeValue(null, "href");
   557                 try {
   558                     xopHref = href;
   559                     Attachment att = getAttachment(href);
   560                     if(att != null){
   561                         DataHandler dh = att.asDataHandler();
   562                         if (dh instanceof StreamingDataHandler) {
   563                             ((StreamingDataHandler)dh).setHrefCid(att.getContentId());
   564                         }
   565                         base64AttData = new Base64Data();
   566                         base64AttData.set(dh);
   567                     }
   568                     xopReferencePresent = true;
   569                 } catch (IOException e) {
   570                     throw new WebServiceException(e);
   571                 }
   572                 //move to the </xop:Include>
   573                 XMLStreamReaderUtil.nextElementContent(reader);
   574                 return XMLStreamConstants.CHARACTERS;
   575             }
   576             if(xopReferencePresent){
   577                 xopReferencePresent = false;
   578                 base64EncodedText = null;
   579                 xopHref = null;
   580             }
   581             return event;
   582         }
   584         private String decodeCid(String cid) {
   585             try {
   586                 cid = URLDecoder.decode(cid, "utf-8");
   587             } catch (UnsupportedEncodingException e) {
   588                 //on recceiving side lets not fail now, try to look for it
   589             }
   590             return cid;
   591         }
   593         private Attachment getAttachment(String cid) throws IOException {
   594             if (cid.startsWith("cid:"))
   595                 cid = cid.substring(4, cid.length());
   596             if (cid.indexOf('%') != -1) {
   597                 cid = decodeCid(cid);
   598                 return mimeMP.getAttachmentPart(cid);
   599             }
   600             return mimeMP.getAttachmentPart(cid);
   601         }
   603         @Override
   604         public char[] getTextCharacters() {
   605             if (xopReferencePresent) {
   606                 char[] chars = new char[base64AttData.length()];
   607                 base64AttData.writeTo(chars, 0);
   608                 return chars;
   609             }
   610             return reader.getTextCharacters();
   611         }
   613         @Override
   614         public int getTextCharacters(int sourceStart, char[] target, int targetStart, int length) throws XMLStreamException {
   615             if(xopReferencePresent){
   616                 if(target == null){
   617                     throw new NullPointerException("target char array can't be null") ;
   618                 }
   620                 if(targetStart < 0 || length < 0 || sourceStart < 0 || targetStart >= target.length ||
   621                         (targetStart + length ) > target.length) {
   622                     throw new IndexOutOfBoundsException();
   623                 }
   625                 int textLength = base64AttData.length();
   626                 if(sourceStart > textLength)
   627                     throw new IndexOutOfBoundsException();
   629                 if(base64EncodedText == null){
   630                     base64EncodedText = new char[base64AttData.length()];
   631                     base64AttData.writeTo(base64EncodedText, 0);
   632                 }
   634                 int copiedLength = Math.min(textLength - sourceStart, length);
   635                 System.arraycopy(base64EncodedText, sourceStart , target, targetStart, copiedLength);
   636                 return copiedLength;
   637             }
   638             return reader.getTextCharacters(sourceStart, target, targetStart, length);
   639         }
   641         @Override
   642         public String getText() {
   643             if (xopReferencePresent) {
   644                 return base64AttData.toString();
   645             }
   646             return reader.getText();
   647         }
   649         protected boolean isXopReference() throws XMLStreamException {
   650             return xopReferencePresent;
   651         }
   653         protected String getXopHref() {
   654             return xopHref;
   655         }
   657         public MimeMultipartParser getMimeMultipartParser() {
   658             return mimeMP;
   659         }
   660     }
   662 }

mercurial