/*
* Copyright 2008-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.nominanuda.zen.obj;
import static com.nominanuda.zen.obj.JsonSerializer.JSON_SERIALIZER;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Set;
import javax.annotation.Nullable;
import com.nominanuda.zen.seq.ReadOnlyCollection;
import com.nominanuda.zen.stereotype.Value;
public class ObjImpl implements Obj {
private final LinkedHashMap<Key,Any> members;
ObjImpl(BinRange range, LinkedHashMap<Key, Any> m) {
this(m);
}
ObjImpl() {
this(new LinkedHashMap<>(16));
}
private ObjImpl(LinkedHashMap<Key, Any> m) {
members = m;
}
@Override
public int len() {
return members.size();
}
//@Override
public Iterator<Entry<String, Object>> iterator() {
final Iterator<Entry<Key,Any>> i = members.entrySet().iterator();
return new Iterator<Entry<String,Object>>() {
public Entry<String, Object> next() {
final Entry<Key,Any> e = i.next();
return new Entry<String, Object>() {
public Object setValue(Object value) {
throw new UnsupportedOperationException();
}
public Object getValue() {
return e.getValue().toJavaObjModel();
}
public String getKey() {
return e.getKey().toString();
}
};
}
@Override
public boolean hasNext() {
return i.hasNext();
}
};
}
private ReadOnlyCollection<? extends Entry<Key, ? extends Any>> members() {
return ReadOnlyCollection.wrap(members.entrySet());
}
@SuppressWarnings("unchecked")
@Override
public <T extends Any> T storeAny(Key k, Any val) {
//Any copy = val.copy();
members.put(k, val);
return (T)val;
}
@Override
public <T> T store(String k, @Nullable T v) {
storeAny(Key.of(k), Any.toStruObjModel(v));
return v;
}
@Override
public Object fetch(String k) {
Any a = members.get(Key.of(k));
return a == null ? null : a.toJavaObjModel();
}
@Override
public Any fetchAny(Key k) {
Any a = members.get(k);
return a == null ? Val.NULL : a;
}
@Override
public Object del(String k) {
Any removed = members.remove(Key.of(k));
return removed == null ? null : removed.toJavaObjModel();
}
@Override
public int push(String k, Object v) {
return pushInternal(k, v, false);
}
private int pushInternal(String k, Object v, boolean recursive) {
if(!recursive && v != null && v instanceof Arr) {
Arr arr = (Arr)v;
if(arr.len() == 0) {
Key key = Key.of(k);
Any a = members.get(key);
if(a == null) {
return 0;
} else if(a.isArr()) {
return a.asArr().len();
} else {
return 1;
}
}
int res = 0;
for(Object x : (Arr)v) {
res = pushInternal(k, x, true);
}
return res;
} else {
Key key = Key.of(k);
Any newVal = Any.toStruObjModel(v);
//illegalargument.assertTrue(newVal.isVal(), "only primitive types can be pushed");
Any a = members.get(key);
if(a == null) {
members.put(key, newVal);
return 1;
} else if(a.isArr() && a.asArr().len() > 0) {
Arr arr = a.asArr();
arr.push(v);
return arr.len();
} else {
Arr arr = newArr();
arr.push(a);
arr.push(v);
members.put(key, arr);
return 2;
}
}
}
/**
*
* @param obj
* @return true if all members are {@link Value#nullSafeEquals(Object, Object)} ignoring order
*/
@Override
public boolean equals(Object obj) {
if(!(obj instanceof Obj)) {
return false;
}
Obj other = (Obj)obj;
if(other.len() != size()) {
return false;
}
for(Entry<Key, ? extends Any> e : ((ObjImpl)other).members()) {
if(! Value.nullSafeEquals(members.get(e.getKey()), e.getValue())) {
return false;
}
}
return true;
}
@Override
public Obj copy() {
LinkedHashMap<Key,Any> m = new LinkedHashMap<Key, Any>(size());
for(Entry<Key, Any> entry : members.entrySet()) {
m.put(entry.getKey().copy(), entry.getValue().copy());
}
return new ObjImpl(m);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result += prime * members.size();
final Iterator<Entry<Key, Any>> i = members.entrySet().iterator();
if(i.hasNext()) {
final Entry<Key, Any> first = i.next();
result += first.getKey().hashCode() + first.getValue().hashCode();
}
return result;
}
@Override
public JsonType getType() {
return JsonType.obj;
}
@Override
public Obj reset() {
members.clear();
return this;
}
// default void stream(Arr array, JsonxContentHandler jch) throws RuntimeException {
// jch.startArray();
// int len = array.getLength();
// for(int i = 0; i < len; i++) {
// Object o = array.get(i);
// streamItem(jch, o);
// }
// jch.endArray();
// }
//
// default void streamItem(JsonxContentHandler jch, Object o) throws RuntimeException {
// switch (Stru.Stru.getDataType(o)) {
// case array:
// stream((Arr)o, jch);
// break;
// case object:
// stream((Obj)o, jch);
// break;
// default:
// jch.primitive(o);
// break;
// }
// }
// default void stream(Obj object, JsonxContentHandler jch) throws RuntimeException {
// jch.startObject();
// for(String k : object.getKeys()) {
// Object o = object.get(k);
// jch.startEntry(k);
// streamItem(jch, o);
// jch.endEntry();
// }
// jch.endObject();
// }
//Map impl
@Override
public boolean containsKey(Object key) {
return key instanceof String ? members.containsKey(Key.of((String)key)) : false;
}
@Override
public boolean containsValue(Object value) {
return members.containsValue(Any.toStruObjModel(value));
}
@Override
public Set<String> keySet() {
Iterator<Key> i = members.keySet().iterator();
return new AbstractSet<String>() {
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
public boolean hasNext() {
return i.hasNext();
}
public String next() {
return i.next().toString();
}};
}
@Override
public int size() {
return len();
}
};
}
@Override
public Collection<Object> values() {
Iterator<Any> i = members.values().iterator();
return ReadOnlyCollection.wrap(new Iterator<Object>() {
public boolean hasNext() {
return i.hasNext();
}
public Object next() {
return i.next().toJavaObjModel();
}
});
}
@Override
public Set<Entry<String, Object>> entrySet() {
return new AbstractSet<Entry<String,Object>>() {
public Iterator<java.util.Map.Entry<String, Object>> iterator() {
return ObjImpl.this.iterator();
}
public int size() {
return len();
}
};
}
@Override
public void sendTo(JixHandler sink) {
sink.startObj();
for(Entry<Key, Any> member : members.entrySet()) {
member.getKey().sendTo(sink);
member.getValue().sendTo(sink);
}
sink.endObj();
}
@Override
public String toString() {
return JSON_SERIALIZER.toString(this);
}
@Override
public Obj newObj() {
return new ObjImpl();
}
@Override
public Arr newArr() {
return new ArrImpl();
}
@SuppressWarnings("unchecked")
@Override
public <T> TArr<T> newArr(Class<T> cl) {
return (TArr<T>)new ArrImpl();
}
}