View | Details | Raw Unified | Return to bug 31789
Collapse All | Expand All

(-)lang/jstl/ELEvaluator.java (-17 / +65 lines)
Lines 27-33 Link Here
27
import org.apache.taglibs.standard.lang.jstl.parser.ParseException;
27
import org.apache.taglibs.standard.lang.jstl.parser.ParseException;
28
import org.apache.taglibs.standard.lang.jstl.parser.Token;
28
import org.apache.taglibs.standard.lang.jstl.parser.Token;
29
import org.apache.taglibs.standard.lang.jstl.parser.TokenMgrError;
29
import org.apache.taglibs.standard.lang.jstl.parser.TokenMgrError;
30
import org.apache.taglibs.standard.extra.commons.collections.map.LRUMap;
30
31
32
import javax.servlet.jsp.PageContext;
33
import javax.servlet.jsp.jstl.core.Config;
34
31
/**
35
/**
32
 *
36
 *
33
 * <p>This is the main class for evaluating expression Strings.  An
37
 * <p>This is the main class for evaluating expression Strings.  An
Lines 89-102 Link Here
89
  // Member variables
93
  // Member variables
90
  //-------------------------------------
94
  //-------------------------------------
91
95
96
  /**
97
   * Name of configuration setting for maximum number of entries in the
98
   * cached expression string map
99
   */
100
  private static final String EXPR_CACHE_PARAM
101
    = "org.apache.taglibs.standard.lang.jstl.exprCacheSize";
102
  /**
103
   * Default maximum  cache size
104
   */
105
  private static final int MAX_SIZE = 100;
106
92
  /** The mapping from expression String to its parsed form (String,
107
  /** The mapping from expression String to its parsed form (String,
93
      Expression, or ExpressionString) **/
108
   *  Expression, or ExpressionString)
94
  static Map sCachedExpressionStrings = 
109
   *
95
    Collections.synchronizedMap (new HashMap ());
110
   *  Using LRU Map with a maximum capacity to avoid out of bound map
111
   *  growth.
112
   *
113
   *  NOTE: use LinkedHashmap if a dependency on J2SE 1.4+ is ok
114
   */
115
  static Map sCachedExpressionStrings = null;
96
116
97
  /** The mapping from ExpectedType to Maps mapping literal String to
117
  /** The mapping from ExpectedType to Maps mapping literal String to
98
      parsed value **/
118
      parsed value **/
99
  static Map sCachedExpectedTypes = new HashMap ();
119
  static Map sCachedExpectedTypes = new HashMap();
100
120
101
  /** The static Logger **/
121
  /** The static Logger **/
102
  static Logger sLogger = new Logger (System.out);
122
  static Logger sLogger = new Logger (System.out);
Lines 107-112 Link Here
107
  /** Flag if the cache should be bypassed **/
127
  /** Flag if the cache should be bypassed **/
108
  boolean mBypassCache;
128
  boolean mBypassCache;
109
129
130
  /** The PageContext **/
131
  PageContext pageContext;
132
133
110
  //-------------------------------------
134
  //-------------------------------------
111
  /**
135
  /**
112
   *
136
   *
Lines 123-142 Link Here
123
147
124
  //-------------------------------------
148
  //-------------------------------------
125
  /**
149
  /**
150
   * Enable cache bypass
126
   *
151
   *
127
   * Constructor
152
   * @param pBypassCache flag indicating cache should be bypassed
128
   *
129
   * @param pResolver the object that should be used to resolve
130
   * variable names encountered in expressions.  If null, all variable
131
   * references will resolve to null.
132
   *
133
   * @param pBypassCache flag indicating if the cache should be
134
   * bypassed
135
   **/
153
   **/
136
  public ELEvaluator (VariableResolver pResolver,
154
  public void setBypassCache(boolean pBypassCache) {
137
		      boolean pBypassCache)
138
  {
139
    mResolver = pResolver;
140
    mBypassCache = pBypassCache;
155
    mBypassCache = pBypassCache;
141
  }
156
  }
142
157
Lines 187-192 Link Here
187
	(Constants.NULL_EXPRESSION_STRING);
202
	(Constants.NULL_EXPRESSION_STRING);
188
    }
203
    }
189
204
205
    // Set the PageContext;
206
    pageContext = (PageContext) pContext;
207
190
    // Get the parsed version of the expression string
208
    // Get the parsed version of the expression string
191
    Object parsedValue = parseExpressionString (pExpressionString);
209
    Object parsedValue = parseExpressionString (pExpressionString);
192
210
Lines 247-252 Link Here
247
      return "";
265
      return "";
248
    }
266
    }
249
267
268
    if (!(mBypassCache) && (sCachedExpressionStrings == null)) {
269
      createExpressionStringMap();
270
    }
271
250
    // See if it's in the cache
272
    // See if it's in the cache
251
    Object ret = 
273
    Object ret = 
252
      mBypassCache ?
274
      mBypassCache ?
Lines 259-265 Link Here
259
      ELParser parser = new ELParser (r);
281
      ELParser parser = new ELParser (r);
260
      try {
282
      try {
261
	ret = parser.ExpressionString ();
283
	ret = parser.ExpressionString ();
262
	sCachedExpressionStrings.put (pExpressionString, ret);
284
        if (!mBypassCache) {
285
	  sCachedExpressionStrings.put (pExpressionString, ret);
286
        }
263
      }
287
      }
264
      catch (ParseException exc) {
288
      catch (ParseException exc) {
265
	throw new ELException 
289
	throw new ELException 
Lines 342-347 Link Here
342
  }
366
  }
343
367
344
  //-------------------------------------
368
  //-------------------------------------
369
  /**
370
   *
371
   * Creates LRU map of expression strings. If context parameter
372
   * specifying cache size is present use that as the maximum size
373
   * of the LRU map otherwise use default.
374
   **/
375
  private synchronized void createExpressionStringMap () {
376
      if (sCachedExpressionStrings != null) {
377
        return;
378
      }
379
380
      String value = pageContext.getServletContext().
381
                     getInitParameter(EXPR_CACHE_PARAM);
382
      if (value != null) {
383
        sCachedExpressionStrings = 
384
          Collections.synchronizedMap(new LRUMap(Integer.parseInt(value)));
385
      }
386
      else {
387
        sCachedExpressionStrings = 
388
          Collections.synchronizedMap(new LRUMap(MAX_SIZE));
389
      }
390
  }
391
392
  //-------------------------------------
345
  // Formatting ParseException
393
  // Formatting ParseException
346
  //-------------------------------------
394
  //-------------------------------------
347
  /**
395
  /**
(-)lang/jstl/Evaluator.java (+2 lines)
Lines 68-74 Link Here
68
			  String pAttributeValue)
68
			  String pAttributeValue)
69
  {
69
  {
70
    try {
70
    try {
71
      sEvaluator.setBypassCache(true);
71
      sEvaluator.parseExpressionString (pAttributeValue);
72
      sEvaluator.parseExpressionString (pAttributeValue);
73
      sEvaluator.setBypassCache(false);
72
      return null;
74
      return null;
73
    }
75
    }
74
    catch (ELException exc) {
76
    catch (ELException exc) {
(-)extra/collections/OrderedMap.java (+81 lines)
Line 0 Link Here
1
/*
2
 *  Copyright 2001-2004 The Apache Software Foundation
3
 *
4
 *  Licensed under the Apache License, Version 2.0 (the "License");
5
 *  you may not use this file except in compliance with the License.
6
 *  You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *  Unless required by applicable law or agreed to in writing, software
11
 *  distributed under the License is distributed on an "AS IS" BASIS,
12
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *  See the License for the specific language governing permissions and
14
 *  limitations under the License.
15
 */
16
package org.apache.taglibs.standard.extra.commons.collections;
17
18
/**
19
 * Defines a map that maintains order and allows both forward and backward
20
 * iteration through that order.
21
 * 
22
 * @since Commons Collections 3.0
23
 * @version $Revision: 218351 $ $Date: 2004-10-20 17:58:23 -0700 (Wed, 20 Oct 2004) $
24
 *
25
 * @author Stephen Colebourne
26
 */
27
public interface OrderedMap extends IterableMap {
28
    
29
    /**
30
     * Obtains an <code>OrderedMapIterator</code> over the map.
31
     * <p>
32
     * A ordered map iterator is an efficient way of iterating over maps
33
     * in both directions.
34
     * <pre>
35
     * BidiMap map = new TreeBidiMap();
36
     * MapIterator it = map.mapIterator();
37
     * while (it.hasNext()) {
38
     *   Object key = it.next();
39
     *   Object value = it.getValue();
40
     *   it.setValue("newValue");
41
     *   Object previousKey = it.previous();
42
     * }
43
     * </pre>
44
     * 
45
     * @return a map iterator
46
     */
47
    OrderedMapIterator orderedMapIterator();
48
    
49
    /**
50
     * Gets the first key currently in this map.
51
     *
52
     * @return the first key currently in this map
53
     * @throws java.util.NoSuchElementException if this map is empty
54
     */
55
    public Object firstKey();
56
57
    /**
58
     * Gets the last key currently in this map.
59
     *
60
     * @return the last key currently in this map
61
     * @throws java.util.NoSuchElementException if this map is empty
62
     */
63
    public Object lastKey();
64
    
65
    /**
66
     * Gets the next key after the one specified.
67
     *
68
     * @param key  the key to search for next from
69
     * @return the next key, null if no match or at end
70
     */
71
    public Object nextKey(Object key);
72
73
    /**
74
     * Gets the previous key before the one specified.
75
     *
76
     * @param key  the key to search for previous from
77
     * @return the previous key, null if no match or at start
78
     */
79
    public Object previousKey(Object key);
80
    
81
}
(-)extra/collections/ResettableIterator.java (+38 lines)
Line 0 Link Here
1
/*
2
 *  Copyright 2003-2004 The Apache Software Foundation
3
 *
4
 *  Licensed under the Apache License, Version 2.0 (the "License");
5
 *  you may not use this file except in compliance with the License.
6
 *  You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *  Unless required by applicable law or agreed to in writing, software
11
 *  distributed under the License is distributed on an "AS IS" BASIS,
12
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *  See the License for the specific language governing permissions and
14
 *  limitations under the License.
15
 */
16
package org.apache.taglibs.standard.extra.commons.collections;
17
18
import java.util.Iterator;
19
20
/** 
21
 * Defines an iterator that can be reset back to an initial state.
22
 * <p>
23
 * This interface allows an iterator to be repeatedly reused.
24
 *
25
 * @since Commons Collections 3.0
26
 * @version $Revision: 218351 $ $Date: 2004-10-20 17:58:23 -0700 (Wed, 20 Oct 2004) $
27
 * 
28
 * @author Stephen Colebourne
29
 */
30
public interface ResettableIterator extends Iterator {
31
32
    /**
33
     * Resets the iterator back to the position at which the iterator
34
     * was created.
35
     */
36
    public void reset();
37
38
}
(-)extra/collections/IterableMap.java (+61 lines)
Line 0 Link Here
1
/*
2
 *  Copyright 2003-2004 The Apache Software Foundation
3
 *
4
 *  Licensed under the Apache License, Version 2.0 (the "License");
5
 *  you may not use this file except in compliance with the License.
6
 *  You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *  Unless required by applicable law or agreed to in writing, software
11
 *  distributed under the License is distributed on an "AS IS" BASIS,
12
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *  See the License for the specific language governing permissions and
14
 *  limitations under the License.
15
 */
16
package org.apache.taglibs.standard.extra.commons.collections;
17
18
import java.util.Map;
19
20
/**
21
 * Defines a map that can be iterated directly without needing to create an entry set.
22
 * <p>
23
 * A map iterator is an efficient way of iterating over maps.
24
 * There is no need to access the entry set or cast to Map Entry objects.
25
 * <pre>
26
 * IterableMap map = new HashedMap();
27
 * MapIterator it = map.mapIterator();
28
 * while (it.hasNext()) {
29
 *   Object key = it.next();
30
 *   Object value = it.getValue();
31
 *   it.setValue("newValue");
32
 * }
33
 * </pre>
34
 * 
35
 * @since Commons Collections 3.0
36
 * @version $Revision: 218351 $ $Date: 2004-10-20 17:58:23 -0700 (Wed, 20 Oct 2004) $
37
 *
38
 * @author Stephen Colebourne
39
 */
40
public interface IterableMap extends Map {
41
42
    /**
43
     * Obtains a <code>MapIterator</code> over the map.
44
     * <p>
45
     * A map iterator is an efficient way of iterating over maps.
46
     * There is no need to access the entry set or cast to Map Entry objects.
47
     * <pre>
48
     * IterableMap map = new HashedMap();
49
     * MapIterator it = map.mapIterator();
50
     * while (it.hasNext()) {
51
     *   Object key = it.next();
52
     *   Object value = it.getValue();
53
     *   it.setValue("newValue");
54
     * }
55
     * </pre>
56
     * 
57
     * @return a map iterator
58
     */
59
    MapIterator mapIterator();
60
    
61
}
(-)extra/collections/KeyValue.java (+46 lines)
Line 0 Link Here
1
/*
2
 *  Copyright 2003-2004 The Apache Software Foundation
3
 *
4
 *  Licensed under the Apache License, Version 2.0 (the "License");
5
 *  you may not use this file except in compliance with the License.
6
 *  You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *  Unless required by applicable law or agreed to in writing, software
11
 *  distributed under the License is distributed on an "AS IS" BASIS,
12
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *  See the License for the specific language governing permissions and
14
 *  limitations under the License.
15
 */
