001: /*
002: Copyright (c) 2006-2007, Dennis M. Sosnoski
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without modification,
006: are permitted provided that the following conditions are met:
007:
008: * Redistributions of source code must retain the above copyright notice, this
009: list of conditions and the following disclaimer.
010: * Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: * Neither the name of JiBX nor the names of its contributors may be used
014: to endorse or promote products derived from this software without specific
015: prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
018: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
019: WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
020: DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
021: ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
024: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026: SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027: */
028:
029: package org.jibx.binding.util;
030:
031: import java.util.ArrayList;
032: import java.util.Collections;
033: import java.util.HashMap;
034: import java.util.List;
035: import java.util.Set;
036:
037: /**
038: * Map supporting multiple values for a single key. The multiple value concept
039: * doesn't really fit with the standard collections idea of a map, so this
040: * provides its own variation of a map interface rather than extend the standard
041: * one.
042: *
043: * @author Dennis M. Sosnoski
044: */
045: public class MultipleValueMap {
046: /** Backing map from key to value or array of values. */
047: private final HashMap m_backingMap;
048:
049: /** Actual number of values (not keys) present in map. */
050: private int m_valueCount;
051:
052: /** Last lookup key (<code>null</code> if none, or if value changed). */
053: private Object m_lastKey;
054:
055: /** Last lookup value (<code>null</code> if none, or if value changed). */
056: private Object m_lastValue;
057:
058: /**
059: * Constructor.
060: */
061: public MultipleValueMap() {
062: super ();
063: m_backingMap = new HashMap();
064: }
065:
066: /**
067: * Internal cached lookup.
068: *
069: * @param key
070: * @return value
071: */
072: private Object getMapped(Object key) {
073: if (key != m_lastKey) {
074: m_lastKey = key;
075: m_lastValue = m_backingMap.get(key);
076: }
077: return m_lastValue;
078: }
079:
080: /**
081: * Clear all entries.
082: */
083: public void clear() {
084: m_backingMap.clear();
085: m_valueCount = 0;
086: }
087:
088: /**
089: * Get number of values present for key.
090: *
091: * @param key
092: * @return value count
093: */
094: public int getCount(Object key) {
095: Object obj = getMapped(key);
096: if (obj instanceof MultipleValueList) {
097: return ((MultipleValueList) obj).size();
098: } else if (obj == null) {
099: return 0;
100: } else {
101: return 1;
102: }
103: }
104:
105: /**
106: * Get indexed value for key.
107: *
108: * @param key
109: * @param index
110: * @return value
111: */
112: public Object get(Object key, int index) {
113: Object obj = getMapped(key);
114: if (obj instanceof MultipleValueList) {
115: return ((MultipleValueList) obj).get(index);
116: } else if (obj == null) {
117: throw new IndexOutOfBoundsException(
118: "No value present for key");
119: } else if (index == 0) {
120: return obj;
121: } else {
122: throw new IndexOutOfBoundsException(
123: "Only one value present for key");
124: }
125: }
126:
127: /**
128: * Add value for key.
129: *
130: * @param key
131: * @param value
132: */
133: public void add(Object key, Object value) {
134:
135: // first force caching of current value
136: getMapped(key);
137:
138: // update value as appropriate
139: if (m_lastValue == null) {
140: m_backingMap.put(key, value);
141: m_lastValue = value;
142: } else if (m_lastValue instanceof MultipleValueList) {
143: ((MultipleValueList) m_lastValue).add(value);
144: } else {
145: MultipleValueList list = new MultipleValueList();
146: list.add(m_lastValue);
147: list.add(value);
148: m_backingMap.put(key, list);
149: m_lastValue = list;
150: }
151: m_valueCount++;
152: }
153:
154: /**
155: * Extract all values for key. This removes the value(s) from the map and
156: * returns them in the form of a list.
157: *
158: * @param key
159: * @return prior list of values
160: */
161: public List extract(Object key) {
162: Object obj = m_backingMap.remove(key);
163: if (key == m_lastKey) {
164: m_lastKey = null;
165: m_lastValue = null;
166: }
167: if (obj instanceof MultipleValueList) {
168: return (MultipleValueList) obj;
169: } else if (obj == null) {
170: return Collections.EMPTY_LIST;
171: } else {
172: MultipleValueList list = new MultipleValueList();
173: list.add(obj);
174: return list;
175: }
176: }
177:
178: /**
179: * Get number of keys.
180: *
181: * @return key count
182: */
183: public int keySize() {
184: return m_backingMap.size();
185: }
186:
187: /**
188: * Get number of values.
189: *
190: * @return value count
191: */
192: public int valueSize() {
193: return m_valueCount;
194: }
195:
196: //
197: // Delegated methods
198:
199: /**
200: * Check key present in map.
201: *
202: * @param key
203: * @return key present flag
204: */
205: public boolean containsKey(Object key) {
206: return m_backingMap.containsKey(key);
207: }
208:
209: /**
210: * Check if map is empty.
211: *
212: * @return empty flag
213: */
214: public boolean isEmpty() {
215: return m_backingMap.isEmpty();
216: }
217:
218: /**
219: * Get key set.
220: *
221: * @return set of keys
222: */
223: public Set keySet() {
224: return m_backingMap.keySet();
225: }
226:
227: /**
228: * List used for multiple values. This is just a marker, so that the actual
229: * values can be anything at all (including lists).
230: */
231: private class MultipleValueList extends ArrayList {
232: public MultipleValueList() {
233: }
234: }
235: }
|