/*
* Copyright 2008-2011 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.dataobject;
import static com.nominanuda.dataobject.DataStructHelper.STRUCT;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import com.nominanuda.code.Nullable;
import com.nominanuda.lang.Check;
import com.nominanuda.lang.Maths;
abstract class AbstractDataStruct<K> implements DataStruct, PropertyBag<K> {
protected final DataStruct parent;
private String json = null;
AbstractDataStruct(DataStruct parent) {
this.parent = parent;
}
// public DataStruct getParent() {
// return parent;
// }
//
// @SuppressWarnings("deprecation")
// public DataStruct getRoot() {
// return parent == null
// ? this
// : parent.getRoot();
// }
public DataStruct cloneStruct() {
return cloneStruct(null);
}
@Override
public Iterator<K> keyIterator() {
return getKeys().iterator();
}
protected abstract DataStruct cloneStruct(@Nullable AbstractDataStruct<?> parent);
protected Object cloneInternal(Object o, AbstractDataStruct<?> parent) {
if (o == null) {
return null;
} else if (isPrimitiveOrNull(o)) {
return o;
} else if (o instanceof AbstractDataStruct) {
AbstractDataStruct<?> tStruct = (AbstractDataStruct<?>) o;
return tStruct.cloneStruct(parent);
}
throw new IllegalArgumentException();
}
protected Map<String, Object> createInnerMap() {
return new LinkedHashMap<String, Object>();
}
protected List<Object> createInnerList() {
return new LinkedList<Object>();
}
/*
public K checkKey(K key) throws IllegalArgumentException {
illegalargument.assertNotNull(key);
if(isArray()) {
illegalargument.assertTrue(
key instanceof Integer && ((Integer)key) >= 0);
} else {
illegalargument.assertTrue(
VALID_OBJ_KEY.matcher((String)key).matches());
}
return key;
}
*/
@Override
public boolean isPrimitiveOrNull(Object o) {
return STRUCT.isPrimitiveOrNull(o);
}
@Override
public Arr asArray() throws ClassCastException {
return (Arr)this;
}
@Override
public Obj asObject() throws ClassCastException {
return (Obj)this;
}
@Override
public boolean isArray() {
return this instanceof Arr;
}
@Override
public boolean isObject() {
return this instanceof Obj;
}
@Override
public Obj putNewObject(K key) {
Obj o = new DataObjectImpl(this);
return (Obj)put(key, o);
}
@Override
public Arr putNewArray(K key) {
Arr a = new DataArrayImpl(this);
return (Arr)put(key, a);
}
@Override
public Object getStrict(K k) throws NullPointerException {
return Check.notNull(get(k));
}
@Override
public Arr getArray(K k) throws IllegalArgumentException{
return (Arr)get(k);
}
@Override
public Obj getObject(K k) throws IllegalArgumentException {
return (Obj)get(k);
}
@Override
public void setPathProperty(String path, @Nullable Object value) {
String[] pathBits = path.split("\\.");
int len = pathBits.length;
explodePath(path).put(pathBits[len-1], value);
}
@Override
public void setOrPushPathProperty(String path, @Nullable Object value) {
String[] pathBits = path.split("\\.");
int len = pathBits.length;
explodePath(path).setOrPushProperty(pathBits[len-1], value);
}
@SuppressWarnings("unchecked")
private PropertyBag<Object> explodePath(String path) {
PropertyBag<Object> _target = asObjectKeyedDataStruct();
String[] pathBits = path.split("\\.");
int len = pathBits.length;
for(int i = 0; i < len - 1; i++) {
String pathBit = pathBits[i];
Object k = toStringOrIntKey(pathBit);
Object newTarget = _target.get(k);
if(isPrimitiveOrNull(newTarget)) {
if(Maths.isInteger(pathBits[i + 1])) {
newTarget = asObjectKeyedDataStruct(_target.putNewArray(k));
} else {
newTarget = asObjectKeyedDataStruct(_target.putNewObject(k));
}
}
_target = (PropertyBag<Object>)newTarget;
}
return _target;
}
private void setProperty(Object key, @Nullable Object value) {
asObjectKeyedDataStruct().put(key, value);
}
@Override
public void setOrPushProperty(Object key, @Nullable Object value) {
PropertyBag<Object> _this = asObjectKeyedDataStruct();
if(_this.exists(key)) {
Object cur = _this.get(key);
if(STRUCT.isDataArray(cur)) {
((Arr)cur).add(value);
} else {
Arr darr = new DataArrayImpl();
darr.add(cur);
darr.add(value);
_this.put(key, darr);
}
} else {
setProperty(key, value);
}
}
@SuppressWarnings("unchecked")
private PropertyBag<Object> asObjectKeyedDataStruct() {
return (PropertyBag<Object>)this;
}
private Object toStringOrIntKey(String s) {
Check.notNull(s);
return Maths.isInteger(s) ? Integer.valueOf(s) : s;
}
@SuppressWarnings("unchecked")
private PropertyBag<Object> asObjectKeyedDataStruct(DataStruct ds) {
return (PropertyBag<Object>)ds;
}
@Override
@SuppressWarnings("unchecked")
public Object getPathSafe(String... pathBits) {
PropertyBag<Object> _target = asObjectKeyedDataStruct();
int len = pathBits.length;
for(int i = 0; i < len; i++) {
String pathBit = pathBits[i];
Object k = toStringOrIntKey(pathBit);
Object newTarget = _target.get(k);
if(isPrimitiveOrNull(newTarget)) {
return i == len - 1 ? newTarget : null;
} else {
_target = (PropertyBag<Object>)newTarget;
}
}
return _target;
}
@Override
public Object getPathSafe(String path) {
return getPathSafe(path.split("\\."));
}
@Override
public String getString(K key) throws ClassCastException {
return (String)get(key);
}
@Override
public Number getNumber(K key) throws ClassCastException {
return (Number)get(key);
}
@Override
public Boolean getBoolean(K key) throws ClassCastException {
return (Boolean)get(key);
}
@Override
public String getPathSafeString(String path) throws ClassCastException {
return (String)getPathSafe(path);
}
@Override
public String getPathSafeString(String... pathBits) throws ClassCastException {
return (String)getPathSafe(pathBits);
}
@Override
public Number getPathSafeNumber(String path) throws ClassCastException {
return (Number)getPathSafe(path);
}
@Override
public Number getPathSafeNumber(String... pathBits) throws ClassCastException {
return (Number)getPathSafe(pathBits);
}
@Override
public Boolean getPathSafeBoolean(String path) throws ClassCastException {
return (Boolean)getPathSafe(path);
}
@Override
public Boolean getPathSafeBoolean(String... pathBits) throws ClassCastException {
return (Boolean)getPathSafe(pathBits);
}
@Override
public Obj getPathSafeObject(String path) throws ClassCastException {
return (Obj)getPathSafe(path);
}
@Override
public Obj getPathSafeObject(String... pathBits) throws ClassCastException {
return (Obj)getPathSafe(pathBits);
}
@Override
public Arr getPathSafeArray(String path) throws ClassCastException {
return (Arr)getPathSafe(path);
}
@Override
public Arr getPathSafeArray(String... pathBits) throws ClassCastException {
return (Arr)getPathSafe(pathBits);
}
@Override
public Long getLong(K key) throws ClassCastException {
Number n = getNumber(key);
return n == null ? null : n.longValue();
}
@Override
public Double getDouble(K key) throws ClassCastException {
Number n = getNumber(key);
return n == null ? null : n.doubleValue();
}
@Override
public String getStrictString(K key) throws ClassCastException, NullPointerException {
return Check.notNull(getString(key));
}
@Override
public Long getStrictLong(K key) throws ClassCastException, NullPointerException {
return Check.notNull(getLong(key));
}
@Override
public Double getStrictDouble(K key) throws ClassCastException, NullPointerException {
return Check.notNull(getDouble(key));
}
@Override
public Boolean getStrictBoolean(K key) throws ClassCastException, NullPointerException {
return Check.notNull(getBoolean(key));
}
@Override
public Obj getStrictObject(K key) throws ClassCastException, NullPointerException {
return Check.notNull(getObject(key));
}
@Override
public Arr getStrictArray(K key) throws ClassCastException, NullPointerException {
return Check.notNull(getArray(key));
}
@Override
public Long putLong(K key, Long o) {
return (Long)put(key, o);
}
@Override
public Double putDouble(K key, Double o) {
return (Double)put(key, o);
}
@Override
public String putString(K key, String o) {
return (String)put(key, o);
}
@Override
public Boolean putBoolean(K key, Boolean o) {
return (Boolean)put(key, o);
}
@Override
public Obj putObject(K key, Obj o) {
return (Obj)put(key, o);
}
@Override
public Arr putArray(K key, Arr o) {
return (Arr)put(key, o);
}
protected void onMutate() {
json = null;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return STRUCT.clone(this);
}
@Override
public String toString() {
return json != null ? json : STRUCT.toJsonString(this);
}
@Override
public boolean equals(Object obj) {
return STRUCT.equals(this, obj);
}
@Override
public int hashCode() {
if (json == null) {
json = toString();
}
return json.hashCode();
}
}