16
package org.apache.taglibs.standard.extra.commons.collections;
17
18
/**
19
 * Defines a simple key value pair.
20
 * <p>
21
 * A Map Entry has considerable additional semantics over and above a simple
22
 * key-value pair. This interface defines the minimum key value, with just the
23
 * two get methods.
24
 *
25
 * @since Commons Collections 3.0
26
 * @version $Revision: 218351 $ $Date: 2004-10-20 17:58:23 -0700 (Wed, 20 Oct 2004) $
27
 * 
28
 * @author Stephen Colebourne
29
 */
30
public interface KeyValue {
31
32
    /**
33
     * Gets the key from the pair.
34
     *
35
     * @return the key 
36
     */
37
    Object getKey();
38
39
    /**
40
     * Gets the value from the pair.
41
     *
42
     * @return the value
43
     */
44
    Object getValue();
45
46
}
(-)extra/collections/MapIterator.java (+108 lines)
Line 0 Link Here
1
/*
2
 *  Copyright 2003-2004 The Apache Software Foundation
3
 *
4
 *  Licensed under the Apache License, Version 2.0 (the "License");
5
 *  you may not use this file except in compliance with the License.
6
 *  You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *  Unless required by applicable law or agreed to in writing, software
11
 *  distributed under the License is distributed on an "AS IS" BASIS,
12
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *  See the License for the specific language governing permissions and
14
 *  limitations under the License.
15
 */
16
package org.apache.taglibs.standard.extra.commons.collections;
17
18
import java.util.Iterator;
19
20
/**
21
 * Defines an iterator that operates over a <code>Map</code>.
22
 * <p>
23
 * This iterator is a special version designed for maps. It can be more
24
 * efficient to use this rather than an entry set iterator where the option
25
 * is available, and it is certainly more convenient.
26
 * <p>
27
 * A map that provides this interface may not hold the data internally using
28
 * Map Entry objects, thus this interface can avoid lots of object creation.
29
 * <p>
30
 * In use, this iterator iterates through the keys in the map. After each call
31
 * to <code>next()</code>, the <code>getValue()</code> method provides direct
32
 * access to the value. The value can also be set using <code>setValue()</code>.
33
 * <pre>
34
 * MapIterator it = map.mapIterator();
35
 * while (it.hasNext()) {
36
 *   Object key = it.next();
37
 *   Object value = it.getValue();
38
 *   it.setValue(newValue);
39
 * }
40
 * </pre>
41
 *  
42
 * @since Commons Collections 3.0
43
 * @version $Revision: 218351 $ $Date: 2004-10-20 17:58:23 -0700 (Wed, 20 Oct 2004) $
44
 *
45
 * @author Stephen Colebourne
46
 */
47
public interface MapIterator extends Iterator {
48
    
49
    /**
50
     * Checks to see if there are more entries still to be iterated.
51
     *
52
     * @return <code>true</code> if the iterator has more elements
53
     */
54
    boolean hasNext();
55
56
    /**
57
     * Gets the next <em>key</em> from the <code>Map</code>.
58
     *
59
     * @return the next key in the iteration
60
     * @throws java.util.NoSuchElementException if the iteration is finished
61
     */
62
    Object next();
63
64
    //-----------------------------------------------------------------------
65
    /**
66
     * Gets the current key, which is the key returned by the last call
67
     * to <code>next()</code>.
68
     *
69
     * @return the current key
70
     * @throws IllegalStateException if <code>next()</code> has not yet been called
71
     */
72
    Object getKey();
73
74
    /**
75
     * Gets the current value, which is the value associated with the last key
76
     * returned by <code>next()</code>.
77
     *
78
     * @return the current value
79
     * @throws IllegalStateException if <code>next()</code> has not yet been called
80
     */
81
    Object getValue();
82
83
    //-----------------------------------------------------------------------
84
    /**
85
     * Removes the last returned key from the underlying <code>Map</code> (optional operation).
86
     * <p>
87
     * This method can be called once per call to <code>next()</code>.
88
     *
89
     * @throws UnsupportedOperationException if remove is not supported by the map
90
     * @throws IllegalStateException if <code>next()</code> has not yet been called
91
     * @throws IllegalStateException if <code>remove()</code> has already been called
92
     *  since the last call to <code>next()</code>
93
     */
94
    void remove();
95
    
96
    /**
97
     * Sets the value associated with the current key (optional operation).
98
     *
99
     * @param value  the new value
100
     * @return the previous value
101
     * @throws UnsupportedOperationException if setValue is not supported by the map
102
     * @throws IllegalStateException if <code>next()</code> has not yet been called
103
     * @throws IllegalStateException if <code>remove()</code> has been called since the
104
     *  last call to <code>next()</code>
105
     */
106
    Object setValue(Object value);
107
108
}
(-)extra/collections/README.txt (+10 lines)
Line 0 Link Here
1
Jakarta Commons Collections classes 
2
Version: 3.2
3
4
These classes are necessary for LRUMap implementation used in ELEvaluator to cache expression strings.
5
6
These classes can be deleted for any of the following reasons:
7
  - a custom caching mechanism is implemented
8
  - jakarta-commons collections jar file is made a required dependency
9
  - J2SE 1.4.x or higher is required
10
    - can leverage LRU LinkedHashmap implementation
(-)extra/collections/iterators/EmptyMapIterator.java (+44 lines)
Line 0 Link Here
1
/*
2
 *  Copyright 2004 The Apache Software Foundation
3
 *
4
 *  Licensed under the Apache License, Version 2.0 (the "License");
5
 *  you may not use this file except in compliance with the License.
6
 *  You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *  Unless required by applicable law or agreed to in writing, software
11
 *  distributed under the License is distributed on an "AS IS" BASIS,
12
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *  See the License for the specific language governing permissions and
14
 *  limitations under the License.
15
 */
16
package org.apache.taglibs.standard.extra.commons.collections.iterators;
17
18
import org.apache.taglibs.standard.extra.commons.collections.MapIterator;
19
import org.apache.taglibs.standard.extra.commons.collections.ResettableIterator;
20
21
/** 
22
 * Provides an implementation of an empty map iterator.
23
 *
24
 * @since Commons Collections 3.1
25
 * @version $Revision: 218351 $ $Date: 2004-10-20 17:58:23 -0700 (Wed, 20 Oct 2004) $
26
 * 
27
 * @author Stephen Colebourne
28
 */
29
public class EmptyMapIterator extends AbstractEmptyIterator implements MapIterator, ResettableIterator {
30
31
    /**
32
     * Singleton instance of the iterator.
33
     * @since Commons Collections 3.1
34
     */
35
    public static final MapIterator INSTANCE = new EmptyMapIterator();
36
37
    /**
38
     * Constructor.
39
     */
40
    protected EmptyMapIterator() {
41
        super();
42
    }
43
44
}
(-)extra/collections/iterators/EmptyIterator.java (+54 lines)
Line 0 Link Here
1
/*
2
 *  Copyright 2004 The Apache Software Foundation
3
 *
4
 *  Licensed under the Apache License, Version 2.0 (the "License");
5
 *  you may not use this file except in compliance with the License.
6
 *  You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *  Unless required by applicable law or agreed to in writing, software
11
 *  distributed under the License is distributed on an "AS IS" BASIS,
12
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *  See the License for the specific language governing permissions and
14
 *  limitations under the License.
15
 */
16
package org.apache.taglibs.standard.extra.commons.collections.iterators;
17
18
import java.util.Iterator;
19
20
import org.apache.taglibs.standard.extra.commons.collections.ResettableIterator;
21
22
/** 
23
 * Provides an implementation of an empty iterator.
24
 * <p>
25
 * This class provides an implementation of an empty iterator.
26
 * This class provides for binary compatability between Commons Collections
27
 * 2.1.1 and 3.1 due to issues with <code>IteratorUtils</code>.
28
 *
29
 * @since Commons Collections 2.1.1 and 3.1
30
 * @version $Revision: 218351 $ $Date: 2004-10-20 17:58:23 -0700 (Wed, 20 Oct 2004) $
31
 * 
32
 * @author Stephen Colebourne
33
 */
34
public class EmptyIterator extends AbstractEmptyIterator implements ResettableIterator {
35
36
    /**
37
     * Singleton instance of the iterator.
38
     * @since Commons Collections 3.1
39
     */
40
    public static final ResettableIterator RESETTABLE_INSTANCE = new EmptyIterator();
41
    /**
42
     * Singleton instance of the iterator.
43
     * @since Commons Collections 2.1.1 and 3.1
44
     */
45
    public static final Iterator INSTANCE = RESETTABLE_INSTANCE;
46
47
    /**
48
     * Constructor.
49
     */
50
    protected EmptyIterator() {
51
        super();
52
    }
53
54
}
(-)extra/collections/iterators/EmptyOrderedMapIterator.java (+44 lines)
Line 0 Link Here
1
/*
2
 *  Copyright 2004 The Apache Software Foundation
3
 *
4
 *  Licensed under the Apache License, Version 2.0 (the "License");
5
 *  you may not use this file except in compliance with the License.
6
 *  You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *  Unless required by applicable law or agreed to in writing, software
11
 *  distributed under the License is distributed on an "AS IS" BASIS,
12
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *  See the License for the specific language governing permissions and
14
 *  limitations under the License.
15
 */
16
package org.apache.taglibs.standard.extra.commons.collections.iterators;
17
18
import org.apache.taglibs.standard.extra.commons.collections.OrderedMapIterator;
19
import org.apache.taglibs.standard.extra.commons.collections.ResettableIterator;
20
21
/** 
22
 * Provides an implementation of an empty ordered map iterator.
23
 *
24
 * @since Commons Collections 3.1
25
 * @version $Revision: 218351 $ $Date: 2004-10-20 17:58:23 -0700 (Wed, 20 Oct 2004) $
26
 * 
27
 * @author Stephen Colebourne
28
 */
29
public class EmptyOrderedMapIterator extends AbstractEmptyIterator implements OrderedMapIterator, ResettableIterator {
30
31
    /**
32
     * Singleton instance of the iterator.
33
     * @since Commons Collections 3.1
34
     */
35
    public static final OrderedMapIterator INSTANCE = new EmptyOrderedMapIterator();
36
37
    /**
38
     * Constructor.
39
     */
40
    protected EmptyOrderedMapIterator() {
41
        super();
42
    }
43
44
}
(-)extra/collections/iterators/AbstractEmptyIterator.java (+89 lines)
Line 0 Link Here
1
/*
2
 *  Copyright 2004 The Apache Software Foundation
3
 *
4
 *  Licensed under the Apache License, Version 2.0 (the "License");
5
 *  you may not use this file except in compliance with the License.
6
 *  You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *  Unless required by applicable law or agreed to in writing, software
11
 *  distributed under the License is distributed on an "AS IS" BASIS,
12
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *  See the License for the specific language governing permissions and
14
 *  limitations under the License.
15
 */
16
package org.apache.taglibs.standard.extra.commons.collections.iterators;
17
18
import java.util.NoSuchElementException;
19
20
/** 
21
 * Provides an implementation of an empty iterator.
22
 *
23
 * @since Commons Collections 3.1
24
 * @version $Revision: 218351 $ $Date: 2004-10-20 17:58:23 -0700 (Wed, 20 Oct 2004) $
25
 * 
26
 * @author Stephen Colebourne
27
 */
28
abstract class AbstractEmptyIterator {
29
 
30
    /**
31
     * Constructor.
32
     */
33
    protected AbstractEmptyIterator() {
34
        super();
35
    }
36
37
    public boolean hasNext() {
38
        return false;
39
    }
40
41
    public Object next() {
42
        throw new NoSuchElementException("Iterator contains no elements");
43
    }
44
45
    public boolean hasPrevious() {
46
        return false;
47
    }
48
49
    public Object previous() {
50
        throw new NoSuchElementException("Iterator contains no elements");
51
    }
52
53
    public int nextIndex() {
54
        return 0;
55
    }
56
57
    public int previousIndex() {
58
        return -1;
59
    }
60
61
    public void add(Object obj) {
62
        throw new UnsupportedOperationException("add() not supported for empty Iterator");
63
    }
64
65
    public void set(Object obj) {
66
        throw new IllegalStateException("Iterator contains no elements");
67
    }
68
69
    public void remove() {
70
        throw new IllegalStateException("Iterator contains no elements");
71
    }
72
73
    public Object getKey() {
74
        throw new IllegalStateException("Iterator contains no elements");
75
    }
76
77
    public Object getValue() {
78
        throw new IllegalStateException("Iterator contains no elements");
79
    }
80
81
    public Object setValue(Object value) {
82
        throw new IllegalStateException("Iterator contains no elements");
83
    }
84
85
    public void reset() {
86
        // do nothing
87
    }
88
89
}
(-)extra/collections/iterators/EmptyOrderedIterator.java (+44 lines)
Line 0 Link Here
1
/*
2
 *  Copyright 2004 The Apache Software Foundation
3
 *
4
 *  Licensed under the Apache License, Version 2.0 (the "License");
5
 *  you may not use this file except in compliance with the License.
6
 *  You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *  Unless required by applicable law or agreed to in writing, software
11
 *  distributed under the License is distributed on an "AS IS" BASIS,
12
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *  See the License for the specific language governing permissions and
14
 *  limitations under the License.
15
 */
16
package org.apache.taglibs.standard.extra.commons.collections.iterators;
17
18
import org.apache.taglibs.standard.extra.commons.collections.OrderedIterator;
19
import org.apache.taglibs.standard.extra.commons.collections.ResettableIterator;
20
21
/** 
22
 * Provides an implementation of an empty ordered iterator.
23
 *
24
 * @since Commons Collections 3.1
25
 * @version $Revision: 218351 $ $Date: 2004-10-20 17:58:23 -0700 (Wed, 20 Oct 2004) $
26
 * 
27
 * @author Stephen Colebourne
28
 */
