PageRenderTime 60ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/src/sys/java/fan/sys/Map.java

https://bitbucket.org/bedlaczech/fan-1.0
Java | 740 lines | 605 code | 91 blank | 44 comment | 132 complexity | e8b395f3bb7e485d4ba70604dda32044 MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. //
  2. // Copyright (c) 2006, Brian Frank and Andy Frank
  3. // Licensed under the Academic Free License version 3.0
  4. //
  5. // History:
  6. // 17 Mar 06 Brian Frank Creation
  7. //
  8. package fan.sys;
  9. import java.lang.Thread;
  10. import java.util.AbstractSet;
  11. import java.util.Arrays;
  12. import java.util.HashMap;
  13. import java.util.Map.Entry;
  14. import java.util.LinkedHashMap;
  15. import java.util.Iterator;
  16. import java.util.Set;
  17. import fanx.serial.*;
  18. import fanx.util.OpUtil;
  19. /**
  20. * Map is a hashmap of key value pairs.
  21. */
  22. public final class Map
  23. extends FanObj
  24. implements Literal
  25. {
  26. //////////////////////////////////////////////////////////////////////////
  27. // Constructors
  28. //////////////////////////////////////////////////////////////////////////
  29. public static Map make(Type type)
  30. {
  31. return new Map((MapType)type, new HashMap());
  32. }
  33. public Map(Type k, Type v)
  34. {
  35. this(new MapType(k, v), new HashMap());
  36. }
  37. public Map(MapType type)
  38. {
  39. this(type, new HashMap());
  40. }
  41. public Map(MapType type, HashMap map)
  42. {
  43. if (type == null || map == null) { Thread.dumpStack(); throw NullErr.make(); }
  44. this.type = type;
  45. this.map = map;
  46. }
  47. //////////////////////////////////////////////////////////////////////////
  48. // Identity
  49. //////////////////////////////////////////////////////////////////////////
  50. public final Type typeof()
  51. {
  52. return type;
  53. }
  54. //////////////////////////////////////////////////////////////////////////
  55. // Methods
  56. //////////////////////////////////////////////////////////////////////////
  57. public final boolean isEmpty()
  58. {
  59. return map.size() == 0;
  60. }
  61. public final long size()
  62. {
  63. return map.size();
  64. }
  65. public final Object get(Object key)
  66. {
  67. Object val = map.get(key);
  68. if (val != null) return val;
  69. if (this.def == null) return null;
  70. return map.containsKey(key) ? null : this.def;
  71. }
  72. public final Object get(Object key, Object def)
  73. {
  74. Object val = map.get(key);
  75. if (val != null) return val;
  76. if (def == null) return null;
  77. return map.containsKey(key) ? null : def;
  78. }
  79. public final Object getOrThrow(Object key)
  80. {
  81. Object val = map.get(key);
  82. if (val != null) return val;
  83. if (map.containsKey(key)) return null;
  84. throw UnknownKeyErr.make(String.valueOf(key));
  85. }
  86. public final boolean containsKey(Object key)
  87. {
  88. return map.containsKey(key);
  89. }
  90. public final List keys()
  91. {
  92. Object[] keys = new Object[map.size()];
  93. Iterator it = pairsIterator();
  94. for (int i=0; it.hasNext(); ++i)
  95. keys[i] = ((Entry)it.next()).getKey();
  96. return new List(type.k, keys);
  97. }
  98. public final List vals()
  99. {
  100. return new List(type.v, map.values());
  101. }
  102. public final Map set(Object key, Object value)
  103. {
  104. modify();
  105. if (key == null)
  106. throw NullErr.make("key is null");
  107. if (!isImmutable(key))
  108. throw NotImmutableErr.make("key is not immutable: " + typeof(key));
  109. map.put(key, value);
  110. return this;
  111. }
  112. public final Map add(Object key, Object value)
  113. {
  114. modify();
  115. if (key == null)
  116. throw NullErr.make("key is null");
  117. if (!isImmutable(key))
  118. throw NotImmutableErr.make("key is not immutable: " + typeof(key));
  119. if (map.containsKey(key))
  120. throw ArgErr.make("Key already mapped: " + key);
  121. map.put(key, value);
  122. return this;
  123. }
  124. public final Object getOrAdd(Object key, Func valFunc)
  125. {
  126. if (map.containsKey(key)) return map.get(key);
  127. Object val = valFunc.call(key);
  128. add(key, val);
  129. return val;
  130. }
  131. public final Map setAll(Map m)
  132. {
  133. modify();
  134. Iterator it = m.pairsIterator();
  135. while (it.hasNext())
  136. {
  137. Entry e = (Entry)it.next();
  138. map.put(e.getKey(), e.getValue());
  139. }
  140. return this;
  141. }
  142. public final Map addAll(Map m)
  143. {
  144. modify();
  145. Iterator it = m.pairsIterator();
  146. while (it.hasNext())
  147. {
  148. Entry e = (Entry)it.next();
  149. add(e.getKey(), e.getValue());
  150. }
  151. return this;
  152. }
  153. public final Map setList(List list) { return setList(list, null); }
  154. public final Map setList(List list, Func f)
  155. {
  156. modify();
  157. if (f == null)
  158. {
  159. for (int i=0; i<list.sz(); ++i)
  160. set(list.get(i), list.get(i));
  161. }
  162. else if (f.arity() == 1)
  163. {
  164. for (int i=0; i<list.sz(); ++i)
  165. set(f.call(list.get(i)), list.get(i));
  166. }
  167. else
  168. {
  169. for (int i=0; i<list.sz(); ++i)
  170. set(f.call(list.get(i), Long.valueOf(i)), list.get(i));
  171. }
  172. return this;
  173. }
  174. public final Map addList(List list) { return addList(list, null); }
  175. public final Map addList(List list, Func f)
  176. {
  177. modify();
  178. if (f == null)
  179. {
  180. for (int i=0; i<list.sz(); ++i)
  181. add(list.get(i), list.get(i));
  182. }
  183. else if (f.arity() == 1)
  184. {
  185. for (int i=0; i<list.sz(); ++i)
  186. add(f.call(list.get(i)), list.get(i));
  187. }
  188. else
  189. {
  190. for (int i=0; i<list.sz(); ++i)
  191. add(f.call(list.get(i), Long.valueOf(i)), list.get(i));
  192. }
  193. return this;
  194. }
  195. public final Object remove(Object key)
  196. {
  197. modify();
  198. return map.remove(key);
  199. }
  200. public final Map dup()
  201. {
  202. Map dup = new Map(type);
  203. dup.map = (HashMap)this.map.clone();
  204. return dup;
  205. }
  206. public final Map clear()
  207. {
  208. modify();
  209. map.clear();
  210. return this;
  211. }
  212. public final boolean caseInsensitive()
  213. {
  214. return map instanceof CIHashMap;
  215. }
  216. public final void caseInsensitive(boolean v)
  217. {
  218. modify();
  219. if (type.k != Sys.StrType)
  220. throw UnsupportedErr.make("Map not keyed by Str: " + type);
  221. if (map.size() != 0)
  222. throw UnsupportedErr.make("Map not empty");
  223. if (v && ordered())
  224. throw UnsupportedErr.make("Map cannot be caseInsensitive and ordered");
  225. if (caseInsensitive() == v) return;
  226. if (v)
  227. map = new CIHashMap();
  228. else
  229. map = new HashMap();
  230. }
  231. public final boolean ordered()
  232. {
  233. return map instanceof LinkedHashMap;
  234. }
  235. public final void ordered(boolean v)
  236. {
  237. modify();
  238. if (map.size() != 0)
  239. throw UnsupportedErr.make("Map not empty");
  240. if (v && caseInsensitive())
  241. throw UnsupportedErr.make("Map cannot be caseInsensitive and ordered");
  242. if (ordered() == v) return;
  243. if (v)
  244. map = new LinkedHashMap();
  245. else
  246. map = new HashMap();
  247. }
  248. public final Object def() { return def; }
  249. public final void def(Object v)
  250. {
  251. modify();
  252. if (v != null && !isImmutable(v))
  253. throw NotImmutableErr.make("def must be immutable: " + typeof(v));
  254. this.def = v;
  255. }
  256. public final boolean equals(Object that)
  257. {
  258. if (that instanceof Map)
  259. {
  260. return type.equals(typeof(that)) && map.equals(((Map)that).map);
  261. }
  262. return false;
  263. }
  264. public final long hash()
  265. {
  266. return map.hashCode();
  267. }
  268. public final String toStr()
  269. {
  270. if (map.size() == 0) return "[:]";
  271. StringBuilder s = new StringBuilder(32+map.size()*32);
  272. s.append("[");
  273. boolean first = true;
  274. Iterator it = pairsIterator();
  275. while (it.hasNext())
  276. {
  277. Entry e = (Entry)it.next();
  278. if (!first) s.append(", ");
  279. else first = false;
  280. s.append(e.getKey()).append(':').append(e.getValue());
  281. }
  282. s.append("]");
  283. return s.toString();
  284. }
  285. public final void encode(ObjEncoder out)
  286. {
  287. // route back to obj encoder
  288. out.writeMap(this);
  289. }
  290. //////////////////////////////////////////////////////////////////////////
  291. // Iterators
  292. //////////////////////////////////////////////////////////////////////////
  293. public final void each(Func f)
  294. {
  295. Iterator it = pairsIterator();
  296. while (it.hasNext())
  297. {
  298. Entry e = (Entry)it.next();
  299. f.call(e.getValue(), e.getKey());
  300. }
  301. }
  302. public final Object eachWhile(Func f)
  303. {
  304. Iterator it = pairsIterator();
  305. while (it.hasNext())
  306. {
  307. Entry e = (Entry)it.next();
  308. Object r = f.call(e.getValue(), e.getKey());
  309. if (r != null) return r;
  310. }
  311. return null;
  312. }
  313. public final Object find(Func f)
  314. {
  315. Iterator it = pairsIterator();
  316. while (it.hasNext())
  317. {
  318. Entry e = (Entry)it.next();
  319. Object key = e.getKey();
  320. Object val = e.getValue();
  321. if (f.callBool(val, key))
  322. return val;
  323. }
  324. return null;
  325. }
  326. public final Map findAll(Func f)
  327. {
  328. Map acc = new Map(type);
  329. if (this.ordered()) acc.ordered(true);
  330. if (this.caseInsensitive()) acc.caseInsensitive(true);
  331. Iterator it = pairsIterator();
  332. while (it.hasNext())
  333. {
  334. Entry e = (Entry)it.next();
  335. Object key = e.getKey();
  336. Object val = e.getValue();
  337. if (f.callBool(val, key))
  338. acc.set(key, val);
  339. }
  340. return acc;
  341. }
  342. public final Map exclude(Func f)
  343. {
  344. Map acc = new Map(type);
  345. if (this.ordered()) acc.ordered(true);
  346. if (this.caseInsensitive()) acc.caseInsensitive(true);
  347. Iterator it = pairsIterator();
  348. while (it.hasNext())
  349. {
  350. Entry e = (Entry)it.next();
  351. Object key = e.getKey();
  352. Object val = e.getValue();
  353. if (!f.callBool(val, key))
  354. acc.set(key, val);
  355. }
  356. return acc;
  357. }
  358. public final boolean any(Func f)
  359. {
  360. if (map.size() == 0) return false;
  361. Iterator it = pairsIterator();
  362. while (it.hasNext())
  363. {
  364. Entry e = (Entry)it.next();
  365. Object key = e.getKey();
  366. Object val = e.getValue();
  367. if (f.callBool(val, key))
  368. return true;
  369. }
  370. return false;
  371. }
  372. public final boolean all(Func f)
  373. {
  374. if (map.size() == 0) return true;
  375. Iterator it = pairsIterator();
  376. while (it.hasNext())
  377. {
  378. Entry e = (Entry)it.next();
  379. Object key = e.getKey();
  380. Object val = e.getValue();
  381. if (!f.callBool(val, key))
  382. return false;
  383. }
  384. return true;
  385. }
  386. public final Object reduce(Object reduction, Func f)
  387. {
  388. Iterator it = pairsIterator();
  389. while (it.hasNext())
  390. {
  391. Entry e = (Entry)it.next();
  392. Object key = e.getKey();
  393. Object val = e.getValue();
  394. reduction = f.call(reduction, val, key);
  395. }
  396. return reduction;
  397. }
  398. public final Map map(Func f)
  399. {
  400. Type r = f.returns();
  401. if (r == Sys.VoidType) r = Sys.ObjType.toNullable();
  402. Map acc = new Map(type.k, r);
  403. if (this.ordered()) acc.ordered(true);
  404. if (this.caseInsensitive()) acc.caseInsensitive(true);
  405. Iterator it = pairsIterator();
  406. while (it.hasNext())
  407. {
  408. Entry e = (Entry)it.next();
  409. Object key = e.getKey();
  410. Object val = e.getValue();
  411. acc.set(key, f.call(val, key));
  412. }
  413. return acc;
  414. }
  415. //////////////////////////////////////////////////////////////////////////
  416. // Conversion
  417. //////////////////////////////////////////////////////////////////////////
  418. public final String join(String sep) { return join(sep, null); }
  419. public final String join(String sep, Func f)
  420. {
  421. int size = (int)size();
  422. if (size == 0) return "";
  423. StringBuilder s = new StringBuilder(32+size*32);
  424. Iterator it = pairsIterator();
  425. while (it.hasNext())
  426. {
  427. Entry e = (Entry)it.next();
  428. Object key = e.getKey();
  429. Object val = e.getValue();
  430. if (s.length() > 0) s.append(sep);
  431. if (f == null)
  432. s.append(key).append(": ").append(val);
  433. else
  434. s.append(f.call(val, key));
  435. }
  436. return s.toString();
  437. }
  438. public final String toCode()
  439. {
  440. int size = (int)size();
  441. StringBuilder s = new StringBuilder(32+size*32);
  442. s.append(type.signature());
  443. s.append('[');
  444. if (size == 0) s.append(':');
  445. Iterator it = pairsIterator();
  446. boolean first = true;
  447. while (it.hasNext())
  448. {
  449. Entry e = (Entry)it.next();
  450. Object key = e.getKey();
  451. Object val = e.getValue();
  452. if (first) first = false;
  453. else s.append(',').append(' ');
  454. s.append(FanObj.trap(key, "toCode", null))
  455. .append(':')
  456. .append(FanObj.trap(val, "toCode", null));
  457. }
  458. s.append(']');
  459. return s.toString();
  460. }
  461. //////////////////////////////////////////////////////////////////////////
  462. // Readonly
  463. //////////////////////////////////////////////////////////////////////////
  464. public final boolean isRW()
  465. {
  466. return !readonly;
  467. }
  468. public final boolean isRO()
  469. {
  470. return readonly;
  471. }
  472. public final Map rw()
  473. {
  474. if (!readonly) return this;
  475. Map rw = new Map(type);
  476. rw.map = (HashMap)map.clone();
  477. rw.readonly = false;
  478. rw.readonlyMap = this;
  479. rw.def = def;
  480. return rw;
  481. }
  482. public final Map ro()
  483. {
  484. if (readonly) return this;
  485. if (readonlyMap == null)
  486. {
  487. Map ro = new Map(type);
  488. ro.map = map;
  489. ro.def = def;
  490. ro.readonly = true;
  491. readonlyMap = ro;
  492. }
  493. return readonlyMap;
  494. }
  495. public final boolean isImmutable()
  496. {
  497. return immutable;
  498. }
  499. public final Object toImmutable()
  500. {
  501. if (immutable) return this;
  502. // allocate new map of correct type
  503. HashMap temp;
  504. if (caseInsensitive()) temp = new CIHashMap(map.size()*2+3);
  505. else if (ordered()) temp = new LinkedHashMap(map.size()*2+3);
  506. else temp = new HashMap(map.size()*2+3);
  507. // make safe copy
  508. Iterator it = pairsIterator();
  509. while (it.hasNext())
  510. {
  511. Entry e = (Entry)it.next();
  512. Object key = e.getKey();
  513. Object val = e.getValue();
  514. if (val != null)
  515. val = FanObj.toImmutable(val);
  516. temp.put(key, val);
  517. }
  518. // return new immutable map
  519. Map ro = new Map(type, temp);
  520. ro.readonly = true;
  521. ro.immutable = true;
  522. ro.def = def;
  523. return ro;
  524. }
  525. private void modify()
  526. {
  527. // if readonly then throw readonly exception
  528. if (readonly)
  529. throw ReadonlyErr.make("Map is readonly");
  530. // if we have a cached readonlyMap, then detach
  531. // it so it remains immutable
  532. if (readonlyMap != null)
  533. {
  534. readonlyMap.map = (HashMap)map.clone();
  535. readonlyMap = null;
  536. }
  537. }
  538. //////////////////////////////////////////////////////////////////////////
  539. // Java
  540. //////////////////////////////////////////////////////////////////////////
  541. public int sz() { return map.size(); }
  542. public Iterator pairsIterator()
  543. {
  544. if (map instanceof CIHashMap)
  545. return ((CIHashMap)map).pairs().iterator();
  546. else
  547. return map.entrySet().iterator();
  548. }
  549. public Iterator keysIterator()
  550. {
  551. return map.keySet().iterator();
  552. }
  553. public HashMap toJava()
  554. {
  555. modify();
  556. return map;
  557. }
  558. //////////////////////////////////////////////////////////////////////////
  559. // CIHashMap (Case Insensitive)
  560. //////////////////////////////////////////////////////////////////////////
  561. static class CIHashMap extends HashMap
  562. {
  563. public CIHashMap() {}
  564. public CIHashMap(int capacity) { super(capacity); }
  565. public Object get(Object key) { return super.get(new CIKey((String)key)); }
  566. public boolean containsKey(Object key) { return super.containsKey(new CIKey((String)key)); }
  567. public Object put(Object key, Object val) { return super.put(new CIKey((String)key), val); }
  568. public Object remove(Object key) { return super.remove(new CIKey((String)key)); }
  569. public Set pairs() { return new CIPairs(entrySet()); }
  570. public Set keySet()
  571. {
  572. java.util.HashSet keys = new java.util.HashSet();
  573. Iterator it = pairs().iterator();
  574. while (it.hasNext())
  575. {
  576. CIEntry entry = (CIEntry)it.next();
  577. keys.add(entry.key);
  578. }
  579. return keys;
  580. }
  581. public int hashCode()
  582. {
  583. int hash = 0;
  584. Iterator it = pairs().iterator();
  585. while (it.hasNext())
  586. hash += it.next().hashCode();
  587. return hash;
  588. }
  589. public boolean equals(Object obj)
  590. {
  591. if (!(obj instanceof HashMap)) return false;
  592. HashMap that = (HashMap)obj;
  593. if (size() != that.size()) return false;
  594. Iterator it = pairs().iterator();
  595. while (it.hasNext())
  596. {
  597. CIEntry entry = (CIEntry)it.next();
  598. Object thatVal = that.get(entry.key);
  599. if (!OpUtil.compareEQ(entry.val, thatVal)) return false;
  600. }
  601. return true;
  602. }
  603. }
  604. static final class CIPairs extends AbstractSet
  605. {
  606. CIPairs(Set set) { this.set = set; }
  607. public int size() { return set.size(); }
  608. public Iterator iterator() { return new CIPairsIterator(set.iterator()); }
  609. Set set;
  610. }
  611. static final class CIPairsIterator implements Iterator
  612. {
  613. CIPairsIterator(Iterator it) { this.it = it; }
  614. public boolean hasNext() { return it.hasNext(); }
  615. public Object next() { entry.set((Entry)it.next()); return entry; }
  616. public void remove() { it.remove(); }
  617. Iterator it;
  618. CIEntry entry = new CIEntry();
  619. }
  620. static final class CIEntry implements Entry
  621. {
  622. public void set(Entry e) { key = ((CIKey)e.getKey()).key; val = e.getValue(); }
  623. public Object getKey() { return key; }
  624. public Object getValue() { return val; }
  625. public int hashCode() { return key.hashCode() ^ (val == null ? 0 : val.hashCode()); }
  626. public boolean equals(Object o) { throw new UnsupportedOperationException(); }
  627. public Object setValue(Object v) { throw new UnsupportedOperationException(); }
  628. String key;
  629. Object val;
  630. }
  631. static final class CIKey
  632. {
  633. CIKey(String key) { this.key = key; this.hash = FanStr.caseInsensitiveHash(key); }
  634. public final int hashCode() { return hash; }
  635. public final boolean equals(Object obj) { return FanStr.equalsIgnoreCase(key, ((CIKey)obj).key); }
  636. public final String toString() { return key; }
  637. final String key;
  638. final int hash;
  639. }
  640. //////////////////////////////////////////////////////////////////////////
  641. // Fields
  642. //////////////////////////////////////////////////////////////////////////
  643. private MapType type;
  644. private HashMap map;
  645. private Map readonlyMap;
  646. private boolean readonly;
  647. private boolean immutable;
  648. private Object def;
  649. }