/**
* This file is part of Erjang - A JVM-based Erlang VM
*
* Copyright (c) 2009 by Trifork
*
* 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 erjang.m.erlang;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.util.Calendar;
import java.util.GregorianCalendar;
import erjang.BIF;
import erjang.EAtom;
import erjang.EBig;
import erjang.EBinary;
import erjang.EBitString;
import erjang.ECons;
import erjang.EExternal;
import erjang.EIOListVisitor;
import erjang.EInputStream;
import erjang.EInteger;
import erjang.EDouble;
import erjang.EObject;
import erjang.EOutputStream;
import erjang.ERT;
import erjang.ESeq;
import erjang.ESmall;
import erjang.EString;
import erjang.ETuple;
import erjang.ERef;
import erjang.ETuple2;
import erjang.ETuple3;
import erjang.ErlangError;
import erjang.NotImplemented;
import erjang.driver.IO;
/**
*
*/
public class ErlConvert {
/**
*
*/
private static final ESmall PLUS_SIGN = ERT.box((int)'+');
public static final EAtom am_compressed = EAtom.intern("compressed");
private static final EAtom am_minor_version = EAtom.intern("minor_version");
private static final EAtom am_safe = EAtom.intern("safe");
@BIF
public static EObject binary_to_term(EObject arg) {
EBinary bin;
if ((bin=arg.testBinary()) == null) throw ERT.badarg(arg);
EInputStream in = bin.getInputStream();
try {
EObject val = in.read_any();
//System.out.println("DECODED:"+val);
return val;
} catch (IOException e) {
throw new ErlangError(ERT.am_badarg, e, arg);
}
}
@BIF
public static EObject binary_to_term(EObject arg, EObject opts) {
EBinary bin; ESeq options;
if ((bin=arg.testBinary()) == null || (options = opts.testSeq()) == null) throw ERT.badarg(arg);
EInputStream in = bin.getInputStream();
if (!options.isNil()) {
if (options.head() == am_safe) {
in.setSafe(true);
}
}
try {
EObject val = in.read_any();
return val;
} catch (ErlangError e) {
throw new ErlangError(ERT.am_badarg, e, arg);
} catch (IOException e) {
throw new ErlangError(ERT.am_badarg, e, arg);
}
}
@BIF
public static EBinary term_to_binary(EObject obj) {
EOutputStream eos = new EOutputStream();
eos.write(EExternal.versionTag);
eos.write_any(obj);
return eos.getBinaryContent();
}
@BIF
public static ESmall external_size(EObject obj) {
EBinary bin = term_to_binary(obj);
return ERT.box(bin.byteSize());
}
@BIF static ETuple2 split_binary(EObject bin, EObject idx) {
EBitString b;
ESmall i;
if ((b=bin.testBitString()) == null
|| ((i=idx.testSmall()) == null)
|| i.value < 0
|| i.value > b.byteSize()) {
throw ERT.badarg(bin, idx);
}
long split = i.value*8;
return new ETuple2(b.substring(0, split),
b.substring(split));
/* more efficient, but works only for EBinary */
// return new ETuple2(b.sub_binary(0, i.value),
// b.sub_binary(i.value, b.byteSize()-i.value));
}
@BIF
public static ESeq fun_to_list(EObject fun) {
return EString.fromString(fun.toString());
}
@BIF
public static EBinary term_to_binary(EObject obj, EObject spec) {
int compression = 0;
int minor = 1;
ESeq opts;
if ((opts=spec.testSeq()) == null) {
throw ERT.badarg(obj, spec);
}
while (!opts.isNil()) {
EObject val = opts.head();
ETuple2 tup;
if (val == am_compressed) {
compression = 6;
} else if ((tup=ETuple2.cast(val)) != null) {
if (tup.elem1 == am_compressed) {
ESmall sm;
if ((sm=tup.elem2.testSmall()) != null) {
compression = sm.value;
} else {
throw ERT.badarg(obj, spec);
}
} else if (tup.elem1 == am_minor_version) {
ESmall sm;
if ((sm=tup.elem2.testSmall()) != null) {
minor = sm.value;
} else {
throw ERT.badarg(obj, spec);
}
}
} else {
throw ERT.badarg(obj, spec);
}
opts = opts.tail();
}
if (compression < 0 || compression > 9 || minor < 0 || minor > 1) {
throw ERT.badarg(obj, spec);
}
if (minor == 0) {
throw new NotImplemented("encoding with minor_version=0");
}
EOutputStream eos = new EOutputStream();
eos.write(EExternal.versionTag);
if (compression != 0) {
eos.write_compressed(obj, compression);
} else {
eos.write_any(obj);
}
return eos.getBinaryContent();
}
@BIF
public static EAtom list_to_existing_atom(EObject obj) {
EString seq;
if ((seq = obj.testString()) == null)
throw ERT.badarg(obj);
return EAtom.existing_atom(seq.stringValue());
}
@BIF
public static EBinary integer_to_binary(EObject arg) {
EInteger i = arg.testInteger();
if (i == null) throw ERT.badarg(arg);
byte[] bin = i.toString().getBytes(IO.ISO_LATIN_1);
return EBinary.make(bin);
}
@BIF
public static EBinary integer_to_binary(EObject arg, EObject radix) {
EInteger i = arg.testInteger();
ESmall r = radix.testSmall();
if (i == null || r == null || r.value < 2 || r.value > 36)
throw ERT.badarg(arg, radix);
String out;
EBig big;
ESmall small;
if ((small=i.testSmall()) != null) {
out = Integer.toString(small.value, r.value);
} else if ((big=i.testBig()) != null) {
out = big.value.toString(r.value);
} else {
throw ERT.badarg(arg, radix);
}
byte[] bin = out.toUpperCase().getBytes(IO.ISO_LATIN_1);
return EBinary.make(bin);
}
@BIF
public static EInteger list_to_integer(EObject obj, EObject radix) {
EString seq;
ESmall rdx;
if ((seq = obj.testString()) == null
|| ((rdx = radix.testSmall()) == null))
throw ERT.badarg(obj, radix);
// remove leading +
if (!seq.isNil()) {
if (seq.head().equalsExactly(PLUS_SIGN)) {
seq = seq.tail().testString();
if (seq == null) {
throw ERT.badarg(obj);
}
}
}
try {
BigInteger val = new BigInteger(seq.stringValue(), rdx.value);
return ERT.box(val);
} catch (NumberFormatException e) {
throw ERT.badarg(obj);
}
}
@BIF
public static EInteger binary_to_integer(EObject obj) {
EBinary bin;
if ((bin = obj.testBinary()) == null)
throw ERT.badarg(obj);
// remove leading +
int off = 0;
if (bin.byteSize() > 0) {
if (bin.byteAt(0) == '+') {
off += 1;
}
}
try {
byte[] bytes = bin.getByteArray();
String sval = new String(bytes, off, bytes.length-off, IO.ISO_LATIN_1);
return ERT.box_parse(sval);
} catch (NumberFormatException e) {
throw ERT.badarg(obj);
}
}
@BIF
public static EInteger binary_to_integer(EObject obj, EObject radix) {
EBinary bin;
ESmall rdx;
if ((bin = obj.testBinary()) == null || (rdx = radix.testSmall()) == null)
throw ERT.badarg(obj, radix);
// remove leading +
int off = 0;
if (bin.byteSize() > 0) {
if (bin.byteAt(0) == '+') {
off += 1;
}
}
try {
byte[] bytes = bin.getByteArray();
String sval = new String(bytes, off, bytes.length-off, IO.ISO_LATIN_1);
return ERT.box(new BigInteger(sval, rdx.value));
} catch (NumberFormatException e) {
throw ERT.badarg(obj);
}
}
@BIF
public static EInteger list_to_integer(EObject obj) {
EString seq;
if ((seq = obj.testString()) == null)
throw ERT.badarg(obj);
// remove leading +
if (!seq.isNil()) {
if (seq.head().equalsExactly(PLUS_SIGN)) {
seq = seq.tail().testString();
if (seq == null) {
throw ERT.badarg(obj);
}
}
}
try {
BigInteger val = new BigInteger(seq.stringValue());
return ERT.box(val);
} catch (NumberFormatException e) {
throw ERT.badarg(obj);
}
}
@BIF
public static EDouble list_to_float(EObject obj) {
EString seq;
if ((seq = obj.testString()) == null)
throw ERT.badarg(obj);
String string = seq.stringValue();
if (string.length() == 0
|| string.charAt(0) == '.'
|| string.charAt(string.length()-1) == '.'
|| string.indexOf('.') == -1) {
throw ERT.badarg(obj);
}
try {
double val = Double.parseDouble(string);
return ERT.box(val);
} catch (NumberFormatException e) {
throw ERT.badarg(obj);
}
}
@BIF
public static ETuple list_to_tuple(EObject obj) {
ESeq seq;
if ((seq = obj.testSeq()) == null)
throw ERT.badarg(obj);
return ETuple.make(seq.toArray());
}
@BIF
public static EString binary_to_list(EObject obj) {
EBinary bin = obj.testBinary();
if (bin == null)
throw ERT.badarg(obj);
return EString.make(bin);
}
@BIF
public static EObject bitstring_to_list(EObject obj) {
EBitString bin = obj.testBitString();
if (bin == null)
throw ERT.badarg(obj);
return bin.toList();
}
@BIF
public static EString binary_to_list(EObject obj, EObject start, EObject stop) {
EBinary bin = obj.testBinary();
ESmall s = start.testSmall();
ESmall e = stop.testSmall();
if (bin == null || s==null || e==null)
throw ERT.badarg(obj,start,stop);
int idx0start = s.value-1;
int len = e.value-s.value+1;
if (idx0start < 0 || len < 0 || (idx0start + len) > bin.byteSize())
throw ERT.badarg(obj, start, stop);
return EString.make(bin, idx0start, len);
}
@BIF
public static EBitString list_to_bitstring(EObject list) {
return EIOListVisitor.collect_bitstring(list);
}
static private class BARR extends ByteArrayOutputStream {
EBinary asBinary() {
return new EBinary(super.buf, 0, super.count);
}
}
@BIF
public static EBitString iolist_to_binary(EObject list) {
EBitString bin = list.testBitString();
if (bin != null)
return bin;
ECons iol = list.testCons();
if (iol == null)
throw ERT.badarg(list);
if (iol.isNil()) {
return EBinary.EMPTY;
}
BARR barr = new BARR();
try {
collectList(list, iol, barr);
} catch (IOException e) {
// should not happen, barr is an byte array stream
throw new InternalError();
}
return barr.asBinary();
}
private static void collectList(EObject list, ECons iol, OutputStream barr) throws IOException {
EObject tail;
ECons cons;
for (tail=iol; (cons = tail.testNonEmptyList()) != null; tail = cons.tail()) {
EObject hd = cons.head();
ESmall sm;
EBinary bi;
ECons co;
if ((sm = hd.testSmall()) != null) {
if (sm.value < 0 || sm.value > 255)
throw ERT.badarg(list);
barr.write(sm.value);
} else if ((bi = hd.testBinary()) != null) {
bi.writeTo(barr);
} else if ((co = hd.testNonEmptyList()) != null) {
collectList(list, co, barr);
} else if (hd.isNil()) {
} else {
throw ERT.badarg(list);
}
}
// Process tail:
EBinary bi;
if ((bi = tail.testBinary()) != null) {
try {
bi.writeTo(barr);
} catch (IOException e) {
throw new InternalError("should not happen");
}
} else if (! tail.isNil()) {
throw ERT.badarg(list);
}
}
/**
* Write io list to output stream.
*
* @param list io list containing terms to write
* @param out output stream
* @throws IOException in case of io errors
*/
public static void collectList(EObject list, OutputStream out) throws IOException {
ECons iol = list.testCons();
if (iol == null)
throw ERT.badarg(list);
if (iol.isNil()) {
return;
}
collectList(list, iol, out);
}
@BIF
public static EObject tuple_to_list(EObject tup) {
ETuple t;
if ((t=tup.testTuple()) == null) { throw ERT.badarg(tup); }
ESeq res = ERT.NIL;
for (int i = t.arity(); i > 0; i--) {
EObject e = t.elm(i);
res = res.cons(e==null?ERT.NIL:e);
}
return res;
}
@BIF
public static EString ref_to_list(EObject obj) {
ERef ref = obj.testReference();
if (ref == null)
throw ERT.badarg(obj);
return new EString(ref.toString());
}
@BIF
public static EBinary atom_to_binary(EObject obj, EObject enc) {
EAtom am = obj.testAtom();
EAtom en = enc.testAtom();
if (am == null || en == null) {
throw ERT.badarg(obj, enc);
}
byte[] data;
String str = am.getName();
if (en == ERT.am_latin1) {
data = str.getBytes(IO.ISO_LATIN_1);
} else if (en == ERT.am_utf8 || en == ERT.am_unicode) {
data = str.getBytes(IO.UTF8);
} else {
throw ERT.badarg(obj, enc);
}
return new EBinary(data);
}
@BIF
public static EAtom binary_to_atom(EObject obj, EObject enc) {
EBinary bi = obj.testBinary();
EAtom en = enc.testAtom();
if (bi == null || en == null) {
throw ERT.badarg(obj, enc);
}
byte[] data = bi.getByteArray();
if (en == ERT.am_latin1) {
return EAtom.intern( new String(data, IO.ISO_LATIN_1) );
} else if (en == ERT.am_utf8 || en == ERT.am_unicode) {
return EAtom.intern( new String(data, IO.UTF8) );
} else {
throw ERT.badarg(obj, enc);
}
}
@BIF
public static EAtom binary_to_existing_atom(EObject obj, EObject enc) {
EBinary bi = obj.testBinary();
EAtom en = enc.testAtom();
if (bi == null || en == null) {
throw ERT.badarg(obj, enc);
}
String name;
byte[] data = bi.getByteArray();
if (en == ERT.am_latin1) {
name = new String(data, IO.ISO_LATIN_1);
} else if (en == ERT.am_utf8 || en == ERT.am_unicode) {
name = new String(data, IO.UTF8);
} else {
throw ERT.badarg(obj, enc);
}
return EAtom.existing_atom(name);
}
}