1/*
2 * Copyright (c) 2004, 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 javax.xml.soap;
27
28import java.util.Iterator;
29import java.util.Vector;
30
31/**
32 * A container for {@code MimeHeader} objects, which represent
33 * the MIME headers present in a MIME part of a message.
34 *
35 * <p>This class is used primarily when an application wants to
36 * retrieve specific attachments based on certain MIME headers and
37 * values. This class will most likely be used by implementations of
38 * {@code AttachmentPart} and other MIME dependent parts of the SAAJ
39 * API.
40 * @see SOAPMessage#getAttachments
41 * @see AttachmentPart
42 * @since 1.6
43 */
44public class MimeHeaders {
45    private Vector<MimeHeader> headers;
46
47   /**
48    * Constructs a default {@code MimeHeaders} object initialized with
49    * an empty {@code Vector} object.
50    */
51    public MimeHeaders() {
52        headers = new Vector<>();
53    }
54
55    /**
56     * Returns all of the values for the specified header as an array of
57     * {@code String} objects.
58     *
59     * @param   name the name of the header for which values will be returned
60     * @return a {@code String} array with all of the values for the
61     *         specified header
62     * @see #setHeader
63     */
64    public String[] getHeader(String name) {
65        Vector<String> values = new Vector<>();
66
67        for(int i = 0; i < headers.size(); i++) {
68            MimeHeader hdr = headers.elementAt(i);
69            if (hdr.getName().equalsIgnoreCase(name)
70                && hdr.getValue() != null)
71                values.addElement(hdr.getValue());
72        }
73
74        if (values.size() == 0)
75            return null;
76
77        String r[] = new String[values.size()];
78        values.copyInto(r);
79        return r;
80    }
81
82    /**
83     * Replaces the current value of the first header entry whose name matches
84     * the given name with the given value, adding a new header if no existing header
85     * name matches. This method also removes all matching headers after the first one.
86     * <P>
87     * Note that RFC822 headers can contain only US-ASCII characters.
88     *
89     * @param   name a {@code String} with the name of the header for
90     *          which to search
91     * @param   value a {@code String} with the value that will replace the
92     *          current value of the specified header
93     *
94     * @exception IllegalArgumentException if there was a problem in the
95     * mime header name or the value being set
96     * @see #getHeader
97     */
98    public void setHeader(String name, String value)
99    {
100        boolean found = false;
101
102        if ((name == null) || name.equals(""))
103            throw new IllegalArgumentException("Illegal MimeHeader name");
104
105        for(int i = 0; i < headers.size(); i++) {
106            MimeHeader hdr = headers.elementAt(i);
107            if (hdr.getName().equalsIgnoreCase(name)) {
108                if (!found) {
109                    headers.setElementAt(new MimeHeader(hdr.getName(),
110                                                        value), i);
111                    found = true;
112                }
113                else
114                    headers.removeElementAt(i--);
115            }
116        }
117
118        if (!found)
119            addHeader(name, value);
120    }
121
122    /**
123     * Adds a {@code MimeHeader} object with the specified name and value
124     * to this {@code MimeHeaders} object's list of headers.
125     * <P>
126     * Note that RFC822 headers can contain only US-ASCII characters.
127     *
128     * @param   name a {@code String} with the name of the header to
129     *          be added
130     * @param   value a {@code String} with the value of the header to
131     *          be added
132     *
133     * @exception IllegalArgumentException if there was a problem in the
134     * mime header name or value being added
135     */
136    public void addHeader(String name, String value)
137    {
138        if ((name == null) || name.equals(""))
139            throw new IllegalArgumentException("Illegal MimeHeader name");
140
141        int pos = headers.size();
142
143        for(int i = pos - 1 ; i >= 0; i--) {
144            MimeHeader hdr = headers.elementAt(i);
145            if (hdr.getName().equalsIgnoreCase(name)) {
146                headers.insertElementAt(new MimeHeader(name, value),
147                                        i+1);
148                return;
149            }
150        }
151        headers.addElement(new MimeHeader(name, value));
152    }
153
154    /**
155     * Remove all {@code MimeHeader} objects whose name matches the
156     * given name.
157     *
158     * @param   name a {@code String} with the name of the header for
159     *          which to search
160     */
161    public void removeHeader(String name) {
162        for(int i = 0; i < headers.size(); i++) {
163            MimeHeader hdr = headers.elementAt(i);
164            if (hdr.getName().equalsIgnoreCase(name))
165                headers.removeElementAt(i--);
166        }
167    }
168
169    /**
170     * Removes all the header entries from this {@code MimeHeaders} object.
171     */
172    public void removeAllHeaders() {
173        headers.removeAllElements();
174    }
175
176
177    /**
178     * Returns all the {@code MimeHeader}s in this {@code MimeHeaders} object.
179     *
180     * @return  an {@code Iterator} object over this {@code MimeHeaders}
181     *          object's list of {@code MimeHeader} objects
182     */
183    public Iterator<MimeHeader> getAllHeaders() {
184        return headers.iterator();
185    }
186
187    static class MatchingIterator implements Iterator<MimeHeader> {
188        private final boolean match;
189        private final Iterator<MimeHeader> iterator;
190        private final String[] names;
191        private MimeHeader nextHeader;
192
193        MatchingIterator(String[] names, boolean match, Iterator<MimeHeader> i) {
194            this.match = match;
195            this.names = names;
196            this.iterator = i;
197        }
198
199        private MimeHeader nextMatch() {
200        next:
201            while (iterator.hasNext()) {
202                MimeHeader hdr = iterator.next();
203
204                if (names == null)
205                    return match ? null : hdr;
206
207                for(int i = 0; i < names.length; i++)
208                    if (hdr.getName().equalsIgnoreCase(names[i]))
209                        if (match)
210                            return hdr;
211                        else
212                            continue next;
213                if (!match)
214                    return hdr;
215            }
216            return null;
217        }
218
219
220        @Override
221        public boolean hasNext() {
222            if (nextHeader == null)
223                nextHeader = nextMatch();
224            return nextHeader != null;
225        }
226
227        @Override
228        public MimeHeader next() {
229            // hasNext should've prefetched the header for us,
230            // return it.
231            if (nextHeader != null) {
232                MimeHeader ret = nextHeader;
233                nextHeader = null;
234                return ret;
235            }
236            if (hasNext())
237                return nextHeader;
238            return null;
239        }
240
241        @Override
242        public void remove() {
243            iterator.remove();
244        }
245    }
246
247
248    /**
249     * Returns all the {@code MimeHeader} objects whose name matches
250     * a name in the given array of names.
251     *
252     * @param names an array of {@code String} objects with the names
253     *         for which to search
254     * @return  an {@code Iterator} object over the {@code MimeHeader}
255     *          objects whose name matches one of the names in the given list
256     */
257    public Iterator<MimeHeader> getMatchingHeaders(String[] names) {
258        return new MatchingIterator(names, true, headers.iterator());
259    }
260
261    /**
262     * Returns all of the {@code MimeHeader} objects whose name does not
263     * match a name in the given array of names.
264     *
265     * @param names an array of {@code String} objects with the names
266     *         for which to search
267     * @return  an {@code Iterator} object over the {@code MimeHeader}
268     *          objects whose name does not match one of the names in the given list
269     */
270    public Iterator<MimeHeader> getNonMatchingHeaders(String[] names) {
271        return new MatchingIterator(names, false, headers.iterator());
272    }
273}
274