Mercurial Hosting > luan
comparison core/src/luan/LuanTableImpl.java @ 221:ec016471c6eb
make LuanTable an interface
git-svn-id: https://luan-java.googlecode.com/svn/trunk@222 21e917c8-12df-6dd8-5cb6-c86387c605b9
author | fschmidt@gmail.com <fschmidt@gmail.com@21e917c8-12df-6dd8-5cb6-c86387c605b9> |
---|---|
date | Thu, 17 Jul 2014 07:49:26 +0000 |
parents | core/src/luan/LuanTable.java@75750ceb45ee |
children | b76fcb72d97d |
comparison
equal
deleted
inserted
replaced
220:61afe2a1ce96 | 221:ec016471c6eb |
---|---|
1 package luan; | |
2 | |
3 import java.util.Iterator; | |
4 import java.util.ListIterator; | |
5 import java.util.Map; | |
6 import java.util.HashMap; | |
7 import java.util.List; | |
8 import java.util.ArrayList; | |
9 import java.util.Collections; | |
10 import java.util.Comparator; | |
11 import java.util.Set; | |
12 import java.util.HashSet; | |
13 import java.util.IdentityHashMap; | |
14 import java.util.regex.Pattern; | |
15 | |
16 | |
17 public final class LuanTableImpl implements LuanTable, DeepCloneable<LuanTableImpl>, LuanRepr { | |
18 private Map<Object,Object> map = null; | |
19 private List<Object> list = null; | |
20 private LuanTable metatable = null; | |
21 | |
22 public LuanTableImpl() {} | |
23 /* | |
24 public LuanTableImpl(LuanTableImpl tbl) { | |
25 if( tbl.map != null ) | |
26 this.map = new HashMap<Object,Object>(tbl.map); | |
27 if( tbl.list != null ) | |
28 this.list = new ArrayList<Object>(tbl.list); | |
29 } | |
30 */ | |
31 LuanTableImpl(List<Object> list) { | |
32 this.list = list; | |
33 this.map = new HashMap<Object,Object>(); | |
34 map.put("n",list.size()); | |
35 for( int i=0; i<list.size(); i++ ) { | |
36 if( list.get(i) == null ) { | |
37 listToMap(i); | |
38 break; | |
39 } | |
40 } | |
41 } | |
42 | |
43 LuanTableImpl(Map<Object,Object> map) { | |
44 map.remove(null); | |
45 for( Iterator<Object> i=map.values().iterator(); i.hasNext(); ) { | |
46 if( i.next() == null ) | |
47 i.remove(); | |
48 } | |
49 this.map = map; | |
50 } | |
51 | |
52 LuanTableImpl(Set<Object> set) { | |
53 map = new HashMap<Object,Object>(); | |
54 for( Object obj : set ) { | |
55 if( obj != null ) | |
56 map.put(obj,Boolean.TRUE); | |
57 } | |
58 } | |
59 | |
60 @Override public LuanTableImpl shallowClone() { | |
61 return new LuanTableImpl(); | |
62 } | |
63 | |
64 @Override public void deepenClone(LuanTableImpl clone,DeepCloner cloner) { | |
65 if( map != null ) { | |
66 clone.map = new HashMap<Object,Object>(); | |
67 for( Map.Entry<Object,Object> entry : map.entrySet() ) { | |
68 clone.map.put( cloner.get(entry.getKey()), cloner.get(entry.getValue()) ); | |
69 } | |
70 } | |
71 if( list != null ) { | |
72 clone.list = new ArrayList<Object>(); | |
73 for( Object obj : list ) { | |
74 clone.list.add( cloner.get(obj) ); | |
75 } | |
76 } | |
77 if( metatable != null ) | |
78 clone.metatable = cloner.get(metatable); | |
79 } | |
80 | |
81 public boolean isList() { | |
82 return map==null || map.isEmpty(); | |
83 } | |
84 | |
85 @Override public List<Object> asList() { | |
86 return list!=null ? list : Collections.emptyList(); | |
87 } | |
88 | |
89 @Override public Map<Object,Object> asMap() { | |
90 if( list == null || list.isEmpty() ) | |
91 return map!=null ? map : Collections.emptyMap(); | |
92 Map<Object,Object> rtn = map!=null ? new HashMap<Object,Object>(map) : new HashMap<Object,Object>(); | |
93 for( ListIterator iter = list.listIterator(); iter.hasNext(); ) { | |
94 int i = iter.nextIndex(); | |
95 rtn.put(i+1,iter.next()); | |
96 } | |
97 return rtn; | |
98 } | |
99 | |
100 public boolean isSet() { | |
101 if( list != null ) { | |
102 for( Object obj : list ) { | |
103 if( obj!=null && !obj.equals(Boolean.TRUE) ) | |
104 return false; | |
105 } | |
106 } | |
107 if( map != null ) { | |
108 for( Object obj : map.values() ) { | |
109 if( !obj.equals(Boolean.TRUE) ) | |
110 return false; | |
111 } | |
112 } | |
113 return true; | |
114 } | |
115 | |
116 public Set<Object> asSet() { | |
117 if( list == null || list.isEmpty() ) | |
118 return map!=null ? map.keySet() : Collections.emptySet(); | |
119 Set<Object> rtn = map!=null ? new HashSet<Object>(map.keySet()) : new HashSet<Object>(); | |
120 for( int i=1; i<=list.size(); i++ ) { | |
121 rtn.add(i); | |
122 } | |
123 return rtn; | |
124 } | |
125 | |
126 @Override public String toString() { | |
127 return "table: " + Integer.toHexString(hashCode()); | |
128 } | |
129 | |
130 @Override public String repr() { | |
131 return repr( Collections.newSetFromMap(new IdentityHashMap<LuanTableImpl,Boolean>()) ); | |
132 } | |
133 | |
134 private String repr(Set<LuanTableImpl> set) { | |
135 if( !set.add(this) ) { | |
136 return "\"<circular reference>\""; | |
137 } | |
138 StringBuilder sb = new StringBuilder(); | |
139 sb.append('{'); | |
140 boolean isFirst = true; | |
141 if( list != null ) { | |
142 boolean gotNull = false; | |
143 for( int i=0; i<list.size(); i++ ) { | |
144 Object obj = list.get(i); | |
145 if( obj==null ) { | |
146 gotNull = true; | |
147 } else { | |
148 if( isFirst ) { | |
149 isFirst = false; | |
150 } else { | |
151 sb.append(", "); | |
152 } | |
153 if( gotNull ) | |
154 sb.append(i+1).append('='); | |
155 sb.append(repr(set,obj)); | |
156 } | |
157 } | |
158 } | |
159 if( map != null ) { | |
160 for( Map.Entry<Object,Object> entry : map.entrySet() ) { | |
161 if( isFirst ) { | |
162 isFirst = false; | |
163 } else { | |
164 sb.append(", "); | |
165 } | |
166 sb.append(reprKey(set,entry.getKey())).append('=').append(repr(set,entry.getValue())); | |
167 } | |
168 } | |
169 sb.append('}'); | |
170 return sb.toString(); | |
171 } | |
172 | |
173 private static final Pattern namePtn = Pattern.compile("[a-zA-Z_][a-zA-Z_0-9]*"); | |
174 | |
175 private static String reprKey(Set<LuanTableImpl> set,Object obj) { | |
176 if( obj instanceof String ) { | |
177 String s = (String)obj; | |
178 if( namePtn.matcher(s).matches() ) | |
179 return s; | |
180 } | |
181 return "[" + repr(set,obj) + "]"; | |
182 } | |
183 | |
184 private static String repr(Set<LuanTableImpl> set,Object obj) { | |
185 if( obj instanceof LuanTableImpl ) { | |
186 LuanTableImpl t = (LuanTableImpl)obj; | |
187 return t.repr(set); | |
188 } else { | |
189 String s = Luan.repr(obj); | |
190 if( s == null ) | |
191 s = "<couldn't repr: " + Luan.stringEncode(Luan.toString(obj)) + ">"; | |
192 return s; | |
193 } | |
194 } | |
195 | |
196 @Override public Object get(Object key) { | |
197 if( list != null ) { | |
198 Integer iT = Luan.asInteger(key); | |
199 if( iT != null ) { | |
200 int i = iT - 1; | |
201 if( i>=0 && i<list.size() ) | |
202 return list.get(i); | |
203 } | |
204 } | |
205 if( map==null ) | |
206 return null; | |
207 return map.get(key); | |
208 } | |
209 | |
210 @Override public Object put(Object key,Object val) { | |
211 Integer iT = Luan.asInteger(key); | |
212 if( iT != null ) { | |
213 int i = iT - 1; | |
214 if( list != null || i == 0 ) { | |
215 if( i == list().size() ) { | |
216 if( val != null ) { | |
217 list.add(val); | |
218 mapToList(); | |
219 } | |
220 return null; | |
221 } else if( i>=0 && i<list.size() ) { | |
222 Object old = list.get(i); | |
223 list.set(i,val); | |
224 if( val == null ) { | |
225 listToMap(i); | |
226 } | |
227 return old; | |
228 } | |
229 } | |
230 } | |
231 if( map==null ) { | |
232 map = new HashMap<Object,Object>(); | |
233 } | |
234 if( key instanceof Number && !(key instanceof Double) ) { | |
235 Number n = (Number)key; | |
236 key = Double.valueOf(n.doubleValue()); | |
237 } | |
238 if( val == null ) { | |
239 return map.remove(key); | |
240 } else { | |
241 return map.put(key,val); | |
242 } | |
243 } | |
244 | |
245 private void mapToList() { | |
246 if( map != null ) { | |
247 while(true) { | |
248 Object v = map.remove(Double.valueOf(list.size()+1)); | |
249 if( v == null ) | |
250 break; | |
251 list.add(v); | |
252 } | |
253 } | |
254 } | |
255 | |
256 private void listToMap(int from) { | |
257 if( list != null ) { | |
258 while( list.size() > from ) { | |
259 int i = list.size() - 1; | |
260 Object v = list.remove(i); | |
261 if( v != null ) { | |
262 if( map==null ) | |
263 map = new HashMap<Object,Object>(); | |
264 map.put(i+1,v); | |
265 } | |
266 } | |
267 } | |
268 } | |
269 | |
270 private List<Object> list() { | |
271 if( list == null ) { | |
272 list = new ArrayList<Object>(); | |
273 mapToList(); | |
274 } | |
275 return list; | |
276 } | |
277 | |
278 @Override public void insert(int pos,Object value) { | |
279 if( value==null ) | |
280 throw new UnsupportedOperationException(); | |
281 list().add(pos-1,value); | |
282 mapToList(); | |
283 } | |
284 | |
285 @Override public void add(Object value) { | |
286 if( value==null ) | |
287 throw new UnsupportedOperationException(); | |
288 list().add(value); | |
289 mapToList(); | |
290 } | |
291 | |
292 @Override public Object remove(int pos) { | |
293 return list().remove(pos-1); | |
294 } | |
295 | |
296 @Override public void sort(Comparator<Object> cmp) { | |
297 Collections.sort(list(),cmp); | |
298 } | |
299 | |
300 @Override public int length() { | |
301 return list==null ? 0 : list.size(); | |
302 } | |
303 | |
304 @Override public Iterator<Map.Entry<Object,Object>> iterator() { | |
305 if( list == null ) { | |
306 if( map == null ) | |
307 return Collections.<Map.Entry<Object,Object>>emptyList().iterator(); | |
308 return map.entrySet().iterator(); | |
309 } | |
310 if( map == null ) | |
311 return listIterator(); | |
312 return new Iterator<Map.Entry<Object,Object>>() { | |
313 Iterator<Map.Entry<Object,Object>> iter = listIterator(); | |
314 boolean isList = true; | |
315 public boolean hasNext() { | |
316 boolean b = iter.hasNext(); | |
317 if( !b && isList ) { | |
318 iter = map.entrySet().iterator(); | |
319 isList = false; | |
320 b = iter.hasNext(); | |
321 } | |
322 return b; | |
323 } | |
324 public Map.Entry<Object,Object> next() { | |
325 return iter.next(); | |
326 } | |
327 public void remove() { | |
328 throw new UnsupportedOperationException(); | |
329 } | |
330 }; | |
331 } | |
332 | |
333 private Iterator<Map.Entry<Object,Object>> listIterator() { | |
334 if( list == null ) | |
335 return Collections.<Map.Entry<Object,Object>>emptyList().iterator(); | |
336 final ListIterator iter = list.listIterator(); | |
337 return new Iterator<Map.Entry<Object,Object>>() { | |
338 public boolean hasNext() { | |
339 return iter.hasNext(); | |
340 } | |
341 public Map.Entry<Object,Object> next() { | |
342 Double key = Double.valueOf(iter.nextIndex()+1); | |
343 return new MapEntry(key,iter.next()); | |
344 } | |
345 public void remove() { | |
346 throw new UnsupportedOperationException(); | |
347 } | |
348 }; | |
349 } | |
350 | |
351 @Override public LuanTable subList(int from,int to) { | |
352 LuanTableImpl tbl = new LuanTableImpl(); | |
353 tbl.list = new ArrayList<Object>(list().subList(from-1,to-1)); | |
354 return tbl; | |
355 } | |
356 | |
357 @Override public LuanTable getMetatable() { | |
358 return metatable; | |
359 } | |
360 | |
361 @Override public void setMetatable(LuanTable metatable) { | |
362 this.metatable = metatable; | |
363 } | |
364 | |
365 private static final class MapEntry implements Map.Entry<Object,Object> { | |
366 private final Object key; | |
367 private final Object value; | |
368 | |
369 MapEntry(Object key,Object value) { | |
370 this.key = key; | |
371 this.value = value; | |
372 } | |
373 | |
374 @Override public Object getKey() { | |
375 return key; | |
376 } | |
377 | |
378 @Override public Object getValue() { | |
379 return value; | |
380 } | |
381 | |
382 @Override public Object setValue(Object value) { | |
383 throw new UnsupportedOperationException(); | |
384 } | |
385 } | |
386 | |
387 public boolean isEmpty() { | |
388 return (list==null || list.isEmpty()) && (map==null || map.isEmpty()); | |
389 } | |
390 } |