29
public class EmptyOrderedIterator extends AbstractEmptyIterator implements OrderedIterator, ResettableIterator {
30
31
    /**
32
     * Singleton instance of the iterator.
33
     * @since Commons Collections 3.1
34
     */
35
    public static final OrderedIterator INSTANCE = new EmptyOrderedIterator();
36
37
    /**
38
     * Constructor.
39
     */
40
    protected EmptyOrderedIterator() {
41
        super();
42
    }
43
44
}
(-)extra/collections/OrderedMapIterator.java (+45 lines)
Line 0 Link Here
1
/*
2
 *  Copyright 2001-2004 The Apache Software Foundation
3
 *
4
 *  Licensed under the Apache License, Version 2.0 (the "License");
5
 *  you may not use this file except in compliance with the License.
6
 *  You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *  Unless required by applicable law or agreed to in writing, software
11
 *  distributed under the License is distributed on an "AS IS" BASIS,
12
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *  See the License for the specific language governing permissions and
14
 *  limitations under the License.
15
 */
16
package org.apache.taglibs.standard.extra.commons.collections;
17
18
/**
19
 * Defines an iterator that operates over an ordered <code>Map</code>.
20
 * <p>
21
 * This iterator allows both forward and reverse iteration through the map.
22
 *  
23
 * @since Commons Collections 3.0
24
 * @version $Revision: 218351 $ $Date: 2004-10-20 17:58:23 -0700 (Wed, 20 Oct 2004) $
25
 *
26
 * @author Stephen Colebourne
27
 */
28
public interface OrderedMapIterator extends MapIterator, OrderedIterator {
29
    
30
    /**
31
     * Checks to see if there is a previous entry that can be iterated to.
32
     *
33
     * @return <code>true</code> if the iterator has a previous element
34
     */
35
    boolean hasPrevious();
36
37
    /**
38
     * Gets the previous <em>key</em> from the <code>Map</code>.
39
     *
40
     * @return the previous key in the iteration
41
     * @throws java.util.NoSuchElementException if the iteration is finished
42
     */
43
    Object previous();
44
45
}
(-)extra/collections/map/LRUMap.java (+391 lines)
Line 0 Link Here
1
/*
2
 *  Copyright 2001-2004 The Apache Software Foundation
3
 *
4
 *  Licensed under the Apache License, Version 2.0 (the "License");
5
 *  you may not use this file except in compliance with the License.
6
 *  You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *  Unless required by applicable law or agreed to in writing, software
11
 *  distributed under the License is distributed on an "AS IS" BASIS,
12
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *  See the License for the specific language governing permissions and
14
 *  limitations under the License.
15
 */
16
package org.apache.taglibs.standard.extra.commons.collections.map;
17
18
import java.io.IOException;
19
import java.io.ObjectInputStream;
20
import java.io.ObjectOutputStream;
21
import java.io.Serializable;
22
import java.util.Map;
23
24
import org.apache.taglibs.standard.extra.commons.collections.BoundedMap;
25
26
/**
27
 * A <code>Map</code> implementation with a fixed maximum size which removes
28
 * the least recently used entry if an entry is added when full.
29
 * <p>
30
 * The least recently used algorithm works on the get and put operations only.
31
 * Iteration of any kind, including setting the value by iteration, does not
32
 * change the order. Queries such as containsKey and containsValue or access
33
 * via views also do not change the order.
34
 * <p>
35
 * The map implements <code>OrderedMap</code> and entries may be queried using
36
 * the bidirectional <code>OrderedMapIterator</code>. The order returned is
37
 * least recently used to most recently used. Iterators from map views can 
38
 * also be cast to <code>OrderedIterator</code> if required.
39
 * <p>
40
 * All the available iterators can be reset back to the start by casting to
41
 * <code>ResettableIterator</code> and calling <code>reset()</code>.
42
 * 
43
 * @since Commons Collections 3.0 (previously in main package v1.0)
44
 * @version $Revision: 218351 $ $Date: 2004-10-20 17:58:23 -0700 (Wed, 20 Oct 2004) $
45
 *
46
 * @author James Strachan
47
 * @author Morgan Delagrange
48
 * @author Stephen Colebourne
49
 * @author Mike Pettypiece
50
 * @author Mario Ivankovits
51
 */
52
public class LRUMap
53
        extends AbstractLinkedMap implements BoundedMap, Serializable, Cloneable {
54
    
55
    /** Serialisation version */
56
    static final long serialVersionUID = -612114643488955218L;
57
    /** Default maximum size */
58
    protected static final int DEFAULT_MAX_SIZE = 100;
59
    
60
    /** Maximum size */
61
    private transient int maxSize;
62
    /** Scan behaviour */
63
    private boolean scanUntilRemovable;
64
65
    /**
66
     * Constructs a new empty map with a maximum size of 100.
67
     */
68
    public LRUMap() {
69
        this(DEFAULT_MAX_SIZE, DEFAULT_LOAD_FACTOR, false);
70
    }
71
72
    /**
73
     * Constructs a new, empty map with the specified maximum size.
74
     *
75
     * @param maxSize  the maximum size of the map
76
     * @throws IllegalArgumentException if the maximum size is less than one
77
     */
78
    public LRUMap(int maxSize) {
79
        this(maxSize, DEFAULT_LOAD_FACTOR);
80
    }
81
82
    /**
83
     * Constructs a new, empty map with the specified maximum size.
84
     *
85
     * @param maxSize  the maximum size of the map
86
     * @param scanUntilRemovable  scan until a removeable entry is found, default false
87
     * @throws IllegalArgumentException if the maximum size is less than one
88
     * @since Commons Collections 3.1
89
     */
90
    public LRUMap(int maxSize, boolean scanUntilRemovable) {
91
        this(maxSize, DEFAULT_LOAD_FACTOR, scanUntilRemovable);
92
    }
93
94
    /**
95
     * Constructs a new, empty map with the specified initial capacity and
96
     * load factor. 
97
     *
98
     * @param maxSize  the maximum size of the map, -1 for no limit,
99
     * @param loadFactor  the load factor
100
     * @throws IllegalArgumentException if the maximum size is less than one
101
     * @throws IllegalArgumentException if the load factor is less than zero
102
     */
103
    public LRUMap(int maxSize, float loadFactor) {
104
        this(maxSize, loadFactor, false);
105
    }
106
107
    /**
108
     * Constructs a new, empty map with the specified initial capacity and
109
     * load factor.
110
     *
111
     * @param maxSize  the maximum size of the map, -1 for no limit,
112
     * @param loadFactor  the load factor
113
     * @param scanUntilRemovable  scan until a removeable entry is found, default false
114
     * @throws IllegalArgumentException if the maximum size is less than one
115
     * @throws IllegalArgumentException if the load factor is less than zero
116
     * @since Commons Collections 3.1
117
     */
118
    public LRUMap(int maxSize, float loadFactor, boolean scanUntilRemovable) {
119
        super((maxSize < 1 ? DEFAULT_CAPACITY : maxSize), loadFactor);
120
        if (maxSize < 1) {
121
            throw new IllegalArgumentException("LRUMap max size must be greater than 0");
122
        }
123
        this.maxSize = maxSize;
124
        this.scanUntilRemovable = scanUntilRemovable;
125
    }
126
127
    /**
128
     * Constructor copying elements from another map.
129
     * <p>
130
     * The maximum size is set from the map's size.
131
     *
132
     * @param map  the map to copy
133
     * @throws NullPointerException if the map is null
134
     * @throws IllegalArgumentException if the map is empty
135
     */
136
    public LRUMap(Map map) {
137
        this(map, false);
138
    }
139
140
    /**
141
     * Constructor copying elements from another map.
142
     * <p/>
143
     * The maximum size is set from the map's size.
144
     *
145
     * @param map  the map to copy
146
     * @param scanUntilRemovable  scan until a removeable entry is found, default false
147
     * @throws NullPointerException if the map is null
148
     * @throws IllegalArgumentException if the map is empty
149
     * @since Commons Collections 3.1
150
     */
151
    public LRUMap(Map map, boolean scanUntilRemovable) {
152
        this(map.size(), DEFAULT_LOAD_FACTOR, scanUntilRemovable);
153
        putAll(map);
154
    }
155
156
    //-----------------------------------------------------------------------
157
    /**
158
     * Gets the value mapped to the key specified.
159
     * <p>
160
     * This operation changes the position of the key in the map to the
161
     * most recently used position (first).
162
     * 
163
     * @param key  the key
164
     * @return the mapped value, null if no match
165
     */
166
    public Object get(Object key) {
167
        LinkEntry entry = (LinkEntry) getEntry(key);
168
        if (entry == null) {
169
            return null;
170
        }
171
        moveToMRU(entry);
172
        return entry.getValue();
173
    }
174
175
    //-----------------------------------------------------------------------
176
    /**
177
     * Moves an entry to the MRU position at the end of the list.
178
     * <p>
179
     * This implementation moves the updated entry to the end of the list.
180
     * 
181
     * @param entry  the entry to update
182
     */
183
    protected void moveToMRU(LinkEntry entry) {
184
        if (entry.after != header) {
185
            modCount++;
186
            // remove
187
            entry.before.after = entry.after;
188
            entry.after.before = entry.before;
189
            // add first
190
            entry.after = header;
191
            entry.before = header.before;
192
            header.before.after = entry;
193
            header.before = entry;
194
        }
195
    }
196
    
197
    /**
198
     * Updates an existing key-value mapping.
199
     * <p>
200
     * This implementation moves the updated entry to the top of the list
201
     * using {@link #moveToMRU(AbstractLinkedMap.LinkEntry)}.
202
     * 
203
     * @param entry  the entry to update
204
     * @param newValue  the new value to store
205
     */
206
    protected void updateEntry(HashEntry entry, Object newValue) {
207
        moveToMRU((LinkEntry) entry);  // handles modCount
208
        entry.setValue(newValue);
209
    }
210
    
211
    /**
212
     * Adds a new key-value mapping into this map.
213
     * <p>
214
     * This implementation checks the LRU size and determines whether to
215
     * discard an entry or not using {@link #removeLRU(AbstractLinkedMap.LinkEntry)}.
216
     * <p>
217
     * From Commons Collections 3.1 this method uses {@link #isFull()} rather
218
     * than accessing <code>size</code> and <code>maxSize</code> directly.
219
     * It also handles the scanUntilRemovable functionality.
220
     * 
221
     * @param hashIndex  the index into the data array to store at
222
     * @param hashCode  the hash code of the key to add
223
     * @param key  the key to add
224
     * @param value  the value to add
225
     */
226
    protected void addMapping(int hashIndex, int hashCode, Object key, Object value) {
227
        if (isFull()) {
228
            LinkEntry reuse = header.after;
229
            boolean removeLRUEntry = false;
230
            if (scanUntilRemovable) {
231
                while (reuse != header) {
232
                    if (removeLRU(reuse)) {
233
                        removeLRUEntry = true;
234
                        break;
235
                    }
236
                    reuse = reuse.after;
237
                }
238
            } else {
239
                removeLRUEntry = removeLRU(reuse);
240
            }
241
            
242
            if (removeLRUEntry) {
243
                reuseMapping(reuse, hashIndex, hashCode, key, value);
244
            } else {
245
                super.addMapping(hashIndex, hashCode, key, value);
246
            }
247
        } else {
248
            super.addMapping(hashIndex, hashCode, key, value);
249
        }
250
    }
251
    
252
    /**
253
     * Reuses an entry by removing it and moving it to a new place in the map.
254
     * <p>
255
     * This method uses {@link #removeEntry}, {@link #reuseEntry} and {@link #addEntry}.
256
     * 
257
     * @param entry  the entry to reuse
258
     * @param hashIndex  the index into the data array to store at
259
     * @param hashCode  the hash code of the key to add
260
     * @param key  the key to add
261
     * @param value  the value to add
262
     */
263
    protected void reuseMapping(LinkEntry entry, int hashIndex, int hashCode, Object key, Object value) {
264
        // find the entry before the entry specified in the hash table
265
        // remember that the parameters (except the first) refer to the new entry,
266
        // not the old one
267
        int removeIndex = hashIndex(entry.hashCode, data.length);
268
        HashEntry loop = data[removeIndex];
269
        HashEntry previous = null;
270
        while (loop != entry) {
271
            previous = loop;
272
            loop = loop.next;
273
        }
274
        
275
        // reuse the entry
276
        modCount++;
277
        removeEntry(entry, removeIndex, previous);
278
        reuseEntry(entry, hashIndex, hashCode, key, value);
279
        addEntry(entry, hashIndex);
280
    }
281
    
282
    /**
283
     * Subclass method to control removal of the least recently used entry from the map.
284
     * <p>
285
     * This method exists for subclasses to override. A subclass may wish to
286
     * provide cleanup of resources when an entry is removed. For example:
287
     * <pre>
288
     * protected boolean removeLRU(LinkEntry entry) {
289
     *   releaseResources(entry.getValue());  // release resources held by entry
290
     *   return true;  // actually delete entry
291
     * }
292
     * </pre>
293
     * <p>
294
     * Alternatively, a subclass may choose to not remove the entry or selectively
295
     * keep certain LRU entries. For example:
296
     * <pre>
297
     * protected boolean removeLRU(LinkEntry entry) {
298
     *   if (entry.getKey().toString().startsWith("System.")) {
299
     *     return false;  // entry not removed from LRUMap
300
     *   } else {
301
     *     return true;  // actually delete entry
302
     *   }
303
     * }
304
     * </pre>
305
     * The effect of returning false is dependent on the scanUntilRemovable flag.
306
     * If the flag is true, the next LRU entry will be passed to this method and so on
307
     * until one returns false and is removed, or every entry in the map has been passed.
308
     * If the scanUntilRemovable flag is false, the map will exceed the maximum size.
309
     * <p>
310
     * NOTE: Commons Collections 3.0 passed the wrong entry to this method.
311
     * This is fixed in version 3.1 onwards.
312
     * 
313
     * @param entry  the entry to be removed
314
     */
315
    protected boolean removeLRU(LinkEntry entry) {
316
        return true;
317
    }
318
319
    //-----------------------------------------------------------------------
320
    /**
321
     * Returns true if this map is full and no new mappings can be added.
322
     *
323
     * @return <code>true</code> if the map is full
324
     */
325
    public boolean isFull() {
326
        return (size >= maxSize);
327
    }
328
329
    /**
330
     * Gets the maximum size of the map (the bound).
331
     *
332
     * @return the maximum number of elements the map can hold
333
     */
334
    public int maxSize() {
335
        return maxSize;
336
    }
337
338
    /**
339
     * Whether this LRUMap will scan until a removable entry is found when the
340
     * map is full.
341
     *
342
     * @return true if this map scans
343
     * @since Commons Collections 3.1
344
     */
345
    public boolean isScanUntilRemovable() {
346
        return scanUntilRemovable;
347
    }
348
349
    //-----------------------------------------------------------------------
350
    /**
351
     * Clones the map without cloning the keys or values.
352
     *
353
     * @return a shallow clone
354
     */
355
    public Object clone() {
356
        return super.clone();
357
    }
358
    
359
    /**
360
     * Write the map out using a custom routine.
361
     */
362
    private void writeObject(ObjectOutputStream out) throws IOException {
363
        out.defaultWriteObject();
364
        doWriteObject(out);
365
    }
366
367
    /**
368
     * Read the map in using a custom routine.
369
     */
370
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
371
        in.defaultReadObject();
372
        doReadObject(in);
373
    }
