001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.core;
018:
019: import java.util.ArrayList;
020: import java.util.Collection;
021: import java.util.Collections;
022: import java.util.HashMap;
023: import java.util.HashSet;
024: import java.util.IdentityHashMap;
025: import java.util.LinkedHashMap;
026: import java.util.LinkedHashSet;
027: import java.util.List;
028: import java.util.Map;
029: import java.util.NavigableMap;
030: import java.util.NavigableSet;
031: import java.util.Set;
032: import java.util.SortedMap;
033: import java.util.SortedSet;
034: import java.util.TreeMap;
035: import java.util.TreeSet;
036: import java.util.concurrent.ConcurrentHashMap;
037: import java.util.concurrent.CopyOnWriteArraySet;
038:
039: import org.apache.commons.collections.map.CaseInsensitiveMap;
040: import org.apache.commons.collections.map.ListOrderedMap;
041: import org.apache.commons.logging.Log;
042: import org.apache.commons.logging.LogFactory;
043:
044: import org.springframework.util.ClassUtils;
045:
046: /**
047: * Factory for collections, being aware of Commons Collection 3.x's extended
048: * collections as well as of JDK 1.5+ concurrent collections and backport-concurrent
049: * collections. Mainly for internal use within the framework.
050: *
051: * <p>The goal of this class is to avoid runtime dependencies on JDK 1.5+ and
052: * Commons Collections 3.x, simply using the best collection implementation
053: * that is available at runtime. As of Spring 2.5, JDK 1.4 is required,
054: * so former adapter methods for JDK 1.3/1.4 always return the JDK 1.4
055: * collections now. The adapter methods are still kept for supporting
056: * Spring-based applications/frameworks which were built to support JDK 1.3.
057: *
058: * @author Juergen Hoeller
059: * @since 1.1.1
060: */
061: public abstract class CollectionFactory {
062:
063: private static final Log logger = LogFactory
064: .getLog(CollectionFactory.class);
065:
066: /** Whether the Commons Collections 3.x library is present on the classpath */
067: private static final boolean commonsCollections3Available = ClassUtils
068: .isPresent(
069: "org.apache.commons.collections.map.CaseInsensitiveMap",
070: CollectionFactory.class.getClassLoader());
071:
072: /** Whether the backport-concurrent library is present on the classpath */
073: private static final boolean backportConcurrentAvailable = ClassUtils
074: .isPresent(
075: "edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap",
076: CollectionFactory.class.getClassLoader());
077:
078: private static final Set approximableCollectionTypes = new HashSet(
079: 5);
080:
081: private static final Set approximableMapTypes = new HashSet(3);
082:
083: static {
084: approximableCollectionTypes.add(Collection.class);
085: approximableCollectionTypes.add(List.class);
086: approximableCollectionTypes.add(Set.class);
087: approximableCollectionTypes.add(SortedSet.class);
088: approximableMapTypes.add(Map.class);
089: approximableMapTypes.add(SortedMap.class);
090: if (JdkVersion.isAtLeastJava16()) {
091: approximableCollectionTypes.add(NavigableSet.class);
092: approximableMapTypes.add(NavigableMap.class);
093: }
094: }
095:
096: /**
097: * Create a linked Set if possible: This implementation always
098: * creates a {@link java.util.LinkedHashSet}, since Spring 2.5
099: * requires JDK 1.4 anyway.
100: * @param initialCapacity the initial capacity of the Set
101: * @return the new Set instance
102: * @deprecated as of Spring 2.5, for usage on JDK 1.4 or higher
103: */
104: public static Set createLinkedSetIfPossible(int initialCapacity) {
105: return new LinkedHashSet(initialCapacity);
106: }
107:
108: /**
109: * Create a copy-on-write Set (allowing for synchronization-less iteration),
110: * requiring JDK >= 1.5 or the backport-concurrent library on the classpath.
111: * Prefers a JDK 1.5+ CopyOnWriteArraySet to its backport-concurrent equivalent.
112: * Throws an IllegalStateException if no copy-on-write Set is available.
113: * @return the new Set instance
114: * @throws IllegalStateException if no copy-on-write Set is available
115: * @see java.util.concurrent.ConcurrentHashMap
116: * @see edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap
117: */
118: public static Set createCopyOnWriteSet() {
119: if (JdkVersion.isAtLeastJava15()) {
120: logger
121: .trace("Creating [java.util.concurrent.CopyOnWriteArraySet]");
122: return JdkConcurrentCollectionFactory
123: .createCopyOnWriteArraySet();
124: } else if (backportConcurrentAvailable) {
125: logger
126: .trace("Creating [edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArraySet]");
127: return BackportConcurrentCollectionFactory
128: .createCopyOnWriteArraySet();
129: } else {
130: throw new IllegalStateException(
131: "Cannot create CopyOnWriteArraySet - "
132: + "neither JDK 1.5 nor backport-concurrent available on the classpath");
133: }
134: }
135:
136: /**
137: * Create a linked Map if possible: This implementation always
138: * creates a {@link java.util.LinkedHashMap}, since Spring 2.5
139: * requires JDK 1.4 anyway.
140: * @param initialCapacity the initial capacity of the Map
141: * @return the new Map instance
142: * @deprecated as of Spring 2.5, for usage on JDK 1.4 or higher
143: */
144: public static Map createLinkedMapIfPossible(int initialCapacity) {
145: return new LinkedHashMap(initialCapacity);
146: }
147:
148: /**
149: * Create a linked case-insensitive Map if possible: if Commons Collections
150: * 3.x is available, a CaseInsensitiveMap with ListOrderedMap decorator will
151: * be created. Else, a JDK {@link java.util.LinkedHashMap} will be used.
152: * @param initialCapacity the initial capacity of the Map
153: * @return the new Map instance
154: * @see org.apache.commons.collections.map.CaseInsensitiveMap
155: * @see org.apache.commons.collections.map.ListOrderedMap
156: */
157: public static Map createLinkedCaseInsensitiveMapIfPossible(
158: int initialCapacity) {
159: if (commonsCollections3Available) {
160: logger
161: .trace("Creating [org.apache.commons.collections.map.ListOrderedMap/CaseInsensitiveMap]");
162: return CommonsCollectionFactory
163: .createListOrderedCaseInsensitiveMap(initialCapacity);
164: } else {
165: logger
166: .debug("Falling back to [java.util.LinkedHashMap] for linked case-insensitive map");
167: return new LinkedHashMap(initialCapacity);
168: }
169: }
170:
171: /**
172: * Create an identity Map if possible: This implementation always
173: * creates a {@link java.util.IdentityHashMap}, since Spring 2.5
174: * requires JDK 1.4 anyway.
175: * @param initialCapacity the initial capacity of the Map
176: * @return the new Map instance
177: * @deprecated as of Spring 2.5, for usage on JDK 1.4 or higher
178: */
179: public static Map createIdentityMapIfPossible(int initialCapacity) {
180: return new IdentityHashMap(initialCapacity);
181: }
182:
183: /**
184: * Create a concurrent Map if possible: that is, if running on JDK >= 1.5
185: * or if the backport-concurrent library is available. Prefers a JDK 1.5+
186: * ConcurrentHashMap to its backport-concurrent equivalent. Falls back
187: * to a plain synchronized HashMap if no concurrent Map is available.
188: * @param initialCapacity the initial capacity of the Map
189: * @return the new Map instance
190: * @see java.util.concurrent.ConcurrentHashMap
191: * @see edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap
192: */
193: public static Map createConcurrentMapIfPossible(int initialCapacity) {
194: if (JdkVersion.isAtLeastJava15()) {
195: logger
196: .trace("Creating [java.util.concurrent.ConcurrentHashMap]");
197: return JdkConcurrentCollectionFactory
198: .createConcurrentHashMap(initialCapacity);
199: } else if (backportConcurrentAvailable) {
200: logger
201: .trace("Creating [edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap]");
202: return BackportConcurrentCollectionFactory
203: .createConcurrentHashMap(initialCapacity);
204: } else {
205: logger
206: .debug("Falling back to plain synchronized [java.util.HashMap] for concurrent map");
207: return Collections.synchronizedMap(new HashMap(
208: initialCapacity));
209: }
210: }
211:
212: /**
213: * Create a concurrent Map with a dedicated {@link ConcurrentMap} interface,
214: * requiring JDK >= 1.5 or the backport-concurrent library on the classpath.
215: * Prefers a JDK 1.5+ ConcurrentHashMap to its backport-concurrent equivalent.
216: * Throws an IllegalStateException if no concurrent Map is available.
217: * @param initialCapacity the initial capacity of the Map
218: * @return the new ConcurrentMap instance
219: * @throws IllegalStateException if no concurrent Map is available
220: * @see java.util.concurrent.ConcurrentHashMap
221: * @see edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap
222: */
223: public static ConcurrentMap createConcurrentMap(int initialCapacity) {
224: if (JdkVersion.isAtLeastJava15()) {
225: logger
226: .trace("Creating [java.util.concurrent.ConcurrentHashMap]");
227: return new JdkConcurrentHashMap(initialCapacity);
228: } else if (backportConcurrentAvailable) {
229: logger
230: .trace("Creating [edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap]");
231: return new BackportConcurrentHashMap(initialCapacity);
232: } else {
233: throw new IllegalStateException(
234: "Cannot create ConcurrentHashMap - "
235: + "neither JDK 1.5 nor backport-concurrent available on the classpath");
236: }
237: }
238:
239: /**
240: * Determine whether the given collection type is an approximable type,
241: * i.e. a type that {@link #createApproximateCollection} can approximate.
242: * @param collectionType the collection type to check
243: * @return <code>true</code> if the type is approximable,
244: * <code>false</code> if it is not
245: */
246: public static boolean isApproximableCollectionType(
247: Class collectionType) {
248: return (collectionType != null && approximableCollectionTypes
249: .contains(collectionType));
250: }
251:
252: /**
253: * Create the most approximate collection for the given collection.
254: * <p>Creates an ArrayList, TreeSet or linked Set for a List, SortedSet
255: * or Set, respectively.
256: * @param collection the original collection object
257: * @param initialCapacity the initial capacity
258: * @return the new collection instance
259: * @see java.util.ArrayList
260: * @see java.util.TreeSet
261: * @see java.util.LinkedHashSet
262: */
263: public static Collection createApproximateCollection(
264: Object collection, int initialCapacity) {
265: if (collection instanceof List) {
266: return new ArrayList(initialCapacity);
267: } else if (collection instanceof SortedSet) {
268: return new TreeSet(((SortedSet) collection).comparator());
269: } else {
270: return new LinkedHashSet(initialCapacity);
271: }
272: }
273:
274: /**
275: * Determine whether the given map type is an approximable type,
276: * i.e. a type that {@link #createApproximateMap} can approximate.
277: * @param mapType the map type to check
278: * @return <code>true</code> if the type is approximable,
279: * <code>false</code> if it is not
280: */
281: public static boolean isApproximableMapType(Class mapType) {
282: return (mapType != null && approximableMapTypes
283: .contains(mapType));
284: }
285:
286: /**
287: * Create the most approximate map for the given map.
288: * <p>Creates a TreeMap or linked Map for a SortedMap or Map, respectively.
289: * @param map the original map object
290: * @param initialCapacity the initial capacity
291: * @return the new collection instance
292: * @see java.util.TreeMap
293: * @see java.util.LinkedHashMap
294: */
295: public static Map createApproximateMap(Object map,
296: int initialCapacity) {
297: if (map instanceof SortedMap) {
298: return new TreeMap(((SortedMap) map).comparator());
299: } else {
300: return new LinkedHashMap(initialCapacity);
301: }
302: }
303:
304: /**
305: * Actual creation of Commons Collections.
306: * In separate inner class to avoid runtime dependency on Commons Collections 3.x.
307: */
308: private static abstract class CommonsCollectionFactory {
309:
310: private static Map createListOrderedCaseInsensitiveMap(
311: int initialCapacity) {
312: // Commons Collections does not support initial capacity of 0.
313: return ListOrderedMap.decorate(new CaseInsensitiveMap(
314: initialCapacity == 0 ? 1 : initialCapacity));
315: }
316: }
317:
318: /**
319: * Actual creation of JDK 1.5+ concurrent Collections.
320: * In separate inner class to avoid runtime dependency on JDK 1.5.
321: */
322: private static abstract class JdkConcurrentCollectionFactory {
323:
324: private static Set createCopyOnWriteArraySet() {
325: return new CopyOnWriteArraySet();
326: }
327:
328: private static Map createConcurrentHashMap(int initialCapacity) {
329: return new ConcurrentHashMap(initialCapacity);
330: }
331: }
332:
333: /**
334: * Actual creation of backport-concurrent Collections.
335: * In separate inner class to avoid runtime dependency on the backport-concurrent library.
336: */
337: private static abstract class BackportConcurrentCollectionFactory {
338:
339: private static Set createCopyOnWriteArraySet() {
340: return new edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArraySet();
341: }
342:
343: private static Map createConcurrentHashMap(int initialCapacity) {
344: return new edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap(
345: initialCapacity);
346: }
347: }
348:
349: /**
350: * ConcurrentMap adapter for the JDK ConcurrentHashMap class.
351: */
352: private static class JdkConcurrentHashMap extends ConcurrentHashMap
353: implements ConcurrentMap {
354:
355: public JdkConcurrentHashMap(int initialCapacity) {
356: super (initialCapacity);
357: }
358: }
359:
360: /**
361: * ConcurrentMap adapter for the backport-concurrent ConcurrentHashMap class.
362: */
363: private static class BackportConcurrentHashMap
364: extends
365: edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap
366: implements ConcurrentMap {
367:
368: public BackportConcurrentHashMap(int initialCapacity) {
369: super(initialCapacity);
370: }
371: }
372:
373: }
|