/**
* 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;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import erjang.driver.IO;
import erjang.m.ets.EMatchContext;
import erjang.m.ets.EPattern;
import erjang.m.ets.ETermPattern;
public abstract class ECons extends EObject implements Iterable<EObject> {
@Override
int cmp_order() {
return CMP_ORDER_LIST;
}
public boolean match(ETermPattern matcher, EMatchContext r) {
return matcher.match(this, r);
}
@Override
public ETermPattern compileMatch(Set<Integer> out) {
return EPattern.compilePattern(this, out);
}
@Override
int compare_same(EObject rhs) {
ECons ths = this;
ECons other = rhs.testCons();
do {
// This would have been nicer if not ENil <: ECons...:
if (other.isNil())
return ths.isNil() ? 0 : 1;
if (ths.isNil()) return -1;
int cmp1 = ths.head().erlangCompareTo(other.head());
if (cmp1 != 0)
return cmp1;
// Iterate instead of recurse if possible:
EObject thisTail = ths.tail();
EObject otherTail = other.tail();
if (! (thisTail instanceof ECons &&
otherTail instanceof ECons))
return thisTail.erlangCompareTo(otherTail);
else {
ths = thisTail.testCons();
other = otherTail.testCons();
}
} while (true);
}
/*
* Here is a generic list-oriented equalsExactly. If need be,
* we can implement optimized versions for subclasses.
*/
@Override
public boolean equalsExactly(EObject rhs) {
if (this==rhs)
return true;
ECons ths = this;
ECons other = rhs.testCons();
if (other == null) return false;
do {
// This would have been nicer if not ENil <: ECons...:
boolean other_is_nil = other.isNil();
boolean this_is_nil = ths.isNil();
if (other_is_nil)
return this_is_nil;
if (this_is_nil)
return false;
if (!ths.head().equalsExactly(other.head())) {
return false;
}
// Iterate instead of recurse if possible:
EObject thisTail = ths.tail();
EObject otherTail = other.tail();
ths = thisTail.testCons();
other = otherTail.testCons();
if (ths==null || other==null)
return thisTail.equalsExactly(otherTail);
} while (true);
}
public abstract EObject head();
public abstract EObject tail();
public ECons testCons() {
return this;
}
// generic count function that also works for non-well-formed lists
public int count() {
EObject cell = tail();
int count = 1;
while (cell != ERT.NIL && (cell instanceof ECons)) {
cell = ((ECons) cell).tail();
count += 1;
}
return count;
}
/**
* @param c1
* @return
*/
public ECons prepend(ESeq list) {
// first, rlist=lists:reverse(list)
ESeq rlist = ERT.NIL;
while (!list.isNil()) {
rlist = rlist.cons(list.head());
list = list.tail();
}
// then, prepend rlist on this
ECons r = this;
while(!rlist.isNil()) {
r = r.cons(rlist.head());
rlist = rlist.tail();
}
return r;
}
@Override
public void visitIOList(EIOListVisitor out) throws ErlangError {
head().visitIOList(out);
tail().visitIOList(out);
}
public boolean collectIOList(List<ByteBuffer> out) {
ECons list;
EObject tail;
list_loop: for (tail=this; (list = tail.testNonEmptyList()) != null; tail = list.tail()) {
EObject head = list.head();
ESmall intval;
EBinary binary;
ECons sublist;
if ((intval = head.testSmall()) != null) {
IO.BARR barr = new IO.BARR();
byte_loop: do {
int byteValue = intval.value & 0xff;
if (intval.value != byteValue) {
return false;
}
barr.write(byteValue);
tail = list.tail();
if ((list = tail.testNonEmptyList()) != null) {
head = list.head();
} else {
out.add(ByteBuffer.wrap(barr.toByteArray()));
break list_loop;
}
} while ((intval = head.testSmall()) != null);
out.add(barr.wrap());
}
if ((binary = head.testBinary()) != null) {
binary.collectIOList(out);
} else if ((sublist = head.testNonEmptyList()) != null) {
if (!sublist.collectIOList(out)) return false;
} else if (head.isNil()) {
// do nothing //
} else {
ERT.debug("head is "+head+"::"+head.getClass());
// not a well-formed iolist
return false;
}
}
if (tail == this || tail.testNonEmptyList() != null) {
throw new RuntimeException("loop logic?");
}
if (!tail.collectIOList(out)) {
return false;
}
return true;
}
public ESeq collectCharList(CharCollector out, ESeq rest)
throws CharCollector.CollectingException,
CharCollector.InvalidElementException,
IOException
{
ECons list;
EObject tail;
for (tail=this; (list = tail.testNonEmptyList()) != null; tail = list.tail()) {
EObject head = list.head();
ESmall intval;
if ((intval = head.testSmall()) != null) {
try {
rest = out.addInteger(intval.value, rest);
} catch (CharCollector.DecodingException e) {
throw new CharCollector.CollectingException(list);
}
} else {
try {
rest = head.collectCharList(out, rest);
} catch (CharCollector.CollectingException e) {
throw new CharCollector.CollectingException(list.tail().cons(e.restOfInput));
}
}
}
if (tail == this) {
throw new ErlangExit(EBigString.fromString("internal error"));
}
return tail.collectCharList(out, rest);
}
@Override
public Iterator<EObject> iterator() {
return new EObjectIterator(this);
}
}