/**
* 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.IOException;
import erjang.m.ets.EMatchContext;
import erjang.m.ets.ETermPattern;
/**
*
*/
public final class ERef extends EObject {
private EAtom node;
private int creation;
private int[] ids;
@Override
public ERef testReference() {
return this;
}
/**
* @param node
* @param refId
* @param creation
*/
public ERef(EAtom node, int[] ids, int creation) {
this.node = node;
this.creation = creation & 0x03; // 2 bits
// use at most 82 bits (18 + 32 + 32)
int len = ids.length;
this.ids = new int[3];
this.ids[0] = 0;
this.ids[1] = 0;
this.ids[2] = 0;
if (len > 3) {
len = 3;
}
System.arraycopy(ids, 0, this.ids, 0, len);
this.ids[0] &= 0x3ffff; // only 18 significant bits in first number
}
public ERef(EAtom node, int id1, int id2, int id3, int creation) {
this.node = node;
this.creation = creation & 0x03; // 2 bits
// use at most 82 bits (18 + 32 + 32)
this.ids = new int[3];
this.ids[0] = id1 & 0x3ffff; // only 18 significant bits in first number
this.ids[1] = id2;
this.ids[2] = id3;
}
/**
* @param node
* @param id
* @param creation
*/
public ERef(EAtom node, int id, int creation) {
this.node = node;
ids = new int[1];
ids[0] = id & 0x3ffff; // 18 bits
this.creation = creation & 0x03; // 2 bits
}
@Override
public boolean match(ETermPattern matcher, EMatchContext r) {
return matcher.match(this, r);
}
@Override
int compare_same(EObject rhs) {
int val = compare_same2(rhs);
// System.err.println("compare_same "+this+" <=> "+rhs+" ==> "+val);
return val;
}
int compare_same2(EObject rhs) {
// System.err.println("compare_same "+this+" <=> "+rhs);
ERef oref = (ERef) rhs;
int cmp = node.compareTo(oref.node);
if (cmp != 0) return cmp;
cmp = cmp(creation, oref.creation);
if (cmp != 0) return cmp;
cmp = cmp(ids.length, oref.ids.length);
if (cmp != 0) return cmp;
for (int i = 0; i < ids.length; i++) {
cmp = cmp(ids[i], oref.ids[i]);
if (cmp != 0) return cmp;
}
return 0;
}
private static int cmp(int i1, int i2) {
if (i1==i2) return 0;
if (i1<i2) return -1;
return 1;
}
/**
* Determine if two refs are equal. Refs are equal if their components are
* equal. New refs and old refs are considered equal if the node, creation
* and first id numnber are equal.
*
* @param o
* the other ref to compare to.
*
* @return true if the refs are equal, false otherwise.
*/
@Override
public boolean equals(Object rhs) {
boolean val = equals2(rhs);
// System.err.println("equals "+this+" =:= "+rhs+" ==> "+val);
return val;
}
public boolean equals2(final Object o) {
if (!(o instanceof ERef)) {
return false;
}
final ERef ref = (ERef) o;
if ((node != ref.node()) || creation != ref.creation()) {
return false;
}
// TODO: Compare the isNewRef()s ?
if (isNewRef() && ref.isNewRef()) {
return ids[0] == ref.ids[0]
&& ids[1] == ref.ids[1]
&& ids[2] == ref.ids[2];
}
return ids[0] == ref.ids[0];
}
@Override
public int hashCode() {
int res =
500000003 * node.hashCode() +
1000000007 * creation +
1500000001 * ids[0];
if (isNewRef()) {
res +=
ids[1] * 250000013 +
ids[2] * 750000007;
}
return res;
}
@Override
int cmp_order() {
return CMP_ORDER_REFERENCE;
}
/**
* Get the id number from the ref. Old style refs have only one id number.
* If this is a new style ref, the first id number is returned.
*
* @return the id number from the ref.
*/
public int id() {
return ids[0];
}
/**
* Get the array of id numbers from the ref. If this is an old style ref,
* the array is of length 1. If this is a new style ref, the array has
* length 3.
*
* @return the array of id numbers from the ref.
*/
public int[] ids() {
return ids;
}
/**
* Determine whether this is a new style ref.
*
* @return true if this ref is a new style ref, false otherwise.
*/
public boolean isNewRef() {
return ids.length > 1;
}
/**
* Get the creation number from the ref.
*
* @return the creation number from the ref.
*/
public int creation() {
return creation;
}
/**
* Get the node name from the ref.
*
* @return the node name from the ref.
*/
public EAtom node() {
return node;
}
/**
* Get the string representation of the ref. Erlang refs are printed as
* #Ref<node.id>
*
* @return the string representation of the ref.
*/
@Override
public String toString() {
String s = "#Ref<" + node + "." + creation();
for (int i = ids.length-1; i >= 0; i--) {
s += "." + ids[i];
}
s += ">";
return s;
}
/**
* @return
*/
public int[] internal_ref_numbers() {
throw new NotImplemented();
}
public static ERef read(EInputStream ei) throws IOException {
return ei.read_ref();
}
/* (non-Javadoc)
* @see erjang.EObject#encode(erjang.EOutputStream)
*/
@Override
public void encode(EOutputStream eos) {
eos.write_ref(node.getName(), ids, creation);
}
}