001package ball.util; 002/*- 003 * ########################################################################## 004 * Utilities 005 * $Id: MapView.java 5285 2020-02-05 04:23:21Z ball $ 006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/ball-util/trunk/src/main/java/ball/util/MapView.java $ 007 * %% 008 * Copyright (C) 2008 - 2020 Allen D. Ball 009 * %% 010 * Licensed under the Apache License, Version 2.0 (the "License"); 011 * you may not use this file except in compliance with the License. 012 * You may obtain a copy of the License at 013 * 014 * http://www.apache.org/licenses/LICENSE-2.0 015 * 016 * Unless required by applicable law or agreed to in writing, software 017 * distributed under the License is distributed on an "AS IS" BASIS, 018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 019 * See the License for the specific language governing permissions and 020 * limitations under the License. 021 * ########################################################################## 022 */ 023import java.io.Serializable; 024import java.util.AbstractMap; 025import java.util.ArrayList; 026import java.util.Iterator; 027import java.util.LinkedHashSet; 028import java.util.Map; 029import java.util.Set; 030 031/** 032 * {@link Map} implementation base class to provide a view of a wrapped 033 * {@link Map}. Subclass designers should override {@link #entrySet()} 034 * first. All implemented methods simply pass the call to the wrapped 035 * {@link Map} (all other methods are implemented by {@link AbstractMap}). 036 * 037 * @param <K> The key type. 038 * @param <V> The value type. 039 * 040 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 041 * @version $Revision: 5285 $ 042 */ 043public class MapView<K,V> extends AbstractMap<K,V> implements Serializable { 044 private static final long serialVersionUID = -1914866590078121842L; 045 046 /** @serial */ private final Map<K,V> map; 047 /** @serial */ protected final EntrySet entrySet = new EntrySet(); 048 049 /** 050 * Sole constructor. 051 * 052 * @param map The {@link Map}. 053 */ 054 public MapView(Map<K,V> map) { 055 super(); 056 057 this.map = map; 058 } 059 060 /** 061 * @return The "wrapped" {@link Map}. 062 */ 063 protected Map<K,V> map() { return map; } 064 065 @Override 066 public V get(Object key) { return map().get(key); } 067 068 @Override 069 public V put(K key, V value) { return map().put(key, value); } 070 071 @Override 072 public V remove(Object key) { return map().remove(key); } 073 074 @Override 075 public void clear() { map().clear(); } 076 077 /** 078 * Method returns a single {@link Set} backed by {@link #map()}. 079 * Subclass implementations should update {@link #entrySet}. 080 * 081 * @return {@link #entrySet} 082 */ 083 @Override 084 public Set<Entry<K,V>> entrySet() { 085 entrySet.clear(); 086 entrySet.addAll(map().entrySet()); 087 088 return entrySet; 089 } 090 091 /** 092 * {@link #entrySet} implementation class. 093 */ 094 protected class EntrySet extends LinkedHashSet<Entry<K,V>> { 095 private static final long serialVersionUID = 5015923978722180032L; 096 097 /** 098 * Sole constructor. 099 */ 100 public EntrySet() { super(); } 101 102 @Override 103 public boolean remove(Object object) { 104 boolean changed = super.remove(object); 105 106 if (changed) { 107 MapView.this.remove(((Entry<?,?>) object).getKey()); 108 } 109 110 return changed; 111 } 112 113 @Override 114 public Iterator<Entry<K,V>> iterator() { 115 ArrayList<Entry<K,V>> list = new ArrayList<>(); 116 Iterator<Entry<K,V>> iterator = super.iterator(); 117 118 while (iterator.hasNext()) { 119 list.add(iterator.next()); 120 } 121 122 return new EntryIterator(list.iterator()); 123 } 124 } 125 126 private class EntryIterator implements Iterator<Entry<K,V>> { 127 private final Iterator<Entry<K,V>> iterator; 128 private Entry<K,V> current = null; 129 130 public EntryIterator(Iterator<Entry<K,V>> iterator) { 131 this.iterator = iterator; 132 } 133 134 @Override 135 public boolean hasNext() { return iterator.hasNext(); } 136 137 @Override 138 public Entry<K,V> next() { 139 current = iterator.next(); 140 141 return current; 142 } 143 144 @Override 145 public void remove() { 146 iterator.remove(); 147 MapView.this.remove(current.getKey()); 148 } 149 150 @Override 151 public String toString() { return iterator.toString(); } 152 } 153}