PageRenderTime 25ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/Java.NET/JavApi Commons collections (Apache Port)/org.apache.commons.collections.map.MultiValueMap.cs

https://github.com/gadfly/nofs
C# | 505 lines | 269 code | 36 blank | 200 comment | 33 complexity | dfd859f07557583312a312c40c2db78d MD5 | raw file
  1. /*
  2. * Licensed under the Apache License, Version 2.0 (the "License");
  3. * you may not use this file except in compliance with the License.
  4. * You may obtain a copy of the License at
  5. *
  6. * http://www.apache.org/licenses/LICENSE-2.0
  7. *
  8. * Unless required by applicable law or agreed to in writing, software
  9. * distributed under the License is distributed on an "AS IS" BASIS,
  10. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. * See the License for the specific language governing permissions and
  12. * limitations under the License.
  13. *
  14. */
  15. using System;
  16. using java = biz.ritter.javapi;
  17. using org.apache.commons.collections;
  18. using org.apache.commons.collections.iterators;
  19. namespace org.apache.commons.collections.map
  20. {
  21. /**
  22. * A MultiValueMap decorates another map, allowing it to have
  23. * more than one value for a key.
  24. * <p>
  25. * A <code>MultiMap</code> is a Map with slightly different semantics.
  26. * Putting a value into the map will add the value to a Collection at that key.
  27. * Getting a value will return a Collection, holding all the values put to that key.
  28. * <p>
  29. * This implementation is a decorator, allowing any Map implementation
  30. * to be used as the base.
  31. * <p>
  32. * In addition, this implementation allows the type of collection used
  33. * for the values to be controlled. By default, an <code>ArrayList</code>
  34. * is used, however a <code>Class</code> to instantiate may be specified,
  35. * or a factory that returns a <code>Collection</code> instance.
  36. * <p>
  37. * <strong>Note that MultiValueMap is not synchronized and is not thread-safe.</strong>
  38. * If you wish to use this map from multiple threads concurrently, you must use
  39. * appropriate synchronization. This class may throw exceptions when accessed
  40. * by concurrent threads without synchronization.
  41. *
  42. * @author James Carman
  43. * @author Christopher Berry
  44. * @author James Strachan
  45. * @author Steve Downey
  46. * @author Stephen Colebourne
  47. * @author Julien Buret
  48. * @author Serhiy Yevtushenko
  49. * @version $Revision$ $Date$
  50. * @since Commons Collections 3.2
  51. */
  52. public class MultiValueMap : AbstractMapDecorator, MultiMap
  53. {
  54. /** The factory for creating value collections. */
  55. private readonly Factory collectionFactory;
  56. /** The cached values. */
  57. [NonSerialized]
  58. private java.util.Collection<Object> valuesJ;
  59. /**
  60. * Creates a map which wraps the given map and
  61. * maps keys to ArrayLists.
  62. *
  63. * @param map the map to wrap
  64. */
  65. public static MultiValueMap decorate(java.util.Map<Object, Object> map)
  66. {
  67. return new MultiValueMap(map, new ReflectionFactory(new java.lang.Class(typeof(java.util.ArrayList<Object>))));
  68. }
  69. /**
  70. * Creates a map which decorates the given <code>map</code> and
  71. * maps keys to collections of type <code>collectionClass</code>.
  72. *
  73. * @param map the map to wrap
  74. * @param collectionClass the type of the collection class
  75. */
  76. public static MultiValueMap decorate(java.util.Map<Object, Object> map, java.lang.Class collectionClass)
  77. {
  78. return new MultiValueMap(map, new ReflectionFactory(collectionClass));
  79. }
  80. /**
  81. * Creates a map which decorates the given <code>map</code> and
  82. * creates the value collections using the supplied <code>collectionFactory</code>.
  83. *
  84. * @param map the map to decorate
  85. * @param collectionFactory the collection factory (must return a Collection object).
  86. */
  87. public static MultiValueMap decorate(java.util.Map<Object, Object> map, Factory collectionFactory)
  88. {
  89. return new MultiValueMap(map, collectionFactory);
  90. }
  91. //-----------------------------------------------------------------------
  92. /**
  93. * Creates a MultiValueMap based on a <code>HashMap</code> and
  94. * storing the multiple values in an <code>ArrayList</code>.
  95. */
  96. public MultiValueMap() :
  97. this(new java.util.HashMap<Object, Object>(), new ReflectionFactory(new java.lang.Class(typeof(java.util.ArrayList<Object>))))
  98. {
  99. }
  100. /**
  101. * Creates a MultiValueMap which decorates the given <code>map</code> and
  102. * creates the value collections using the supplied <code>collectionFactory</code>.
  103. *
  104. * @param map the map to decorate
  105. * @param collectionFactory the collection factory which must return a Collection instance
  106. */
  107. protected MultiValueMap(java.util.Map<Object, Object> map, Factory collectionFactory) :
  108. base(map)
  109. {
  110. if (collectionFactory == null)
  111. {
  112. throw new java.lang.IllegalArgumentException("The factory must not be null");
  113. }
  114. this.collectionFactory = collectionFactory;
  115. }
  116. //-----------------------------------------------------------------------
  117. /**
  118. * Clear the map.
  119. */
  120. public override void clear()
  121. {
  122. // If you believe that you have GC issues here, try uncommenting this code
  123. // Set pairs = getMap().entrySet();
  124. // Iterator pairsIterator = pairs.iterator();
  125. // while (pairsIterator.hasNext()) {
  126. // Map.Entry keyValuePair = (Map.Entry) pairsIterator.next();
  127. // Collection coll = (Collection) keyValuePair.getValue();
  128. // coll.clear();
  129. // }
  130. getMap().clear();
  131. }
  132. /**
  133. * Removes a specific value from map.
  134. * <p>
  135. * The item is removed from the collection mapped to the specified key.
  136. * Other values attached to that key are unaffected.
  137. * <p>
  138. * If the last value for a key is removed, <code>null</code> will be returned
  139. * from a subsequant <code>get(key)</code>.
  140. *
  141. * @param key the key to remove from
  142. * @param value the value to remove
  143. * @return the value removed (which was passed in), null if nothing removed
  144. */
  145. public virtual Object remove(Object key, Object value)
  146. {
  147. java.util.Collection<Object> valuesForKey = getCollection(key);
  148. if (valuesForKey == null)
  149. {
  150. return null;
  151. }
  152. bool removed = valuesForKey.remove(value);
  153. if (removed == false)
  154. {
  155. return null;
  156. }
  157. if (valuesForKey.isEmpty())
  158. {
  159. remove(key);
  160. }
  161. return value;
  162. }
  163. /**
  164. * Checks whether the map contains the value specified.
  165. * <p>
  166. * This checks all collections against all keys for the value, and thus could be slow.
  167. *
  168. * @param value the value to search for
  169. * @return true if the map contains the value
  170. */
  171. public override bool containsValue(Object value)
  172. {
  173. java.util.Set<java.util.MapNS.Entry<Object, Object>> pairs = getMap().entrySet();
  174. if (pairs == null)
  175. {
  176. return false;
  177. }
  178. java.util.Iterator<java.util.MapNS.Entry<Object, Object>> pairsIterator = pairs.iterator();
  179. while (pairsIterator.hasNext())
  180. {
  181. java.util.MapNS.Entry<Object, Object> keyValuePair = pairsIterator.next();
  182. java.util.Collection<Object> coll = (java.util.Collection<Object>)keyValuePair.getValue();
  183. if (coll.contains(value))
  184. {
  185. return true;
  186. }
  187. }
  188. return false;
  189. }
  190. /**
  191. * Adds the value to the collection associated with the specified key.
  192. * <p>
  193. * Unlike a normal <code>Map</code> the previous value is not replaced.
  194. * Instead the new value is added to the collection stored against the key.
  195. *
  196. * @param key the key to store against
  197. * @param value the value to add to the collection at the key
  198. * @return the value added if the map changed and null if the map did not change
  199. */
  200. public override Object put(Object key, Object value)
  201. {
  202. bool result = false;
  203. java.util.Collection<Object> coll = getCollection(key);
  204. if (coll == null)
  205. {
  206. coll = createCollection(1);
  207. result = coll.add(value);
  208. if (coll.size() > 0)
  209. {
  210. // only add if non-zero size to maintain class state
  211. getMap().put(key, coll);
  212. result = false;
  213. }
  214. }
  215. else
  216. {
  217. result = coll.add(value);
  218. }
  219. return (result ? value : null);
  220. }
  221. /**
  222. * Override superclass to ensure that MultiMap instances are
  223. * correctly handled.
  224. * <p>
  225. * If you call this method with a normal map, each entry is
  226. * added using <code>put(Object,Object)</code>.
  227. * If you call this method with a multi map, each entry is
  228. * added using <code>putAll(Object,Collection)</code>.
  229. *
  230. * @param map the map to copy (either a normal or multi map)
  231. */
  232. public override void putAll(java.util.Map<Object, Object> map)
  233. {
  234. if (map is MultiMap)
  235. {
  236. for (java.util.Iterator<java.util.MapNS.Entry<Object, Object>> it = map.entrySet().iterator(); it.hasNext(); )
  237. {
  238. java.util.MapNS.Entry<Object, Object> entry = it.next();
  239. java.util.Collection<Object> coll = (java.util.Collection<Object>)entry.getValue();
  240. putAll(entry.getKey(), coll);
  241. }
  242. }
  243. else
  244. {
  245. for (java.util.Iterator<java.util.MapNS.Entry<Object, Object>> it = map.entrySet().iterator(); it.hasNext(); )
  246. {
  247. java.util.MapNS.Entry<Object, Object> entry = it.next();
  248. put(entry.getKey(), entry.getValue());
  249. }
  250. }
  251. }
  252. /**
  253. * Gets a collection containing all the values in the map.
  254. * <p>
  255. * This returns a collection containing the combination of values from all keys.
  256. *
  257. * @return a collection view of the values contained in this map
  258. */
  259. public override java.util.Collection<Object> values()
  260. {
  261. java.util.Collection<Object> vs = valuesJ;
  262. return (vs != null ? vs : (valuesJ = new Values(this)));
  263. }
  264. /**
  265. * Checks whether the collection at the specified key contains the value.
  266. *
  267. * @param value the value to search for
  268. * @return true if the map contains the value
  269. */
  270. public virtual bool containsValue(Object key, Object value)
  271. {
  272. java.util.Collection<Object> coll = getCollection(key);
  273. if (coll == null)
  274. {
  275. return false;
  276. }
  277. return coll.contains(value);
  278. }
  279. /**
  280. * Gets the collection mapped to the specified key.
  281. * This method is a convenience method to typecast the result of <code>get(key)</code>.
  282. *
  283. * @param key the key to retrieve
  284. * @return the collection mapped to the key, null if no mapping
  285. */
  286. public java.util.Collection<Object> getCollection(Object key)
  287. {
  288. return (java.util.Collection<Object>)getMap().get(key);
  289. }
  290. /**
  291. * Gets the size of the collection mapped to the specified key.
  292. *
  293. * @param key the key to get size for
  294. * @return the size of the collection at the key, zero if key not in map
  295. */
  296. public int size(Object key)
  297. {
  298. java.util.Collection<Object> coll = getCollection(key);
  299. if (coll == null)
  300. {
  301. return 0;
  302. }
  303. return coll.size();
  304. }
  305. /**
  306. * Adds a collection of values to the collection associated with
  307. * the specified key.
  308. *
  309. * @param key the key to store against
  310. * @param values the values to add to the collection at the key, null ignored
  311. * @return true if this map changed
  312. */
  313. public virtual bool putAll(Object key, java.util.Collection<Object> values)
  314. {
  315. if (values == null || values.size() == 0)
  316. {
  317. return false;
  318. }
  319. java.util.Collection<Object> coll = getCollection(key);
  320. if (coll == null)
  321. {
  322. coll = createCollection(values.size());
  323. bool result = coll.addAll(values);
  324. if (coll.size() > 0)
  325. {
  326. // only add if non-zero size to maintain class state
  327. getMap().put(key, coll);
  328. result = false;
  329. }
  330. return result;
  331. }
  332. else
  333. {
  334. return coll.addAll(values);
  335. }
  336. }
  337. /**
  338. * Gets an iterator for the collection mapped to the specified key.
  339. *
  340. * @param key the key to get an iterator for
  341. * @return the iterator of the collection at the key, empty iterator if key not in map
  342. */
  343. public virtual java.util.Iterator<Object> iterator(Object key)
  344. {
  345. if (!containsKey(key))
  346. {
  347. return EmptyIterator.INSTANCE;
  348. }
  349. else
  350. {
  351. return new ValuesIterator(key, this);
  352. }
  353. }
  354. /**
  355. * Gets the total size of the map by counting all the values.
  356. *
  357. * @return the total size of the map counting all values
  358. */
  359. public virtual int totalSize()
  360. {
  361. int total = 0;
  362. java.util.Collection<Object> values = getMap().values();
  363. for (java.util.Iterator<Object> it = values.iterator(); it.hasNext(); )
  364. {
  365. java.util.Collection<Object> coll = (java.util.Collection<Object>)it.next();
  366. total += coll.size();
  367. }
  368. return total;
  369. }
  370. /**
  371. * Creates a new instance of the map value Collection container
  372. * using the factory.
  373. * <p>
  374. * This method can be overridden to perform your own processing
  375. * instead of using the factory.
  376. *
  377. * @param size the collection size that is about to be added
  378. * @return the new collection
  379. */
  380. protected virtual java.util.Collection<Object> createCollection(int size)
  381. {
  382. return (java.util.Collection<Object>)collectionFactory.create();
  383. }
  384. //-----------------------------------------------------------------------
  385. /**
  386. * Inner class that provides the values view.
  387. */
  388. private class Values : java.util.AbstractCollection<Object>
  389. {
  390. private readonly MultiValueMap root;
  391. public Values(MultiValueMap root)
  392. {
  393. this.root = root;
  394. }
  395. public override java.util.Iterator<Object> iterator()
  396. {
  397. IteratorChain chain = new IteratorChain();
  398. for (java.util.Iterator<Object> it = root.keySet().iterator(); it.hasNext(); )
  399. {
  400. chain.addIterator(new ValuesIterator(it.next(), root));
  401. }
  402. return chain;
  403. }
  404. public override int size()
  405. {
  406. return root.totalSize();
  407. }
  408. public override void clear()
  409. {
  410. root.clear();
  411. }
  412. }
  413. /**
  414. * Inner class that provides the values iterator.
  415. */
  416. private class ValuesIterator : java.util.Iterator<Object>
  417. {
  418. private readonly MultiValueMap root;
  419. private readonly Object key;
  420. private readonly java.util.Collection<Object> values;
  421. private readonly java.util.Iterator<Object> iterator;
  422. public ValuesIterator(Object key, MultiValueMap root)
  423. {
  424. this.root = root;
  425. this.key = key;
  426. this.values = root.getCollection(key);
  427. this.iterator = values.iterator();
  428. }
  429. public void remove()
  430. {
  431. iterator.remove();
  432. if (values.isEmpty())
  433. {
  434. root.remove(key);
  435. }
  436. }
  437. public bool hasNext()
  438. {
  439. return iterator.hasNext();
  440. }
  441. public Object next()
  442. {
  443. return iterator.next();
  444. }
  445. }
  446. /**
  447. * Inner class that provides a simple reflection factory.
  448. */
  449. private class ReflectionFactory : Factory
  450. {
  451. private readonly java.lang.Class clazz;
  452. public ReflectionFactory(java.lang.Class clazz)
  453. {
  454. this.clazz = clazz;
  455. }
  456. public Object create()
  457. {
  458. try
  459. {
  460. return clazz.newInstance();
  461. }
  462. catch (java.lang.Exception ex)
  463. {
  464. throw new FunctorException("Cannot instantiate class: " + clazz, ex);
  465. }
  466. }
  467. }
  468. }
  469. }