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.Set;
030: import java.util.SortedMap;
031: import java.util.SortedSet;
032: import java.util.TreeMap;
033: import java.util.TreeSet;
034: import java.util.concurrent.ConcurrentHashMap;
035:
036: import org.apache.commons.collections.map.CaseInsensitiveMap;
037: import org.apache.commons.collections.map.IdentityMap;
038: import org.apache.commons.collections.map.LinkedMap;
039: import org.apache.commons.collections.map.ListOrderedMap;
040: import org.apache.commons.collections.set.ListOrderedSet;
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 JDK 1.4+ extended collections
048: * and Commons Collection 3.x's corresponding versions for older JDKs,
049: * as well as JDK 1.5+ concurrent collections and backport-concurrent
050: * versions of those. Mainly for internal use within the framework.
051: *
052: * <p>The goal of this class is to avoid runtime dependencies on JDK 1.4+
053: * or Commons Collections 3.x, simply using the best collection implementation
054: * that is available. Prefers JDK 1.4+ collection implementations to Commons
055: * Collections 3.x versions, falling back to JDK 1.3 collections as worst case.
056: *
057: * @author Juergen Hoeller
058: * @since 1.1.1
059: * @see #createLinkedSetIfPossible
060: * @see #createLinkedMapIfPossible
061: * @see #createLinkedCaseInsensitiveMapIfPossible
062: * @see #createIdentityMapIfPossible
063: */
064: public abstract class CollectionFactory {
065:
066: private static final Log logger = LogFactory
067: .getLog(CollectionFactory.class);
068:
069: /** Whether the Commons Collections 3.x library is present on the classpath */
070: private static final boolean commonsCollections3Available = ClassUtils
071: .isPresent("org.apache.commons.collections.map.LinkedMap",
072: CollectionFactory.class.getClassLoader());
073:
074: /** Whether the backport-concurrent library is present on the classpath */
075: private static final boolean backportConcurrentAvailable = ClassUtils
076: .isPresent(
077: "edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap",
078: CollectionFactory.class.getClassLoader());
079:
080: /**
081: * Create a linked set if possible: that is, if running on JDK >= 1.4
082: * or if Commons Collections 3.x is available. Prefers a JDK 1.4+
083: * LinkedHashSet to a Commons Collections 3.x ListOrderedSet.
084: * @param initialCapacity the initial capacity of the set
085: * @return the new set instance
086: * @see java.util.LinkedHashSet
087: * @see org.apache.commons.collections.set.ListOrderedSet
088: */
089: public static Set createLinkedSetIfPossible(int initialCapacity) {
090: if (JdkVersion.isAtLeastJava14()) {
091: logger.trace("Creating [java.util.LinkedHashSet]");
092: return JdkCollectionFactory
093: .createLinkedHashSet(initialCapacity);
094: } else if (commonsCollections3Available) {
095: logger
096: .trace("Creating [org.apache.commons.collections.set.ListOrderedSet]");
097: return CommonsCollectionFactory
098: .createListOrderedSet(initialCapacity);
099: } else {
100: logger
101: .debug("Falling back to plain [java.util.HashSet] for linked set");
102: return new HashSet(initialCapacity);
103: }
104: }
105:
106: /**
107: * Create a linked map if possible: that is, if running on JDK >= 1.4
108: * or if Commons Collections 3.x is available. Prefers a JDK 1.4+
109: * LinkedHashMap to a Commons Collections 3.x LinkedMap.
110: * @param initialCapacity the initial capacity of the map
111: * @return the new map instance
112: * @see java.util.LinkedHashMap
113: * @see org.apache.commons.collections.map.LinkedMap
114: */
115: public static Map createLinkedMapIfPossible(int initialCapacity) {
116: if (JdkVersion.isAtLeastJava14()) {
117: logger.trace("Creating [java.util.LinkedHashMap]");
118: return JdkCollectionFactory
119: .createLinkedHashMap(initialCapacity);
120: } else if (commonsCollections3Available) {
121: logger
122: .trace("Creating [org.apache.commons.collections.map.LinkedMap]");
123: return CommonsCollectionFactory
124: .createLinkedMap(initialCapacity);
125: } else {
126: logger
127: .debug("Falling back to plain[java.util.HashMap] for linked map");
128: return new HashMap(initialCapacity);
129: }
130: }
131:
132: /**
133: * Create a linked case-insensitive map if possible: if Commons Collections
134: * 3.x is available, a CaseInsensitiveMap with ListOrderedMap decorator will
135: * be created. Else, a JDK 1.4+ LinkedHashMap or plain HashMap will be used.
136: * @param initialCapacity the initial capacity of the map
137: * @return the new map instance
138: * @see org.apache.commons.collections.map.CaseInsensitiveMap
139: * @see org.apache.commons.collections.map.ListOrderedMap
140: */
141: public static Map createLinkedCaseInsensitiveMapIfPossible(
142: int initialCapacity) {
143: if (commonsCollections3Available) {
144: logger
145: .trace("Creating [org.apache.commons.collections.map.ListOrderedMap/CaseInsensitiveMap]");
146: return CommonsCollectionFactory
147: .createListOrderedCaseInsensitiveMap(initialCapacity);
148: } else if (JdkVersion.isAtLeastJava14()) {
149: logger
150: .debug("Falling back to [java.util.LinkedHashMap] for linked case-insensitive map");
151: return JdkCollectionFactory
152: .createLinkedHashMap(initialCapacity);
153: } else {
154: logger
155: .debug("Falling back to plain [java.util.HashMap] for linked case-insensitive map");
156: return new HashMap(initialCapacity);
157: }
158: }
159:
160: /**
161: * Create an identity map if possible: that is, if running on JDK >= 1.4
162: * or if Commons Collections 3.x is available. Prefers a JDK 1.4+
163: * IdentityHashMap to a Commons Collections 3.x IdentityMap.
164: * @param initialCapacity the initial capacity of the map
165: * @return the new map instance
166: * @see java.util.IdentityHashMap
167: * @see org.apache.commons.collections.map.IdentityMap
168: */
169: public static Map createIdentityMapIfPossible(int initialCapacity) {
170: if (JdkVersion.isAtLeastJava14()) {
171: logger.trace("Creating [java.util.IdentityHashMap]");
172: return JdkCollectionFactory
173: .createIdentityHashMap(initialCapacity);
174: } else if (commonsCollections3Available) {
175: logger
176: .trace("Creating [org.apache.commons.collections.map.IdentityMap]");
177: return CommonsCollectionFactory
178: .createIdentityMap(initialCapacity);
179: } else {
180: logger
181: .debug("Falling back to plain [java.util.HashMap] for identity map");
182: return new HashMap(initialCapacity);
183: }
184: }
185:
186: /**
187: * Create a concurrent map if possible: that is, if running on JDK >= 1.5
188: * or if the backport-concurrent library is available. Prefers a JDK 1.5+
189: * ConcurrentHashMap to its backport-concurrent equivalent.
190: * @param initialCapacity the initial capacity of the map
191: * @return the new map instance
192: * @see java.util.concurrent.ConcurrentHashMap
193: * @see edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap
194: */
195: public static Map createConcurrentMapIfPossible(int initialCapacity) {
196: if (JdkVersion.isAtLeastJava15()) {
197: logger
198: .trace("Creating [java.util.concurrent.ConcurrentHashMap]");
199: return JdkCollectionFactory
200: .createConcurrentHashMap(initialCapacity);
201: } else if (backportConcurrentAvailable) {
202: logger
203: .trace("Creating [edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap]");
204: return BackportConcurrentCollectionFactory
205: .createConcurrentHashMap(initialCapacity);
206: } else {
207: logger
208: .debug("Falling back to plain synchronized [java.util.HashMap] for concurrent map");
209: return Collections.synchronizedMap(new HashMap(
210: initialCapacity));
211: }
212: }
213:
214: /**
215: * Create the most approximate collection for the given collection.
216: * <p>Creates an ArrayList, TreeSet or linked Set for a List, SortedSet
217: * or Set, respectively.
218: * @param collection the original collection object
219: * @param initialCapacity the initial capacity
220: * @return the new collection instance
221: * @see java.util.ArrayList
222: * @see java.util.TreeSet
223: * @see #createLinkedSetIfPossible
224: */
225: public static Collection createApproximateCollection(
226: Object collection, int initialCapacity) {
227: if (collection instanceof List) {
228: return new ArrayList(initialCapacity);
229: } else if (collection instanceof SortedSet) {
230: return new TreeSet(((SortedSet) collection).comparator());
231: } else {
232: return createLinkedSetIfPossible(initialCapacity);
233: }
234: }
235:
236: /**
237: * Create the most approximate map for the given map.
238: * <p>Creates a TreeMap or linked Map for a SortedMap or Map, respectively.
239: * @param map the original map object
240: * @param initialCapacity the initial capacity
241: * @return the new collection instance
242: * @see java.util.TreeMap
243: * @see #createLinkedMapIfPossible
244: */
245: public static Map createApproximateMap(Object map,
246: int initialCapacity) {
247: if (map instanceof SortedMap) {
248: return new TreeMap(((SortedMap) map).comparator());
249: } else {
250: return createLinkedMapIfPossible(initialCapacity);
251: }
252: }
253:
254: /**
255: * Actual creation of JDK 1.4+ Collections.
256: * In separate inner class to avoid runtime dependency on JDK 1.4+.
257: */
258: private static abstract class JdkCollectionFactory {
259:
260: private static Set createLinkedHashSet(int initialCapacity) {
261: return new LinkedHashSet(initialCapacity);
262: }
263:
264: private static Map createLinkedHashMap(int initialCapacity) {
265: return new LinkedHashMap(initialCapacity);
266: }
267:
268: private static Map createIdentityHashMap(int initialCapacity) {
269: return new IdentityHashMap(initialCapacity);
270: }
271:
272: private static Map createConcurrentHashMap(int initialCapacity) {
273: return new ConcurrentHashMap(initialCapacity);
274: }
275: }
276:
277: /**
278: * Actual creation of Commons Collections.
279: * In separate inner class to avoid runtime dependency on Commons Collections 3.x.
280: */
281: private static abstract class CommonsCollectionFactory {
282:
283: private static Set createListOrderedSet(int initialCapacity) {
284: return ListOrderedSet
285: .decorate(new HashSet(initialCapacity));
286: }
287:
288: private static Map createLinkedMap(int initialCapacity) {
289: // Commons Collections does not support initial capacity of 0.
290: return new LinkedMap(initialCapacity == 0 ? 1
291: : initialCapacity);
292: }
293:
294: private static Map createListOrderedCaseInsensitiveMap(
295: int initialCapacity) {
296: // Commons Collections does not support initial capacity of 0.
297: return ListOrderedMap.decorate(new CaseInsensitiveMap(
298: initialCapacity == 0 ? 1 : initialCapacity));
299: }
300:
301: private static Map createIdentityMap(int initialCapacity) {
302: // Commons Collections does not support initial capacity of 0.
303: return new IdentityMap(initialCapacity == 0 ? 1
304: : initialCapacity);
305: }
306: }
307:
308: /**
309: * Actual creation of backport-concurrent Collections.
310: * In separate inner class to avoid runtime dependency on the backport-concurrent library.
311: */
312: private static abstract class BackportConcurrentCollectionFactory {
313:
314: private static Map createConcurrentHashMap(int initialCapacity) {
315: return new edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap(
316: initialCapacity);
317: }
318: }
319:
320: }
|