374
    
375
    /**
376
     * Writes the data necessary for <code>put()</code> to work in deserialization.
377
     */
378
    protected void doWriteObject(ObjectOutputStream out) throws IOException {
379
        out.writeInt(maxSize);
380
        super.doWriteObject(out);
381
    }
382
383
    /**
384
     * Reads the data necessary for <code>put()</code> to work in the superclass.
385
     */
386
    protected void doReadObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
387
        maxSize = in.readInt();
388
        super.doReadObject(in);
389
    }
390
    
391
}
(-)extra/collections/map/AbstractLinkedMap.java (+608 lines)
Line 0 Link Here
1
/*
2
 *  Copyright 2003-2004 The Apache Software Foundation
3
 *
4
 *  Licensed under the Apache License, Version 2.0 (the "License");
5
 *  you may not use this file except in compliance with the License.
6
 *  You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *  Unless required by applicable law or agreed to in writing, software
11
 *  distributed under the License is distributed on an "AS IS" BASIS,
12
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *  See the License for the specific language governing permissions and
14
 *  limitations under the License.
15
 */
16
package org.apache.taglibs.standard.extra.commons.collections.map;
17
18
import java.util.ConcurrentModificationException;
19
import java.util.Iterator;
20
import java.util.Map;
21
import java.util.NoSuchElementException;
22
23
import org.apache.taglibs.standard.extra.commons.collections.MapIterator;
24
import org.apache.taglibs.standard.extra.commons.collections.OrderedIterator;
25
import org.apache.taglibs.standard.extra.commons.collections.OrderedMap;
26
import org.apache.taglibs.standard.extra.commons.collections.OrderedMapIterator;
27
import org.apache.taglibs.standard.extra.commons.collections.ResettableIterator;
28
import org.apache.taglibs.standard.extra.commons.collections.iterators.EmptyOrderedIterator;
29
import org.apache.taglibs.standard.extra.commons.collections.iterators.EmptyOrderedMapIterator;
30
31
/**
32
 * An abstract implementation of a hash-based map that links entries to create an
33
 * ordered map and which provides numerous points for subclasses to override.
34
 * <p>
35
 * This class implements all the features necessary for a subclass linked
36
 * hash-based map. Key-value entries are stored in instances of the
37
 * <code>LinkEntry</code> class which can be overridden and replaced.
38
 * The iterators can similarly be replaced, without the need to replace the KeySet,
39
 * EntrySet and Values view classes.
40
 * <p>
41
 * Overridable methods are provided to change the default hashing behaviour, and
42
 * to change how entries are added to and removed from the map. Hopefully, all you
43
 * need for unusual subclasses is here.
44
 * <p>
45
 * This implementation maintains order by original insertion, but subclasses
46
 * may work differently. The <code>OrderedMap</code> interface is implemented
47
 * to provide access to bidirectional iteration and extra convenience methods.
48
 * <p>
49
 * The <code>orderedMapIterator()</code> method provides direct access to a
50
 * bidirectional iterator. The iterators from the other views can also be cast
51
 * to <code>OrderedIterator</code> if required.
52
 * <p>
53
 * All the available iterators can be reset back to the start by casting to
54
 * <code>ResettableIterator</code> and calling <code>reset()</code>.
55
 * <p>
56
 * The implementation is also designed to be subclassed, with lots of useful
57
 * methods exposed.
58
 * 
59
 * @since Commons Collections 3.0
60
 * @version $Revision: 218351 $ $Date: 2004-10-20 17:58:23 -0700 (Wed, 20 Oct 2004) $
61
 *
62
 * @author java util LinkedHashMap
63
 * @author Stephen Colebourne
64
 */
65
public class AbstractLinkedMap extends AbstractHashedMap implements OrderedMap {
66
    
67
    /** Header in the linked list */
68
    protected transient LinkEntry header;
69
70
    /**
71
     * Constructor only used in deserialization, do not use otherwise.
72
     */
73
    protected AbstractLinkedMap() {
74
        super();
75
    }
76
77
    /**
78
     * Constructor which performs no validation on the passed in parameters.
79
     * 
80
     * @param initialCapacity  the initial capacity, must be a power of two
81
     * @param loadFactor  the load factor, must be > 0.0f and generally < 1.0f
82
     * @param threshold  the threshold, must be sensible
83
     */
84
    protected AbstractLinkedMap(int initialCapacity, float loadFactor, int threshold) {
85
        super(initialCapacity, loadFactor, threshold);
86
    }
87
88
    /**
89
     * Constructs a new, empty map with the specified initial capacity. 
90
     *
91
     * @param initialCapacity  the initial capacity
92
     * @throws IllegalArgumentException if the initial capacity is less than one
93
     */
94
    protected AbstractLinkedMap(int initialCapacity) {
95
        super(initialCapacity);
96
    }
97
98
    /**
99
     * Constructs a new, empty map with the specified initial capacity and
100
     * load factor. 
101
     *
102
     * @param initialCapacity  the initial capacity
103
     * @param loadFactor  the load factor
104
     * @throws IllegalArgumentException if the initial capacity is less than one
105
     * @throws IllegalArgumentException if the load factor is less than zero
106
     */
107
    protected AbstractLinkedMap(int initialCapacity, float loadFactor) {
108
        super(initialCapacity, loadFactor);
109
    }
110
111
    /**
112
     * Constructor copying elements from another map.
113
     *
114
     * @param map  the map to copy
115
     * @throws NullPointerException if the map is null
116
     */
117
    protected AbstractLinkedMap(Map map) {
118
        super(map);
119
    }
120
121
    /**
122
     * Initialise this subclass during construction.
123
     */
124
    protected void init() {
125
        header = new LinkEntry(null, -1, null, null);
126
        header.before = header.after = header;
127
    }
128
129
    //-----------------------------------------------------------------------
130
    /**
131
     * Checks whether the map contains the specified value.
132
     * 
133
     * @param value  the value to search for
134
     * @return true if the map contains the value
135
     */
136
    public boolean containsValue(Object value) {
137
        // override uses faster iterator
138
        if (value == null) {
139
            for (LinkEntry entry = header.after; entry != header; entry = entry.after) {
140
                if (entry.getValue() == null) {
141
                    return true;
142
                }
143
            }
144
        } else {
145
            for (LinkEntry entry = header.after; entry != header; entry = entry.after) {
146
                if (isEqualValue(value, entry.getValue())) {
147
                    return true;
148
                }
149
            }
150
        }
151
        return false;
152
    }
153
154
    /**
155
     * Clears the map, resetting the size to zero and nullifying references
156
     * to avoid garbage collection issues.
157
     */
158
    public void clear() {
159
        // override to reset the linked list
160
        super.clear();
161
        header.before = header.after = header;
162
    }
163
164
    //-----------------------------------------------------------------------
165
    /**
166
     * Gets the first key in the map, which is the most recently inserted.
167
     * 
168
     * @return the most recently inserted key
169
     */
170
    public Object firstKey() {
171
        if (size == 0) {
172
            throw new NoSuchElementException("Map is empty");
173
        }
174
        return header.after.getKey();
175
    }
176
177
    /**
178
     * Gets the last key in the map, which is the first inserted.
179
     * 
180
     * @return the eldest key
181
     */
182
    public Object lastKey() {
183
        if (size == 0) {
184
            throw new NoSuchElementException("Map is empty");
185
        }
186
        return header.before.getKey();
187
    }
188
189
    /**
190
     * Gets the next key in sequence.
191
     * 
192
     * @param key  the key to get after
193
     * @return the next key
194
     */
195
    public Object nextKey(Object key) {
196
        LinkEntry entry = (LinkEntry) getEntry(key);
197
        return (entry == null || entry.after == header ? null : entry.after.getKey());
198
    }
199
200
    /**
201
     * Gets the previous key in sequence.
202
     * 
203
     * @param key  the key to get before
204
     * @return the previous key
205
     */
206
    public Object previousKey(Object key) {
207
        LinkEntry entry = (LinkEntry) getEntry(key);
208
        return (entry == null || entry.before == header ? null : entry.before.getKey());
209
    }
210
211
    //-----------------------------------------------------------------------    
212
    /**
213
     * Gets the key at the specified index.
214
     * 
215
     * @param index  the index to retrieve
216
     * @return the key at the specified index
217
     * @throws IndexOutOfBoundsException if the index is invalid
218
     */
219
    protected LinkEntry getEntry(int index) {
220
        if (index < 0) {
221
            throw new IndexOutOfBoundsException("Index " + index + " is less than zero");
222
        }
223
        if (index >= size) {
224
            throw new IndexOutOfBoundsException("Index " + index + " is invalid for size " + size);
225
        }
226
        LinkEntry entry;
227
        if (index < (size / 2)) {
228
            // Search forwards
229
            entry = header.after;
230
            for (int currentIndex = 0; currentIndex < index; currentIndex++) {
231
                entry = entry.after;
232
            }
233
        } else {
234
            // Search backwards
235
            entry = header;
236
            for (int currentIndex = size; currentIndex > index; currentIndex--) {
237
                entry = entry.before;
238
            }
239
        }
240
        return entry;
241
    }
242
    
243
    /**
244
     * Adds an entry into this map, maintaining insertion order.
245
     * <p>
246
     * This implementation adds the entry to the data storage table and
247
     * to the end of the linked list.
248
     * 
249
     * @param entry  the entry to add
250
     * @param hashIndex  the index into the data array to store at
251
     */
252
    protected void addEntry(HashEntry entry, int hashIndex) {
253
        LinkEntry link = (LinkEntry) entry;
254
        link.after  = header;
255
        link.before = header.before;
256
        header.before.after = link;
257
        header.before = link;
258
        data[hashIndex] = entry;
259
    }
260
    
261
    /**
262
     * Creates an entry to store the data.
263
     * <p>
264
     * This implementation creates a new LinkEntry instance.
265
     * 
266
     * @param next  the next entry in sequence
267
     * @param hashCode  the hash code to use
268
     * @param key  the key to store
269
     * @param value  the value to store
270
     * @return the newly created entry
271
     */
272
    protected HashEntry createEntry(HashEntry next, int hashCode, Object key, Object value) {
273
        return new LinkEntry(next, hashCode, key, value);
274
    }
275
    
276
    /**
277
     * Removes an entry from the map and the linked list.
278
     * <p>
279
     * This implementation removes the entry from the linked list chain, then
280
     * calls the superclass implementation.
281
     * 
282
     * @param entry  the entry to remove
283
     * @param hashIndex  the index into the data structure
284
     * @param previous  the previous entry in the chain
285
     */
286
    protected void removeEntry(HashEntry entry, int hashIndex, HashEntry previous) {
287
        LinkEntry link = (LinkEntry) entry;
288
        link.before.after = link.after;
289
        link.after.before = link.before;
290
        link.after = null;
291
        link.before = null;
292
        super.removeEntry(entry, hashIndex, previous);
293
    }
294
    
295
    //-----------------------------------------------------------------------
296
    /**
297
     * Gets the <code>before</code> field from a <code>LinkEntry</code>.
298
     * Used in subclasses that have no visibility of the field.
299
     * 
300
     * @param entry  the entry to query, must not be null
301
     * @return the <code>before</code> field of the entry
302
     * @throws NullPointerException if the entry is null
303
     * @since Commons Collections 3.1
304
     */
305
    protected LinkEntry entryBefore(LinkEntry entry) {
306
        return entry.before;
307
    }
308
    
309
    /**
310
     * Gets the <code>after</code> field from a <code>LinkEntry</code>.
311
     * Used in subclasses that have no visibility of the field.
312
     * 
313
     * @param entry  the entry to query, must not be null
314
     * @return the <code>after</code> field of the entry
315
     * @throws NullPointerException if the entry is null
316
     * @since Commons Collections 3.1
317
     */
318
    protected LinkEntry entryAfter(LinkEntry entry) {
319
        return entry.after;
320
    }
321
    
322
    //-----------------------------------------------------------------------
323
    /**
324
     * Gets an iterator over the map.
325
     * Changes made to the iterator affect this map.
326
     * <p>
327
     * A MapIterator returns the keys in the map. It also provides convenient
328
     * methods to get the key and value, and set the value.
329
     * It avoids the need to create an entrySet/keySet/values object.
330
     * 
331
     * @return the map iterator
332
     */
333
    public MapIterator mapIterator() {
334
        if (size == 0) {
335
            return EmptyOrderedMapIterator.INSTANCE;
336
        }
337
        return new LinkMapIterator(this);
338
    }
339
340
    /**
341
     * Gets a bidirectional iterator over the map.
342
     * Changes made to the iterator affect this map.
343
     * <p>
344
     * A MapIterator returns the keys in the map. It also provides convenient
345
     * methods to get the key and value, and set the value.
346
     * It avoids the need to create an entrySet/keySet/values object.
347
     * 
348
     * @return the map iterator
349
     */
350
    public OrderedMapIterator orderedMapIterator() {
351
        if (size == 0) {
352
            return EmptyOrderedMapIterator.INSTANCE;
353
        }
354
        return new LinkMapIterator(this);
355
    }
356
357
    /**
358
     * MapIterator implementation.
359
     */
360
    protected static class LinkMapIterator extends LinkIterator implements OrderedMapIterator {
361
        
362
        protected LinkMapIterator(AbstractLinkedMap parent) {
363
            super(parent);
364
        }
365
366
        public Object next() {
367
            return super.nextEntry().getKey();
368
        }
369
370
        public Object previous() {
371
            return super.previousEntry().getKey();
372
        }
373
374
        public Object getKey() {
375
            HashEntry current = currentEntry();
376
            if (current == null) {
377
                throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID);
378
            }
379
            return current.getKey();
380
        }
381
382
        public Object getValue() {
383
            HashEntry current = currentEntry();
384
            if (current == null) {
385
                throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID);
386
            }
387
            return current.getValue();
388
        }
