2
* Copyright (C) 2008 Steve Ratcliffe
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License version 2 as
6
* published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
14
* Author: Steve Ratcliffe
15
* Create date: 08-Dec-2008
17
package uk.me.parabola.mkgmap.reader.osm;
19
import java.util.Iterator;
22
* Store the tags that belong to an Element.
24
* Used to use a HashMap for this. We have a requirement to be able
25
* to add to the map during iteration over it so this class was written
28
* It should also uses less memory (hash maps are the main use of memory in the
29
* application), as it doesn't allocate a Map.Entry object for every tag.
30
* Performance of the whole application is unchanged compared with when
31
* a regular HashMap was used.
33
* It doesn't fully behave the same way that a map would.
35
* @author Steve Ratcliffe
37
public class Tags implements Iterable<String> {
38
private static final int INIT_SIZE = 8;
41
private short capacity;
43
private String[] keys;
44
private String[] values;
46
// This is a flag that iteration is taking place and a place to store
47
// tags that are added during iteration.
48
private ExtraEntry extra;
51
* Used for tags that are added during iteration.
53
static class ExtraEntry {
56
private ExtraEntry next;
60
keys = new String[INIT_SIZE];
61
values = new String[INIT_SIZE];
65
public String get(Object key) {
66
Integer ind = keyPos((String) key);
72
public String put(String key, String value) {
74
// Deal with the case where we are adding a tag during iteration
75
// of the tags. This is flagged by extra being non null.
76
ExtraEntry emptyEntry = extra;
77
while (emptyEntry.next != null)
78
emptyEntry = emptyEntry.next;
80
emptyEntry.value = value;
81
emptyEntry.next = new ExtraEntry();
86
Integer ind = keyPos(key);
91
String old = values[ind];
99
public String remove(Object key) {
100
Integer k = keyPos((String) key);
103
// because of the way this works, you can never remove keys
104
// except when resizing.
105
String old = values[k];
115
* Make a deep copy of this object.
116
* @return A copy of this object.
121
Tags cp = new Tags();
123
cp.capacity = capacity;
125
// Copy the arrays. (For java 1.6 we can use Arrays.copyOf().)
126
cp.keys = new String[keys.length];
127
System.arraycopy(keys, 0, cp.keys, 0, keys.length);
128
cp.values = new String[values.length];
129
System.arraycopy(values, 0, cp.values, 0, values.length);
133
private void ensureSpace() {
134
while (size + 1 >= capacity) {
135
short ncap = (short) (capacity*2);
136
String[] okey = keys;
137
String[] oval = values;
138
keys = new String[ncap];
139
values = new String[ncap];
141
for (int i = 0; i < okey.length; i++) {
147
assert size < capacity;
150
private Integer keyPos(String key) {
151
int h = key.hashCode();
152
int k = h & (capacity - 1);
157
if (fk == null || fk.equals(key))
167
* Iterates over the tags in a special way that is used to look up in
170
* If you have the tags a=b, c=d then you will get the following strings
171
* returned: "a=b", "a=*", "c=d", "c=*".
173
* If you add a tag during the iteration, then it is guaranteed to
174
* appear later in the iteration.
176
public Iterator<String> iterator() {
177
return new Iterator<String>() {
180
private boolean doWild;
181
private ExtraEntry nextEntry;
183
// Set the extra field in the containing class.
186
extra = new ExtraEntry();
190
public boolean hasNext() {
191
// After every normal entry there is a wild card entry.
195
// Normal entries in the map
196
for (int i = pos; i < capacity; i++) {
197
if (values[i] != null) {
203
// Entries that were added during iteration
204
if (nextEntry != null && nextEntry.value != null)
207
// Add everything from extra and clean up, there is no guarantee
208
// that this gets called here (because you may stop the
209
// iteration early) but in the normal case it will be called
210
// and will remove all the ExtraEntry objects, thus keeping
211
// the memory usage to a minimum.
217
* Get the next tag as a single string. Also returns wild card
220
public String next() {
224
} else if (pos < capacity) {
225
for (int i = pos; i < capacity; i++) {
226
if (values[i] != null) {
230
return (keys[i] + "=" + values[i]);
236
ExtraEntry ex = nextEntry;
237
if (nextEntry != null && nextEntry.value != null) {
241
return ex.key + '=' + ex.value;
246
public void remove() {
247
throw new UnsupportedOperationException();
253
* Add the items that are in 'extra' to the map proper.
255
private void addExtraItems() {
257
ExtraEntry e = extra;
259
for (; e != null; e = e.next)