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 }