389
390
        public Object setValue(Object value) {
391
            HashEntry current = currentEntry();
392
            if (current == null) {
393
                throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID);
394
            }
395
            return current.setValue(value);
396
        }
397
    }
398
    
399
    //-----------------------------------------------------------------------    
400
    /**
401
     * Creates an entry set iterator.
402
     * Subclasses can override this to return iterators with different properties.
403
     * 
404
     * @return the entrySet iterator
405
     */
406
    protected Iterator createEntrySetIterator() {
407
        if (size() == 0) {
408
            return EmptyOrderedIterator.INSTANCE;
409
        }
410
        return new EntrySetIterator(this);
411
    }
412
413
    /**
414
     * EntrySet iterator.
415
     */
416
    protected static class EntrySetIterator extends LinkIterator {
417
        
418
        protected EntrySetIterator(AbstractLinkedMap parent) {
419
            super(parent);
420
        }
421
422
        public Object next() {
423
            return super.nextEntry();
424
        }
425
426
        public Object previous() {
427
            return super.previousEntry();
428
        }
429
    }
430
431
    //-----------------------------------------------------------------------    
432
    /**
433
     * Creates a key set iterator.
434
     * Subclasses can override this to return iterators with different properties.
435
     * 
436
     * @return the keySet iterator
437
     */
438
    protected Iterator createKeySetIterator() {
439
        if (size() == 0) {
440
            return EmptyOrderedIterator.INSTANCE;
441
        }
442
        return new KeySetIterator(this);
443
    }
444
445
    /**
446
     * KeySet iterator.
447
     */
448
    protected static class KeySetIterator extends EntrySetIterator {
449
        
450
        protected KeySetIterator(AbstractLinkedMap parent) {
451
            super(parent);
452
        }
453
454
        public Object next() {
455
            return super.nextEntry().getKey();
456
        }
457
458
        public Object previous() {
459
            return super.previousEntry().getKey();
460
        }
461
    }
462
    
463
    //-----------------------------------------------------------------------    
464
    /**
465
     * Creates a values iterator.
466
     * Subclasses can override this to return iterators with different properties.
467
     * 
468
     * @return the values iterator
469
     */
470
    protected Iterator createValuesIterator() {
471
        if (size() == 0) {
472
            return EmptyOrderedIterator.INSTANCE;
473
        }
474
        return new ValuesIterator(this);
475
    }
476
477
    /**
478
     * Values iterator.
479
     */
480
    protected static class ValuesIterator extends LinkIterator {
481
        
482
        protected ValuesIterator(AbstractLinkedMap parent) {
483
            super(parent);
484
        }
485
486
        public Object next() {
487
            return super.nextEntry().getValue();
488
        }
489
490
        public Object previous() {
491
            return super.previousEntry().getValue();
492
        }
493
    }
494
    
495
    //-----------------------------------------------------------------------
496
    /**
497
     * LinkEntry that stores the data.
498
     * <p>
499
     * If you subclass <code>AbstractLinkedMap</code> but not <code>LinkEntry</code>
500
     * then you will not be able to access the protected fields.
501
     * The <code>entryXxx()</code> methods on <code>AbstractLinkedMap</code> exist
502
     * to provide the necessary access.
503
     */
504
    protected static class LinkEntry extends HashEntry {
505
        /** The entry before this one in the order */
506
        protected LinkEntry before;
507
        /** The entry after this one in the order */
508
        protected LinkEntry after;
509
        
510
        /**
511
         * Constructs a new entry.
512
         * 
513
         * @param next  the next entry in the hash bucket sequence
514
         * @param hashCode  the hash code
515
         * @param key  the key
516
         * @param value  the value
517
         */
518
        protected LinkEntry(HashEntry next, int hashCode, Object key, Object value) {
519
            super(next, hashCode, key, value);
520
        }
521
    }
522
    
523
    /**
524
     * Base Iterator that iterates in link order.
525
     */
526
    protected static abstract class LinkIterator
527
            implements OrderedIterator, ResettableIterator {
528
                
529
        /** The parent map */
530
        protected final AbstractLinkedMap parent;
531
        /** The current (last returned) entry */
532
        protected LinkEntry last;
533
        /** The next entry */
534
        protected LinkEntry next;
535
        /** The modification count expected */
536
        protected int expectedModCount;
537
        
538
        protected LinkIterator(AbstractLinkedMap parent) {
539
            super();
540
            this.parent = parent;
541
            this.next = parent.header.after;
542
            this.expectedModCount = parent.modCount;
543
        }
544
545
        public boolean hasNext() {
546
            return (next != parent.header);
547
        }
548
        
549
        public boolean hasPrevious() {
550
            return (next.before != parent.header);
551
        }
552
553
        protected LinkEntry nextEntry() {
554
            if (parent.modCount != expectedModCount) {
555
                throw new ConcurrentModificationException();
556
            }
557
            if (next == parent.header)  {
558
                throw new NoSuchElementException(AbstractHashedMap.NO_NEXT_ENTRY);
559
            }
560
            last = next;
561
            next = next.after;
562
            return last;
563
        }
564
565
        protected LinkEntry previousEntry() {
566
            if (parent.modCount != expectedModCount) {
567
                throw new ConcurrentModificationException();
568
            }
569
            LinkEntry previous = next.before;
570
            if (previous == parent.header)  {
571
                throw new NoSuchElementException(AbstractHashedMap.NO_PREVIOUS_ENTRY);
572
            }
573
            next = previous;
574
            last = previous;
575
            return last;
576
        }
577
        
578
        protected LinkEntry currentEntry() {
579
            return last;
580
        }
581
        
582
        public void remove() {
583
            if (last == null) {
584
                throw new IllegalStateException(AbstractHashedMap.REMOVE_INVALID);
585
            }
586
            if (parent.modCount != expectedModCount) {
587
                throw new ConcurrentModificationException();
588
            }
589
            parent.remove(last.getKey());
590
            last = null;
591
            expectedModCount = parent.modCount;
592
        }
593
        
594
        public void reset() {
595
            last = null;
596
            next = parent.header.after;
597
        }
598
599
        public String toString() {
600
            if (last != null) {
601
                return "Iterator[" + last.getKey() + "=" + last.getValue() + "]";
602
            } else {
603
                return "Iterator[]";
604
            }
605
        }
606
    }
607
    
608
}
(-)extra/collections/map/AbstractHashedMap.java (+1327 lines)
Line 0 Link Here
1
/*
2
 *  Copyright 2003-2004 The Apache Software Foundation
3
 *
4
 *  Licensed under the Apache License, Version 2.0 (the "License");
5
 *  you may not use this file except in compliance with the License.
6
 *  You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *  Unless required by applicable law or agreed to in writing, software
11
 *  distributed under the License is distributed on an "AS IS" BASIS,
12
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *  See the License for the specific language governing permissions and
14
 *  limitations under the License.
15
 */
16
package org.apache.taglibs.standard.extra.commons.collections.map;
17
18
import java.io.IOException;
19
import java.io.ObjectInputStream;
20
import java.io.ObjectOutputStream;
21
import java.util.AbstractCollection;
22
import java.util.AbstractMap;
23
import java.util.AbstractSet;
24
import java.util.Collection;
25
import java.util.ConcurrentModificationException;
26
import java.util.Iterator;
27
import java.util.Map;
28
import java.util.NoSuchElementException;
29
import java.util.Set;
30
31
import org.apache.taglibs.standard.extra.commons.collections.IterableMap;
32
import org.apache.taglibs.standard.extra.commons.collections.KeyValue;
33
import org.apache.taglibs.standard.extra.commons.collections.MapIterator;
34
import org.apache.taglibs.standard.extra.commons.collections.iterators.EmptyIterator;
35
import org.apache.taglibs.standard.extra.commons.collections.iterators.EmptyMapIterator;
36
37
/**
38
 * An abstract implementation of a hash-based map which provides numerous points for
39
 * subclasses to override.
40
 * <p>
41
 * This class implements all the features necessary for a subclass hash-based map.
42
 * Key-value entries are stored in instances of the <code>HashEntry</code> class,
43
 * which can be overridden and replaced. The iterators can similarly be replaced,
44
 * without the need to replace the KeySet, EntrySet and Values view classes.
45
 * <p>
46
 * Overridable methods are provided to change the default hashing behaviour, and
47
 * to change how entries are added to and removed from the map. Hopefully, all you
48
 * need for unusual subclasses is here.
49
 * <p>
50
 * NOTE: From Commons Collections 3.1 this class extends AbstractMap.
51
 * This is to provide backwards compatibility for ReferenceMap between v3.0 and v3.1.
52
 * This extends clause will be removed in v4.0.
53
 * 
54
 * @since Commons Collections 3.0
55
 * @version $Revision: 218351 $ $Date: 2004-10-20 17:58:23 -0700 (Wed, 20 Oct 2004) $
56
 *
57
 * @author java util HashMap
58
 * @author Stephen Colebourne
59
 */
