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.bidimap;
  17. import java.util.Collection;
  18. import java.util.Iterator;
  19. import java.util.Map;
  20. import java.util.Set;
  21. import org.apache.commons.collections.BidiMap;
  22. import org.apache.commons.collections.MapIterator;
  23. import org.apache.commons.collections.ResettableIterator;
  24. import org.apache.commons.collections.collection.AbstractCollectionDecorator;
  25. import org.apache.commons.collections.iterators.AbstractIteratorDecorator;
  26. import org.apache.commons.collections.keyvalue.AbstractMapEntryDecorator;
  27. /**
  28. * Abstract <code>BidiMap</code> implemented using two maps.
  29. * <p>
  30. * An implementation can be written simply by implementing the
  31. * <code>createMap</code> method.
  32. *
  33. * @see DualHashBidiMap
  34. * @see DualTreeBidiMap
  35. * @since Commons Collections 3.0
  36. * @version $Id: AbstractDualBidiMap.java,v 1.14 2004/06/22 21:54:35 scolebourne Exp $
  37. *
  38. * @author Matthew Hawthorne
  39. * @author Stephen Colebourne
  40. */
  41. public abstract class AbstractDualBidiMap implements BidiMap {
  42. /**
  43. * Delegate map array. The first map contains standard entries, and the
  44. * second contains inverses.
  45. */
  46. protected transient final Map[] maps = new Map[2];
  47. /**
  48. * Inverse view of this map.
  49. */
  50. protected transient BidiMap inverseBidiMap = null;
  51. /**
  52. * View of the keys.
  53. */
  54. protected transient Set keySet = null;
  55. /**
  56. * View of the values.
  57. */
  58. protected transient Collection values = null;
  59. /**
  60. * View of the entries.
  61. */
  62. protected transient Set entrySet = null;
  63. /**
  64. * Creates an empty map, initialised by <code>createMap</code>.
  65. * <p>
  66. * This constructor remains in place for deserialization.
  67. * All other usage is deprecated in favour of
  68. * {@link #AbstractDualBidiMap(Map, Map)}.
  69. */
  70. protected AbstractDualBidiMap() {
  71. super();
  72. maps[0] = createMap();
  73. maps[1] = createMap();
  74. }
  75. /**
  76. * Creates an empty map using the two maps specified as storage.
  77. * <p>
  78. * The two maps must be a matching pair, normal and reverse.
  79. * They will typically both be empty.
  80. * <p>
  81. * Neither map is validated, so nulls may be passed in.
  82. * If you choose to do this then the subclass constructor must populate
  83. * the <code>maps[]</code> instance variable itself.
  84. *
  85. * @param normalMap the normal direction map
  86. * @param reverseMap the reverse direction map
  87. * @since Commons Collections 3.1
  88. */
  89. protected AbstractDualBidiMap(Map normalMap, Map reverseMap) {
  90. super();
  91. maps[0] = normalMap;
  92. maps[1] = reverseMap;
  93. }
  94. /**
  95. * Constructs a map that decorates the specified maps,
  96. * used by the subclass <code>createBidiMap</code> implementation.
  97. *
  98. * @param normalMap the normal direction map
  99. * @param reverseMap the reverse direction map
  100. * @param inverseBidiMap the inverse BidiMap
  101. */
  102. protected AbstractDualBidiMap(Map normalMap, Map reverseMap, BidiMap inverseBidiMap) {
  103. super();
  104. maps[0] = normalMap;
  105. maps[1] = reverseMap;
  106. this.inverseBidiMap = inverseBidiMap;
  107. }
  108. /**
  109. * Creates a new instance of the map used by the subclass to store data.
  110. * <p>
  111. * This design is deeply flawed and has been deprecated.
  112. * It relied on subclass data being used during a superclass constructor.
  113. *
  114. * @return the map to be used for internal storage
  115. * @deprecated For constructors, use the new two map constructor.
  116. * For deserialization, populate the maps array directly in readObject.
  117. */
  118. protected Map createMap() {
  119. return null;
  120. }
  121. /**
  122. * Creates a new instance of the subclass.
  123. *
  124. * @param normalMap the normal direction map
  125. * @param reverseMap the reverse direction map
  126. * @param inverseMap this map, which is the inverse in the new map
  127. * @return the inverse map
  128. */
  129. protected abstract BidiMap createBidiMap(Map normalMap, Map reverseMap, BidiMap inverseMap);
  130. // Map delegation
  131. //-----------------------------------------------------------------------
  132. public Object get(Object key) {
  133. return maps[0].get(key);
  134. }
  135. public int size() {
  136. return maps[0].size();
  137. }
  138. public boolean isEmpty() {
  139. return maps[0].isEmpty();
  140. }
  141. public boolean containsKey(Object key) {
  142. return maps[0].containsKey(key);
  143. }
  144. public boolean equals(Object obj) {
  145. return maps[0].equals(obj);
  146. }
  147. public int hashCode() {
  148. return maps[0].hashCode();
  149. }
  150. public String toString() {
  151. return maps[0].toString();
  152. }
  153. // BidiMap changes
  154. //-----------------------------------------------------------------------
  155. public Object put(Object key, Object value) {
  156. if (maps[0].containsKey(key)) {
  157. maps[1].remove(maps[0].get(key));
  158. }
  159. if (maps[1].containsKey(value)) {
  160. maps[0].remove(maps[1].get(value));
  161. }
  162. final Object obj = maps[0].put(key, value);
  163. maps[1].put(value, key);
  164. return obj;
  165. }
  166. public void putAll(Map map) {
  167. for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
  168. Map.Entry entry = (Map.Entry) it.next();
  169. put(entry.getKey(), entry.getValue());
  170. }
  171. }
  172. public Object remove(Object key) {
  173. Object value = null;
  174. if (maps[0].containsKey(key)) {
  175. value = maps[0].remove(key);
  176. maps[1].remove(value);
  177. }
  178. return value;
  179. }
  180. public void clear() {
  181. maps[0].clear();
  182. maps[1].clear();
  183. }
  184. public boolean containsValue(Object value) {
  185. return maps[1].containsKey(value);
  186. }
  187. // BidiMap
  188. //-----------------------------------------------------------------------
  189. /**
  190. * Obtains a <code>MapIterator</code> over the map.
  191. * The iterator implements <code>ResetableMapIterator</code>.
  192. * This implementation relies on the entrySet iterator.
  193. * <p>
  194. * The setValue() methods only allow a new value to be set.
  195. * If the value being set is already in the map, an IllegalArgumentException
  196. * is thrown (as setValue cannot change the size of the map).
  197. *
  198. * @return a map iterator
  199. */
  200. public MapIterator mapIterator() {
  201. return new BidiMapIterator(this);
  202. }
  203. public Object getKey(Object value) {
  204. return maps[1].get(value);
  205. }
  206. public Object removeValue(Object value) {
  207. Object key = null;
  208. if (maps[1].containsKey(value)) {
  209. key = maps[1].remove(value);
  210. maps[0].remove(key);
  211. }
  212. return key;
  213. }
  214. public BidiMap inverseBidiMap() {
  215. if (inverseBidiMap == null) {
  216. inverseBidiMap = createBidiMap(maps[1], maps[0], this);
  217. }
  218. return inverseBidiMap;
  219. }
  220. // Map views
  221. //-----------------------------------------------------------------------
  222. /**
  223. * Gets a keySet view of the map.
  224. * Changes made on the view are reflected in the map.
  225. * The set supports remove and clear but not add.
  226. *
  227. * @return the keySet view
  228. */
  229. public Set keySet() {
  230. if (keySet == null) {
  231. keySet = new KeySet(this);
  232. }
  233. return keySet;
  234. }
  235. /**
  236. * Creates a key set iterator.
  237. * Subclasses can override this to return iterators with different properties.
  238. *
  239. * @param iterator the iterator to decorate
  240. * @return the keySet iterator
  241. */
  242. protected Iterator createKeySetIterator(Iterator iterator) {
  243. return new KeySetIterator(iterator, this);
  244. }
  245. /**
  246. * Gets a values view of the map.
  247. * Changes made on the view are reflected in the map.
  248. * The set supports remove and clear but not add.
  249. *
  250. * @return the values view
  251. */
  252. public Collection values() {
  253. if (values == null) {
  254. values = new Values(this);
  255. }
  256. return values;
  257. }
  258. /**
  259. * Creates a values iterator.
  260. * Subclasses can override this to return iterators with different properties.
  261. *
  262. * @param iterator the iterator to decorate
  263. * @return the values iterator
  264. */
  265. protected Iterator createValuesIterator(Iterator iterator) {
  266. return new ValuesIterator(iterator, this);
  267. }
  268. /**
  269. * Gets an entrySet view of the map.
  270. * Changes made on the set are reflected in the map.
  271. * The set supports remove and clear but not add.
  272. * <p>
  273. * The Map Entry setValue() method only allow a new value to be set.
  274. * If the value being set is already in the map, an IllegalArgumentException
  275. * is thrown (as setValue cannot change the size of the map).
  276. *
  277. * @return the entrySet view
  278. */
  279. public Set entrySet() {
  280. if (entrySet == null) {
  281. entrySet = new EntrySet(this);
  282. }
  283. return entrySet;
  284. }
  285. /**
  286. * Creates an entry set iterator.
  287. * Subclasses can override this to return iterators with different properties.
  288. *
  289. * @param iterator the iterator to decorate
  290. * @return the entrySet iterator
  291. */
  292. protected Iterator createEntrySetIterator(Iterator iterator) {
  293. return new EntrySetIterator(iterator, this);
  294. }
  295. //-----------------------------------------------------------------------
  296. /**
  297. * Inner class View.
  298. */
  299. protected static abstract class View extends AbstractCollectionDecorator {
  300. /** The parent map */
  301. protected final AbstractDualBidiMap parent;
  302. /**
  303. * Constructs a new view of the BidiMap.
  304. *
  305. * @param coll the collection view being decorated
  306. * @param parent the parent BidiMap
  307. */
  308. protected View(Collection coll, AbstractDualBidiMap parent) {
  309. super(coll);
  310. this.parent = parent;
  311. }
  312. public boolean removeAll(Collection coll) {
  313. if (parent.isEmpty() || coll.isEmpty()) {
  314. return false;
  315. }
  316. boolean modified = false;
  317. Iterator it = iterator();
  318. while (it.hasNext()) {
  319. if (coll.contains(it.next())) {
  320. it.remove();
  321. modified = true;
  322. }
  323. }
  324. return modified;
  325. }
  326. public boolean retainAll(Collection coll) {
  327. if (parent.isEmpty()) {
  328. return false;
  329. }
  330. if (coll.isEmpty()) {
  331. parent.clear();
  332. return true;
  333. }
  334. boolean modified = false;
  335. Iterator it = iterator();
  336. while (it.hasNext()) {
  337. if (coll.contains(it.next()) == false) {
  338. it.remove();
  339. modified = true;
  340. }
  341. }
  342. return modified;
  343. }
  344. public void clear() {
  345. parent.clear();
  346. }
  347. }
  348. //-----------------------------------------------------------------------
  349. /**
  350. * Inner class KeySet.
  351. */
  352. protected static class KeySet extends View implements Set {
  353. /**
  354. * Constructs a new view of the BidiMap.
  355. *
  356. * @param parent the parent BidiMap
  357. */
  358. protected KeySet(AbstractDualBidiMap parent) {
  359. super(parent.maps[0].keySet(), parent);
  360. }
  361. public Iterator iterator() {
  362. return parent.createKeySetIterator(super.iterator());
  363. }
  364. public boolean contains(Object key) {
  365. return parent.maps[0].containsKey(key);
  366. }
  367. public boolean remove(Object key) {
  368. if (parent.maps[0].containsKey(key)) {
  369. Object value = parent.maps[0].remove(key);
  370. parent.maps[1].remove(value);
  371. return true;
  372. }
  373. return false;
  374. }
  375. }
  376. /**
  377. * Inner class KeySetIterator.
  378. */
  379. protected static class KeySetIterator extends AbstractIteratorDecorator {
  380. /** The parent map */
  381. protected final AbstractDualBidiMap parent;
  382. /** The last returned key */
  383. protected Object lastKey = null;
  384. /** Whether remove is allowed at present */
  385. protected boolean canRemove = false;
  386. /**
  387. * Constructor.
  388. * @param iterator the iterator to decorate
  389. * @param parent the parent map
  390. */
  391. protected KeySetIterator(Iterator iterator, AbstractDualBidiMap parent) {
  392. super(iterator);
  393. this.parent = parent;
  394. }
  395. public Object next() {
  396. lastKey = super.next();
  397. canRemove = true;
  398. return lastKey;
  399. }
  400. public void remove() {
  401. if (canRemove == false) {
  402. throw new IllegalStateException("Iterator remove() can only be called once after next()");
  403. }
  404. Object value = parent.maps[0].get(lastKey);
  405. super.remove();
  406. parent.maps[1].remove(value);
  407. lastKey = null;
  408. canRemove = false;
  409. }
  410. }
  411. //-----------------------------------------------------------------------
  412. /**
  413. * Inner class Values.
  414. */
  415. protected static class Values extends View implements Set {
  416. /**
  417. * Constructs a new view of the BidiMap.
  418. *
  419. * @param parent the parent BidiMap
  420. */
  421. protected Values(AbstractDualBidiMap parent) {
  422. super(parent.maps[0].values(), parent);
  423. }
  424. public Iterator iterator() {
  425. return parent.createValuesIterator(super.iterator());
  426. }
  427. public boolean contains(Object value) {
  428. return parent.maps[1].containsKey(value);
  429. }
  430. public boolean remove(Object value) {
  431. if (parent.maps[1].containsKey(value)) {
  432. Object key = parent.maps[1].remove(value);
  433. parent.maps[0].remove(key);
  434. return true;
  435. }
  436. return false;
  437. }
  438. }
  439. /**
  440. * Inner class ValuesIterator.
  441. */
  442. protected static class ValuesIterator extends AbstractIteratorDecorator {
  443. /** The parent map */
  444. protected final AbstractDualBidiMap parent;
  445. /** The last returned value */
  446. protected Object lastValue = null;
  447. /** Whether remove is allowed at present */
  448. protected boolean canRemove = false;
  449. /**
  450. * Constructor.
  451. * @param iterator the iterator to decorate
  452. * @param parent the parent map
  453. */
  454. protected ValuesIterator(Iterator iterator, AbstractDualBidiMap parent) {
  455. super(iterator);
  456. this.parent = parent;
  457. }
  458. public Object next() {
  459. lastValue = super.next();
  460. canRemove = true;
  461. return lastValue;
  462. }
  463. public void remove() {
  464. if (canRemove == false) {
  465. throw new IllegalStateException("Iterator remove() can only be called once after next()");
  466. }
  467. super.remove(); // removes from maps[0]
  468. parent.maps[1].remove(lastValue);
  469. lastValue = null;
  470. canRemove = false;
  471. }
  472. }
  473. //-----------------------------------------------------------------------
  474. /**
  475. * Inner class EntrySet.
  476. */
  477. protected static class EntrySet extends View implements Set {
  478. /**
  479. * Constructs a new view of the BidiMap.
  480. *
  481. * @param parent the parent BidiMap
  482. */
  483. protected EntrySet(AbstractDualBidiMap parent) {
  484. super(parent.maps[0].entrySet(), parent);
  485. }
  486. public Iterator iterator() {
  487. return parent.createEntrySetIterator(super.iterator());
  488. }
  489. public boolean remove(Object obj) {
  490. if (obj instanceof Map.Entry == false) {
  491. return false;
  492. }
  493. Map.Entry entry = (Map.Entry) obj;
  494. Object key = entry.getKey();
  495. if (parent.containsKey(key)) {
  496. Object value = parent.maps[0].get(key);
  497. if (value == null ? entry.getValue() == null : value.equals(entry.getValue())) {
  498. parent.maps[0].remove(key);
  499. parent.maps[1].remove(value);
  500. return true;
  501. }
  502. }
  503. return false;
  504. }
  505. }
  506. /**
  507. * Inner class EntrySetIterator.
  508. */
  509. protected static class EntrySetIterator extends AbstractIteratorDecorator {
  510. /** The parent map */
  511. protected final AbstractDualBidiMap parent;
  512. /** The last returned entry */
  513. protected Map.Entry last = null;
  514. /** Whether remove is allowed at present */
  515. protected boolean canRemove = false;
  516. /**
  517. * Constructor.
  518. * @param iterator the iterator to decorate
  519. * @param parent the parent map
  520. */
  521. protected EntrySetIterator(Iterator iterator, AbstractDualBidiMap parent) {
  522. super(iterator);
  523. this.parent = parent;
  524. }
  525. public Object next() {
  526. last = new MapEntry((Map.Entry) super.next(), parent);
  527. canRemove = true;
  528. return last;
  529. }
  530. public void remove() {
  531. if (canRemove == false) {
  532. throw new IllegalStateException("Iterator remove() can only be called once after next()");
  533. }
  534. // store value as remove may change the entry in the decorator (eg.TreeMap)
  535. Object value = last.getValue();
  536. super.remove();
  537. parent.maps[1].remove(value);
  538. last = null;
  539. canRemove = false;
  540. }
  541. }
  542. /**
  543. * Inner class MapEntry.
  544. */
  545. protected static class MapEntry extends AbstractMapEntryDecorator {
  546. /** The parent map */
  547. protected final AbstractDualBidiMap parent;
  548. /**
  549. * Constructor.
  550. * @param entry the entry to decorate
  551. * @param parent the parent map
  552. */
  553. protected MapEntry(Map.Entry entry, AbstractDualBidiMap parent) {
  554. super(entry);
  555. this.parent = parent;
  556. }
  557. public Object setValue(Object value) {
  558. Object key = MapEntry.this.getKey();
  559. if (parent.maps[1].containsKey(value) &&
  560. parent.maps[1].get(value) != key) {
  561. throw new IllegalArgumentException("Cannot use setValue() when the object being set is already in the map");
  562. }
  563. parent.put(key, value);
  564. final Object oldValue = super.setValue(value);
  565. return oldValue;
  566. }
  567. }
  568. /**
  569. * Inner class MapIterator.
  570. */
  571. protected static class BidiMapIterator implements MapIterator, ResettableIterator {
  572. /** The parent map */
  573. protected final AbstractDualBidiMap parent;
  574. /** The iterator being wrapped */
  575. protected Iterator iterator;
  576. /** The last returned entry */
  577. protected Map.Entry last = null;
  578. /** Whether remove is allowed at present */
  579. protected boolean canRemove = false;
  580. /**
  581. * Constructor.
  582. * @param parent the parent map
  583. */
  584. protected BidiMapIterator(AbstractDualBidiMap parent) {
  585. super();
  586. this.parent = parent;
  587. this.iterator = parent.maps[0].entrySet().iterator();
  588. }
  589. public boolean hasNext() {
  590. return iterator.hasNext();
  591. }
  592. public Object next() {
  593. last = (Map.Entry) iterator.next();
  594. canRemove = true;
  595. return last.getKey();
  596. }
  597. public void remove() {
  598. if (canRemove == false) {
  599. throw new IllegalStateException("Iterator remove() can only be called once after next()");
  600. }
  601. // store value as remove may change the entry in the decorator (eg.TreeMap)
  602. Object value = last.getValue();
  603. iterator.remove();
  604. parent.maps[1].remove(value);
  605. last = null;
  606. canRemove = false;
  607. }
  608. public Object getKey() {
  609. if (last == null) {
  610. throw new IllegalStateException("Iterator getKey() can only be called after next() and before remove()");
  611. }
  612. return last.getKey();
  613. }
  614. public Object getValue() {
  615. if (last == null) {
  616. throw new IllegalStateException("Iterator getValue() can only be called after next() and before remove()");
  617. }
  618. return last.getValue();
  619. }
  620. public Object setValue(Object value) {
  621. if (last == null) {
  622. throw new IllegalStateException("Iterator setValue() can only be called after next() and before remove()");
  623. }
  624. if (parent.maps[1].containsKey(value) &&
  625. parent.maps[1].get(value) != last.getKey()) {
  626. throw new IllegalArgumentException("Cannot use setValue() when the object being set is already in the map");
  627. }
  628. return parent.put(last.getKey(), value);
  629. }
  630. public void reset() {
  631. iterator = parent.maps[0].entrySet().iterator();
  632. last = null;
  633. canRemove = false;
  634. }
  635. public String toString() {
  636. if (last != null) {
  637. return "MapIterator[" + getKey() + "=" + getValue() + "]";
  638. } else {
  639. return "MapIterator[]";
  640. }
  641. }
  642. }
  643. }