001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021 022package org.biojava.utils; 023 024import java.util.AbstractMap; 025import java.util.AbstractSet; 026import java.util.HashMap; 027import java.util.Iterator; 028import java.util.Map; 029import java.util.NoSuchElementException; 030import java.util.Set; 031 032/** 033 * <p> 034 * Overlap one map onto another. This allows you to have a map with local values 035 * and default values. The local and default values are provided by a child and 036 * parent map. 037 * </p> 038 * 039 * @author Thomas Down 040 * @author Matthew Pocock 041 */ 042 043public class OverlayMap extends AbstractMap { 044 private Map parent; 045 private Map overlay; 046 047 /** 048 * Build a new map with default key-value pairs. 049 * 050 * @param parent the default fall-through Map 051 * @param overlay the overriding Map 052 */ 053 public OverlayMap(Map parent, Map overlay) { 054 super(); 055 this.parent = parent; 056 this.overlay = overlay; 057 } 058 059 /** 060 * Build a new map with default key-value pairs. 061 * 062 * @param parent the default fall-through Map 063 */ 064 public OverlayMap(Map parent) { 065 super(); 066 this.parent = parent; 067 this.overlay = new HashMap(); 068 } 069 070 /** 071 * Return the object containing the fallback mappings. 072 * This is the actual parent map, not a copy. 073 * 074 * @return the parent map 075 */ 076 077 public Map getParentMap() { 078 return parent; 079 } 080 081 /** 082 * Return the object containing the overlay mappings. 083 * This is the actual child map, not a copy. 084 * 085 * @return the child map 086 */ 087 088 public Map getOverlayMap() { 089 return overlay; 090 } 091 092 // 093 // Basic map operations, done explicitly 094 // 095 096 public Object get(Object key) { 097 Object value = overlay.get(key); 098 if (value == null) 099 value = parent.get(key); 100 return value; 101 } 102 103 public Set entrySet() { 104 return new OEntrySet(); 105 } 106 107 public Set keySet() { 108 return new OKeySet(); 109 } 110 111 public boolean containsKey(Object key) { 112 return overlay.containsKey(key) || parent.containsKey(key); 113 } 114 115 public Object put(Object key, Object value) { 116 Object old = get(key); 117 overlay.put(key, value); 118 return old; 119 } 120 121 private class OKeySet extends AbstractSet { 122 private Set parentKeys; 123 124 private OKeySet() { 125 super(); 126 parentKeys = parent.keySet(); 127 } 128 129 public Iterator iterator() { 130 return new Iterator() { 131 Iterator oi = overlay.keySet().iterator(); 132 Iterator pi = parentKeys.iterator(); 133 Object peek = null; 134 135 public boolean hasNext() { 136 if (peek == null) 137 peek = nextObject(); 138 return (peek != null); 139 } 140 141 public Object next() { 142 if (peek == null) { 143 peek = nextObject(); 144 } 145 if (peek == null) { 146 throw new NoSuchElementException(); 147 } 148 Object o = peek; 149 peek = null; 150 return o; 151 } 152 153 private Object nextObject() { 154 if (oi.hasNext()) { 155 return oi.next(); 156 } 157 Object po = null; 158 while (po == null && pi.hasNext()) { 159 po = pi.next(); 160 if (overlay.containsKey(po)) { 161 po = null; 162 } 163 } 164 return po; 165 } 166 167 public void remove() { 168 throw new UnsupportedOperationException(); 169 } 170 }; 171 } 172 173 public int size() { 174 int i = 0; 175 Iterator keys = iterator(); 176 while(keys.hasNext()) { 177 keys.next(); 178 ++i; 179 } 180 return i; 181 } 182 183 public boolean contains(Object o) { 184 return overlay.containsKey(o) || parentKeys.contains(o); 185 } 186 } 187 188 private class OEntrySet extends AbstractSet { 189 OKeySet ks; 190 191 private OEntrySet() { 192 super(); 193 ks = new OKeySet(); 194 } 195 196 public Iterator iterator() { 197 return new Iterator() { 198 Iterator ksi = ks.iterator(); 199 200 public boolean hasNext() { 201 return ksi.hasNext(); 202 } 203 204 public Object next() { 205 Object k = ksi.next(); 206 Object v = get(k); 207 return new OMapEntry(k, v); 208 } 209 210 public void remove() { 211 throw new UnsupportedOperationException(); 212 } 213 }; 214 } 215 216 public int size() { 217 return ks.size(); 218 } 219 } 220 221 private static class OMapEntry implements Map.Entry { 222 private Object key; 223 private Object value; 224 225 private OMapEntry(Object key, Object value) { 226 this.key = key; 227 this.value = value; 228 } 229 230 public Object getKey() { 231 return key; 232 } 233 234 public Object getValue() { 235 return value; 236 } 237 238 public Object setValue(Object v) { 239 throw new UnsupportedOperationException(); 240 } 241 242 public boolean equals(Object o) { 243 if (! (o instanceof Map.Entry)) { 244 return false; 245 } 246 247 Map.Entry mo = (Map.Entry) o; 248 return ((key == null ? mo.getKey() == null : key.equals(mo.getKey())) && 249 (value == null ? mo.getValue() == null : value.equals(mo.getValue()))); 250 } 251 252 public int hashCode() { 253 return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); 254 } 255 } 256}