1/*
2 * Copyright (c) 1997, 2015, 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.ws.message.stream;
27
28import com.sun.istack.internal.FinalArrayList;
29import com.sun.istack.internal.NotNull;
30import com.sun.xml.internal.stream.buffer.XMLStreamBuffer;
31import com.sun.xml.internal.stream.buffer.XMLStreamBufferSource;
32import com.sun.xml.internal.ws.api.SOAPVersion;
33import com.sun.xml.internal.ws.api.addressing.AddressingVersion;
34import com.sun.xml.internal.ws.api.addressing.WSEndpointReference;
35import com.sun.xml.internal.ws.api.message.Header;
36import com.sun.xml.internal.ws.message.AbstractHeaderImpl;
37import com.sun.xml.internal.ws.util.xml.XmlUtil;
38import org.w3c.dom.Node;
39import org.xml.sax.ContentHandler;
40import org.xml.sax.ErrorHandler;
41import org.xml.sax.SAXException;
42
43import javax.xml.soap.SOAPException;
44import javax.xml.soap.SOAPHeader;
45import javax.xml.soap.SOAPMessage;
46import javax.xml.stream.XMLStreamException;
47import javax.xml.stream.XMLStreamReader;
48import javax.xml.stream.XMLStreamWriter;
49import javax.xml.transform.Transformer;
50import javax.xml.transform.TransformerFactory;
51import javax.xml.transform.dom.DOMResult;
52import java.util.List;
53import java.util.Set;
54
55/**
56 * {@link Header} whose physical data representation is an XMLStreamBuffer.
57 *
58 * @author Paul.Sandoz@Sun.Com
59 */
60public abstract class StreamHeader extends AbstractHeaderImpl {
61    protected final XMLStreamBuffer _mark;
62
63    protected boolean _isMustUnderstand;
64
65    /**
66     * Role or actor value.
67     */
68    protected @NotNull String _role;
69
70    protected boolean _isRelay;
71
72    protected String _localName;
73
74    protected String _namespaceURI;
75
76    /**
77     * Keep the information about an attribute on the header element.
78     *
79     * TODO: this whole attribute handling could be done better, I think.
80     */
81    protected static final class Attribute {
82        /**
83         * Can be empty but never null.
84         */
85        final String nsUri;
86        final String localName;
87        final String value;
88
89        public Attribute(String nsUri, String localName, String value) {
90            this.nsUri = fixNull(nsUri);
91            this.localName = localName;
92            this.value = value;
93        }
94    }
95
96    /**
97     * The attributes on the header element.
98     * We expect there to be only a small number of them,
99     * so the use of {@link List} would be justified.
100     *
101     * Null if no attribute is present.
102     */
103    private final FinalArrayList<Attribute> attributes;
104
105    /**
106     * Creates a {@link StreamHeader}.
107     *
108     * @param reader
109     *      The parser pointing at the start of the mark.
110     *      Technically this information is redundant,
111     *      but it achieves a better performance.
112     * @param mark
113     *      The start of the buffered header content.
114     */
115    protected StreamHeader(XMLStreamReader reader, XMLStreamBuffer mark) {
116        assert reader!=null && mark!=null;
117        _mark = mark;
118        _localName = reader.getLocalName();
119        _namespaceURI = reader.getNamespaceURI();
120        attributes = processHeaderAttributes(reader);
121    }
122
123    /**
124     * Creates a {@link StreamHeader}.
125     *
126     * @param reader
127     *      The parser that points to the start tag of the header.
128     *      By the end of this method, the parser will point at
129     *      the end tag of this element.
130     */
131    protected StreamHeader(XMLStreamReader reader) throws XMLStreamException {
132        _localName = reader.getLocalName();
133        _namespaceURI = reader.getNamespaceURI();
134        attributes = processHeaderAttributes(reader);
135        // cache the body
136        _mark = XMLStreamBuffer.createNewBufferFromXMLStreamReader(reader);
137    }
138
139    public final boolean isIgnorable(@NotNull SOAPVersion soapVersion, @NotNull Set<String> roles) {
140        // check mustUnderstand
141        if(!_isMustUnderstand) return true;
142
143        if (roles == null)
144            return true;
145
146        // now role
147        return !roles.contains(_role);
148    }
149
150    public @NotNull String getRole(@NotNull SOAPVersion soapVersion) {
151        assert _role!=null;
152        return _role;
153    }
154
155    public boolean isRelay() {
156        return _isRelay;
157    }
158
159    public @NotNull String getNamespaceURI() {
160        return _namespaceURI;
161    }
162
163    public @NotNull String getLocalPart() {
164        return _localName;
165    }
166
167    public String getAttribute(String nsUri, String localName) {
168        if(attributes!=null) {
169            for(int i=attributes.size()-1; i>=0; i-- ) {
170                Attribute a = attributes.get(i);
171                if(a.localName.equals(localName) && a.nsUri.equals(nsUri))
172                    return a.value;
173            }
174        }
175        return null;
176    }
177
178    /**
179     * Reads the header as a {@link XMLStreamReader}
180     */
181    public XMLStreamReader readHeader() throws XMLStreamException {
182        return _mark.readAsXMLStreamReader();
183    }
184
185    public void writeTo(XMLStreamWriter w) throws XMLStreamException {
186        if(_mark.getInscopeNamespaces().size() > 0)
187            _mark.writeToXMLStreamWriter(w,true);
188        else
189            _mark.writeToXMLStreamWriter(w);
190    }
191
192    public void writeTo(SOAPMessage saaj) throws SOAPException {
193        try {
194            // TODO what about in-scope namespaces
195            // Not very efficient consider implementing a stream buffer
196            // processor that produces a DOM node from the buffer.
197            TransformerFactory tf = XmlUtil.newTransformerFactory(true);
198            Transformer t = tf.newTransformer();
199            XMLStreamBufferSource source = new XMLStreamBufferSource(_mark);
200            DOMResult result = new DOMResult();
201            t.transform(source, result);
202            Node d = result.getNode();
203            if(d.getNodeType() == Node.DOCUMENT_NODE)
204                d = d.getFirstChild();
205            SOAPHeader header = saaj.getSOAPHeader();
206            if(header == null)
207                header = saaj.getSOAPPart().getEnvelope().addHeader();
208            Node node = header.getOwnerDocument().importNode(d, true);
209            header.appendChild(node);
210        } catch (Exception e) {
211            throw new SOAPException(e);
212        }
213    }
214
215    public void writeTo(ContentHandler contentHandler, ErrorHandler errorHandler) throws SAXException {
216        _mark.writeTo(contentHandler);
217    }
218
219    /**
220     * Creates an EPR without copying infoset.
221     *
222     * This is the most common implementation on which {@link Header#readAsEPR(AddressingVersion)}
223     * is invoked on.
224     */
225    @Override @NotNull
226    public WSEndpointReference readAsEPR(AddressingVersion expected) throws XMLStreamException {
227        return new WSEndpointReference(_mark,expected);
228    }
229
230    protected abstract FinalArrayList<Attribute> processHeaderAttributes(XMLStreamReader reader);
231
232    /**
233     * Convert null to "".
234     */
235    private static String fixNull(String s) {
236        if(s==null) return "";
237        else        return s;
238    }
239}
240