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.commons.collections;
  17. import java.io.PrintStream;
  18. import java.text.NumberFormat;
  19. import java.text.ParseException;
  20. import java.util.Collections;
  21. import java.util.Enumeration;
  22. import java.util.HashMap;
  23. import java.util.Iterator;
  24. import java.util.Map;
  25. import java.util.Properties;
  26. import java.util.ResourceBundle;
  27. import java.util.SortedMap;
  28. import java.util.TreeMap;
  29. import org.apache.commons.collections.map.FixedSizeMap;
  30. import org.apache.commons.collections.map.FixedSizeSortedMap;
  31. import org.apache.commons.collections.map.LazyMap;
  32. import org.apache.commons.collections.map.LazySortedMap;
  33. import org.apache.commons.collections.map.ListOrderedMap;
  34. import org.apache.commons.collections.map.PredicatedMap;
  35. import org.apache.commons.collections.map.PredicatedSortedMap;
  36. import org.apache.commons.collections.map.TransformedMap;
  37. import org.apache.commons.collections.map.TransformedSortedMap;
  38. import org.apache.commons.collections.map.TypedMap;
  39. import org.apache.commons.collections.map.TypedSortedMap;
  40. import org.apache.commons.collections.map.UnmodifiableMap;
  41. import org.apache.commons.collections.map.UnmodifiableSortedMap;
  42. /**
  43. * Provides utility methods and decorators for
  44. * {@link Map} and {@link SortedMap} instances.
  45. * <p>
  46. * It contains various type safe methods
  47. * as well as other useful features like deep copying.
  48. * <p>
  49. * It also provides the following decorators:
  50. *
  51. * <ul>
  52. * <li>{@link #fixedSizeMap(Map)}
  53. * <li>{@link #fixedSizeSortedMap(SortedMap)}
  54. * <li>{@link #lazyMap(Map,Factory)}
  55. * <li>{@link #lazyMap(Map,Transformer)}
  56. * <li>{@link #lazySortedMap(SortedMap,Factory)}
  57. * <li>{@link #lazySortedMap(SortedMap,Transformer)}
  58. * <li>{@link #predicatedMap(Map,Predicate,Predicate)}
  59. * <li>{@link #predicatedSortedMap(SortedMap,Predicate,Predicate)}
  60. * <li>{@link #transformedMap(Map, Transformer, Transformer)}
  61. * <li>{@link #transformedSortedMap(SortedMap, Transformer, Transformer)}
  62. * <li>{@link #typedMap(Map, Class, Class)}
  63. * <li>{@link #typedSortedMap(SortedMap, Class, Class)}
  64. * </ul>
  65. *
  66. * @since Commons Collections 1.0
  67. * @version $Revision: 1.46 $ $Date: 2004/04/21 20:34:11 $
  68. *
  69. * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
  70. * @author <a href="mailto:nissim@nksystems.com">Nissim Karpenstein</a>
  71. * @author <a href="mailto:knielsen@apache.org">Kasper Nielsen</a>
  72. * @author Paul Jack
  73. * @author Stephen Colebourne
  74. * @author Matthew Hawthorne
  75. * @author Arun Mammen Thomas
  76. * @author Janek Bogucki
  77. * @author Max Rydahl Andersen
  78. * @author <a href="mailto:equinus100@hotmail.com">Ashwin S</a>
  79. */
  80. public class MapUtils {
  81. /**
  82. * An empty unmodifiable map.
  83. * This was not provided in JDK1.2.
  84. */
  85. public static final Map EMPTY_MAP = UnmodifiableMap.decorate(new HashMap(1));
  86. /**
  87. * An empty unmodifiable sorted map.
  88. * This is not provided in the JDK.
  89. */
  90. public static final SortedMap EMPTY_SORTED_MAP = UnmodifiableSortedMap.decorate(new TreeMap());
  91. /**
  92. * String used to indent the verbose and debug Map prints.
  93. */
  94. private static final String INDENT_STRING = " ";
  95. /**
  96. * <code>MapUtils</code> should not normally be instantiated.
  97. */
  98. public MapUtils() {
  99. }
  100. // Type safe getters
  101. //-------------------------------------------------------------------------
  102. /**
  103. * Gets from a Map in a null-safe manner.
  104. *
  105. * @param map the map to use
  106. * @param key the key to look up
  107. * @return the value in the Map, <code>null</code> if null map input
  108. */
  109. public static Object getObject(final Map map, final Object key) {
  110. if (map != null) {
  111. return map.get(key);
  112. }
  113. return null;
  114. }
  115. /**
  116. * Gets a String from a Map in a null-safe manner.
  117. * <p>
  118. * The String is obtained via <code>toString</code>.
  119. *
  120. * @param map the map to use
  121. * @param key the key to look up
  122. * @return the value in the Map as a String, <code>null</code> if null map input
  123. */
  124. public static String getString(final Map map, final Object key) {
  125. if (map != null) {
  126. Object answer = map.get(key);
  127. if (answer != null) {
  128. return answer.toString();
  129. }
  130. }
  131. return null;
  132. }
  133. /**
  134. * Gets a Boolean from a Map in a null-safe manner.
  135. * <p>
  136. * If the value is a <code>Boolean</code> it is returned directly.
  137. * If the value is a <code>String</code> and it equals 'true' ignoring case
  138. * then <code>true</code> is returned, otherwise <code>false</code>.
  139. * If the value is a <code>Number</code> an integer zero value returns
  140. * <code>false</code> and non-zero returns <code>true</code>.
  141. * Otherwise, <code>null</code> is returned.
  142. *
  143. * @param map the map to use
  144. * @param key the key to look up
  145. * @return the value in the Map as a Boolean, <code>null</code> if null map input
  146. */
  147. public static Boolean getBoolean(final Map map, final Object key) {
  148. if (map != null) {
  149. Object answer = map.get(key);
  150. if (answer != null) {
  151. if (answer instanceof Boolean) {
  152. return (Boolean) answer;
  153. } else if (answer instanceof String) {
  154. return new Boolean((String) answer);
  155. } else if (answer instanceof Number) {
  156. Number n = (Number) answer;
  157. return (n.intValue() != 0) ? Boolean.TRUE : Boolean.FALSE;
  158. }
  159. }
  160. }
  161. return null;
  162. }
  163. /**
  164. * Gets a Number from a Map in a null-safe manner.
  165. * <p>
  166. * If the value is a <code>Number</code> it is returned directly.
  167. * If the value is a <code>String</code> it is converted using
  168. * {@link NumberFormat#parse(String)} on the system default formatter
  169. * returning <code>null</code> if the conversion fails.
  170. * Otherwise, <code>null</code> is returned.
  171. *
  172. * @param map the map to use
  173. * @param key the key to look up
  174. * @return the value in the Map as a Number, <code>null</code> if null map input
  175. */
  176. public static Number getNumber(final Map map, final Object key) {
  177. if (map != null) {
  178. Object answer = map.get(key);
  179. if (answer != null) {
  180. if (answer instanceof Number) {
  181. return (Number) answer;
  182. } else if (answer instanceof String) {
  183. try {
  184. String text = (String) answer;
  185. return NumberFormat.getInstance().parse(text);
  186. } catch (ParseException e) {
  187. logInfo(e);
  188. }
  189. }
  190. }
  191. }
  192. return null;
  193. }
  194. /**
  195. * Gets a Byte from a Map in a null-safe manner.
  196. * <p>
  197. * The Byte is obtained from the results of {@link #getNumber(Map,Object)}.
  198. *
  199. * @param map the map to use
  200. * @param key the key to look up
  201. * @return the value in the Map as a Byte, <code>null</code> if null map input
  202. */
  203. public static Byte getByte(final Map map, final Object key) {
  204. Number answer = getNumber(map, key);
  205. if (answer == null) {
  206. return null;
  207. } else if (answer instanceof Byte) {
  208. return (Byte) answer;
  209. }
  210. return new Byte(answer.byteValue());
  211. }
  212. /**
  213. * Gets a Short from a Map in a null-safe manner.
  214. * <p>
  215. * The Short is obtained from the results of {@link #getNumber(Map,Object)}.
  216. *
  217. * @param map the map to use
  218. * @param key the key to look up
  219. * @return the value in the Map as a Short, <code>null</code> if null map input
  220. */
  221. public static Short getShort(final Map map, final Object key) {
  222. Number answer = getNumber(map, key);
  223. if (answer == null) {
  224. return null;
  225. } else if (answer instanceof Short) {
  226. return (Short) answer;
  227. }
  228. return new Short(answer.shortValue());
  229. }
  230. /**
  231. * Gets a Integer from a Map in a null-safe manner.
  232. * <p>
  233. * The Integer is obtained from the results of {@link #getNumber(Map,Object)}.
  234. *
  235. * @param map the map to use
  236. * @param key the key to look up
  237. * @return the value in the Map as a Integer, <code>null</code> if null map input
  238. */
  239. public static Integer getInteger(final Map map, final Object key) {
  240. Number answer = getNumber(map, key);
  241. if (answer == null) {
  242. return null;
  243. } else if (answer instanceof Integer) {
  244. return (Integer) answer;
  245. }
  246. return new Integer(answer.intValue());
  247. }
  248. /**
  249. * Gets a Long from a Map in a null-safe manner.
  250. * <p>
  251. * The Long is obtained from the results of {@link #getNumber(Map,Object)}.
  252. *
  253. * @param map the map to use
  254. * @param key the key to look up
  255. * @return the value in the Map as a Long, <code>null</code> if null map input
  256. */
  257. public static Long getLong(final Map map, final Object key) {
  258. Number answer = getNumber(map, key);
  259. if (answer == null) {
  260. return null;
  261. } else if (answer instanceof Long) {
  262. return (Long) answer;
  263. }
  264. return new Long(answer.longValue());
  265. }
  266. /**
  267. * Gets a Float from a Map in a null-safe manner.
  268. * <p>
  269. * The Float is obtained from the results of {@link #getNumber(Map,Object)}.
  270. *
  271. * @param map the map to use
  272. * @param key the key to look up
  273. * @return the value in the Map as a Float, <code>null</code> if null map input
  274. */
  275. public static Float getFloat(final Map map, final Object key) {
  276. Number answer = getNumber(map, key);
  277. if (answer == null) {
  278. return null;
  279. } else if (answer instanceof Float) {
  280. return (Float) answer;
  281. }
  282. return new Float(answer.floatValue());
  283. }
  284. /**
  285. * Gets a Double from a Map in a null-safe manner.
  286. * <p>
  287. * The Double is obtained from the results of {@link #getNumber(Map,Object)}.
  288. *
  289. * @param map the map to use
  290. * @param key the key to look up
  291. * @return the value in the Map as a Double, <code>null</code> if null map input
  292. */
  293. public static Double getDouble(final Map map, final Object key) {
  294. Number answer = getNumber(map, key);
  295. if (answer == null) {
  296. return null;
  297. } else if (answer instanceof Double) {
  298. return (Double) answer;
  299. }
  300. return new Double(answer.doubleValue());
  301. }
  302. /**
  303. * Gets a Map from a Map in a null-safe manner.
  304. * <p>
  305. * If the value returned from the specified map is not a Map then
  306. * <code>null</code> is returned.
  307. *
  308. * @param map the map to use
  309. * @param key the key to look up
  310. * @return the value in the Map as a Map, <code>null</code> if null map input
  311. */
  312. public static Map getMap(final Map map, final Object key) {
  313. if (map != null) {
  314. Object answer = map.get(key);
  315. if (answer != null && answer instanceof Map) {
  316. return (Map) answer;
  317. }
  318. }
  319. return null;
  320. }
  321. // Type safe getters with default values
  322. //-------------------------------------------------------------------------
  323. /**
  324. * Looks up the given key in the given map, converting null into the
  325. * given default value.
  326. *
  327. * @param map the map whose value to look up
  328. * @param key the key of the value to look up in that map
  329. * @param defaultValue what to return if the value is null
  330. * @return the value in the map, or defaultValue if the original value
  331. * is null or the map is null
  332. */
  333. public static Object getObject( Map map, Object key, Object defaultValue ) {
  334. if ( map != null ) {
  335. Object answer = map.get( key );
  336. if ( answer != null ) {
  337. return answer;
  338. }
  339. }
  340. return defaultValue;
  341. }
  342. /**
  343. * Looks up the given key in the given map, converting the result into
  344. * a string, using the default value if the the conversion fails.
  345. *
  346. * @param map the map whose value to look up
  347. * @param key the key of the value to look up in that map
  348. * @param defaultValue what to return if the value is null or if the
  349. * conversion fails
  350. * @return the value in the map as a string, or defaultValue if the
  351. * original value is null, the map is null or the string conversion
  352. * fails
  353. */
  354. public static String getString( Map map, Object key, String defaultValue ) {
  355. String answer = getString( map, key );
  356. if ( answer == null ) {
  357. answer = defaultValue;
  358. }
  359. return answer;
  360. }
  361. /**
  362. * Looks up the given key in the given map, converting the result into
  363. * a boolean, using the default value if the the conversion fails.
  364. *
  365. * @param map the map whose value to look up
  366. * @param key the key of the value to look up in that map
  367. * @param defaultValue what to return if the value is null or if the
  368. * conversion fails
  369. * @return the value in the map as a boolean, or defaultValue if the
  370. * original value is null, the map is null or the boolean conversion
  371. * fails
  372. */
  373. public static Boolean getBoolean( Map map, Object key, Boolean defaultValue ) {
  374. Boolean answer = getBoolean( map, key );
  375. if ( answer == null ) {
  376. answer = defaultValue;
  377. }
  378. return answer;
  379. }
  380. /**
  381. * Looks up the given key in the given map, converting the result into
  382. * a number, using the default value if the the conversion fails.
  383. *
  384. * @param map the map whose value to look up
  385. * @param key the key of the value to look up in that map
  386. * @param defaultValue what to return if the value is null or if the
  387. * conversion fails
  388. * @return the value in the map as a number, or defaultValue if the
  389. * original value is null, the map is null or the number conversion
  390. * fails
  391. */
  392. public static Number getNumber( Map map, Object key, Number defaultValue ) {
  393. Number answer = getNumber( map, key );
  394. if ( answer == null ) {
  395. answer = defaultValue;
  396. }
  397. return answer;
  398. }
  399. /**
  400. * Looks up the given key in the given map, converting the result into
  401. * a byte, using the default value if the the conversion fails.
  402. *
  403. * @param map the map whose value to look up
  404. * @param key the key of the value to look up in that map
  405. * @param defaultValue what to return if the value is null or if the
  406. * conversion fails
  407. * @return the value in the map as a number, or defaultValue if the
  408. * original value is null, the map is null or the number conversion
  409. * fails
  410. */
  411. public static Byte getByte( Map map, Object key, Byte defaultValue ) {
  412. Byte answer = getByte( map, key );
  413. if ( answer == null ) {
  414. answer = defaultValue;
  415. }
  416. return answer;
  417. }
  418. /**
  419. * Looks up the given key in the given map, converting the result into
  420. * a short, using the default value if the the conversion fails.
  421. *
  422. * @param map the map whose value to look up
  423. * @param key the key of the value to look up in that map
  424. * @param defaultValue what to return if the value is null or if the
  425. * conversion fails
  426. * @return the value in the map as a number, or defaultValue if the
  427. * original value is null, the map is null or the number conversion
  428. * fails
  429. */
  430. public static Short getShort( Map map, Object key, Short defaultValue ) {
  431. Short answer = getShort( map, key );
  432. if ( answer == null ) {
  433. answer = defaultValue;
  434. }
  435. return answer;
  436. }
  437. /**
  438. * Looks up the given key in the given map, converting the result into
  439. * an integer, using the default value if the the conversion fails.
  440. *
  441. * @param map the map whose value to look up
  442. * @param key the key of the value to look up in that map
  443. * @param defaultValue what to return if the value is null or if the
  444. * conversion fails
  445. * @return the value in the map as a number, or defaultValue if the
  446. * original value is null, the map is null or the number conversion
  447. * fails
  448. */
  449. public static Integer getInteger( Map map, Object key, Integer defaultValue ) {
  450. Integer answer = getInteger( map, key );
  451. if ( answer == null ) {
  452. answer = defaultValue;
  453. }
  454. return answer;
  455. }
  456. /**
  457. * Looks up the given key in the given map, converting the result into
  458. * a long, using the default value if the the conversion fails.
  459. *
  460. * @param map the map whose value to look up
  461. * @param key the key of the value to look up in that map
  462. * @param defaultValue what to return if the value is null or if the
  463. * conversion fails
  464. * @return the value in the map as a number, or defaultValue if the
  465. * original value is null, the map is null or the number conversion
  466. * fails
  467. */
  468. public static Long getLong( Map map, Object key, Long defaultValue ) {
  469. Long answer = getLong( map, key );
  470. if ( answer == null ) {
  471. answer = defaultValue;
  472. }
  473. return answer;
  474. }
  475. /**
  476. * Looks up the given key in the given map, converting the result into
  477. * a float, using the default value if the the conversion fails.
  478. *
  479. * @param map the map whose value to look up
  480. * @param key the key of the value to look up in that map
  481. * @param defaultValue what to return if the value is null or if the
  482. * conversion fails
  483. * @return the value in the map as a number, or defaultValue if the
  484. * original value is null, the map is null or the number conversion
  485. * fails
  486. */
  487. public static Float getFloat( Map map, Object key, Float defaultValue ) {
  488. Float answer = getFloat( map, key );
  489. if ( answer == null ) {
  490. answer = defaultValue;
  491. }
  492. return answer;
  493. }
  494. /**
  495. * Looks up the given key in the given map, converting the result into
  496. * a double, using the default value if the the conversion fails.
  497. *
  498. * @param map the map whose value to look up
  499. * @param key the key of the value to look up in that map
  500. * @param defaultValue what to return if the value is null or if the
  501. * conversion fails
  502. * @return the value in the map as a number, or defaultValue if the
  503. * original value is null, the map is null or the number conversion
  504. * fails
  505. */
  506. public static Double getDouble( Map map, Object key, Double defaultValue ) {
  507. Double answer = getDouble( map, key );
  508. if ( answer == null ) {
  509. answer = defaultValue;
  510. }
  511. return answer;
  512. }
  513. /**
  514. * Looks up the given key in the given map, converting the result into
  515. * a map, using the default value if the the conversion fails.
  516. *
  517. * @param map the map whose value to look up
  518. * @param key the key of the value to look up in that map
  519. * @param defaultValue what to return if the value is null or if the
  520. * conversion fails
  521. * @return the value in the map as a number, or defaultValue if the
  522. * original value is null, the map is null or the map conversion
  523. * fails
  524. */
  525. public static Map getMap( Map map, Object key, Map defaultValue ) {
  526. Map answer = getMap( map, key );
  527. if ( answer == null ) {
  528. answer = defaultValue;
  529. }
  530. return answer;
  531. }
  532. // Type safe primitive getters
  533. //-------------------------------------------------------------------------
  534. /**
  535. * Gets a boolean from a Map in a null-safe manner.
  536. * <p>
  537. * If the value is a <code>Boolean</code> its value is returned.
  538. * If the value is a <code>String</code> and it equals 'true' ignoring case
  539. * then <code>true</code> is returned, otherwise <code>false</code>.
  540. * If the value is a <code>Number</code> an integer zero value returns
  541. * <code>false</code> and non-zero returns <code>true</code>.
  542. * Otherwise, <code>false</code> is returned.
  543. *
  544. * @param map the map to use
  545. * @param key the key to look up
  546. * @return the value in the Map as a Boolean, <code>false</code> if null map input
  547. */
  548. public static boolean getBooleanValue(final Map map, final Object key) {
  549. Boolean booleanObject = getBoolean(map, key);
  550. if (booleanObject == null) {
  551. return false;
  552. }
  553. return booleanObject.booleanValue();
  554. }
  555. /**
  556. * Gets a byte from a Map in a null-safe manner.
  557. * <p>
  558. * The byte is obtained from the results of {@link #getNumber(Map,Object)}.
  559. *
  560. * @param map the map to use
  561. * @param key the key to look up
  562. * @return the value in the Map as a byte, <code>0</code> if null map input
  563. */
  564. public static byte getByteValue(final Map map, final Object key) {
  565. Byte byteObject = getByte(map, key);
  566. if (byteObject == null) {
  567. return 0;
  568. }
  569. return byteObject.byteValue();
  570. }
  571. /**
  572. * Gets a short from a Map in a null-safe manner.
  573. * <p>
  574. * The short is obtained from the results of {@link #getNumber(Map,Object)}.
  575. *
  576. * @param map the map to use
  577. * @param key the key to look up
  578. * @return the value in the Map as a short, <code>0</code> if null map input
  579. */
  580. public static short getShortValue(final Map map, final Object key) {
  581. Short shortObject = getShort(map, key);
  582. if (shortObject == null) {
  583. return 0;
  584. }
  585. return shortObject.shortValue();
  586. }
  587. /**
  588. * Gets an int from a Map in a null-safe manner.
  589. * <p>
  590. * The int is obtained from the results of {@link #getNumber(Map,Object)}.
  591. *
  592. * @param map the map to use
  593. * @param key the key to look up
  594. * @return the value in the Map as an int, <code>0</code> if null map input
  595. */
  596. public static int getIntValue(final Map map, final Object key) {
  597. Integer integerObject = getInteger(map, key);
  598. if (integerObject == null) {
  599. return 0;
  600. }
  601. return integerObject.intValue();
  602. }
  603. /**
  604. * Gets a long from a Map in a null-safe manner.
  605. * <p>
  606. * The long is obtained from the results of {@link #getNumber(Map,Object)}.
  607. *
  608. * @param map the map to use
  609. * @param key the key to look up
  610. * @return the value in the Map as a long, <code>0L</code> if null map input
  611. */
  612. public static long getLongValue(final Map map, final Object key) {
  613. Long longObject = getLong(map, key);
  614. if (longObject == null) {
  615. return 0L;
  616. }
  617. return longObject.longValue();
  618. }
  619. /**
  620. * Gets a float from a Map in a null-safe manner.
  621. * <p>
  622. * The float is obtained from the results of {@link #getNumber(Map,Object)}.
  623. *
  624. * @param map the map to use
  625. * @param key the key to look up
  626. * @return the value in the Map as a float, <code>0.0F</code> if null map input
  627. */
  628. public static float getFloatValue(final Map map, final Object key) {
  629. Float floatObject = getFloat(map, key);
  630. if (floatObject == null) {
  631. return 0f;
  632. }
  633. return floatObject.floatValue();
  634. }
  635. /**
  636. * Gets a double from a Map in a null-safe manner.
  637. * <p>
  638. * The double is obtained from the results of {@link #getNumber(Map,Object)}.
  639. *
  640. * @param map the map to use
  641. * @param key the key to look up
  642. * @return the value in the Map as a double, <code>0.0</code> if null map input
  643. */
  644. public static double getDoubleValue(final Map map, final Object key) {
  645. Double doubleObject = getDouble(map, key);
  646. if (doubleObject == null) {
  647. return 0d;
  648. }
  649. return doubleObject.doubleValue();
  650. }
  651. // Type safe primitive getters with default values
  652. //-------------------------------------------------------------------------
  653. /**
  654. * Gets a boolean from a Map in a null-safe manner,
  655. * using the default value if the the conversion fails.
  656. * <p>
  657. * If the value is a <code>Boolean</code> its value is returned.
  658. * If the value is a <code>String</code> and it equals 'true' ignoring case
  659. * then <code>true</code> is returned, otherwise <code>false</code>.
  660. * If the value is a <code>Number</code> an integer zero value returns
  661. * <code>false</code> and non-zero returns <code>true</code>.
  662. * Otherwise, <code>defaultValue</code> is returned.
  663. *
  664. * @param map the map to use
  665. * @param key the key to look up
  666. * @param defaultValue return if the value is null or if the
  667. * conversion fails
  668. * @return the value in the Map as a Boolean, <code>defaultValue</code> if null map input
  669. */
  670. public static boolean getBooleanValue(final Map map, final Object key, boolean defaultValue) {
  671. Boolean booleanObject = getBoolean(map, key);
  672. if (booleanObject == null) {
  673. return defaultValue;
  674. }
  675. return booleanObject.booleanValue();
  676. }
  677. /**
  678. * Gets a byte from a Map in a null-safe manner,
  679. * using the default value if the the conversion fails.
  680. * <p>
  681. * The byte is obtained from the results of {@link #getNumber(Map,Object)}.
  682. *
  683. * @param map the map to use
  684. * @param key the key to look up
  685. * @param defaultValue return if the value is null or if the
  686. * conversion fails
  687. * @return the value in the Map as a byte, <code>defaultValue</code> if null map input
  688. */
  689. public static byte getByteValue(final Map map, final Object key, byte defaultValue) {
  690. Byte byteObject = getByte(map, key);
  691. if (byteObject == null) {
  692. return defaultValue;
  693. }
  694. return byteObject.byteValue();
  695. }
  696. /**
  697. * Gets a short from a Map in a null-safe manner,
  698. * using the default value if the the conversion fails.
  699. * <p>
  700. * The short is obtained from the results of {@link #getNumber(Map,Object)}.
  701. *
  702. * @param map the map to use
  703. * @param key the key to look up
  704. * @param defaultValue return if the value is null or if the
  705. * conversion fails
  706. * @return the value in the Map as a short, <code>defaultValue</code> if null map input
  707. */
  708. public static short getShortValue(final Map map, final Object key, short defaultValue) {
  709. Short shortObject = getShort(map, key);
  710. if (shortObject == null) {
  711. return defaultValue;
  712. }
  713. return shortObject.shortValue();
  714. }
  715. /**
  716. * Gets an int from a Map in a null-safe manner,
  717. * using the default value if the the conversion fails.
  718. * <p>
  719. * The int is obtained from the results of {@link #getNumber(Map,Object)}.
  720. *
  721. * @param map the map to use
  722. * @param key the key to look up
  723. * @param defaultValue return if the value is null or if the
  724. * conversion fails
  725. * @return the value in the Map as an int, <code>defaultValue</code> if null map input
  726. */
  727. public static int getIntValue(final Map map, final Object key, int defaultValue) {
  728. Integer integerObject = getInteger(map, key);
  729. if (integerObject == null) {
  730. return defaultValue;
  731. }
  732. return integerObject.intValue();
  733. }
  734. /**
  735. * Gets a long from a Map in a null-safe manner,
  736. * using the default value if the the conversion fails.
  737. * <p>
  738. * The long is obtained from the results of {@link #getNumber(Map,Object)}.
  739. *
  740. * @param map the map to use
  741. * @param key the key to look up
  742. * @param defaultValue return if the value is null or if the
  743. * conversion fails
  744. * @return the value in the Map as a long, <code>defaultValue</code> if null map input
  745. */
  746. public static long getLongValue(final Map map, final Object key, long defaultValue) {
  747. Long longObject = getLong(map, key);
  748. if (longObject == null) {
  749. return defaultValue;
  750. }
  751. return longObject.longValue();
  752. }
  753. /**
  754. * Gets a float from a Map in a null-safe manner,
  755. * using the default value if the the conversion fails.
  756. * <p>
  757. * The float is obtained from the results of {@link #getNumber(Map,Object)}.
  758. *
  759. * @param map the map to use
  760. * @param key the key to look up
  761. * @param defaultValue return if the value is null or if the
  762. * conversion fails
  763. * @return the value in the Map as a float, <code>defaultValue</code> if null map input
  764. */
  765. public static float getFloatValue(final Map map, final Object key, float defaultValue) {
  766. Float floatObject = getFloat(map, key);
  767. if (floatObject == null) {
  768. return defaultValue;
  769. }
  770. return floatObject.floatValue();
  771. }
  772. /**
  773. * Gets a double from a Map in a null-safe manner,
  774. * using the default value if the the conversion fails.
  775. * <p>
  776. * The double is obtained from the results of {@link #getNumber(Map,Object)}.
  777. *
  778. * @param map the map to use
  779. * @param key the key to look up
  780. * @param defaultValue return if the value is null or if the
  781. * conversion fails
  782. * @return the value in the Map as a double, <code>defaultValue</code> if null map input
  783. */
  784. public static double getDoubleValue(final Map map, final Object key, double defaultValue) {
  785. Double doubleObject = getDouble(map, key);
  786. if (doubleObject == null) {
  787. return defaultValue;
  788. }
  789. return doubleObject.doubleValue();
  790. }
  791. // Conversion methods
  792. //-------------------------------------------------------------------------
  793. /**
  794. * Gets a new Properties object initialised with the values from a Map.
  795. * A null input will return an empty properties object.
  796. *
  797. * @param map the map to convert to a Properties object, may not be null
  798. * @return the properties object
  799. */
  800. public static Properties toProperties(final Map map) {
  801. Properties answer = new Properties();
  802. if (map != null) {
  803. for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
  804. Map.Entry entry = (Map.Entry) iter.next();
  805. Object key = entry.getKey();
  806. Object value = entry.getValue();
  807. answer.put(key, value);
  808. }
  809. }
  810. return answer;
  811. }
  812. /**
  813. * Creates a new HashMap using data copied from a ResourceBundle.
  814. *
  815. * @param resourceBundle the resource bundle to convert, may not be null
  816. * @return the hashmap containing the data
  817. * @throws NullPointerException if the bundle is null
  818. */
  819. public static Map toMap(final ResourceBundle resourceBundle) {
  820. Enumeration enumeration = resourceBundle.getKeys();
  821. Map map = new HashMap();
  822. while (enumeration.hasMoreElements()) {
  823. String key = (String) enumeration.nextElement();
  824. Object value = resourceBundle.getObject(key);
  825. map.put(key, value);
  826. }
  827. return map;
  828. }
  829. // Printing methods
  830. //-------------------------------------------------------------------------
  831. /**
  832. * Prints the given map with nice line breaks.
  833. * <p>
  834. * This method prints a nicely formatted String describing the Map.
  835. * Each map entry will be printed with key and value.
  836. * When the value is a Map, recursive behaviour occurs.
  837. * <p>
  838. * This method is NOT thread-safe in any special way. You must manually
  839. * synchronize on either this class or the stream as required.
  840. *
  841. * @param out the stream to print to, must not be null
  842. * @param label The label to be used, may be <code>null</code>.
  843. * If <code>null</code>, the label is not output.
  844. * It typically represents the name of the property in a bean or similar.
  845. * @param map The map to print, may be <code>null</code>.
  846. * If <code>null</code>, the text 'null' is output.
  847. * @throws NullPointerException if the stream is <code>null</code>
  848. */
  849. public static void verbosePrint(
  850. final PrintStream out,
  851. final Object label,
  852. final Map map) {
  853. verbosePrintInternal(out, label, map, new ArrayStack(), false);
  854. }
  855. /**
  856. * Prints the given map with nice line breaks.
  857. * <p>
  858. * This method prints a nicely formatted String describing the Map.
  859. * Each map entry will be printed with key, value and value classname.
  860. * When the value is a Map, recursive behaviour occurs.
  861. * <p>
  862. * This method is NOT thread-safe in any special way. You must manually
  863. * synchronize on either this class or the stream as required.
  864. *
  865. * @param out the stream to print to, must not be null
  866. * @param label The label to be used, may be <code>null</code>.
  867. * If <code>null</code>, the label is not output.
  868. * It typically represents the name of the property in a bean or similar.
  869. * @param map The map to print, may be <code>null</code>.
  870. * If <code>null</code>, the text 'null' is output.
  871. * @throws NullPointerException if the stream is <code>null</code>
  872. */
  873. public static void debugPrint(
  874. final PrintStream out,
  875. final Object label,
  876. final Map map) {
  877. verbosePrintInternal(out, label, map, new ArrayStack(), true);
  878. }
  879. // Implementation methods
  880. //-------------------------------------------------------------------------
  881. /**
  882. * Logs the given exception to <code>System.out</code>.
  883. * <p>
  884. * This method exists as Jakarta Collections does not depend on logging.
  885. *
  886. * @param ex the exception to log
  887. */
  888. protected static void logInfo(final Exception ex) {
  889. System.out.println("INFO: Exception: " + ex);
  890. }
  891. /**
  892. * Implementation providing functionality for {@link #debugPrint} and for
  893. * {@link #verbosePrint}. This prints the given map with nice line breaks.
  894. * If the debug flag is true, it additionally prints the type of the object
  895. * value. If the contents of a map include the map itself, then the text
  896. * <em>(this Map)</em> is printed out. If the contents include a
  897. * parent container of the map, the the text <em>(ancestor[i] Map)</em> is
  898. * printed, where i actually indicates the number of levels which must be
  899. * traversed in the sequential list of ancestors (e.g. father, grandfather,
  900. * great-grandfather, etc).
  901. *
  902. * @param out the stream to print to
  903. * @param label the label to be used, may be <code>null</code>.
  904. * If <code>null</code>, the label is not output.
  905. * It typically represents the name of the property in a bean or similar.
  906. * @param map the map to print, may be <code>null</code>.
  907. * If <code>null</code>, the text 'null' is output
  908. * @param lineage a stack consisting of any maps in which the previous
  909. * argument is contained. This is checked to avoid infinite recursion when
  910. * printing the output
  911. * @param debug flag indicating whether type names should be output.
  912. * @throws NullPointerException if the stream is <code>null</code>
  913. */
  914. private static void verbosePrintInternal(
  915. final PrintStream out,
  916. final Object label,
  917. final Map map,
  918. final ArrayStack lineage,
  919. final boolean debug) {
  920. printIndent(out, lineage.size());
  921. if (map == null) {
  922. if (label != null) {
  923. out.print(label);
  924. out.print(" = ");
  925. }
  926. out.println("null");
  927. return;
  928. }
  929. if (label != null) {
  930. out.print(label);
  931. out.println(" = ");
  932. }
  933. printIndent(out, lineage.size());
  934. out.println("{");
  935. lineage.push(map);
  936. for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
  937. Map.Entry entry = (Map.Entry) it.next();
  938. Object childKey = entry.getKey();
  939. Object childValue = entry.getValue();
  940. if (childValue instanceof Map && !lineage.contains(childValue)) {
  941. verbosePrintInternal(
  942. out,
  943. (childKey == null ? "null" : childKey),
  944. (Map) childValue,
  945. lineage,
  946. debug);
  947. } else {
  948. printIndent(out, lineage.size());
  949. out.print(childKey);
  950. out.print(" = ");
  951. final int lineageIndex = lineage.indexOf(childValue);
  952. if (lineageIndex == -1) {
  953. out.print(childValue);
  954. } else if (lineage.size() - 1 == lineageIndex) {
  955. out.print("(this Map)");
  956. } else {
  957. out.print(
  958. "(ancestor["
  959. + (lineage.size() - 1 - lineageIndex - 1)
  960. + "] Map)");
  961. }
  962. if (debug && childValue != null) {
  963. out.print(' ');
  964. out.println(childValue.getClass().getName());
  965. } else {
  966. out.println();
  967. }
  968. }
  969. }
  970. lineage.pop();
  971. printIndent(out, lineage.size());
  972. out.println(debug ? "} " + map.getClass().getName() : "}");
  973. }
  974. /**
  975. * Writes indentation to the given stream.
  976. *
  977. * @param out the stream to indent
  978. */
  979. private static void printIndent(final PrintStream out, final int indent) {
  980. for (int i = 0; i < indent; i++) {
  981. out.print(INDENT_STRING);
  982. }
  983. }
  984. // Misc
  985. //-----------------------------------------------------------------------
  986. /**
  987. * Inverts the supplied map returning a new HashMap such that the keys of
  988. * the input are swapped with the values.
  989. * <p>
  990. * This operation assumes that the inverse mapping is well defined.
  991. * If the input map had multiple entries with the same value mapped to
  992. * different keys, the returned map will map one of those keys to the
  993. * value, but the exact key which will be mapped is undefined.
  994. *
  995. * @see DoubleOrderedMap
  996. * @param map the map to invert, may not be null
  997. * @return a new HashMap containing the inverted data
  998. * @throws NullPointerException if the map is null
  999. */
  1000. public static Map invertMap(Map map) {
  1001. Map out = new HashMap(map.size());
  1002. for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
  1003. Map.Entry entry = (Map.Entry) it.next();
  1004. out.put(entry.getValue(), entry.getKey());
  1005. }
  1006. return out;
  1007. }
  1008. /**
  1009. * Nice method for adding data to a map in such a way
  1010. * as to not get NPE's. The point being that if the
  1011. * value is null, map.put() will throw an exception.
  1012. * That blows in the case of this class cause you may want to
  1013. * essentially treat put("Not Null", null ) == put("Not Null", "")
  1014. * We will still throw a NPE if the key is null cause that should
  1015. * never happen.
  1016. *
  1017. * @param map the map to add to, may not be null
  1018. * @param key the key
  1019. * @param value the value
  1020. * @throws NullPointerException if the map is null
  1021. */
  1022. public static void safeAddToMap(Map map, Object key, Object value) throws NullPointerException {
  1023. if (value == null) {
  1024. map.put ( key, "" );
  1025. } else {
  1026. map.put ( key, value );
  1027. }
  1028. }
  1029. // Map decorators
  1030. //-----------------------------------------------------------------------
  1031. /**
  1032. * Returns a synchronized map backed by the given map.
  1033. * <p>
  1034. * You must manually synchronize on the returned buffer's iterator to
  1035. * avoid non-deterministic behavior:
  1036. *
  1037. * <pre>
  1038. * Map m = MapUtils.synchronizedMap(myMap);
  1039. * Set s = m.keySet(); // outside synchronized block
  1040. * synchronized (m) { // synchronized on MAP!
  1041. * Iterator i = s.iterator();
  1042. * while (i.hasNext()) {
  1043. * process (i.next());
  1044. * }
  1045. * }
  1046. * </pre>
  1047. *
  1048. * This method uses the implementation in {@link java.util.Collections Collections}.
  1049. *
  1050. * @param map the map to synchronize, must not be null
  1051. * @return a synchronized map backed by the given map
  1052. * @throws IllegalArgumentException if the map is null
  1053. */
  1054. public static Map synchronizedMap(Map map) {
  1055. return Collections.synchronizedMap(map);
  1056. }
  1057. /**
  1058. * Returns an unmodifiable map backed by the given map.
  1059. * <p>
  1060. * This method uses the implementation in the decorators subpackage.
  1061. *
  1062. * @param map the map to make unmodifiable, must not be null
  1063. * @return an unmodifiable map backed by the given map
  1064. * @throws IllegalArgumentException if the map is null
  1065. */
  1066. public static Map unmodifiableMap(Map map) {
  1067. return UnmodifiableMap.decorate(map);
  1068. }
  1069. /**
  1070. * Returns a predicated (validating) map backed by the given map.
  1071. * <p>
  1072. * Only objects that pass the tests in the given predicates can be added to the map.
  1073. * Trying to add an invalid object results in an IllegalArgumentException.
  1074. * Keys must pass the key predicate, values must pass the value predicate.
  1075. * It is important not to use the original map after invoking this method,
  1076. * as it is a backdoor for adding invalid objects.
  1077. *
  1078. * @param map the map to predicate, must not be null
  1079. * @param keyPred the predicate for keys, null means no check
  1080. * @param valuePred the predicate for values, null means no check
  1081. * @return a predicated map backed by the given map
  1082. * @throws IllegalArgumentException if the Map is null
  1083. */
  1084. public static Map predicatedMap(Map map, Predicate keyPred, Predicate valuePred) {
  1085. return PredicatedMap.decorate(map, keyPred, valuePred);
  1086. }
  1087. /**
  1088. * Returns a typed map backed by the given map.
  1089. * <p>
  1090. * Only keys and values of the specified types can be added to the map.
  1091. *
  1092. * @param map the map to limit to a specific type, must not be null
  1093. * @param keyType the type of keys which may be added to the map, must not be null
  1094. * @param valueType the type of values which may be added to the map, must not be null
  1095. * @return a typed map backed by the specified map
  1096. * @throws IllegalArgumentException if the Map or Class is null
  1097. */
  1098. public static Map typedMap(Map map, Class keyType, Class valueType) {
  1099. return TypedMap.decorate(map, keyType, valueType);
  1100. }
  1101. /**
  1102. * Returns a transformed map backed by the given map.
  1103. * <p>
  1104. * Each object is passed through the transformers as it is added to the
  1105. * Map. It is important not to use the original map after invoking this
  1106. * method, as it is a backdoor for adding untransformed objects.
  1107. *
  1108. * @param map the map to transform, must not be null
  1109. * @param keyTransformer the transformer for the map keys, null means no transformation
  1110. * @param valueTransformer the transformer for the map values, null means no transformation
  1111. * @return a transformed map backed by the given map
  1112. * @throws IllegalArgumentException if the Map is null
  1113. */
  1114. public static Map transformedMap(Map map, Transformer keyTransformer, Transformer valueTransformer) {
  1115. return TransformedMap.decorate(map, keyTransformer, valueTransformer);
  1116. }
  1117. /**
  1118. * Returns a fixed-sized map backed by the given map.
  1119. * Elements may not be added or removed from the returned map, but
  1120. * existing elements can be changed (for instance, via the
  1121. * {@link Map#put(Object,Object)} method).
  1122. *
  1123. * @param map the map whose size to fix, must not be null
  1124. * @return a fixed-size map backed by that map
  1125. * @throws IllegalArgumentException if the Map is null
  1126. */
  1127. public static Map fixedSizeMap(Map map) {
  1128. return FixedSizeMap.decorate(map);
  1129. }
  1130. /**
  1131. * Returns a "lazy" map whose values will be created on demand.
  1132. * <p>
  1133. * When the key passed to the returned map's {@link Map#get(Object)}
  1134. * method is not present in the map, then the factory will be used
  1135. * to create a new object and that object will become the value
  1136. * associated with that key.
  1137. * <p>
  1138. * For instance:
  1139. * <pre>
  1140. * Factory factory = new Factory() {
  1141. * public Object create() {
  1142. * return new Date();
  1143. * }
  1144. * }
  1145. * Map lazyMap = MapUtils.lazyMap(new HashMap(), factory);
  1146. * Object obj = lazyMap.get("test");
  1147. * </pre>
  1148. *
  1149. * After the above code is executed, <code>obj</code> will contain
  1150. * a new <code>Date</code> instance. Furthermore, that <code>Date</code>
  1151. * instance is the value for the <code>"test"</code> key in the map.
  1152. *
  1153. * @param map the map to make lazy, must not be null
  1154. * @param factory the factory for creating new objects, must not be null
  1155. * @return a lazy map backed by the given map
  1156. * @throws IllegalArgumentException if the Map or Factory is null
  1157. */
  1158. public static Map lazyMap(Map map, Factory factory) {
  1159. return LazyMap.decorate(map, factory);
  1160. }
  1161. /**
  1162. * Returns a "lazy" map whose values will be created on demand.
  1163. * <p>
  1164. * When the key passed to the returned map's {@link Map#get(Object)}
  1165. * method is not present in the map, then the factory will be used
  1166. * to create a new object and that object will become the value
  1167. * associated with that key. The factory is a {@link Transformer}
  1168. * that will be passed the key which it must transform into the value.
  1169. * <p>
  1170. * For instance:
  1171. * <pre>
  1172. * Transformer factory = new Transformer() {
  1173. * public Object transform(Object mapKey) {
  1174. * return new File(mapKey);
  1175. * }
  1176. * }
  1177. * Map lazyMap = MapUtils.lazyMap(new HashMap(), factory);
  1178. * Object obj = lazyMap.get("C:/dev");
  1179. * </pre>
  1180. *
  1181. * After the above code is executed, <code>obj</code> will contain
  1182. * a new <code>File</code> instance for the C drive dev directory.
  1183. * Furthermore, that <code>File</code> instance is the value for the
  1184. * <code>"C:/dev"</code> key in the map.
  1185. * <p>
  1186. * If a lazy map is wrapped by a synchronized map, the result is a simple
  1187. * synchronized cache. When an object is not is the cache, the cache itself
  1188. * calls back to the factory Transformer to populate itself, all within the
  1189. * same synchronized block.
  1190. *
  1191. * @param map the map to make lazy, must not be null
  1192. * @param transformerFactory the factory for creating new objects, must not be null
  1193. * @return a lazy map backed by the given map
  1194. * @throws IllegalArgumentException if the Map or Transformer is null
  1195. */
  1196. public static Map lazyMap(Map map, Transformer transformerFactory) {
  1197. return LazyMap.decorate(map, transformerFactory);
  1198. }
  1199. /**
  1200. * Returns a map that maintains the order of keys that are added
  1201. * backed by the given map.
  1202. * <p>
  1203. * If a key is added twice, the order is determined by the first add.
  1204. * The order is observed through the keySet, values and entrySet.
  1205. *
  1206. * @param map the map to order, must not be null
  1207. * @return an ordered map backed by the given map
  1208. * @throws IllegalArgumentException if the Map is null
  1209. */
  1210. public static Map orderedMap(Map map) {
  1211. return ListOrderedMap.decorate(map);
  1212. }
  1213. // SortedMap decorators
  1214. //-----------------------------------------------------------------------
  1215. /**
  1216. * Returns a synchronized sorted map backed by the given sorted map.
  1217. * <p>
  1218. * You must manually synchronize on the returned buffer's iterator to
  1219. * avoid non-deterministic behavior:
  1220. *
  1221. * <pre>
  1222. * Map m = MapUtils.synchronizedSortedMap(myMap);
  1223. * Set s = m.keySet(); // outside synchronized block
  1224. * synchronized (m) { // synchronized on MAP!
  1225. * Iterator i = s.iterator();
  1226. * while (i.hasNext()) {
  1227. * process (i.next());
  1228. * }
  1229. * }
  1230. * </pre>
  1231. *
  1232. * This method uses the implementation in {@link java.util.Collections Collections}.
  1233. *
  1234. * @param map the map to synchronize, must not be null
  1235. * @return a synchronized map backed by the given map
  1236. * @throws IllegalArgumentException if the map is null
  1237. */
  1238. public static Map synchronizedSortedMap(SortedMap map) {
  1239. return Collections.synchronizedSortedMap(map);
  1240. }
  1241. /**
  1242. * Returns an unmodifiable sorted map backed by the given sorted map.
  1243. * <p>
  1244. * This method uses the implementation in the decorators subpackage.
  1245. *
  1246. * @param map the sorted map to make unmodifiable, must not be null
  1247. * @return an unmodifiable map backed by the given map
  1248. * @throws IllegalArgumentException if the map is null
  1249. */
  1250. public static Map unmodifiableSortedMap(SortedMap map) {
  1251. return UnmodifiableSortedMap.decorate(map);
  1252. }
  1253. /**
  1254. * Returns a predicated (validating) sorted map backed by the given map.
  1255. * <p>
  1256. * Only objects that pass the tests in the given predicates can be added to the map.
  1257. * Trying to add an invalid object results in an IllegalArgumentException.
  1258. * Keys must pass the key predicate, values must pass the value predicate.
  1259. * It is important not to use the original map after invoking this method,
  1260. * as it is a backdoor for adding invalid objects.
  1261. *
  1262. * @param map the map to predicate, must not be null
  1263. * @param keyPred the predicate for keys, null means no check
  1264. * @param valuePred the predicate for values, null means no check
  1265. * @return a predicated map backed by the given map
  1266. * @throws IllegalArgumentException if the SortedMap is null
  1267. */
  1268. public static SortedMap predicatedSortedMap(SortedMap map, Predicate keyPred, Predicate valuePred) {
  1269. return PredicatedSortedMap.decorate(map, keyPred, valuePred);
  1270. }
  1271. /**
  1272. * Returns a typed sorted map backed by the given map.
  1273. * <p>
  1274. * Only keys and values of the specified types can be added to the map.
  1275. *
  1276. * @param map the map to limit to a specific type, must not be null
  1277. * @param keyType the type of keys which may be added to the map, must not be null
  1278. * @param valueType the type of values which may be added to the map, must not be null
  1279. * @return a typed map backed by the specified map
  1280. */
  1281. public static SortedMap typedSortedMap(SortedMap map, Class keyType, Class valueType) {
  1282. return TypedSortedMap.decorate(map, keyType, valueType);
  1283. }
  1284. /**
  1285. * Returns a transformed sorted map backed by the given map.
  1286. * <p>
  1287. * Each object is passed through the transformers as it is added to the
  1288. * Map. It is important not to use the original map after invoking this
  1289. * method, as it is a backdoor for adding untransformed objects.
  1290. *
  1291. * @param map the map to transform, must not be null
  1292. * @param keyTransformer the transformer for the map keys, null means no transformation
  1293. * @param valueTransformer the transformer for the map values, null means no transformation
  1294. * @return a transformed map backed by the given map
  1295. * @throws IllegalArgumentException if the SortedMap is null
  1296. */
  1297. public static SortedMap transformedSortedMap(SortedMap map, Transformer keyTransformer, Transformer valueTransformer) {
  1298. return TransformedSortedMap.decorate(map, keyTransformer, valueTransformer);
  1299. }
  1300. /**
  1301. * Returns a fixed-sized sorted map backed by the given sorted map.
  1302. * Elements may not be added or removed from the returned map, but
  1303. * existing elements can be changed (for instance, via the
  1304. * {@link Map#put(Object,Object)} method).
  1305. *
  1306. * @param map the map whose size to fix, must not be null
  1307. * @return a fixed-size map backed by that map
  1308. * @throws IllegalArgumentException if the SortedMap is null
  1309. */
  1310. public static SortedMap fixedSizeSortedMap(SortedMap map) {
  1311. return FixedSizeSortedMap.decorate(map);
  1312. }
  1313. /**
  1314. * Returns a "lazy" sorted map whose values will be created on demand.
  1315. * <p>
  1316. * When the key passed to the returned map's {@link Map#get(Object)}
  1317. * method is not present in the map, then the factory will be used
  1318. * to create a new object and that object will become the value
  1319. * associated with that key.
  1320. * <p>
  1321. * For instance:
  1322. *
  1323. * <pre>
  1324. * Factory factory = new Factory() {
  1325. * public Object create() {
  1326. * return new Date();
  1327. * }
  1328. * }
  1329. * SortedMap lazy = MapUtils.lazySortedMap(new TreeMap(), factory);
  1330. * Object obj = lazy.get("test");
  1331. * </pre>
  1332. *
  1333. * After the above code is executed, <code>obj</code> will contain
  1334. * a new <code>Date</code> instance. Furthermore, that <code>Date</code>
  1335. * instance is the value for the <code>"test"</code> key.
  1336. *
  1337. * @param map the map to make lazy, must not be null
  1338. * @param factory the factory for creating new objects, must not be null
  1339. * @return a lazy map backed by the given map
  1340. * @throws IllegalArgumentException if the SortedMap or Factory is null
  1341. */
  1342. public static SortedMap lazySortedMap(SortedMap map, Factory factory) {
  1343. return LazySortedMap.decorate(map, factory);
  1344. }
  1345. /**
  1346. * Returns a "lazy" sorted map whose values will be created on demand.
  1347. * <p>
  1348. * When the key passed to the returned map's {@link Map#get(Object)}
  1349. * method is not present in the map, then the factory will be used
  1350. * to create a new object and that object will become the value
  1351. * associated with that key. The factory is a {@link Transformer}
  1352. * that will be passed the key which it must transform into the value.
  1353. * <p>
  1354. * For instance:
  1355. * <pre>
  1356. * Transformer factory = new Transformer() {
  1357. * public Object transform(Object mapKey) {
  1358. * return new File(mapKey);
  1359. * }
  1360. * }
  1361. * SortedMap lazy = MapUtils.lazySortedMap(new TreeMap(), factory);
  1362. * Object obj = lazy.get("C:/dev");
  1363. * </pre>
  1364. *
  1365. * After the above code is executed, <code>obj</code> will contain
  1366. * a new <code>File</code> instance for the C drive dev directory.
  1367. * Furthermore, that <code>File</code> instance is the value for the
  1368. * <code>"C:/dev"</code> key in the map.
  1369. * <p>
  1370. * If a lazy map is wrapped by a synchronized map, the result is a simple
  1371. * synchronized cache. When an object is not is the cache, the cache itself
  1372. * calls back to the factory Transformer to populate itself, all within the
  1373. * same synchronized block.
  1374. *
  1375. * @param map the map to make lazy, must not be null
  1376. * @param transformerFactory the factory for creating new objects, must not be null
  1377. * @return a lazy map backed by the given map
  1378. * @throws IllegalArgumentException if the Map or Transformer is null
  1379. */
  1380. public static SortedMap lazySortedMap(SortedMap map, Transformer transformerFactory) {
  1381. return LazySortedMap.decorate(map, transformerFactory);
  1382. }
  1383. }