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 */
25
26package com.sun.xml.internal.ws.encoding;
27
28import java.util.List;
29
30import org.xml.sax.helpers.AttributesImpl;
31import org.xml.sax.ContentHandler;
32import org.xml.sax.SAXException;
33
34import javax.xml.stream.XMLStreamReader;
35import javax.xml.stream.XMLStreamWriter;
36import javax.xml.stream.XMLStreamException;
37
38import com.sun.xml.internal.ws.message.stream.StreamMessage;
39import com.sun.xml.internal.ws.encoding.StreamSOAPCodec;
40import com.sun.istack.internal.Nullable;
41import com.sun.istack.internal.NotNull;
42
43/**
44 * Complete infoset about a start tag.
45 *
46 * <p>
47 * This is used between {@link StreamMessage} and {@link StreamSOAPCodec}
48 * to capture the infoset of the s:Envelope, s:Header, and s:Body elements.
49 *
50 *
51 * <h3>Design Note</h3>
52 * <p>
53 * Since StAX and SAX uses different null vs empty string convention, one has
54 * to choose which format we store things. It can go either way, but I'm assuming
55 * that we'll be using StAX more in JAX-WS, so things are kept in the StAX style
56 * in this class.
57 *
58 * @author Kohsuke Kawaguchi
59 */
60public final class TagInfoset {
61    /**
62     * Namespace declarations on this tag. Read-only.
63     *
64     * This is an array of the even length of the form { prefix0, uri0, prefix1, uri1, ... }.
65     *
66     * URIs/prefixes can be null (StAX-style)
67     */
68    public final @NotNull String[] ns;
69    /**
70     * Attributes on this tag. Read-only.
71     */
72    public final @NotNull AttributesImpl atts;
73
74    /**
75     * Prefix of the start tag in stax-style.
76     */
77    public final @Nullable String prefix;
78
79    /**
80     * Namespace URI of the start tag in stax-style.
81     */
82    public final @Nullable String nsUri;
83
84    /**
85     * Local name of the start tag.
86     */
87    public final @NotNull String localName;
88
89    /**
90     * Lazily computed QName (i.e., "foo:bar")
91     */
92    private @Nullable String qname;
93
94    public TagInfoset(String nsUri, String localName, String prefix, AttributesImpl atts, String... ns) {
95        this.nsUri = nsUri;
96        this.prefix = prefix;
97        this.localName = localName;
98        this.atts = atts;
99        this.ns = ns;
100    }
101
102    /**
103     * Fills a {@link TagInfoset} object by the current element
104     * that the reader points to.
105     */
106    public TagInfoset(XMLStreamReader reader) {
107        prefix = reader.getPrefix();
108        nsUri = reader.getNamespaceURI();
109        localName = reader.getLocalName();
110
111        int nsc = reader.getNamespaceCount();
112        if(nsc>0) {
113            ns = new String[nsc*2];
114            for(int i=0; i<nsc; i++){
115                ns[i*2  ] = fixNull(reader.getNamespacePrefix(i));
116                ns[i*2+1] = fixNull(reader.getNamespaceURI(i));
117            }
118        } else {
119            ns = EMPTY_ARRAY;
120        }
121
122        int ac = reader.getAttributeCount();
123        if(ac>0) {
124            atts = new AttributesImpl();
125            StringBuilder sb = new StringBuilder();
126            for(int i=0; i< ac;i++){
127                sb.setLength(0);
128                String prefix = reader.getAttributePrefix(i);
129                String localName = reader.getAttributeLocalName(i);
130
131                String qname;
132                if(prefix != null && prefix.length()!=0){
133                    sb.append(prefix);
134                    sb.append(":");
135                    sb.append(localName);
136                    qname = sb.toString();
137                } else {
138                    qname = localName;
139                }
140
141                atts.addAttribute(
142                    fixNull(reader.getAttributeNamespace(i)),
143                    localName,
144                    qname,
145                    reader.getAttributeType(i),
146                    reader.getAttributeValue(i));
147            }
148        } else {
149            atts = EMPTY_ATTRIBUTES;
150        }
151    }
152
153    /**
154     * Writes the start element event.
155     */
156    public void writeStart(ContentHandler contentHandler) throws SAXException {
157        for( int i=0; i<ns.length; i+=2 )
158            contentHandler.startPrefixMapping(fixNull(ns[i]),fixNull(ns[i+1]));
159        contentHandler.startElement(fixNull(nsUri), localName ,getQName(), atts);
160    }
161
162    /**
163     * Writes the end element event.
164     */
165    public void writeEnd(ContentHandler contentHandler) throws SAXException{
166        contentHandler.endElement(fixNull(nsUri),localName,getQName());
167        for( int i=ns.length-2; i>=0; i-=2 ) {
168            contentHandler.endPrefixMapping(fixNull(ns[i]));
169        }
170    }
171
172    /**
173     * Writes the start element event.
174     */
175    public void writeStart(XMLStreamWriter w) throws XMLStreamException {
176        // write start tag.
177        if(prefix==null) {
178            if(nsUri==null)
179                w.writeStartElement(localName);
180            else {
181                //fix Null prefix. otherwise throws XMLStreamException,
182                // if the namespace URI has not been bound to a prefix
183                w.writeStartElement("",localName,nsUri);
184            }
185        } else {
186            w.writeStartElement(prefix,localName,nsUri);
187        }
188
189        for( int i=0; i<ns.length; i+=2 ) {
190            w.writeNamespace(ns[i],ns[i+1]);
191        }
192
193        for( int i=0; i<atts.getLength(); i++ ) {
194            String nsUri = atts.getURI(i);
195            if(nsUri==null || nsUri.length() ==0) {
196                w.writeAttribute(atts.getLocalName(i),atts.getValue(i));
197            } else {
198                String rawName = atts.getQName(i);
199                String prefix = rawName.substring(0,rawName.indexOf(':'));
200                w.writeAttribute(prefix,nsUri,atts.getLocalName(i),atts.getValue(i));
201            }
202        }
203    }
204
205    private String getQName() {
206        if(qname!=null) return qname;
207
208        StringBuilder sb = new StringBuilder();
209        if(prefix!=null){
210            sb.append(prefix);
211            sb.append(':');
212            sb.append(localName);
213            qname = sb.toString();
214        } else {
215            qname = localName;
216        }
217        return qname;
218    }
219    private static String fixNull(String s) {
220        if(s==null) return "";
221        else        return s;
222    }
223
224    private static final String[] EMPTY_ARRAY = new String[0];
225    private static final AttributesImpl EMPTY_ATTRIBUTES = new AttributesImpl();
226
227    public String getNamespaceURI(String prefix) {
228        int size = ns.length/2;
229        for(int i=0; i<size; i++){
230            String p = ns[i*2  ];
231            String n = ns[i*2+1];
232            if (prefix.equals(p)) return n;
233        }
234        return null;
235    }
236
237    public String getPrefix(String namespaceURI) {
238        int size = ns.length/2;
239        for(int i=0; i<size; i++){
240            String p = ns[i*2  ];
241            String n = ns[i*2+1];
242            if (namespaceURI.equals(n)) return p;
243        }
244        return null;
245    }
246    //Who wants this?
247    public List<String> allPrefixes(String namespaceURI) {
248        int size = ns.length/2;
249        List<String> l = new java.util.ArrayList<String>();
250        for(int i=0; i<size; i++){
251            String p = ns[i*2  ];
252            String n = ns[i*2+1];
253            if (namespaceURI.equals(n)) l.add(p);
254        }
255        return l;
256    }
257}
258