/*
* 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.DataType.array;
import static com.nominanuda.dataobject.DataType.bool;
import static com.nominanuda.dataobject.DataType.nil;
import static com.nominanuda.dataobject.DataType.number;
import static com.nominanuda.dataobject.DataType.object;
import static com.nominanuda.dataobject.DataType.string;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Serializable;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Function;
import com.nominanuda.code.Nullable;
import com.nominanuda.code.ThreadSafe;
import com.nominanuda.io.DevNull;
import com.nominanuda.lang.Check;
import com.nominanuda.lang.Collections;
import com.nominanuda.lang.Maths;
import com.nominanuda.lang.SafeConvertor;
import com.nominanuda.lang.SetList;
import com.nominanuda.lang.Strings;
import com.nominanuda.lang.Tuple2;
@ThreadSafe
public class DataStructHelper implements Serializable, DataStructFactory {
public static final DataStructHelper STRUCT = new DataStructHelper();
public static final DataStructHelper Z = new DataStructHelper();
private static final long serialVersionUID = -4825883006001134937L;
public static final int MERGE_POLICY_OVERRIDE = 0;
public static final int MERGE_POLICY_PUSH = 1;
private static final String MULTIVALUE_SUFFIX = "[]";
private static final int MULTIVALUE_SUFFIX_LEN = MULTIVALUE_SUFFIX.length();
public boolean isPrimitiveOrNull(Object o) {
return o == null || o instanceof Boolean || o instanceof Number || o instanceof String;
}
public DataType getDataType(Object o) {
return o == null ? nil
: o instanceof Boolean ? bool
: o instanceof Number ? number
: o instanceof String ? string
: o instanceof Arr ? array
: o instanceof Obj ? object
: (DataType) Check.illegalargument.fail();
}
public boolean isDataStruct(Object o) {
return o != null && o instanceof DataStruct;
}
public boolean isDataObject(Object o) {
return o != null && o instanceof Obj;
}
public boolean isDataArray(Object o) {
return o != null && o instanceof Arr;
}
public @Nullable
String toJsonStringValsNoNulls(Object o) {
if (o == null) {
return null;
} else if (isDataObject(o)) {
Obj obj = (Obj) o;
StringBuilder sb = new StringBuilder();
sb.append("{");
Iterator<String> itr = obj.getKeys().iterator();
while (itr.hasNext()) {
String k = itr.next();
String s = toJsonStringValsNoNulls(obj.get(k));
if (s != null) {
sb.append("\"" + k + "\":" + s);
if (itr.hasNext()) {
sb.append(",");
}
}
}
sb.append("}");
return sb.toString();
} else if (isDataArray(o)) {
Arr arr = (Arr) o;
StringBuilder sb = new StringBuilder();
sb.append("[");
int len = arr.getLength();
for (int i = 0; i < len; i++) {
String s = toJsonStringValsNoNulls(arr.get(i));
if (s != null) {
sb.append(s);
if (i < len - 1) {
sb.append(",");
}
}
}
sb.append("]");
return sb.toString();
} else if (o instanceof Number) {
Number n = (Number) o;
if (Maths.isInteger(n.doubleValue())) {
return "\"" + new Long(n.longValue()).toString() + "\"";
} else {
return "\"" + n.toString() + "\"";
}
} else if (o instanceof String) {
return "\"" + ((String) o).replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
} else if (o instanceof Boolean) {
return "\"" + ((Boolean) o).toString() + "\"";
} else {
throw new IllegalArgumentException("cannot convert to string an object of type:" + o.getClass().getName());
}
}
private final JsonPrinter nullJsonPrinter = new JsonPrinter(DevNull.asWriter());
public String toJsonString(Object o) {
return toJsonString(o, false);
}
public String toJsonString(Object o, boolean pretty) {
if (o == null) {
return "null";
} else if (isDataObject(o)) {
StringWriter sw = new StringWriter();
JsonPrinter p = new JsonPrinter(sw, pretty);
DataStructStreamer.stream((Obj) o, p);
return sw.toString();
} else if (isDataArray(o)) {
StringWriter sw = new StringWriter();
JsonPrinter p = new JsonPrinter(sw, pretty);
DataStructStreamer.stream((Arr) o, p);
return sw.toString();
} else if (o instanceof Number) {
return nullJsonPrinter.numberEncode((Number)o);
} else if (o instanceof String) {
return "\"" + nullJsonPrinter.stringEncode((String) o) + "\"";
} else if (o instanceof Boolean) {
return ((Boolean) o).toString();
} else {
throw new IllegalArgumentException("cannot convert to string an object of type:" + o.getClass().getName());
}
}
public String numberToString(Number n) {
return nullJsonPrinter.numberEncode(n);
}
public String jsonStringEscape(String s) {
return nullJsonPrinter.stringEncode(s);
}
ParserUtils parserUtils = new ParserUtils();
public String jsonStringUnescape(String s) {
return parserUtils.parseStringContent(s);
}
public void copy(Obj src, Obj dst, int policy) throws UnsupportedOperationException {
switch (policy) {
case MERGE_POLICY_OVERRIDE:
copyOverwite(src, dst);
break;
case MERGE_POLICY_PUSH:
copyPush(src, dst);
break;
default:
throw new UnsupportedOperationException("unknown merge policy:" + policy);
}
}
public <K> void copyOverwite(PropertyBag<K> source, PropertyBag<K> target) {
Iterator<K> itr = source.keyIterator();
while (itr.hasNext()) {
K key = itr.next();
Object o = source.get(key);
if (isPrimitiveOrNull(o)) {
target.put(key, o);
} else if (isDataArray(o)) {
copyOverwite((Arr) o, target.putNewArray(key));
} else if (isDataObject(o)) {
copyOverwite((Obj) o, target.putNewObject(key));
} else {
throw new IllegalStateException(o.getClass().getName() + " is neither a DataStruct nor a primitive type or null");
}
}
}
/**
* {a:1} {b:1} => {a:1, b:1} {a:1} {a:1} => {a:[1, 1]} {a:1} {a:null} =>
* {a:[1, null]} {a:{b:1}} {a:null} => {a:[{b:1}, null]} {a:{b:1}}
* {a:{b:{c:null}}} => {a:{b:[1,{c:null}]}} {a:1} {a:[2]} => {a:[1,2]}
* {a:[1]} {a:[2]} => {a:[1,2]} {a:{b:1}} {a:[2]} => {a:[{b:1},2]} {a:{b:1}}
* {a:{b:[2]}} => {a:{b:[1,2]}} {a:1} {} => {a:1}}
*/
public void copyPush(Obj source, Obj target) {
Iterator<String> itr = source.keyIterator();
while (itr.hasNext()) {
String key = itr.next();
Object sval = source.get(key);
putPush(target, key, sval);
}
}
public void putPush(Obj target, String key, Object val) {
if (target.exists(key)) {
Object tval = target.get(key);
if (isDataObject(val) && isDataObject(tval)) {
copyPush((Obj) val, (Obj) tval);
} else if (isDataArray(tval)) {
((Arr) tval).add(val);
} else {
Arr a = target.putNewArray(key);
a.add(tval);
a.add(val);
}
} else {
target.put(key, val);
}
}
public String primitiveOrNullToString(@Nullable Object o) {
Check.illegalargument.assertTrue(isPrimitiveOrNull(o));
return o == null ? "null" : o instanceof Number ? Maths.toString((Number) o) : o.toString();
}
@SuppressWarnings("unchecked")
public Arr fromMapsAndCollections(@SuppressWarnings("rawtypes") Collection c) {
DataArrayImpl a = new DataArrayImpl();
deepCopy(c, a);
return a;
}
public Obj fromMapsAndCollections(Map<String, Object> m) {
DataObjectImpl o = new DataObjectImpl();
deepCopy(m, o);
return o;
}
public Arr fromStringsCollection(Collection<String> list) {
return fromMapsAndCollections(list);
}
public Obj fromStringsMap(Map<String, ?> map) {
Obj obj = newObject();
for (String key : map.keySet()) {
obj.put(key, map.get(key));
}
return obj;
}
@SuppressWarnings("unchecked")
private void deepCopy(Map<String, Object> m, Obj o) {
for (Entry<String, Object> e : m.entrySet()) {
Object v = e.getValue();
if (v == null) {
o.put(e.getKey(), null);
} else if (v instanceof Map<?, ?>) {
Obj tobj = o.putNewObject(e.getKey());
deepCopy((Map<String, Object>) v, tobj);
} else if (v instanceof Collection<?>) {
Arr tarr = o.putNewArray(e.getKey());
deepCopy((Collection<Object>) v, tarr);
} else {
o.put(e.getKey(), v);
}
}
}
@SuppressWarnings("unchecked")
private void deepCopy(Collection<Object> c, Arr tarr) {
for (Object v : c) {
if (v == null) {
tarr.add(null);
} else if (v instanceof Map<?, ?>) {
Obj tobj = tarr.addNewObject();
deepCopy((Map<String, Object>) v, tobj);
} else if (v instanceof Collection<?>) {
Arr a = tarr.addNewArray();
deepCopy((Collection<Object>) v, a);
} else {
tarr.add(v);
}
}
}
public boolean equals(Object o1, Object o2) {
if (o1 == null && o2 == null) {
return true;
}
if (o1 == null || o2 == null) {
return false;
}
if (o1 == o2) {
return true;
}
if (!o1.getClass().equals(o2.getClass())) {
return false;
}
if (o1 instanceof DataStruct) {
@SuppressWarnings("unchecked")
PropertyBag<Object> ds1 = (PropertyBag<Object>) o1;
@SuppressWarnings("unchecked")
PropertyBag<Object> ds2 = (PropertyBag<Object>) o2;
Iterator<Object> k1Itr = ds1.keyIterator();
Set<Object> analyzedKeys = new HashSet<Object>();
while (k1Itr.hasNext()) {
Object k1 = k1Itr.next();
Object v1 = ds1.get(k1);
Object v2 = ds2.get(k1);
if (!equals(v1, v2)) {
return false;
}
analyzedKeys.add(k1);
}
Iterator<Object> k2Itr = ds2.keyIterator();
while (k2Itr.hasNext()) {
if (!analyzedKeys.contains(k2Itr.next())) {
return false;
}
}
return true;
} else {
Check.illegalstate.assertTrue(isPrimitiveOrNull(o1)
&& isPrimitiveOrNull(o1));
return o1.equals(o2);
}
}
public <K, T extends PropertyBag<K>> T clone(T struct) {
Check.notNull(struct);
Iterator<K> keyItr = struct.keyIterator();
@SuppressWarnings("unchecked")
T target = (T) (struct instanceof Arr ? new DataArrayImpl() : new DataObjectImpl());
while (keyItr.hasNext()) {
K key = keyItr.next();
Object val = struct.get(key);
if (isPrimitiveOrNull(val)) {
target.put(key, val);
} else if (val instanceof Arr) {
target.put(key, clone((Arr) val));
} else {
target.put(key, clone((Obj) val));
}
}
return target;
}
public <K, T extends PropertyBag<K>> T clone(T struct, AbstractDataStruct<K> parent) {
Check.notNull(struct);
Iterator<K> keyItr = struct.keyIterator();
@SuppressWarnings("unchecked")
T target = (T) (struct instanceof Arr ? new DataArrayImpl(parent) : new DataObjectImpl(parent));
while (keyItr.hasNext()) {
K key = keyItr.next();
Object val = struct.get(key);
if (isPrimitiveOrNull(val)) {
target.put(key, val);
} else if (val instanceof Arr) {
target.put(key, clone((Arr) val));
} else {
target.put(key, clone((Obj) val));
}
}
return target;
}
@SuppressWarnings("unchecked")
public <T> Iterable<T> castAsIterable(Arr arr) {
return (Iterable<T>) arr;
}
public void toFlatMap(DataStruct from, Map<String, Object> map) {
toFlatMap(map, "", from);
}
private void toFlatMap(Map<String, Object> map, String key, @Nullable Object value) {
switch (getDataType(value)) {
case object:
Obj obj = (Obj) value;
for (String k : obj.getKeys()) {
Object val = obj.getStrict(k);
toFlatMap(map, keyJoin(key, k), val);
}
break;
case array:
Arr arr = ((Arr) value);
int len = arr.getLength();
for (int i = 0; i < len; i++) {
toFlatMap(map, keyJoin(key, i), arr.get(i));
}
break;
case nil:
case number:
case bool:
case string:
map.put(key, value);
break;
default:
throw new IllegalArgumentException();
}
}
private String keyJoin(String prefix, Object suffix) {
Check.illegalstate.assertTrue(suffix instanceof Integer || suffix instanceof String);
return (prefix.length() > 0) ? prefix + "." + suffix.toString() : suffix.toString();
}
private void writeFlatMapTo(Map<String, Object> pMap, Obj dest) {
for (Entry<String, ?> e : pMap.entrySet()) {
writeScalarOrMultivaluedProperty(dest, e.getKey(), e.getValue());
}
}
public DataStruct fromFlatMap(Map<String, Object> map) {
Obj dest = new DataObjectImpl();
writeFlatMapTo(map, dest);
return dest;
}
private void writeScalarOrMultivaluedProperty(Obj target, String path, Object val) {
if (val != null && val.getClass().isArray()) {
val = Arrays.asList((Object[]) val);
}
if (!(val == null || val instanceof Collection<?>)) {// scalar
if (path.endsWith(MULTIVALUE_SUFFIX)) {
path = path.substring(0, path.length() - MULTIVALUE_SUFFIX_LEN) + ".0";
}
writeScalarProperty(target, path, val);
} else {// Collection
Collection<?> l = (Collection<?>) val;
int len = l.size();
if (len == 0) {
return;
// writeScalarProperty(path, null);
} else if (len == 1) {
if (path.endsWith(MULTIVALUE_SUFFIX)) {
path = path.substring(0, path.length() - MULTIVALUE_SUFFIX_LEN) + ".0";
}
writeScalarProperty(target, path, l.iterator().next());
} else {
if (path.endsWith(MULTIVALUE_SUFFIX)) {
path = path.substring(0, path.length() - MULTIVALUE_SUFFIX_LEN);
}
int i = 0;
for (Object v : l) {
writeScalarProperty(target, path + "." + i, v);
i++;
}
}
}
}
private void writeScalarProperty(Obj target, String path, Object val) {
if (val != null && (val.getClass().isArray() || val instanceof Collection<?>)) {
throw new IllegalArgumentException(val.getClass() + " is not a scalar type");
}
// to allow alternative array syntax a[0] instead of a.0
path = path.replaceAll("\\[(\\d+)\\]", "\\.$1");
String[] bits = path.split("\\.");
writeScalarProperty(target, bits, val);
}
private void writeScalarProperty(Obj target, String[] path, Object val) {
String k = path[0];
if (path.length == 1) {
target.put(k, val);
} else {
Object o = target.get(k);
String[] subPath = new String[path.length - 1];// Arrays.copyOfRange(path, 1, path.length);
System.arraycopy(path, 1, subPath, 0, subPath.length);
String nextKey = subPath[0];
if (Maths.isInteger(nextKey)) {
Arr newTarget = isDataArray(o) ? (Arr) o : target.putNewArray(k);
writeScalarProperty(newTarget, subPath, val);
} else {
Obj newTarget = isDataObject(o) ? (Obj) o : target.putNewObject(k);
writeScalarProperty(newTarget, subPath, val);
}
}
}
private void writeScalarProperty(Arr target, String[] path, Object val) {
Integer k = Integer.parseInt(path[0]);
if (path.length == 1) {
target.put(k, val);
} else {
Object o = target.getLength() > k ? target.get(k) : null;
// String[] subPath = Arrays.copyOfRange(path, 1, path.length);
String[] subPath = new String[path.length - 1];// Arrays.copyOfRange(path, 1, path.length);
System.arraycopy(path, 1, subPath, 0, subPath.length);
String nextKey = subPath[0];
if (Maths.isInteger(nextKey)) {
Arr newTarget = isDataArray(o) ? (Arr) o : target.putNewArray(k);
writeScalarProperty(newTarget, subPath, val);
} else {
Obj newTarget = isDataObject(o) ? (Obj) o : target.putNewObject(k);
writeScalarProperty(newTarget, subPath, val);
}
}
}
public Map<String, ? super Object> toMapsAndLists(Obj obj) {
Map<String,Object> res = new LinkedHashMap<>();
if (obj != null) {
for (String k : obj.getKeys()) {
Object v = obj.get(k);
Object vToPut =
v == null ? null
: v instanceof Obj ? toMapsAndLists((Obj)v)
: v instanceof Arr ? toMapsAndLists((Arr)v)
: v;
res.put(k, vToPut);
}
}
return res;
}
public List<? super Object> toMapsAndLists(Arr arr) {
List<? super Object> res = new LinkedList<>();
if (arr != null) {
for (Object v : arr) {
Object vToPut =
v == null ? null
: v instanceof Obj ? toMapsAndLists((Obj)v)
: v instanceof Arr ? toMapsAndLists((Arr)v)
: v;
res.add(vToPut);
}
}
return res;
}
public Map<String, ? super Object> toMapsAndSetLists(Obj obj) {
Map<String,Object> res = new LinkedHashMap<>();
if (obj != null) {
for (String k : obj.getKeys()) {
Object v = obj.get(k);
Object vToPut =
v == null ? null
: v instanceof Obj ? toMapsAndSetLists((Obj)v)
: v instanceof Arr ? toMapsAndSetLists((Arr)v)
: v;
res.put(k, vToPut);
}
}
return res;
}
public SetList<? super Object> toMapsAndSetLists(Arr arr) {
SetList<? super Object> res = new SetList<>();
if (arr != null) {
for (Object v : arr) {
Object vToPut =
v == null ? null
: v instanceof Obj ? toMapsAndSetLists((Obj)v)
: v instanceof Arr ? toMapsAndSetLists((Arr)v)
: v;
res.add(vToPut);
}
}
return res;
}
public List<String> toStringsList(Arr arr, boolean allowNulls) {
List<String> list = new ArrayList<>();
if (arr != null) {
for (Object obj : arr) {
if (obj != null || allowNulls) {
list.add(obj.toString());
}
}
}
return list;
}
public List<String> toStringsList(Arr arr) {
return toStringsList(arr, true);
}
public Map<String, String> toStringsMap(Obj obj, boolean allowNulls) {
Map<String, String> map = new LinkedHashMap<String, String>();
if (obj != null) {
for (String key : obj.getKeys()) {
Object v = obj.get(key);
if (v != null) {
map.put(key, v.toString());
} else if (allowNulls) {
map.put(key, null);
}
}
}
return map;
}
public Map<String, String> toStringsMap(Obj obj) {
return toStringsMap(obj, true);
}
//if convertor#canConvert returns false value is not added to result
@SuppressWarnings("unchecked")
public <X extends DataStruct> X convertLeaves(X source, SafeConvertor<Object, Object> convertor) {
return Check.notNull(source) instanceof Obj
? (X)convertLeavesInternal((Obj)source, convertor)
: (X)convertLeavesInternal((Arr)source, convertor);
}
private Obj convertLeavesInternal(Obj source, SafeConvertor<Object, Object> convertor) {
Obj res = new DataObjectImpl();
for (String k : source.getKeys()) {
Object v = source.get(k);
if(isPrimitiveOrNull(v) && convertor.canConvert(v)) {
res.put(k, convertor.apply(v));
} else if(v instanceof Obj) {
res.put(k, convertLeavesInternal((Obj)v, convertor));
} else {
res.put(k, convertLeavesInternal((Arr)v, convertor));
}
}
return res;
}
private Arr convertLeavesInternal(Arr source, SafeConvertor<Object, Object> convertor) {
Arr res = new DataArrayImpl();
int len = source.getLength();
for (int i = 0; i < len; i++) {
Object v = source.get(i);
if(isPrimitiveOrNull(v) && convertor.canConvert(v)) {
res.add(convertor.apply(v));
} else if(v instanceof Obj) {
res.add(convertLeavesInternal((Obj)v, convertor));
} else {
res.add(convertLeavesInternal((Arr)v, convertor));
}
}
return res;
}
/**
* members in the form key, val, key, val etc.
*/
public Obj buildObject(Object... members) {
Obj o = newObject();
for (int i = 0; i < members.length; i+=2) {
o.put((String)members[i], members[i+1]);
}
return o;
}
public Arr buildArray(Object... members) {
Arr a = newArray();
for (Object member : members) {
a.add(member);
}
return a;
}
public Obj newObject() {
return new DataObjectImpl();
}
public <T> Obj newObject(Iterable<T> iterable, Function<T, Tuple2<String, Object>> fnc) {
final Obj obj = newObject();
for (T item : iterable) {
Tuple2<String, Object> v = Check.notNull(fnc.apply(item));
obj.put(v.get0(), v.get1());
}
return obj;
}
public <T> Obj newObject(Iterable<T> iterable, BiFunction<T, Obj, Tuple2<String, Object>> fnc) {
final Obj obj = newObject();
for (T item : iterable) {
Tuple2<String, Object> v = fnc.apply(item, obj);
if (v != null) { // it's allowed to return null and directly manipulate obj from fnc
obj.put(v.get0(), v.get1());
}
}
return obj;
}
public Arr newArray() {
return new DataArrayImpl();
}
public <T> Arr newArray(Iterable<T> iterable, Function<T, Object> fnc) {
final Arr arr = newArray();
for (T item : iterable) {
arr.add(fnc.apply(item));
}
return arr;
}
public DataStruct parse(Reader json, boolean loose) {
try {
return loose ? new JsonLooseParser().parse(json) : new JSONParser().parse(json);
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
public DataStruct parseUtf8(InputStream json, boolean loose) {
return parse(new InputStreamReader(json, Strings.UTF8), loose);
}
public DataStruct parse(String json, boolean loose) {
return parse(new StringReader(json), loose);
}
public Obj parseObject(Reader json, boolean loose) {
return (Obj)parse(json, loose);
}
public Obj parseObjectUtf8(InputStream json, boolean loose) {
return (Obj)parseUtf8(json, loose);
}
public Obj parseObject(String json, boolean loose) {
return (Obj)parse(new StringReader(json), loose);
}
public Arr parseArray(Reader json, boolean loose) {
return (Arr)parse(json, loose);
}
public Arr parseArrayUtf8(InputStream json, boolean loose) {
return (Arr)parseUtf8(json, loose);
}
public Arr parseArray(String json, boolean loose) {
return (Arr)parse(new StringReader(json), loose);
}
public DataStruct parse(Reader json) {
return parse(json, false);
}
public DataStruct parseUtf8(InputStream json) {
return parseUtf8(json, false);
}
public DataStruct parse(String json) {
return parse(json, false);
}
public Obj parseObject(Reader json) {
return parseObject(json, false);
}
public Obj parseObjectUtf8(InputStream json) {
return parseObjectUtf8(json, false);
}
public Obj parseObject(String json) {
return (Obj)parse(new StringReader(json));
}
public Arr parseArray(Reader json) {
return parseArray(json, false);
}
public Arr parseArrayUtf8(InputStream json) {
return parseArrayUtf8(json, false);
}
public Arr parseArray(String json) {
return parseArray(json, false);
}
// can lead to classcastexception in case it is not a dataobject array
@SuppressWarnings("unchecked")
public Iterable<Obj> asObjSeq(@Nullable Arr arr) {
if (arr != null) {
return (Iterable<Obj>)(Iterable<?>)arr;
}
return Collections.emptyIterable();
}
@SuppressWarnings("unchecked")
public Iterable<Arr> asArrSeq(@Nullable Arr arr) {
if (arr != null) {
return (Iterable<Arr>)(Iterable<?>)arr;
}
return Collections.emptyIterable();
}
// can lead to classcastexception in case it is not a map of dataobjects
public Iterable<Obj> asObjSeq(final @Nullable Obj obj) {
return new Iterable<Obj>() {
@Override public Iterator<Obj> iterator() {
if (obj != null) {
final Iterator<Entry<String, Object>> i = obj.iterator();
return new Iterator<Obj>() {
@Override public boolean hasNext() {
return i.hasNext();
}
@Override public Obj next() {
return (Obj) i.next().getValue();
}
@Override public void remove() {
Check.unsupportedoperation.fail();
}
};
}
return java.util.Collections.emptyIterator();
}
};
}
// can lead to classcastexception in case it is not a map of dataobjects
public Iterable<Tuple2<String, Obj>> asKeyObjSeq(final @Nullable Obj obj) {
return new Iterable<Tuple2<String, Obj>>() {
@Override public Iterator<Tuple2<String, Obj>> iterator() {
if (obj != null) {
final Iterator<Entry<String, Object>> i = obj.iterator();
return new Iterator<Tuple2<String, Obj>>() {
@Override public boolean hasNext() {
return i.hasNext();
}
@Override public Tuple2<String, Obj> next() {
Entry<String, Object> entry = i.next();
return new Tuple2<String, Obj>(entry.getKey(), (Obj) entry.getValue());
}
@Override public void remove() {
Check.unsupportedoperation.fail();
}
};
}
return java.util.Collections.emptyIterator();
}
};
}
// can lead to classcastexception if comparator is not of the right type
@SuppressWarnings("unchecked")
public <T> void sort(Arr arr, Comparator<T> c) {
int l = arr.getLength();
Object[] objs = new Object[l];
for (int i=0; i<l; i++) {
objs[i] = arr.get(i);
}
Arrays.sort((T[])objs, c);
for (int i=0; i<l; i++) {
arr.put(i, objs[i]);
}
}
public Arr subArray(Arr arr, int from, int to) {
Arr res = newArray();
int limit = Math.min(arr.getLength(), to) - from;
for (int i = 0; i < limit; i++) {
res.put(i, arr.get(i + from));
}
return res;
}
public Arr arr(Object... attributes) {
return STRUCT.buildArray(attributes);
}
public Obj obj(Object... attributes) {
return STRUCT.buildObject(attributes);
}
public Obj obj(Map map) {
return STRUCT.fromMapsAndCollections(map);
}
}