60
public class AbstractHashedMap extends AbstractMap implements IterableMap {
61
    
62
    protected static final String NO_NEXT_ENTRY = "No next() entry in the iteration";
63
    protected static final String NO_PREVIOUS_ENTRY = "No previous() entry in the iteration";
64
    protected static final String REMOVE_INVALID = "remove() can only be called once after next()";
65
    protected static final String GETKEY_INVALID = "getKey() can only be called after next() and before remove()";
66
    protected static final String GETVALUE_INVALID = "getValue() can only be called after next() and before remove()";
67
    protected static final String SETVALUE_INVALID = "setValue() can only be called after next() and before remove()";
68
    
69
    /** The default capacity to use */
70
    protected static final int DEFAULT_CAPACITY = 16;
71
    /** The default threshold to use */
72
    protected static final int DEFAULT_THRESHOLD = 12;
73
    /** The default load factor to use */
74
    protected static final float DEFAULT_LOAD_FACTOR = 0.75f;
75
    /** The maximum capacity allowed */
76
    protected static final int MAXIMUM_CAPACITY = 1 << 30;
77
    /** An object for masking null */
78
    protected static final Object NULL = new Object();
79
    
80
    /** Load factor, normally 0.75 */
81
    protected transient float loadFactor;
82
    /** The size of the map */
83
    protected transient int size;
84
    /** Map entries */
85
    protected transient HashEntry[] data;
86
    /** Size at which to rehash */
87
    protected transient int threshold;
88
    /** Modification count for iterators */
89
    protected transient int modCount;
90
    /** Entry set */
91
    protected transient EntrySet entrySet;
92
    /** Key set */
93
    protected transient KeySet keySet;
94
    /** Values */
95
    protected transient Values values;
96
97
    /**
98
     * Constructor only used in deserialization, do not use otherwise.
99
     */
100
    protected AbstractHashedMap() {
101
        super();
102
    }
103
104
    /**
105
     * Constructor which performs no validation on the passed in parameters.
106
     * 
107
     * @param initialCapacity  the initial capacity, must be a power of two
108
     * @param loadFactor  the load factor, must be &gt; 0.0f and generally &lt; 1.0f
109
     * @param threshold  the threshold, must be sensible
110
     */
111
    protected AbstractHashedMap(int initialCapacity, float loadFactor, int threshold) {
112
        super();
113
        this.loadFactor = loadFactor;
114
        this.data = new HashEntry[initialCapacity];
115
        this.threshold = threshold;
116
        init();
117
    }
118
119
    /**
120
     * Constructs a new, empty map with the specified initial capacity and
121
     * default load factor. 
122
     *
123
     * @param initialCapacity  the initial capacity
124
     * @throws IllegalArgumentException if the initial capacity is less than one
125
     */
126
    protected AbstractHashedMap(int initialCapacity) {
127
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
128
    }
129
130
    /**
131
     * Constructs a new, empty map with the specified initial capacity and
132
     * load factor. 
133
     *
134
     * @param initialCapacity  the initial capacity
135
     * @param loadFactor  the load factor
136
     * @throws IllegalArgumentException if the initial capacity is less than one
137
     * @throws IllegalArgumentException if the load factor is less than or equal to zero
138
     */
139
    protected AbstractHashedMap(int initialCapacity, float loadFactor) {
140
        super();
141
        if (initialCapacity < 1) {
142
            throw new IllegalArgumentException("Initial capacity must be greater than 0");
143
        }
144
        if (loadFactor <= 0.0f || Float.isNaN(loadFactor)) {
145
            throw new IllegalArgumentException("Load factor must be greater than 0");
146
        }
147
        this.loadFactor = loadFactor;
148
        this.threshold = calculateThreshold(initialCapacity, loadFactor);
149
        initialCapacity = calculateNewCapacity(initialCapacity);
150
        this.data = new HashEntry[initialCapacity];
151
        init();
152
    }
153
154
    /**
155
     * Constructor copying elements from another map.
156
     *
157
     * @param map  the map to copy
158
     * @throws NullPointerException if the map is null
159
     */
160
    protected AbstractHashedMap(Map map) {
161
        this(Math.max(2 * map.size(), DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR);
162
        putAll(map);
163
    }
164
165
    /**
166
     * Initialise subclasses during construction, cloning or deserialization.
167
     */
168
    protected void init() {
169
    }
170
171
    //-----------------------------------------------------------------------
172
    /**
173
     * Gets the value mapped to the key specified.
174
     * 
175
     * @param key  the key
176
     * @return the mapped value, null if no match
177
     */
178
    public Object get(Object key) {
179
        key = convertKey(key);
180
        int hashCode = hash(key);
181
        HashEntry entry = data[hashIndex(hashCode, data.length)]; // no local for hash index
182
        while (entry != null) {
183
            if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) {
184
                return entry.getValue();
185
            }
186
            entry = entry.next;
187
        }
188
        return null;
189
    }
190
191
    /**
192
     * Gets the size of the map.
193
     * 
194
     * @return the size
195
     */
196
    public int size() {
197
        return size;
198
    }
199
200
    /**
201
     * Checks whether the map is currently empty.
202
     * 
203
     * @return true if the map is currently size zero
204
     */
205
    public boolean isEmpty() {
206
        return (size == 0);
207
    }
208
209
    //-----------------------------------------------------------------------
210
    /**
211
     * Checks whether the map contains the specified key.
212
     * 
213
     * @param key  the key to search for
214
     * @return true if the map contains the key
215
     */
216
    public boolean containsKey(Object key) {
217
        key = convertKey(key);
218
        int hashCode = hash(key);
219
        HashEntry entry = data[hashIndex(hashCode, data.length)]; // no local for hash index
220
        while (entry != null) {
221
            if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) {
222
                return true;
223
            }
224
            entry = entry.next;
225
        }
226
        return false;
227
    }
228
229
    /**
230
     * Checks whether the map contains the specified value.
231
     * 
232
     * @param value  the value to search for
233
     * @return true if the map contains the value
234
     */
235
    public boolean containsValue(Object value) {
236
        if (value == null) {
237
            for (int i = 0, isize = data.length; i < isize; i++) {
238
                HashEntry entry = data[i];
239
                while (entry != null) {
240
                    if (entry.getValue() == null) {
241
                        return true;
242
                    }
243
                    entry = entry.next;
244
                }
245
            }
246
        } else {
247
            for (int i = 0, isize = data.length; i < isize; i++) {
248
                HashEntry entry = data[i];
249
                while (entry != null) {
250
                    if (isEqualValue(value, entry.getValue())) {
251
                        return true;
252
                    }
253
                    entry = entry.next;
254
                }
255
            }
256
        }
257
        return false;
258
    }
259
260
    //-----------------------------------------------------------------------
261
    /**
262
     * Puts a key-value mapping into this map.
263
     * 
264
     * @param key  the key to add
265
     * @param value  the value to add
266
     * @return the value previously mapped to this key, null if none
267
     */
268
    public Object put(Object key, Object value) {
269
        key = convertKey(key);
270
        int hashCode = hash(key);
271
        int index = hashIndex(hashCode, data.length);
272
        HashEntry entry = data[index];
273
        while (entry != null) {
274
            if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) {
275
                Object oldValue = entry.getValue();
276
                updateEntry(entry, value);
277
                return oldValue;
278
            }
279
            entry = entry.next;
280
        }
281
        
282
        addMapping(index, hashCode, key, value);
283
        return null;
284
    }
285
286
    /**
287
     * Puts all the values from the specified map into this map.
288
     * <p>
289
     * This implementation iterates around the specified map and
290
     * uses {@link #put(Object, Object)}.
291
     * 
292
     * @param map  the map to add
293
     * @throws NullPointerException if the map is null
294
     */
295
    public void putAll(Map map) {
296
        int mapSize = map.size();
297
        if (mapSize == 0) {
298
            return;
299
        }
300
        int newSize = (int) ((size + mapSize) / loadFactor + 1);
301
        ensureCapacity(calculateNewCapacity(newSize));
302
        for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
303
            Map.Entry entry = (Map.Entry) it.next();
304
            put(entry.getKey(), entry.getValue());
305
        }
306
    }
307
308
    /**
309
     * Removes the specified mapping from this map.
310
     * 
311
     * @param key  the mapping to remove
312
     * @return the value mapped to the removed key, null if key not in map
313
     */
314
    public Object remove(Object key) {
315
        key = convertKey(key);
316
        int hashCode = hash(key);
317
        int index = hashIndex(hashCode, data.length);
318
        HashEntry entry = data[index];
319
        HashEntry previous = null;
320
        while (entry != null) {
321
            if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) {
322
                Object oldValue = entry.getValue();
323
                removeMapping(entry, index, previous);
324
                return oldValue;
325
            }
326
            previous = entry;
327
            entry = entry.next;
328
        }
329
        return null;
330
    }
331
332
    /**
333
     * Clears the map, resetting the size to zero and nullifying references
334
     * to avoid garbage collection issues.
335
     */
336
    public void clear() {
337
        modCount++;
338
        HashEntry[] data = this.data;
339
        for (int i = data.length - 1; i >= 0; i--) {
340
            data[i] = null;
341
        }
342
        size = 0;
343
    }
344
345
    //-----------------------------------------------------------------------
346
    /**
347
     * Converts input keys to another object for storage in the map.
348
     * This implementation masks nulls.
349
     * Subclasses can override this to perform alternate key conversions.
350
     * <p>
351
     * The reverse conversion can be changed, if required, by overriding the
352
     * getKey() method in the hash entry.
353
     * 
354
     * @param key  the key convert
355
     * @return the converted key
356
     */
357
    protected Object convertKey(Object key) {
358
        return (key == null ? NULL : key);
359
    }
360
    
361
    /**
362
     * Gets the hash code for the key specified.
363
     * This implementation uses the additional hashing routine from JDK1.4.
364
     * Subclasses can override this to return alternate hash codes.
365
     * 
366
     * @param key  the key to get a hash code for
367
     * @return the hash code
368
     */
369
    protected int hash(Object key) {
370
        // same as JDK 1.4
371
        int h = key.hashCode();
372
        h += ~(h << 9);
373
        h ^=  (h >>> 14);
374
        h +=  (h << 4);
375
        h ^=  (h >>> 10);
376
        return h;
377
    }
378
    
379
    /**
380
     * Compares two keys, in internal converted form, to see if they are equal.
381
     * This implementation uses the equals method and assumes neither key is null.
382
     * Subclasses can override this to match differently.
383
     * 
384
     * @param key1  the first key to compare passed in from outside
385
     * @param key2  the second key extracted from the entry via <code>entry.key</code>
386
     * @return true if equal
387
     */
388
    protected boolean isEqualKey(Object key1, Object key2) {
389
        return (key1 == key2 || key1.equals(key2));
390
    }
391
    
392
    /**
393
     * Compares two values, in external form, to see if they are equal.
394
     * This implementation uses the equals method and assumes neither value is null.
395
     * Subclasses can override this to match differently.
396
     * 
397
     * @param value1  the first value to compare passed in from outside
398
     * @param value2  the second value extracted from the entry via <code>getValue()</code>
399
     * @return true if equal
400
     */
401
    protected boolean isEqualValue(Object value1, Object value2) {
402
        return (value1 == value2 || value1.equals(value2));
403
    }
404
    
405
    /**
406
     * Gets the index into the data storage for the hashCode specified.
407
     * This implementation uses the least significant bits of the hashCode.
408
     * Subclasses can override this to return alternate bucketing.
409
     * 
410
     * @param hashCode  the hash code to use
411
     * @param dataSize  the size of the data to pick a bucket from
412
     * @return the bucket index
413
     */
414
    protected int hashIndex(int hashCode, int dataSize) {
415
        return hashCode & (dataSize - 1);
416
    }
417
    
418
    //-----------------------------------------------------------------------
419
    /**
420
     * Gets the entry mapped to the key specified.
421
     * <p>
422
     * This method exists for subclasses that may need to perform a multi-step
423
     * process accessing the entry. The public methods in this class don't use this
424
     * method to gain a small performance boost.
425
     * 
426
     * @param key  the key
427
     * @return the entry, null if no match
428
     */
429
    protected HashEntry getEntry(Object key) {
430
        key = convertKey(key);
431
        int hashCode = hash(key);
432
        HashEntry entry = data[hashIndex(hashCode, data.length)]; // no local for hash index
433
        while (entry != null) {
434
            if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) {
435
                return entry;
436
            }
437
            entry = entry.next;
438
        }
439
        return null;
440
    }
441
442
    //-----------------------------------------------------------------------
443
    /**
444
     * Updates an existing key-value mapping to change the value.
445
     * <p>
446
     * This implementation calls <code>setValue()</code> on the entry.
447
     * Subclasses could override to handle changes to the map.
448
     * 
449
     * @param entry  the entry to update
450
     * @param newValue  the new value to store
451
     */
452
    protected void updateEntry(HashEntry entry, Object newValue) {
453
        entry.setValue(newValue);
454
    }
455
    
456
    /**
457
     * Reuses an existing key-value mapping, storing completely new data.
458
     * <p>
459
     * This implementation sets all the data fields on the entry.
460
     * Subclasses could populate additional entry fields.
461
     * 
462
     * @param entry  the entry to update, not null
463
     * @param hashIndex  the index in the data array
464
     * @param hashCode  the hash code of the key to add
465
     * @param key  the key to add
466
     * @param value  the value to add
467
     */
468
    protected void reuseEntry(HashEntry entry, int hashIndex, int hashCode, Object key, Object value) {
469
        entry.next = data[hashIndex];
470
        entry.hashCode = hashCode;
471
        entry.key = key;
472
        entry.value = value;
473
    }
474
    
475
    //-----------------------------------------------------------------------
476
    /**
477
     * Adds a new key-value mapping into this map.
478
     * <p>
479
     * This implementation calls <code>createEntry()</code>, <code>addEntry()</code>
480
     * and <code>checkCapacity()</code>.
481
     * It also handles changes to <code>modCount</code> and <code>size</code>.
482
     * Subclasses could override to fully control adds to the map.
483
     * 
484
     * @param hashIndex  the index into the data array to store at
485
     * @param hashCode  the hash code of the key to add
486
     * @param key  the key to add
487
     * @param value  the value to add
488
     */
489
    protected void addMapping(int hashIndex, int hashCode, Object key, Object value) {
490
        modCount++;
491
        HashEntry entry = createEntry(data[hashIndex], hashCode, key, value);
492
        addEntry(entry, hashIndex);
493
        size++;
494
        checkCapacity();
495
    }
496
    
497
    /**
498
     * Creates an entry to store the key-value data.
499
     * <p>
500
     * This implementation creates a new HashEntry instance.
501
     * Subclasses can override this to return a different storage class,
502
     * or implement caching.
503
     * 
504
     * @param next  the next entry in sequence
505
     * @param hashCode  the hash code to use
506
     * @param key  the key to store
507
     * @param value  the value to store
508
     * @return the newly created entry
509
     */
510
    protected HashEntry createEntry(HashEntry next, int hashCode, Object key, Object value) {
511
        return new HashEntry(next, hashCode, key, value);
512
    }
513
    
514
    /**
515
     * Adds an entry into this map.
516
     * <p>
517
     * This implementation adds the entry to the data storage table.
518
     * Subclasses could override to handle changes to the map.
519
     *
520
     * @param entry  the entry to add
521
     * @param hashIndex  the index into the data array to store at
522
     */
523
    protected void addEntry(HashEntry entry, int hashIndex) {
524
        data[hashIndex] = entry;
525
    }
526
    
527
    //-----------------------------------------------------------------------
528
    /**
529
     * Removes a mapping from the map.
530
     * <p>
531
     * This implementation calls <code>removeEntry()</code> and <code>destroyEntry()</code>.
532
     * It also handles changes to <code>modCount</code> and <code>size</code>.
533
     * Subclasses could override to fully control removals from the map.
534
     * 
535
     * @param entry  the entry to remove
536
     * @param hashIndex  the index into the data structure
537
     * @param previous  the previous entry in the chain
538
     */
539
    protected void removeMapping(HashEntry entry, int hashIndex, HashEntry previous) {
540
        modCount++;
541
        removeEntry(entry, hashIndex, previous);
542
        size--;
543
        destroyEntry(entry);
544
    }
545
    
