File: MultiMap.java

package info (click to toggle)
tomcat6 6.0.41-3
  • links: PTS, VCS
  • area: main
  • in suites: jessie-kfreebsd
  • size: 20,640 kB
  • sloc: java: 175,884; xml: 40,992; jsp: 1,943; sh: 1,262; makefile: 101
file content (236 lines) | stat: -rw-r--r-- 6,211 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package org.apache.tomcat.util.collections;

import org.apache.tomcat.util.buf.MessageBytes;

// Originally MimeHeaders

/**
 * An efficient representation for certain type of map. The keys 
 * can have a single or multi values, but most of the time there are
 * single values.
 *
 * The data is of "MessageBytes" type, meaning bytes[] that can be
 * converted to Strings ( if needed, and encoding is lazy-binded ).
 *
 * This is a base class for MimeHeaders, Parameters and Cookies.
 *
 * Data structures: each field is a single-valued key/value.
 * The fields are allocated when needed, and are recycled.
 * The current implementation does linear search, in future we'll
 * also use the hashkey.
 * 
 * @author dac@eng.sun.com
 * @author James Todd [gonzo@eng.sun.com]
 * @author Costin Manolache
 */
public class MultiMap {

    protected Field[] fields;
    // fields in use
    protected int count;

    /**
     * 
     */
    public MultiMap(int initial_size) {
	fields=new Field[initial_size];
    }

    /**
     * Clears all header fields.
     */
    public void recycle() {
	for (int i = 0; i < count; i++) {
	    fields[i].recycle();
	}
	count = 0;
    }

    // -------------------- Idx access to headers ----------
    // This allows external iterators.
    
    /**
     * Returns the current number of header fields.
     */
    public int size() {
	return count;
    }

    /**
     * Returns the Nth header name
     * This may be used to iterate through all header fields.
     *
     * An exception is thrown if the index is not valid ( <0 or >size )
     */
    public MessageBytes getName(int n) {
	// n >= 0 && n < count ? headers[n].getName() : null
	return fields[n].name;
    }

    /**
     * Returns the Nth header value
     * This may be used to iterate through all header fields.
     */
    public MessageBytes getValue(int n) {
	return fields[n].value;
    }

    /** Find the index of a field with the given name.
     */
    public int find( String name, int starting ) {
	// We can use a hash - but it's not clear how much
	// benefit you can get - there is an  overhead 
	// and the number of headers is small (4-5 ?)
	// Another problem is that we'll pay the overhead
	// of constructing the hashtable

	// A custom search tree may be better
        for (int i = starting; i < count; i++) {
	    if (fields[i].name.equals(name)) {
                return i;
            }
        }
        return -1;
    }

    /** Find the index of a field with the given name.
     */
    public int findIgnoreCase( String name, int starting ) {
	// We can use a hash - but it's not clear how much
	// benefit you can get - there is an  overhead 
	// and the number of headers is small (4-5 ?)
	// Another problem is that we'll pay the overhead
	// of constructing the hashtable

	// A custom search tree may be better
        for (int i = starting; i < count; i++) {
	    if (fields[i].name.equalsIgnoreCase(name)) {
                return i;
            }
        }
        return -1;
    }

    /**
     * Removes the field at the specified position.  
     *
     * MultiMap will preserve the order of field add unless remove()
     * is called. This is not thread-safe, and will invalidate all
     * iterators. 
     *
     * This is not a frequent operation for Headers and Parameters -
     * there are better ways ( like adding a "isValid" field )
     */
    public void remove( int i ) {
	// reset and swap with last header
	Field mh = fields[i];
	// reset the field
	mh.recycle();
	
	fields[i] = fields[count - 1];
	fields[count - 1] = mh;
	count--;
    }

    /** Create a new, unitialized entry. 
     */
    public int addField() {
	int len = fields.length;
	int pos=count;
	if (count >= len) {
	    // expand header list array
	    Field tmp[] = new Field[pos * 2];
	    System.arraycopy(fields, 0, tmp, 0, len);
	    fields = tmp;
	}
	if (fields[pos] == null) {
	    fields[pos] = new Field();
	}
	count++;
	return pos;
    }

    public MessageBytes get( String name) {
        for (int i = 0; i < count; i++) {
	    if (fields[i].name.equals(name)) {
		return fields[i].value;
	    }
	}
        return null;
    }

    public int findFirst( String name ) {
        for (int i = 0; i < count; i++) {
	    if (fields[i].name.equals(name)) {
		return i;
	    }
	}
        return -1;
    }

    public int findNext( int startPos ) {
	int next= fields[startPos].nextPos;
	if( next != MultiMap.NEED_NEXT ) {
	    return next;
	}

	// next==NEED_NEXT, we never searched for this header
	MessageBytes name=fields[startPos].name;
        for (int i = startPos; i < count; i++) {
	    if (fields[i].name.equals(name)) {
		// cache the search result
		fields[startPos].nextPos=i;
		return i;
	    }
	}
	fields[startPos].nextPos= MultiMap.LAST;
        return -1;
    }

    // workaround for JDK1.1.8/solaris
    static final int NEED_NEXT=-2;
    static final int LAST=-1;

    // -------------------- Internal representation --------------------
    final class Field {
	MessageBytes name;
	MessageBytes value;

	// Extra info for speed
	
	//  multiple fields with same name - a linked list will
	// speed up multiple name enumerations and search.
	int nextPos;

	// hashkey
	int hash;
	Field nextSameHash;

	Field() {
	    nextPos=MultiMap.NEED_NEXT;
	}
	
	void recycle() {
	    name.recycle();
	    value.recycle();
	    nextPos=MultiMap.NEED_NEXT;
	}
    }
}