1/*
2 * Copyright (c) 2013, 2017, 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 */
25
26package com.sun.xml.internal.messaging.saaj.util.stax;
27
28import java.io.OutputStream;
29import java.util.Arrays;
30import java.util.Iterator;
31import java.util.UUID;
32
33import javax.activation.DataHandler;
34import javax.xml.bind.attachment.AttachmentMarshaller;
35import javax.xml.soap.SOAPException;
36import javax.xml.soap.SOAPMessage;
37import javax.xml.stream.XMLStreamException;
38
39import com.sun.xml.internal.org.jvnet.staxex.Base64Data;
40import com.sun.xml.internal.org.jvnet.staxex.BinaryText;
41import com.sun.xml.internal.org.jvnet.staxex.MtomEnabled;
42import com.sun.xml.internal.org.jvnet.staxex.NamespaceContextEx;
43import com.sun.xml.internal.org.jvnet.staxex.StreamingDataHandler;
44import com.sun.xml.internal.org.jvnet.staxex.XMLStreamWriterEx;
45import com.sun.xml.internal.org.jvnet.staxex.util.MtomStreamWriter;
46//
47//import com.sun.xml.internal.ws.api.message.saaj.SaajStaxWriter;
48//import com.sun.xml.internal.ws.developer.StreamingDataHandler;
49//import com.sun.xml.internal.ws.streaming.MtomStreamWriter;
50
51/**
52 * SaajStaxWriterEx converts XMLStreamWriterEx calls to build an orasaaj SOAPMessage with BinaryTextImpl.
53 *
54 * @author shih-chang.chen@oracle.com
55 */
56public class SaajStaxWriterEx extends SaajStaxWriter implements XMLStreamWriterEx, MtomStreamWriter {
57
58    static final protected String xopNS = "http://www.w3.org/2004/08/xop/include";
59    static final protected String Include = "Include";
60    static final protected String href = "href";
61
62    private enum State {xopInclude, others};
63    private State state = State.others;
64    private BinaryText binaryText;
65
66    public SaajStaxWriterEx(SOAPMessage msg, String uri) throws SOAPException {
67        super(msg, uri);
68    }
69
70    @Override
71    public void writeStartElement(String prefix, String ln, String ns) throws XMLStreamException {
72        if (xopNS.equals(ns) && Include.equals(ln)) {
73            state = State.xopInclude;
74            return;
75        } else {
76            super.writeStartElement(prefix, ln, ns);
77        }
78    }
79
80    @Override
81    public void writeEndElement() throws XMLStreamException {
82        if (state.equals(State.xopInclude)) {
83            state = State.others;
84        } else {
85            super.writeEndElement();
86        }
87    }
88
89    @Override
90    public void writeAttribute(String prefix, String ns, String ln, String value) throws XMLStreamException {
91        if (binaryText != null && href.equals(ln)) {
92            return;
93        } else {
94            super.writeAttribute(prefix, ns, ln, value);
95        }
96    }
97
98//    @Override
99//    public void writeComment(String data) throws XMLStreamException {
100//        ((ElementImpl)currentElement).addCommentNode(data);
101//    }
102//
103//    @Override
104//    public void writeCData(String data) throws XMLStreamException {
105//      CDataTextImpl cdt = new CDataTextImpl(soap.getSOAPPart(), data);
106//        currentElement.appendChild(cdt);
107//    }
108
109    @Override
110    public NamespaceContextEx getNamespaceContext() {
111        return new NamespaceContextEx() {
112            @Override
113            public String getNamespaceURI(String prefix) {
114                return currentElement.getNamespaceURI(prefix);
115            }
116            @Override
117            public String getPrefix(String namespaceURI) {
118                return currentElement.lookupPrefix(namespaceURI);
119            }
120            @Override
121            public Iterator getPrefixes(final String namespaceURI) {
122                return new Iterator<String>() {
123                    String prefix = getPrefix(namespaceURI);
124                    @Override
125                    public boolean hasNext() {
126                        return (prefix != null);
127                    }
128                    @Override
129                    public String next() {
130                        if (prefix == null) throw new java.util.NoSuchElementException();
131                        String next = prefix;
132                        prefix = null;
133                        return next;
134                    }
135                    @Override
136                    public void remove() {}
137                };
138            }
139            @Override
140            public Iterator<Binding> iterator() {
141                return new Iterator<Binding>() {
142                    @Override
143                    public boolean hasNext() { return false; }
144                    @Override
145                    public Binding next() { return null; }
146                    @Override
147                    public void remove() {}
148                };
149            }
150        };
151    }
152
153    @Override
154    public void writeBinary(DataHandler data) throws XMLStreamException {
155//      binaryText = BinaryTextImpl.createBinaryTextFromDataHandler((MessageImpl)soap, null, currentElement.getOwnerDocument(), data);
156//      currentElement.appendChild(binaryText);
157        addBinaryText(data);
158    }
159
160    @Override
161    public OutputStream writeBinary(String arg0) throws XMLStreamException {
162        return null;
163    }
164
165    @Override
166    public void writeBinary(byte[] data, int offset, int length, String contentType) throws XMLStreamException {
167//        if (mtomThreshold == -1 || mtomThreshold > length) return null;
168        byte[] bytes = (offset == 0 && length == data.length) ? data : Arrays.copyOfRange(data, offset, offset + length);
169        if (currentElement instanceof MtomEnabled) {
170            binaryText = ((MtomEnabled) currentElement).addBinaryText(bytes);
171        } else {
172            throw new IllegalStateException("The currentElement is not MtomEnabled " + currentElement);
173        }
174    }
175
176    @Override
177    public void writePCDATA(CharSequence arg0) throws XMLStreamException {
178        if (arg0 instanceof Base64Data) {
179            // The fix of StreamReaderBufferCreator preserves this dataHandler
180            addBinaryText(((Base64Data) arg0).getDataHandler());
181        } else {
182            // We should not normally get here as we expect a DataHandler,
183            // but this is the most general solution.  If we do get
184            // something other than a Data Handler, create a Text node with
185            // the data.  Another alternative would be to throw an exception,
186            // but in the most general case, we don't know whether this input
187            // is expected.
188            try {
189                currentElement.addTextNode(arg0.toString());
190            } catch (SOAPException e) {
191                throw new XMLStreamException("Cannot add Text node", e);
192            }
193        }
194    }
195
196    static private String encodeCid() {
197        String cid = "example.jaxws.sun.com";
198        String name = UUID.randomUUID() + "@";
199        return name + cid;
200    }
201
202    private String addBinaryText(DataHandler data) {
203        String hrefOrCid = null;
204        if (data instanceof StreamingDataHandler) {
205            hrefOrCid = ((StreamingDataHandler) data).getHrefCid();
206        }
207        if (hrefOrCid == null) hrefOrCid = encodeCid();
208
209        String prefixedCid = (hrefOrCid.startsWith("cid:")) ? hrefOrCid : "cid:" + hrefOrCid;
210        // Should we do the threshold processing on DataHandler ? But that would be
211        // expensive as DataHolder need to read the data again from its source
212      //binaryText = BinaryTextImpl.createBinaryTextFromDataHandler((MessageImpl) soap, prefixedCid, currentElement.getOwnerDocument(), data);
213      //currentElement.appendChild(binaryText);
214        if (currentElement instanceof MtomEnabled) {
215            binaryText = ((MtomEnabled) currentElement).addBinaryText(prefixedCid, data);
216        } else {
217            throw new IllegalStateException("The currentElement is not MtomEnabled " + currentElement);
218        }
219        return hrefOrCid;
220    }
221
222    @Override
223    public AttachmentMarshaller getAttachmentMarshaller() {
224        return new AttachmentMarshaller() {
225            @Override
226            public String addMtomAttachment(DataHandler data, String ns, String ln) {
227//                if (mtomThreshold == -1) return null;
228                String hrefOrCid = addBinaryText(data);
229//                return binaryText.getHref();
230                return hrefOrCid;
231            }
232
233            @Override
234            public String addMtomAttachment(byte[] data, int offset, int length, String mimeType, String ns, String ln) {
235//                if (mtomThreshold == -1 || mtomThreshold > length) return null;
236                byte[] bytes = (offset == 0 && length == data.length) ? data : Arrays.copyOfRange(data, offset, offset + length);
237//                binaryText = (BinaryTextImpl) ((ElementImpl) currentElement).addAsBase64TextNode(bytes);
238                if (currentElement instanceof MtomEnabled) {
239                    binaryText = ((MtomEnabled) currentElement).addBinaryText(bytes);
240                } else {
241                    throw new IllegalStateException("The currentElement is not MtomEnabled " + currentElement);
242                }
243                return binaryText.getHref();
244            }
245
246            @Override
247            public String addSwaRefAttachment(DataHandler data) {
248                return "cid:"+encodeCid();
249            }
250
251            @Override
252            public boolean isXOPPackage() {
253                return true;
254            }
255        };
256    }
257}
258