546
    /**
547
     * Removes an entry from the chain stored in a particular index.
548
     * <p>
549
     * This implementation removes the entry from the data storage table.
550
     * The size is not updated.
551
     * Subclasses could override to handle changes to the map.
552
     * 
553
     * @param entry  the entry to remove
554
     * @param hashIndex  the index into the data structure
555
     * @param previous  the previous entry in the chain
556
     */
557
    protected void removeEntry(HashEntry entry, int hashIndex, HashEntry previous) {
558
        if (previous == null) {
559
            data[hashIndex] = entry.next;
560
        } else {
561
            previous.next = entry.next;
562
        }
563
    }
564
    
565
    /**
566
     * Kills an entry ready for the garbage collector.
567
     * <p>
568
     * This implementation prepares the HashEntry for garbage collection.
569
     * Subclasses can override this to implement caching (override clear as well).
570
     * 
571
     * @param entry  the entry to destroy
572
     */
573
    protected void destroyEntry(HashEntry entry) {
574
        entry.next = null;
575
        entry.key = null;
576
        entry.value = null;
577
    }
578
    
579
    //-----------------------------------------------------------------------
580
    /**
581
     * Checks the capacity of the map and enlarges it if necessary.
582
     * <p>
583
     * This implementation uses the threshold to check if the map needs enlarging
584
     */
585
    protected void checkCapacity() {
586
        if (size >= threshold) {
587
            int newCapacity = data.length * 2;
588
            if (newCapacity <= MAXIMUM_CAPACITY) {
589
                ensureCapacity(newCapacity);
590
            }
591
        }
592
    }
593
    
594
    /**
595
     * Changes the size of the data structure to the capacity proposed.
596
     * 
597
     * @param newCapacity  the new capacity of the array (a power of two, less or equal to max)
598
     */
599
    protected void ensureCapacity(int newCapacity) {
600
        int oldCapacity = data.length;
601
        if (newCapacity <= oldCapacity) {
602
            return;
603
        }
604
        if (size == 0) {
605
            threshold = calculateThreshold(newCapacity, loadFactor);
606
            data = new HashEntry[newCapacity];
607
        } else {
608
            HashEntry oldEntries[] = data;
609
            HashEntry newEntries[] = new HashEntry[newCapacity];
610
611
            modCount++;
612
            for (int i = oldCapacity - 1; i >= 0; i--) {
613
                HashEntry entry = oldEntries[i];
614
                if (entry != null) {
615
                    oldEntries[i] = null;  // gc
616
                    do {
617
                        HashEntry next = entry.next;
618
                        int index = hashIndex(entry.hashCode, newCapacity);  
619
                        entry.next = newEntries[index];
620
                        newEntries[index] = entry;
621
                        entry = next;
622
                    } while (entry != null);
623
                }
624
            }
625
            threshold = calculateThreshold(newCapacity, loadFactor);
626
            data = newEntries;
627
        }
628
    }
629
630
    /**
631
     * Calculates the new capacity of the map.
632
     * This implementation normalizes the capacity to a power of two.
633
     * 
634
     * @param proposedCapacity  the proposed capacity
635
     * @return the normalized new capacity
636
     */
637
    protected int calculateNewCapacity(int proposedCapacity) {
638
        int newCapacity = 1;
639
        if (proposedCapacity > MAXIMUM_CAPACITY) {
640
            newCapacity = MAXIMUM_CAPACITY;
641
        } else {
642
            while (newCapacity < proposedCapacity) {
643
                newCapacity <<= 1;  // multiply by two
644
            }
645
            if (newCapacity > MAXIMUM_CAPACITY) {
646
                newCapacity = MAXIMUM_CAPACITY;
647
            }
648
        }
649
        return newCapacity;
650
    }
651
    
652
    /**
653
     * Calculates the new threshold of the map, where it will be resized.
654
     * This implementation uses the load factor.
655
     * 
656
     * @param newCapacity  the new capacity
657
     * @param factor  the load factor
658
     * @return the new resize threshold
659
     */
660
    protected int calculateThreshold(int newCapacity, float factor) {
661
        return (int) (newCapacity * factor);
662
    }
663
    
664
    //-----------------------------------------------------------------------
665
    /**
666
     * Gets the <code>next</code> field from a <code>HashEntry</code>.
667
     * Used in subclasses that have no visibility of the field.
668
     * 
669
     * @param entry  the entry to query, must not be null
670
     * @return the <code>next</code> field of the entry
671
     * @throws NullPointerException if the entry is null
672
     * @since Commons Collections 3.1
673
     */
674
    protected HashEntry entryNext(HashEntry entry) {
675
        return entry.next;
676
    }
677
    
678
    /**
679
     * Gets the <code>hashCode</code> field from a <code>HashEntry</code>.
680
     * Used in subclasses that have no visibility of the field.
681
     * 
682
     * @param entry  the entry to query, must not be null
683
     * @return the <code>hashCode</code> field of the entry
684
     * @throws NullPointerException if the entry is null
685
     * @since Commons Collections 3.1
686
     */
687
    protected int entryHashCode(HashEntry entry) {
688
        return entry.hashCode;
689
    }
690
    
691
    /**
692
     * Gets the <code>key</code> field from a <code>HashEntry</code>.
693
     * Used in subclasses that have no visibility of the field.
694
     * 
695
     * @param entry  the entry to query, must not be null
696
     * @return the <code>key</code> field of the entry
697
     * @throws NullPointerException if the entry is null
698
     * @since Commons Collections 3.1
699
     */
700
    protected Object entryKey(HashEntry entry) {
701
        return entry.key;
702
    }
703
    
704
    /**
705
     * Gets the <code>value</code> field from a <code>HashEntry</code>.
706
     * Used in subclasses that have no visibility of the field.
707
     * 
708
     * @param entry  the entry to query, must not be null
709
     * @return the <code>value</code> field of the entry
710
     * @throws NullPointerException if the entry is null
711
     * @since Commons Collections 3.1
712
     */
713
    protected Object entryValue(HashEntry entry) {
714
        return entry.value;
715
    }
716
    
717
    //-----------------------------------------------------------------------
718
    /**
719
     * Gets an iterator over the map.
720
     * Changes made to the iterator affect this map.
721
     * <p>
722
     * A MapIterator returns the keys in the map. It also provides convenient
723
     * methods to get the key and value, and set the value.
724
     * It avoids the need to create an entrySet/keySet/values object.
725
     * It also avoids creating the Map.Entry object.
726
     * 
727
     * @return the map iterator
728
     */
729
    public MapIterator mapIterator() {
730
        if (size == 0) {
731
            return EmptyMapIterator.INSTANCE;
732
        }
733
        return new HashMapIterator(this);
734
    }
735
736
    /**
737
     * MapIterator implementation.
738
     */
739
    protected static class HashMapIterator extends HashIterator implements MapIterator {
740
        
741
        protected HashMapIterator(AbstractHashedMap parent) {
742
            super(parent);
743
        }
744
745
        public Object next() {
746
            return super.nextEntry().getKey();
747
        }
748
749
        public Object getKey() {
750
            HashEntry current = currentEntry();
751
            if (current == null) {
752
                throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID);
753
            }
754
            return current.getKey();
755
        }
756
757
        public Object getValue() {
758
            HashEntry current = currentEntry();
759
            if (current == null) {
760
                throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID);
761
            }
762
            return current.getValue();
763
        }
764
765
        public Object setValue(Object value) {
766
            HashEntry current = currentEntry();
767
            if (current == null) {
768
                throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID);
769
            }
770
            return current.setValue(value);
771
        }
772
    }
773
    
774
    //-----------------------------------------------------------------------    
775
    /**
776
     * Gets the entrySet view of the map.
777
     * Changes made to the view affect this map.
778
     * To simply iterate through the entries, use {@link #mapIterator()}.
779
     * 
780
     * @return the entrySet view
781
     */
782
    public Set entrySet() {
783
        if (entrySet == null) {
784
            entrySet = new EntrySet(this);
785
        }
786
        return entrySet;
787
    }
788
    
789
    /**
790
     * Creates an entry set iterator.
791
     * Subclasses can override this to return iterators with different properties.
792
     * 
793
     * @return the entrySet iterator
794
     */
795
    protected Iterator createEntrySetIterator() {
796
        if (size() == 0) {
797
            return EmptyIterator.INSTANCE;
798
        }
799
        return new EntrySetIterator(this);
800
    }
801
802
    /**
803
     * EntrySet implementation.
804
     */
805
    protected static class EntrySet extends AbstractSet {
806
        /** The parent map */
807
        protected final AbstractHashedMap parent;
808
        
809
        protected EntrySet(AbstractHashedMap parent) {
810
            super();
811
            this.parent = parent;
812
        }
813
814
        public int size() {
815
            return parent.size();
816
        }
817
        
818
        public void clear() {
819
            parent.clear();
820
        }
821
        
822
        public boolean contains(Object entry) {
823
            if (entry instanceof Map.Entry) {
824
                Map.Entry e = (Map.Entry) entry;
825
                Entry match = parent.getEntry(e.getKey());
826
                return (match != null && match.equals(e));
827
            }
828
            return false;
829
        }
830
        
831
        public boolean remove(Object obj) {
832
            if (obj instanceof Map.Entry == false) {
833
                return false;
834
            }
835
            if (contains(obj) == false) {
836
                return false;
837
            }
838
            Map.Entry entry = (Map.Entry) obj;
839
            Object key = entry.getKey();
840
            parent.remove(key);
841
            return true;
842
        }
843
844
        public Iterator iterator() {
845
            return parent.createEntrySetIterator();
846
        }
847
    }
848
849
    /**
850
     * EntrySet iterator.
851
     */
852
    protected static class EntrySetIterator extends HashIterator {
853
        
854
        protected EntrySetIterator(AbstractHashedMap parent) {
855
            super(parent);
856
        }
857
858
        public Object next() {
859
            return super.nextEntry();
860
        }
861
    }
862
863
    //-----------------------------------------------------------------------    
864
    /**
865
     * Gets the keySet view of the map.
866
     * Changes made to the view affect this map.
867
     * To simply iterate through the keys, use {@link #mapIterator()}.
868
     * 
869
     * @return the keySet view
870
     */
871
    public Set keySet() {
872
        if (keySet == null) {
873
            keySet = new KeySet(this);
874
        }
875
        return keySet;
876
    }
877
878
    /**
879
     * Creates a key set iterator.
880
     * Subclasses can override this to return iterators with different properties.
881
     * 
882
     * @return the keySet iterator
883
     */
884
    protected Iterator createKeySetIterator() {
885
        if (size() == 0) {
886
            return EmptyIterator.INSTANCE;
887
        }
888
        return new KeySetIterator(this);
889
    }
890
891
    /**
892
     * KeySet implementation.
893
     */
894
    protected static class KeySet extends AbstractSet {
895
        /** The parent map */
896
        protected final AbstractHashedMap parent;
897
        
898
        protected KeySet(AbstractHashedMap parent) {
899
            super();
900
            this.parent = parent;
901
        }
902
903
        public int size() {
904
            return parent.size();
905
        }
906
        
907
        public void clear() {
908
            parent.clear();
909
        }
910
        
911
        public boolean contains(Object key) {
912
            return parent.containsKey(key);
913
        }
914
        
915
        public boolean remove(Object key) {
916
            boolean result = parent.containsKey(key);
917
            parent.remove(key);
918
            return result;
919
        }
920
921
        public Iterator iterator() {
922
            return parent.createKeySetIterator();
923
        }
924
    }
925
926
    /**
927
     * KeySet iterator.
928
     */
929
    protected static class KeySetIterator extends EntrySetIterator {
930
        
931
        protected KeySetIterator(AbstractHashedMap parent) {
932
            super(parent);
933
        }
934
935
        public Object next() {
936
            return super.nextEntry().getKey();
937
        }
938
    }
939
    
940
    //-----------------------------------------------------------------------    
941
    /**
942
     * Gets the values view of the map.
943
     * Changes made to the view affect this map.
944
     * To simply iterate through the values, use {@link #mapIterator()}.
945
     * 
946
     * @return the values view
947
     */
948
    public Collection values() {
949
        if (values == null) {
950
            values = new Values(this);
951
        }
952
        return values;
953
    }
954
955
    /**
956
     * Creates a values iterator.
957
     * Subclasses can override this to return iterators with different properties.
958
     * 
959
     * @return the values iterator
960
     */
961
    protected Iterator createValuesIterator() {
962
        if (size() == 0) {
963
            return EmptyIterator.INSTANCE;
964
        }
965
        return new ValuesIterator(this);
966
    }
967
968
    /**
969
     * Values implementation.
970
     */
971
    protected static class Values extends AbstractCollection {
972
        /** The parent map */
973
        protected final AbstractHashedMap parent;
974
        
975
        protected Values(AbstractHashedMap parent) {
976
            super();
977
            this.parent = parent;
978
        }
979
980
        public int size() {
981
            return parent.size();
982
        }
983
        
984
        public void clear() {
985
            parent.clear();
986
        }
987
        
988
        public boolean contains(Object value) {
989
            return parent.containsValue(value);
990
        }
991
        
992
        public Iterator iterator() {
993
            return parent.createValuesIterator();
994
        }
995
    }
996
997
    /**
998
     * Values iterator.
999
     */
1000
    protected static class ValuesIterator extends HashIterator {
1001
        
1002
        protected ValuesIterator(AbstractHashedMap parent) {
1003
            super(parent);
1004
        }
1005
1006
        public Object next() {
1007
            return super.nextEntry().getValue();
1008
        }
1009
    }
1010
    
1011
    //-----------------------------------------------------------------------
