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.Serializable;
  18. import java.util.AbstractSet;
  19. import java.util.Collection;
  20. import java.util.Collections;
  21. import java.util.Iterator;
  22. import java.util.Map;
  23. import java.util.NoSuchElementException;
  24. import java.util.Set;
  25. import org.apache.commons.collections.BoundedMap;
  26. import org.apache.commons.collections.KeyValue;
  27. import org.apache.commons.collections.MapIterator;
  28. import org.apache.commons.collections.OrderedMap;
  29. import org.apache.commons.collections.OrderedMapIterator;
  30. import org.apache.commons.collections.ResettableIterator;
  31. import org.apache.commons.collections.iterators.SingletonIterator;
  32. import org.apache.commons.collections.keyvalue.TiedMapEntry;
  33. /**
  34. * A <code>Map</code> implementation that holds a single item and is fixed size.
  35. * <p>
  36. * The single key/value pair is specified at creation.
  37. * The map is fixed size so any action that would change the size is disallowed.
  38. * However, the <code>put</code> or <code>setValue</code> methods can <i>change</i>
  39. * the value associated with the key.
  40. * <p>
  41. * If trying to remove or clear the map, an UnsupportedOperationException is thrown.
  42. * If trying to put a new mapping into the map, an IllegalArgumentException is thrown.
  43. * The put method will only suceed if the key specified is the same as the
  44. * singleton key.
  45. * <p>
  46. * The key and value can be obtained by:
  47. * <ul>
  48. * <li>normal Map methods and views
  49. * <li>the <code>MapIterator</code>, see {@link #mapIterator()}
  50. * <li>the <code>KeyValue</code> interface (just cast - no object creation)
  51. * </ul>
  52. *
  53. * @since Commons Collections 3.1
  54. * @version $Revision: 1.1 $ $Date: 2004/04/09 14:46:35 $
  55. *
  56. * @author Stephen Colebourne
  57. */
  58. public class SingletonMap
  59. implements OrderedMap, BoundedMap, KeyValue, Serializable, Cloneable {
  60. /** Serialization version */
  61. private static final long serialVersionUID = -8931271118676803261L;
  62. /** Singleton key */
  63. private final Object key;
  64. /** Singleton value */
  65. private Object value;
  66. /**
  67. * Constructor that creates a map of <code>null</code> to <code>null</code>.
  68. */
  69. public SingletonMap() {
  70. super();
  71. this.key = null;
  72. }
  73. /**
  74. * Constructor specifying the key and value.
  75. *
  76. * @param key the key to use
  77. * @param value the value to use
  78. */
  79. public SingletonMap(Object key, Object value) {
  80. super();
  81. this.key = key;
  82. this.value = value;
  83. }
  84. /**
  85. * Constructor specifying the key and value as a <code>KeyValue</code>.
  86. *
  87. * @param keyValue the key value pair to use
  88. */
  89. public SingletonMap(KeyValue keyValue) {
  90. super();
  91. this.key = keyValue.getKey();
  92. this.value = keyValue.getValue();
  93. }
  94. /**
  95. * Constructor specifying the key and value as a <code>MapEntry</code>.
  96. *
  97. * @param keyValue the key value pair to use
  98. */
  99. public SingletonMap(Map.Entry entry) {
  100. super();
  101. this.key = entry.getKey();
  102. this.value = entry.getValue();
  103. }
  104. /**
  105. * Constructor copying elements from another map.
  106. *
  107. * @param map the map to copy, must be size 1
  108. * @throws NullPointerException if the map is null
  109. * @throws IllegalArgumentException if the size is not 1
  110. */
  111. public SingletonMap(Map map) {
  112. super();
  113. if (map.size() != 1) {
  114. throw new IllegalArgumentException("The map size must be 1");
  115. }
  116. Map.Entry entry = (Map.Entry) map.entrySet().iterator().next();
  117. this.key = entry.getKey();
  118. this.value = entry.getValue();
  119. }
  120. // KeyValue
  121. //-----------------------------------------------------------------------
  122. /**
  123. * Gets the key.
  124. *
  125. * @return the key
  126. */
  127. public Object getKey() {
  128. return key;
  129. }
  130. /**
  131. * Gets the value.
  132. *
  133. * @return the value
  134. */
  135. public Object getValue() {
  136. return value;
  137. }
  138. /**
  139. * Sets the value.
  140. *
  141. * @param value the new value to set
  142. * @return the old value
  143. */
  144. public Object setValue(Object value) {
  145. Object old = this.value;
  146. this.value = value;
  147. return old;
  148. }
  149. // BoundedMap
  150. //-----------------------------------------------------------------------
  151. /**
  152. * Is the map currently full, always true.
  153. *
  154. * @return true always
  155. */
  156. public boolean isFull() {
  157. return true;
  158. }
  159. /**
  160. * Gets the maximum size of the map, always 1.
  161. *
  162. * @return 1 always
  163. */
  164. public int maxSize() {
  165. return 1;
  166. }
  167. // Map
  168. //-----------------------------------------------------------------------
  169. /**
  170. * Gets the value mapped to the key specified.
  171. *
  172. * @param key the key
  173. * @return the mapped value, null if no match
  174. */
  175. public Object get(Object key) {
  176. if (isEqualKey(key)) {
  177. return value;
  178. }
  179. return null;
  180. }
  181. /**
  182. * Gets the size of the map, always 1.
  183. *
  184. * @return the size of 1
  185. */
  186. public int size() {
  187. return 1;
  188. }
  189. /**
  190. * Checks whether the map is currently empty, which it never is.
  191. *
  192. * @return false always
  193. */
  194. public boolean isEmpty() {
  195. return false;
  196. }
  197. //-----------------------------------------------------------------------
  198. /**
  199. * Checks whether the map contains the specified key.
  200. *
  201. * @param key the key to search for
  202. * @return true if the map contains the key
  203. */
  204. public boolean containsKey(Object key) {
  205. return (isEqualKey(key));
  206. }
  207. /**
  208. * Checks whether the map contains the specified value.
  209. *
  210. * @param value the value to search for
  211. * @return true if the map contains the key
  212. */
  213. public boolean containsValue(Object value) {
  214. return (isEqualValue(value));
  215. }
  216. //-----------------------------------------------------------------------
  217. /**
  218. * Puts a key-value mapping into this map where the key must match the existing key.
  219. * <p>
  220. * An IllegalArgumentException is thrown if the key does not match as the map
  221. * is fixed size.
  222. *
  223. * @param key the key to set, must be the key of the map
  224. * @param value the value to set
  225. * @return the value previously mapped to this key, null if none
  226. * @throws IllegalArgumentException if the key does not match
  227. */
  228. public Object put(Object key, Object value) {
  229. if (isEqualKey(key)) {
  230. return setValue(value);
  231. }
  232. throw new IllegalArgumentException("Cannot put new key/value pair - Map is fixed size singleton");
  233. }
  234. /**
  235. * Puts the values from the specified map into this map.
  236. * <p>
  237. * The map must be of size 0 or size 1.
  238. * If it is size 1, the key must match the key of this map otherwise an
  239. * IllegalArgumentException is thrown.
  240. *
  241. * @param map the map to add, must be size 0 or 1, and the key must match
  242. * @throws NullPointerException if the map is null
  243. * @throws IllegalArgumentException if the key does not match
  244. */
  245. public void putAll(Map map) {
  246. switch (map.size()) {
  247. case 0:
  248. return;
  249. case 1:
  250. Map.Entry entry = (Map.Entry) map.entrySet().iterator().next();
  251. put(entry.getKey(), entry.getValue());
  252. return;
  253. default:
  254. throw new IllegalArgumentException("The map size must be 0 or 1");
  255. }
  256. }
  257. /**
  258. * Unsupported operation.
  259. *
  260. * @param key the mapping to remove
  261. * @return the value mapped to the removed key, null if key not in map
  262. * @throws UnsupportedOperationException always
  263. */
  264. public Object remove(Object key) {
  265. throw new UnsupportedOperationException();
  266. }
  267. /**
  268. * Unsupported operation.
  269. */
  270. public void clear() {
  271. throw new UnsupportedOperationException();
  272. }
  273. //-----------------------------------------------------------------------
  274. /**
  275. * Gets the entrySet view of the map.
  276. * Changes made via <code>setValue</code> affect this map.
  277. * To simply iterate through the entries, use {@link #mapIterator()}.
  278. *
  279. * @return the entrySet view
  280. */
  281. public Set entrySet() {
  282. Map.Entry entry = new TiedMapEntry(this, getKey());
  283. return Collections.singleton(entry);
  284. }
  285. /**
  286. * Gets the unmodifiable keySet view of the map.
  287. * Changes made to the view affect this map.
  288. * To simply iterate through the keys, use {@link #mapIterator()}.
  289. *
  290. * @return the keySet view
  291. */
  292. public Set keySet() {
  293. return Collections.singleton(key);
  294. }
  295. /**
  296. * Gets the unmodifiable values view of the map.
  297. * Changes made to the view affect this map.
  298. * To simply iterate through the values, use {@link #mapIterator()}.
  299. *
  300. * @return the values view
  301. */
  302. public Collection values() {
  303. return new SingletonValues(this);
  304. }
  305. /**
  306. * Gets an iterator over the map.
  307. * Changes made to the iterator using <code>setValue</code> affect this map.
  308. * The <code>remove</code> method is unsupported.
  309. * <p>
  310. * A MapIterator returns the keys in the map. It also provides convenient
  311. * methods to get the key and value, and set the value.
  312. * It avoids the need to create an entrySet/keySet/values object.
  313. * It also avoids creating the Map Entry object.
  314. *
  315. * @return the map iterator
  316. */
  317. public MapIterator mapIterator() {
  318. return new SingletonMapIterator(this);
  319. }
  320. // OrderedMap
  321. //-----------------------------------------------------------------------
  322. /**
  323. * Obtains an <code>OrderedMapIterator</code> over the map.
  324. * <p>
  325. * A ordered map iterator is an efficient way of iterating over maps
  326. * in both directions.
  327. *
  328. * @return an ordered map iterator
  329. */
  330. public OrderedMapIterator orderedMapIterator() {
  331. return new SingletonMapIterator(this);
  332. }
  333. /**
  334. * Gets the first (and only) key in the map.
  335. *
  336. * @return the key
  337. */
  338. public Object firstKey() {
  339. return getKey();
  340. }
  341. /**
  342. * Gets the last (and only) key in the map.
  343. *
  344. * @return the key
  345. */
  346. public Object lastKey() {
  347. return getKey();
  348. }
  349. /**
  350. * Gets the next key after the key specified, always null.
  351. *
  352. * @param key the next key
  353. * @return null always
  354. */
  355. public Object nextKey(Object key) {
  356. return null;
  357. }
  358. /**
  359. * Gets the previous key before the key specified, always null.
  360. *
  361. * @param key the next key
  362. * @return null always
  363. */
  364. public Object previousKey(Object key) {
  365. return null;
  366. }
  367. //-----------------------------------------------------------------------
  368. /**
  369. * Compares the specified key to the stored key.
  370. *
  371. * @param key the key to compare
  372. * @return true if equal
  373. */
  374. protected boolean isEqualKey(Object key) {
  375. return (key == null ? getKey() == null : key.equals(getKey()));
  376. }
  377. /**
  378. * Compares the specified value to the stored value.
  379. *
  380. * @param value the value to compare
  381. * @return true if equal
  382. */
  383. protected boolean isEqualValue(Object value) {
  384. return (value == null ? getValue() == null : value.equals(getValue()));
  385. }
  386. //-----------------------------------------------------------------------
  387. /**
  388. * SingletonMapIterator.
  389. */
  390. static class SingletonMapIterator implements OrderedMapIterator, ResettableIterator {
  391. private final SingletonMap parent;
  392. private boolean hasNext = true;
  393. private boolean canGetSet = false;
  394. SingletonMapIterator(SingletonMap parent) {
  395. super();
  396. this.parent = parent;
  397. }
  398. public boolean hasNext() {
  399. return hasNext;
  400. }
  401. public Object next() {
  402. if (hasNext == false) {
  403. throw new NoSuchElementException(AbstractHashedMap.NO_NEXT_ENTRY);
  404. }
  405. hasNext = false;
  406. canGetSet = true;
  407. return parent.getKey();
  408. }
  409. public boolean hasPrevious() {
  410. return (hasNext == false);
  411. }
  412. public Object previous() {
  413. if (hasNext == true) {
  414. throw new NoSuchElementException(AbstractHashedMap.NO_PREVIOUS_ENTRY);
  415. }
  416. hasNext = true;
  417. return parent.getKey();
  418. }
  419. public void remove() {
  420. throw new UnsupportedOperationException();
  421. }
  422. public Object getKey() {
  423. if (canGetSet == false) {
  424. throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID);
  425. }
  426. return parent.getKey();
  427. }
  428. public Object getValue() {
  429. if (canGetSet == false) {
  430. throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID);
  431. }
  432. return parent.getValue();
  433. }
  434. public Object setValue(Object value) {
  435. if (canGetSet == false) {
  436. throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID);
  437. }
  438. return parent.setValue(value);
  439. }
  440. public void reset() {
  441. hasNext = true;
  442. }
  443. public String toString() {
  444. if (hasNext) {
  445. return "Iterator[]";
  446. } else {
  447. return "Iterator[" + getKey() + "=" + getValue() + "]";
  448. }
  449. }
  450. }
  451. /**
  452. * Values implementation for the SingletonMap.
  453. * This class is needed as values is a view that must update as the map updates.
  454. */
  455. static class SingletonValues extends AbstractSet implements Serializable {
  456. private static final long serialVersionUID = -3689524741863047872L;
  457. private final SingletonMap parent;
  458. SingletonValues(SingletonMap parent) {
  459. super();
  460. this.parent = parent;
  461. }
  462. public int size() {
  463. return 1;
  464. }
  465. public boolean isEmpty() {
  466. return false;
  467. }
  468. public boolean contains(Object object) {
  469. return parent.containsValue(object);
  470. }
  471. public void clear() {
  472. throw new UnsupportedOperationException();
  473. }
  474. public Iterator iterator() {
  475. return new SingletonIterator(parent.getValue(), false);
  476. }
  477. }
  478. //-----------------------------------------------------------------------
  479. /**
  480. * Clones the map without cloning the key or value.
  481. *
  482. * @return a shallow clone
  483. */
  484. public Object clone() {
  485. try {
  486. SingletonMap cloned = (SingletonMap) super.clone();
  487. return cloned;
  488. } catch (CloneNotSupportedException ex) {
  489. throw new InternalError();
  490. }
  491. }
  492. /**
  493. * Compares this map with another.
  494. *
  495. * @param obj the object to compare to
  496. * @return true if equal
  497. */
  498. public boolean equals(Object obj) {
  499. if (obj == this) {
  500. return true;
  501. }
  502. if (obj instanceof Map == false) {
  503. return false;
  504. }
  505. Map other = (Map) obj;
  506. if (other.size() != 1) {
  507. return false;
  508. }
  509. Map.Entry entry = (Map.Entry) other.entrySet().iterator().next();
  510. return isEqualKey(entry.getKey()) && isEqualValue(entry.getValue());
  511. }
  512. /**
  513. * Gets the standard Map hashCode.
  514. *
  515. * @return the hash code defined in the Map interface
  516. */
  517. public int hashCode() {
  518. return (getKey() == null ? 0 : getKey().hashCode()) ^
  519. (getValue() == null ? 0 : getValue().hashCode());
  520. }
  521. /**
  522. * Gets the map as a String.
  523. *
  524. * @return a string version of the map
  525. */
  526. public String toString() {
  527. return new StringBuffer(128)
  528. .append('{')
  529. .append((getKey() == this ? "(this Map)" : getKey()))
  530. .append('=')
  531. .append((getValue() == this ? "(this Map)" : getValue()))
  532. .append('}')
  533. .toString();
  534. }
  535. }