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.commons.collections.map;
  17. import java.lang.reflect.Array;
  18. import java.util.Iterator;
  19. import java.util.Map;
  20. import java.util.Set;
  21. import org.apache.commons.collections.iterators.AbstractIteratorDecorator;
  22. import org.apache.commons.collections.keyvalue.AbstractMapEntryDecorator;
  23. import org.apache.commons.collections.set.AbstractSetDecorator;
  24. /**
  25. * An abstract base class that simplifies the task of creating map decorators.
  26. * <p>
  27. * The Map API is very difficult to decorate correctly, and involves implementing
  28. * lots of different classes. This class exists to provide a simpler API.
  29. * <p>
  30. * Special hook methods are provided that are called when objects are added to
  31. * the map. By overriding these methods, the input can be validated or manipulated.
  32. * In addition to the main map methods, the entrySet is also affected, which is
  33. * the hardest part of writing map implementations.
  34. * <p>
  35. * This class is package-scoped, and may be withdrawn or replaced in future
  36. * versions of Commons Collections.
  37. *
  38. * @since Commons Collections 3.1
  39. * @version $Revision: 1.2 $ $Date: 2004/05/21 22:01:04 $
  40. *
  41. * @author Stephen Colebourne
  42. */
  43. abstract class AbstractInputCheckedMapDecorator
  44. extends AbstractMapDecorator {
  45. /**
  46. * Constructor only used in deserialization, do not use otherwise.
  47. */
  48. protected AbstractInputCheckedMapDecorator() {
  49. super();
  50. }
  51. /**
  52. * Constructor that wraps (not copies).
  53. *
  54. * @param map the map to decorate, must not be null
  55. * @throws IllegalArgumentException if map is null
  56. */
  57. protected AbstractInputCheckedMapDecorator(Map map) {
  58. super(map);
  59. }
  60. //-----------------------------------------------------------------------
  61. /**
  62. * Hook method called when a value is being set using <code>setValue</code>.
  63. * <p>
  64. * An implementation may validate the value and throw an exception
  65. * or it may transform the value into another object.
  66. * <p>
  67. * This implementation returns the input value.
  68. *
  69. * @param value the value to check
  70. * @throws UnsupportedOperationException if the map may not be changed by setValue
  71. * @throws IllegalArgumentException if the specified value is invalid
  72. * @throws ClassCastException if the class of the specified value is invalid
  73. * @throws NullPointerException if the specified value is null and nulls are invalid
  74. */
  75. protected abstract Object checkSetValue(Object value);
  76. /**
  77. * Hook method called to determine if <code>checkSetValue</code> has any effect.
  78. * <p>
  79. * An implementation should return false if the <code>checkSetValue</code> method
  80. * has no effect as this optimises the implementation.
  81. * <p>
  82. * This implementation returns <code>true</code>.
  83. *
  84. * @param value the value to check
  85. */
  86. protected boolean isSetValueChecking() {
  87. return true;
  88. }
  89. //-----------------------------------------------------------------------
  90. public Set entrySet() {
  91. if (isSetValueChecking()) {
  92. return new EntrySet(map.entrySet(), this);
  93. } else {
  94. return map.entrySet();
  95. }
  96. }
  97. //-----------------------------------------------------------------------
  98. /**
  99. * Implementation of an entry set that checks additions via setValue.
  100. */
  101. static class EntrySet extends AbstractSetDecorator {
  102. /** The parent map */
  103. private final AbstractInputCheckedMapDecorator parent;
  104. protected EntrySet(Set set, AbstractInputCheckedMapDecorator parent) {
  105. super(set);
  106. this.parent = parent;
  107. }
  108. public Iterator iterator() {
  109. return new EntrySetIterator(collection.iterator(), parent);
  110. }
  111. public Object[] toArray() {
  112. Object[] array = collection.toArray();
  113. for (int i = 0; i < array.length; i++) {
  114. array[i] = new MapEntry((Map.Entry) array[i], parent);
  115. }
  116. return array;
  117. }
  118. public Object[] toArray(Object array[]) {
  119. Object[] result = array;
  120. if (array.length > 0) {
  121. // we must create a new array to handle multi-threaded situations
  122. // where another thread could access data before we decorate it
  123. result = (Object[]) Array.newInstance(array.getClass().getComponentType(), 0);
  124. }
  125. result = collection.toArray(result);
  126. for (int i = 0; i < result.length; i++) {
  127. result[i] = new MapEntry((Map.Entry) result[i], parent);
  128. }
  129. // check to see if result should be returned straight
  130. if (result.length > array.length) {
  131. return result;
  132. }
  133. // copy back into input array to fulfil the method contract
  134. System.arraycopy(result, 0, array, 0, result.length);
  135. if (array.length > result.length) {
  136. array[result.length] = null;
  137. }
  138. return array;
  139. }
  140. }
  141. /**
  142. * Implementation of an entry set iterator that checks additions via setValue.
  143. */
  144. static class EntrySetIterator extends AbstractIteratorDecorator {
  145. /** The parent map */
  146. private final AbstractInputCheckedMapDecorator parent;
  147. protected EntrySetIterator(Iterator iterator, AbstractInputCheckedMapDecorator parent) {
  148. super(iterator);
  149. this.parent = parent;
  150. }
  151. public Object next() {
  152. Map.Entry entry = (Map.Entry) iterator.next();
  153. return new MapEntry(entry, parent);
  154. }
  155. }
  156. /**
  157. * Implementation of a map entry that checks additions via setValue.
  158. */
  159. static class MapEntry extends AbstractMapEntryDecorator {
  160. /** The parent map */
  161. private final AbstractInputCheckedMapDecorator parent;
  162. protected MapEntry(Map.Entry entry, AbstractInputCheckedMapDecorator parent) {
  163. super(entry);
  164. this.parent = parent;
  165. }
  166. public Object setValue(Object value) {
  167. value = parent.checkSetValue(value);
  168. return entry.setValue(value);
  169. }
  170. }
  171. }