Mercurial Hosting > luan
diff core/src/luan/LuanTable.java @ 408:1b38de2b1845
merge LuanTableImpl into LuanTable
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Wed, 29 Apr 2015 13:15:17 -0600 |
parents | core/src/luan/LuanTableImpl.java@9321a33b9b1c |
children | 23b99a5039b5 |
line wrap: on
line diff
--- a/core/src/luan/LuanTable.java Wed Apr 29 13:01:00 2015 -0600 +++ b/core/src/luan/LuanTable.java Wed Apr 29 13:15:17 2015 -0600 @@ -1,29 +1,388 @@ package luan; +import java.util.Iterator; +import java.util.ListIterator; import java.util.Map; +import java.util.AbstractMap; +import java.util.LinkedHashMap; import java.util.List; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.Set; -import java.util.Comparator; +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.regex.Pattern; -public interface LuanTable extends Iterable<Map.Entry<Object,Object>> { - public boolean isEmpty(); - public boolean isList(); - public boolean isSet(); - public Set<Object> asSet(); - public List<Object> asList(); - public Map<Object,Object> asMap(); - public Object get(Object key); - public void put(Object key,Object val); - public void insert(int pos,Object value); - public void add(Object value); - public Object remove(int pos); - public void sort(Comparator<Object> cmp); - public int length(); - public LuanTable subList(int from,int to); - public LuanTable getMetatable(); - public void setMetatable(LuanTable metatable); - public LuanTable cloneTable(); - public boolean hasJava(); - public void setJava(); +public final class LuanTable implements Iterable<Map.Entry<Object,Object>>, LuanRepr, DeepCloneable<LuanTable> { + private Map<Object,Object> map = null; + private List<Object> list = null; + private LuanTable metatable = null; + private boolean hasJava = false; + + public LuanTable() {} + + LuanTable(List<Object> list) { + this.list = list; + this.map = newMap(); + map.put("n",list.size()); + for( int i=0; i<list.size(); i++ ) { + if( list.get(i) == null ) { + listToMap(i); + break; + } + } + } + + LuanTable(Map<Object,Object> map) { + map.remove(null); + for( Iterator<Object> i=map.values().iterator(); i.hasNext(); ) { + if( i.next() == null ) + i.remove(); + } + this.map = map; + } + + LuanTable(Set<Object> set) { + map = newMap(); + for( Object obj : set ) { + if( obj != null ) + map.put(obj,Boolean.TRUE); + } + } + + @Override public LuanTable shallowClone() { + return new LuanTable(); + } + + @Override public void deepenClone(LuanTable clone,DeepCloner cloner) { + if( map != null ) { + clone.map = newMap(); + for( Map.Entry<Object,Object> entry : map.entrySet() ) { + clone.map.put( cloner.get(entry.getKey()), cloner.get(entry.getValue()) ); + } + } + if( list != null ) { + clone.list = new ArrayList<Object>(); + for( Object obj : list ) { + clone.list.add( cloner.get(obj) ); + } + } + if( metatable != null ) + clone.metatable = cloner.get(metatable); + clone.hasJava = hasJava; + } + + public boolean isList() { + return map==null || map.isEmpty(); + } + + public List<Object> asList() { + return list!=null ? list : Collections.emptyList(); + } + + private Map<Object,Object> map() { + return map!=null ? map : Collections.emptyMap(); + } +/* + @Override public boolean isSet() { + if( list != null ) { + for( Object obj : list ) { + if( obj!=null && !obj.equals(Boolean.TRUE) ) + return false; + } + } + if( map != null ) { + for( Object obj : map.values() ) { + if( !obj.equals(Boolean.TRUE) ) + return false; + } + } + return true; + } + + @Override public Set<Object> asSet() { + if( list == null || list.isEmpty() ) + return map!=null ? map.keySet() : Collections.emptySet(); + Set<Object> rtn = map!=null ? new HashSet<Object>(map.keySet()) : new HashSet<Object>(); + for( int i=1; i<=list.size(); i++ ) { + rtn.add(i); + } + return rtn; + } +*/ + protected String type() { + return "table"; + } + + public Object get(Object key) { + if( list != null ) { + Integer iT = Luan.asInteger(key); + if( iT != null ) { + int i = iT - 1; + if( i>=0 && i<list.size() ) + return list.get(i); + } + } + if( map==null ) + return null; + return map.get(key); + } + + public void put(Object key,Object val) { + Integer iT = Luan.asInteger(key); + if( iT != null ) { + int i = iT - 1; + if( list != null || i == 0 ) { + if( i == list().size() ) { + if( val != null ) { + list.add(val); + mapToList(); + } + return; + } else if( i>=0 && i<list.size() ) { + list.set(i,val); + if( val == null ) { + listToMap(i); + } + return; + } + } + } + if( map==null ) { + map = newMap(); + } + if( key instanceof Number && !(key instanceof Double) ) { + Number n = (Number)key; + key = Double.valueOf(n.doubleValue()); + } + if( val == null ) { + map.remove(key); + } else { + map.put(key,val); + } + } + + private void mapToList() { + if( map != null ) { + while(true) { + Object v = map.remove(Double.valueOf(list.size()+1)); + if( v == null ) + break; + list.add(v); + } + } + } + + private void listToMap(int from) { + if( list != null ) { + while( list.size() > from ) { + int i = list.size() - 1; + Object v = list.remove(i); + if( v != null ) { + if( map==null ) + map = newMap(); + map.put(i+1,v); + } + } + } + } + + private List<Object> list() { + if( list == null ) { + list = new ArrayList<Object>(); + mapToList(); + } + return list; + } + + public void insert(int pos,Object value) { + if( value==null ) + throw new IllegalArgumentException("can't insert a nil value"); + list().add(pos-1,value); + mapToList(); + } + + public void add(Object value) { + if( value==null ) + throw new IllegalArgumentException("can't add a nil value"); + list().add(value); + mapToList(); + } + + public Object remove(int pos) { + return list().remove(pos-1); + } + + public void sort(Comparator<Object> cmp) { + Collections.sort(list(),cmp); + } + + public int length() { + return list==null ? 0 : list.size(); + } + + @Override public Iterator<Map.Entry<Object,Object>> iterator() { + if( list == null ) { + if( map == null ) + return Collections.<Map.Entry<Object,Object>>emptyList().iterator(); + return map.entrySet().iterator(); + } + if( map == null ) + return listIterator(); + return new Iterator<Map.Entry<Object,Object>>() { + Iterator<Map.Entry<Object,Object>> iter = listIterator(); + boolean isList = true; + public boolean hasNext() { + boolean b = iter.hasNext(); + if( !b && isList ) { + iter = map.entrySet().iterator(); + isList = false; + b = iter.hasNext(); + } + return b; + } + public Map.Entry<Object,Object> next() { + return iter.next(); + } + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + private Iterator<Map.Entry<Object,Object>> listIterator() { + if( list == null ) + return Collections.<Map.Entry<Object,Object>>emptyList().iterator(); + final ListIterator iter = list.listIterator(); + return new Iterator<Map.Entry<Object,Object>>() { + public boolean hasNext() { + return iter.hasNext(); + } + public Map.Entry<Object,Object> next() { + Double key = Double.valueOf(iter.nextIndex()+1); + return new AbstractMap.SimpleEntry<Object,Object>(key,iter.next()); + } + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + public LuanTable subList(int from,int to) { + LuanTable tbl = shallowClone(); + tbl.list = new ArrayList<Object>(list().subList(from-1,to-1)); + return tbl; + } + + public LuanTable getMetatable() { + return metatable; + } + + public void setMetatable(LuanTable metatable) { + this.metatable = metatable; + } + + public boolean hasJava() { + return hasJava; + } + + public void setJava() { + hasJava = true; + } + + + // from AbstractLuanTable + + protected final Map<Object,Object> newMap() { + return new LinkedHashMap<Object,Object>(); + } + + public boolean isEmpty() { + return isList() && length()==0; + } + + public boolean isSet() { + for( Map.Entry<Object,Object> entry : this ) { + if( !entry.getValue().equals(Boolean.TRUE) ) + return false; + } + return true; + } + + public Set<Object> asSet() { + Set<Object> set = new HashSet<Object>(); + for( Map.Entry<Object,Object> entry : this ) { + set.add(entry.getKey()); + } + return set; + } + + public Map<Object,Object> asMap() { + Map<Object,Object> map = newMap(); + for( Map.Entry<Object,Object> entry : this ) { + map.put(entry.getKey(),entry.getValue()); + } + return map; + } + + public LuanTable cloneTable() { + return isList() ? new LuanTable(new ArrayList<Object>(asList())) : new LuanTable(asMap()); + } + + @Override public String repr() { + return repr( Collections.newSetFromMap(new IdentityHashMap<LuanTable,Boolean>()) ); + } + + private String repr(Set<LuanTable> set) { + if( !set.add(this) ) { + return "\"<circular reference>\""; + } + StringBuilder sb = new StringBuilder(); + sb.append('{'); + boolean isFirst = true; + for( Object obj : asList() ) { + if( isFirst ) { + isFirst = false; + } else { + sb.append(", "); + } + sb.append(repr(set,obj)); + } + for( Map.Entry<Object,Object> entry : map().entrySet() ) { + if( isFirst ) { + isFirst = false; + } else { + sb.append(", "); + } + sb.append(reprKey(set,entry.getKey())).append('=').append(repr(set,entry.getValue())); + } + sb.append('}'); + return sb.toString(); + } + + private static final Pattern namePtn = Pattern.compile("[a-zA-Z_][a-zA-Z_0-9]*"); + + private String reprKey(Set<LuanTable> set,Object obj) { + if( obj instanceof String ) { + String s = (String)obj; + if( namePtn.matcher(s).matches() ) + return s; + } + return "[" + repr(set,obj) + "]"; + } + + String repr(Set<LuanTable> set,Object obj) { + if( obj instanceof LuanTable ) { + LuanTable t = (LuanTable)obj; + return t.repr(set); + } else { + String s = Luan.repr(obj); + if( s == null ) + s = "<couldn't repr: " + Luan.stringEncode(Luan.toString(obj)) + ">"; + return s; + } + } + }