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.commons.collections.map;
  17. import java.io.IOException;
  18. import java.io.ObjectInputStream;
  19. import java.io.ObjectOutputStream;
  20. import java.io.Serializable;
  21. import java.util.Iterator;
  22. import java.util.Map;
  23. import org.apache.commons.collections.Predicate;
  24. /**
  25. * Decorates another <code>Map</code> to validate that additions
  26. * match a specified predicate.
  27. * <p>
  28. * This map exists to provide validation for the decorated map.
  29. * It is normally created to decorate an empty map.
  30. * If an object cannot be added to the map, an IllegalArgumentException is thrown.
  31. * <p>
  32. * One usage would be to ensure that no null keys are added to the map.
  33. * <pre>Map map = PredicatedSet.decorate(new HashMap(), NotNullPredicate.INSTANCE, null);</pre>
  34. * <p>
  35. * This class is Serializable from Commons Collections 3.1.
  36. *
  37. * @since Commons Collections 3.0
  38. * @version $Revision: 1.14 $ $Date: 2004/06/07 22:14:42 $
  39. *
  40. * @author Stephen Colebourne
  41. * @author Paul Jack
  42. */
  43. public class PredicatedMap
  44. extends AbstractInputCheckedMapDecorator
  45. implements Serializable {
  46. /** Serialization version */
  47. private static final long serialVersionUID = 7412622456128415156L;
  48. /** The key predicate to use */
  49. protected final Predicate keyPredicate;
  50. /** The value predicate to use */
  51. protected final Predicate valuePredicate;
  52. /**
  53. * Factory method to create a predicated (validating) map.
  54. * <p>
  55. * If there are any elements already in the list being decorated, they
  56. * are validated.
  57. *
  58. * @param map the map to decorate, must not be null
  59. * @param keyPredicate the predicate to validate the keys, null means no check
  60. * @param valuePredicate the predicate to validate to values, null means no check
  61. * @throws IllegalArgumentException if the map is null
  62. */
  63. public static Map decorate(Map map, Predicate keyPredicate, Predicate valuePredicate) {
  64. return new PredicatedMap(map, keyPredicate, valuePredicate);
  65. }
  66. //-----------------------------------------------------------------------
  67. /**
  68. * Constructor that wraps (not copies).
  69. *
  70. * @param map the map to decorate, must not be null
  71. * @param keyPredicate the predicate to validate the keys, null means no check
  72. * @param valuePredicate the predicate to validate to values, null means no check
  73. * @throws IllegalArgumentException if the map is null
  74. */
  75. protected PredicatedMap(Map map, Predicate keyPredicate, Predicate valuePredicate) {
  76. super(map);
  77. this.keyPredicate = keyPredicate;
  78. this.valuePredicate = valuePredicate;
  79. Iterator it = map.entrySet().iterator();
  80. while (it.hasNext()) {
  81. Map.Entry entry = (Map.Entry) it.next();
  82. Object key = entry.getKey();
  83. Object value = entry.getValue();
  84. validate(key, value);
  85. }
  86. }
  87. //-----------------------------------------------------------------------
  88. /**
  89. * Write the map out using a custom routine.
  90. *
  91. * @param out the output stream
  92. * @throws IOException
  93. * @since Commons Collections 3.1
  94. */
  95. private void writeObject(ObjectOutputStream out) throws IOException {
  96. out.defaultWriteObject();
  97. out.writeObject(map);
  98. }
  99. /**
  100. * Read the map in using a custom routine.
  101. *
  102. * @param in the input stream
  103. * @throws IOException
  104. * @throws ClassNotFoundException
  105. * @since Commons Collections 3.1
  106. */
  107. private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
  108. in.defaultReadObject();
  109. map = (Map) in.readObject();
  110. }
  111. //-----------------------------------------------------------------------
  112. /**
  113. * Validates a key value pair.
  114. *
  115. * @param key the key to validate
  116. * @param value the value to validate
  117. * @throws IllegalArgumentException if invalid
  118. */
  119. protected void validate(Object key, Object value) {
  120. if (keyPredicate != null && keyPredicate.evaluate(key) == false) {
  121. throw new IllegalArgumentException("Cannot add key - Predicate rejected it");
  122. }
  123. if (valuePredicate != null && valuePredicate.evaluate(value) == false) {
  124. throw new IllegalArgumentException("Cannot add value - Predicate rejected it");
  125. }
  126. }
  127. /**
  128. * Override to validate an object set into the map via <code>setValue</code>.
  129. *
  130. * @param value the value to validate
  131. * @throws IllegalArgumentException if invalid
  132. * @since Commons Collections 3.1
  133. */
  134. protected Object checkSetValue(Object value) {
  135. if (valuePredicate.evaluate(value) == false) {
  136. throw new IllegalArgumentException("Cannot set value - Predicate rejected it");
  137. }
  138. return value;
  139. }
  140. /**
  141. * Override to only return true when there is a value transformer.
  142. *
  143. * @return true if a value predicate is in use
  144. * @since Commons Collections 3.1
  145. */
  146. protected boolean isSetValueChecking() {
  147. return (valuePredicate != null);
  148. }
  149. //-----------------------------------------------------------------------
  150. public Object put(Object key, Object value) {
  151. validate(key, value);
  152. return map.put(key, value);
  153. }
  154. public void putAll(Map mapToCopy) {
  155. Iterator it = mapToCopy.entrySet().iterator();
  156. while (it.hasNext()) {
  157. Map.Entry entry = (Map.Entry) it.next();
  158. Object key = entry.getKey();
  159. Object value = entry.getValue();
  160. validate(key, value);
  161. }
  162. map.putAll(mapToCopy);
  163. }
  164. }