1012
    /**
1013
     * HashEntry used to store the data.
1014
     * <p>
1015
     * If you subclass <code>AbstractHashedMap</code> but not <code>HashEntry</code>
1016
     * then you will not be able to access the protected fields.
1017
     * The <code>entryXxx()</code> methods on <code>AbstractHashedMap</code> exist
1018
     * to provide the necessary access.
1019
     */
1020
    protected static class HashEntry implements Map.Entry, KeyValue {
1021
        /** The next entry in the hash chain */
1022
        protected HashEntry next;
1023
        /** The hash code of the key */
1024
        protected int hashCode;
1025
        /** The key */
1026
        protected Object key;
1027
        /** The value */
1028
        protected Object value;
1029
        
1030
        protected HashEntry(HashEntry next, int hashCode, Object key, Object value) {
1031
            super();
1032
            this.next = next;
1033
            this.hashCode = hashCode;
1034
            this.key = key;
1035
            this.value = value;
1036
        }
1037
        
1038
        public Object getKey() {
1039
            return (key == NULL ? null : key);
1040
        }
1041
        
1042
        public Object getValue() {
1043
            return value;
1044
        }
1045
        
1046
        public Object setValue(Object value) {
1047
            Object old = this.value;
1048
            this.value = value;
1049
            return old;
1050
        }
1051
        
1052
        public boolean equals(Object obj) {
1053
            if (obj == this) {
1054
                return true;
1055
            }
1056
            if (obj instanceof Map.Entry == false) {
1057
                return false;
1058
            }
1059
            Map.Entry other = (Map.Entry) obj;
1060
            return
1061
                (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey())) &&
1062
                (getValue() == null ? other.getValue() == null : getValue().equals(other.getValue()));
1063
        }
1064
        
1065
        public int hashCode() {
1066
            return (getKey() == null ? 0 : getKey().hashCode()) ^
1067
                   (getValue() == null ? 0 : getValue().hashCode()); 
1068
        }
1069
        
1070
        public String toString() {
1071
            return new StringBuffer().append(getKey()).append('=').append(getValue()).toString();
1072
        }
1073
    }
1074
    
1075
    /**
1076
     * Base Iterator
1077
     */
1078
    protected static abstract class HashIterator implements Iterator {
1079
        
1080
        /** The parent map */
1081
        protected final AbstractHashedMap parent;
1082
        /** The current index into the array of buckets */
1083
        protected int hashIndex;
1084
        /** The last returned entry */
1085
        protected HashEntry last;
1086
        /** The next entry */
1087
        protected HashEntry next;
1088
        /** The modification count expected */
1089
        protected int expectedModCount;
1090
        
1091
        protected HashIterator(AbstractHashedMap parent) {
1092
            super();
1093
            this.parent = parent;
1094
            HashEntry[] data = parent.data;
1095
            int i = data.length;
1096
            HashEntry next = null;
1097
            while (i > 0 && next == null) {
1098
                next = data[--i];
1099
            }
1100
            this.next = next;
1101
            this.hashIndex = i;
1102
            this.expectedModCount = parent.modCount;
1103
        }
1104
1105
        public boolean hasNext() {
1106
            return (next != null);
1107
        }
1108
1109
        protected HashEntry nextEntry() { 
1110
            if (parent.modCount != expectedModCount) {
1111
                throw new ConcurrentModificationException();
1112
            }
1113
            HashEntry newCurrent = next;
1114
            if (newCurrent == null)  {
1115
                throw new NoSuchElementException(AbstractHashedMap.NO_NEXT_ENTRY);
1116
            }
1117
            HashEntry[] data = parent.data;
1118
            int i = hashIndex;
1119
            HashEntry n = newCurrent.next;
1120
            while (n == null && i > 0) {
1121
                n = data[--i];
1122
            }
1123
            next = n;
1124
            hashIndex = i;
1125
            last = newCurrent;
1126
            return newCurrent;
1127
        }
1128
1129
        protected HashEntry currentEntry() {
1130
            return last;
1131
        }
1132
        
1133
        public void remove() {
1134
            if (last == null) {
1135
                throw new IllegalStateException(AbstractHashedMap.REMOVE_INVALID);
1136
            }
1137
            if (parent.modCount != expectedModCount) {
1138
                throw new ConcurrentModificationException();
1139
            }
1140
            parent.remove(last.getKey());
1141
            last = null;
1142
            expectedModCount = parent.modCount;
1143
        }
1144
1145
        public String toString() {
1146
            if (last != null) {
1147
                return "Iterator[" + last.getKey() + "=" + last.getValue() + "]";
1148
            } else {
1149
                return "Iterator[]";
1150
            }
1151
        }
1152
    }
1153
    
1154
    //-----------------------------------------------------------------------
1155
    /**
1156
     * Writes the map data to the stream. This method must be overridden if a
1157
     * subclass must be setup before <code>put()</code> is used.
1158
     * <p>
1159
     * Serialization is not one of the JDK's nicest topics. Normal serialization will
1160
     * initialise the superclass before the subclass. Sometimes however, this isn't
1161
     * what you want, as in this case the <code>put()</code> method on read can be
1162
     * affected by subclass state.
1163
     * <p>
1164
     * The solution adopted here is to serialize the state data of this class in
1165
     * this protected method. This method must be called by the
1166
     * <code>writeObject()</code> of the first serializable subclass.
1167
     * <p>
1168
     * Subclasses may override if they have a specific field that must be present
1169
     * on read before this implementation will work. Generally, the read determines
1170
     * what must be serialized here, if anything.
1171
     * 
1172
     * @param out  the output stream
1173
     */
1174
    protected void doWriteObject(ObjectOutputStream out) throws IOException {
1175
        out.writeFloat(loadFactor);
1176
        out.writeInt(data.length);
1177
        out.writeInt(size);
1178
        for (MapIterator it = mapIterator(); it.hasNext();) {
1179
            out.writeObject(it.next());
1180
            out.writeObject(it.getValue());
1181
        }
1182
    }
1183
1184
    /**
1185
     * Reads the map data from the stream. This method must be overridden if a
1186
     * subclass must be setup before <code>put()</code> is used.
1187
     * <p>
1188
     * Serialization is not one of the JDK's nicest topics. Normal serialization will
1189
     * initialise the superclass before the subclass. Sometimes however, this isn't
1190
     * what you want, as in this case the <code>put()</code> method on read can be
1191
     * affected by subclass state.
1192
     * <p>
1193
     * The solution adopted here is to deserialize the state data of this class in
1194
     * this protected method. This method must be called by the
1195
     * <code>readObject()</code> of the first serializable subclass.
1196
     * <p>
1197
     * Subclasses may override if the subclass has a specific field that must be present
1198
     * before <code>put()</code> or <code>calculateThreshold()</code> will work correctly.
1199
     * 
1200
     * @param in  the input stream
1201
     */
1202
    protected void doReadObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
1203
        loadFactor = in.readFloat();
1204
        int capacity = in.readInt();
1205
        int size = in.readInt();
1206
        init();
1207
        data = new HashEntry[capacity];
1208
        for (int i = 0; i < size; i++) {
1209
            Object key = in.readObject();
1210
            Object value = in.readObject();
1211
            put(key, value);
1212
        }
1213
        threshold = calculateThreshold(data.length, loadFactor);
1214
    }
1215
    
1216
    //-----------------------------------------------------------------------
1217
    /**
1218
     * Clones the map without cloning the keys or values.
1219
     * <p>
1220
     * To implement <code>clone()</code>, a subclass must implement the
1221
     * <code>Cloneable</code> interface and make this method public.
1222
     *
1223
     * @return a shallow clone
1224
     */
1225
    protected Object clone() {
1226
        try {
1227
            AbstractHashedMap cloned = (AbstractHashedMap) super.clone();
1228
            cloned.data = new HashEntry[data.length];
1229
            cloned.entrySet = null;
1230
            cloned.keySet = null;
1231
            cloned.values = null;
1232
            cloned.modCount = 0;
1233
            cloned.size = 0;
1234
            cloned.init();
1235
            cloned.putAll(this);
1236
            return cloned;
1237
            
1238
        } catch (CloneNotSupportedException ex) {
1239
            return null;  // should never happen
1240
        }
1241
    }
1242
    
1243
    /**
1244
     * Compares this map with another.
1245
     * 
1246
     * @param obj  the object to compare to
1247
     * @return true if equal
1248
     */
1249
    public boolean equals(Object obj) {
1250
        if (obj == this) {
1251
            return true;
1252
        }
1253
        if (obj instanceof Map == false) {
1254
            return false;
1255
        }
1256
        Map map = (Map) obj;
1257
        if (map.size() != size()) {
1258
            return false;
1259
        }
1260
        MapIterator it = mapIterator();
1261
        try {
1262
            while (it.hasNext()) {
1263
                Object key = it.next();
1264
                Object value = it.getValue();
1265
                if (value == null) {
1266
                    if (map.get(key) != null || map.containsKey(key) == false) {
1267
                        return false;
1268
                    }
1269
                } else {
1270
                    if (value.equals(map.get(key)) == false) {
1271
                        return false;
1272
                    }
1273
                }
1274
            }
1275
        } catch (ClassCastException ignored)   {
1276
            return false;
1277
        } catch (NullPointerException ignored) {
1278
            return false;
1279
        }
1280
        return true;
1281
    }
1282
1283
    /**
1284
     * Gets the standard Map hashCode.
1285
     * 
1286
     * @return the hash code defined in the Map interface
1287
     */
1288
    public int hashCode() {
1289
        int total = 0;
1290
        Iterator it = createEntrySetIterator();
1291
        while (it.hasNext()) {
1292
            total += it.next().hashCode();
1293
        }
1294
        return total;
1295
    }
1296
1297
    /**
1298
     * Gets the map as a String.
1299
     * 
1300
     * @return a string version of the map
1301
     */
1302
    public String toString() {
1303
        if (size() == 0) {
1304
            return "{}";
1305
        }
1306
        StringBuffer buf = new StringBuffer(32 * size());
1307
        buf.append('{');
1308
1309
        MapIterator it = mapIterator();
1310
        boolean hasNext = it.hasNext();
1311
        while (hasNext) {
1312
            Object key = it.next();
1313
            Object value = it.getValue();
1314
            buf.append(key == this ? "(this Map)" : key)
1315
               .append('=')
1316
               .append(value == this ? "(this Map)" : value);
1317
1318
            hasNext = it.hasNext();
1319
            if (hasNext) {
1320
                buf.append(',').append(' ');
1321
            }
1322
        }
1323
1324
        buf.append('}');
1325
        return buf.toString();
1326
    }
1327
}
(-)extra/collections/BoundedMap.java (+48 lines)
Line 0 Link Here
1
/*
2
 *  Copyright 2003-2004 The Apache Software Foundation
3
 *
4
 *  Licensed under the Apache License, Version 2.0 (the "License");
5
 *  you may not use this file except in compliance with the License.
6
 *  You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *  Unless required by applicable law or agreed to in writing, software
11
 *  distributed under the License is distributed on an "AS IS" BASIS,
12
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *  See the License for the specific language governing permissions and
14
 *  limitations under the License.
15
 */
16
package org.apache.taglibs.standard.extra.commons.collections;
17
18
import java.util.Map;
19
20
/**
21
 * Defines a map that is bounded in size.
22
 * <p>
23
 * The size of the map can vary, but it can never exceed a preset 
24
 * maximum number of elements. This interface allows the querying of details
25
 * associated with the maximum number of elements.
26
 *
27
 * @since Commons Collections 3.0
28
 * @version $Revision: 218351 $ $Date: 2004-10-20 17:58:23 -0700 (Wed, 20 Oct 2004) $
29
 * 
30
 * @author Stephen Colebourne
31
 */
32
public interface BoundedMap extends Map {
33
34
    /**
35
     * Returns true if this map is full and no new elements can be added.
36
     *
37
     * @return <code>true</code> if the map is full
38
     */
39
    boolean isFull();
40
41
    /**
42
     * Gets the maximum size of the map (the bound).
43
     *
44
     * @return the maximum number of elements the map can hold
45
     */
46
    int maxSize();
47
48
}
(-)extra/collections/OrderedIterator.java (+47 lines)
Line 0 Link Here
1
/*
2
 *  Copyright 2003-2004 The Apache Software Foundation
3
 *
4
 *  Licensed under the Apache License, Version 2.0 (the "License");
5
 *  you may not use this file except in compliance with the License.
6
 *  You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 *  Unless required by applicable law or agreed to in writing, software
11
 *  distributed under the License is distributed on an "AS IS" BASIS,
12
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 *  See the License for the specific language governing permissions and
14
 *  limitations under the License.
15
 */
16
package org.apache.taglibs.standard.extra.commons.collections;
17
18
import java.util.Iterator;
19
20
/**
21
 * Defines an iterator that operates over an ordered collection.
22
 * <p>
23
 * This iterator allows both forward and reverse iteration through the collection.
24
 *  
25
 * @since Commons Collections 3.0
26
 * @version $Revision: 218351 $ $Date: 2004-10-20 17:58:23 -0700 (Wed, 20 Oct 2004) $
27
 *
28
 * @author Stephen Colebourne
29
 */
30
public interface OrderedIterator extends Iterator {
31
32
    /**
33
     * Checks to see if there is a previous element that can be iterated to.
34
     *
35
     * @return <code>true</code> if the iterator has a previous element
36
     */
37
    boolean hasPrevious();
38
39
    /**
40
     * Gets the previous element from the collection.
41
     *
42
     * @return the previous element in the iteration
43
     * @throws java.util.NoSuchElementException if the iteration is finished
44
     */
45
    Object previous();
46
47
}

Return to bug 31789