package railo.commons.lang;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import railo.loader.engine.CFMLEngineFactory;
import railo.loader.util.Util;
import railo.runtime.exp.PageException;
import railo.runtime.type.Collection;
import railo.runtime.type.Query;
import railo.runtime.util.Creation;
public class SizeAndCount {
private static final int OBJECT_GRANULARITY_IN_BYTES = 8;
private static final int WORD_SIZE = Arch.getVMArchitecture().getWordSize();
private static final int HEADER_SIZE = 2 * WORD_SIZE;
private static final int DOUBLE_SIZE = 8;
private static final int FLOAT_SIZE = 4;
private static final int LONG_SIZE = 8;
private static final int INT_SIZE = 4;
private static final int SHORT_SIZE = 2;
private static final int BYTE_SIZE = 1;
private static final int BOOLEAN_SIZE = 1;
private static final int CHAR_SIZE = 2;
private static final int REF_SIZE = WORD_SIZE;
public static Size sizeOf(Object obj) throws PageException {
Creation creator = CFMLEngineFactory.getInstance().getCreationUtil();
Size size = new Size(0,0);
sizeOf(creator,size, obj, new HashSet<Object>());
return size;
}
private static void sizeOf(Creation creator,Size size,Object obj, Set<Object> parents) throws PageException {
if(obj==null) return;
Object raw=obj;
// TODO this is just a patch solution, find a better way to handle this kind of situation (Wrapper classes)
if(isInstaneOf(obj.getClass(),"railo.runtime.text.xml.struct.XMLStruct")) {
try {
Method toNode = raw.getClass().getMethod("toNode", new Class[0]);
raw=toNode.invoke(obj, new Object[0]);
}
catch (Throwable e) {
e.printStackTrace();
}
}
if(parents.contains(raw)) return;
parents.add(raw);
try {
if(obj instanceof Collection) {
if(obj instanceof Query)
sizeOf(creator,size,(Query)obj,parents);
else
sizeOf(creator,size,((Collection)obj).valueIterator(),parents);
return;
}
// Map
else if(obj instanceof Map) {
sizeOf(creator,size,((Map)obj).values().iterator(),parents);
return;
}
// List
else if(obj instanceof List) {
sizeOf(creator,size,((List)obj).iterator(),parents);
return;
}
// String
else if(obj instanceof String){
size.size+= (CHAR_SIZE*((String)obj).length())+REF_SIZE;
}
// Number
else if(obj instanceof Number){
if(obj instanceof Double) size.size+= DOUBLE_SIZE+REF_SIZE;
else if(obj instanceof Float) size.size+= FLOAT_SIZE+REF_SIZE;
else if(obj instanceof Long) size.size+= LONG_SIZE+REF_SIZE;
else if(obj instanceof Integer) size.size+= INT_SIZE+REF_SIZE;
else if(obj instanceof Short) size.size+= SHORT_SIZE+REF_SIZE;
else if(obj instanceof Byte) size.size+= BYTE_SIZE+REF_SIZE;
}
else if(obj instanceof Boolean) size.size+= REF_SIZE+BOOLEAN_SIZE;
else if(obj instanceof Character) size.size+= REF_SIZE+CHAR_SIZE;
else size.size+=_sizeOf(obj);
size.count++;
}
finally {
//parents.remove(raw);// TODO should we not remove, to see if sister is me.
}
}
private static void sizeOf(Creation creator,Size size,Iterator it, Set<Object> parents) throws PageException {
size.count++;
size.size+=REF_SIZE;
while(it.hasNext()){
sizeOf(creator,size,it.next(),parents);
}
}
private static void sizeOf(Creation creator,Size size,Query qry, Set<Object> parents) throws PageException {
size.count++;
size.size+=REF_SIZE;
int rows=qry.getRecordcount();
String[] strColumns = qry.getColumns();
Collection.Key[] columns = new Collection.Key[strColumns.length];
for(int col=0;col<columns.length;col++){
columns[col]=creator.createKey(strColumns[col]);
}
for(int row=1;row<=rows;row++){
for(int col=0;col<columns.length;col++){
sizeOf(creator,size,qry.getAt(columns[col], row),parents);
}
}
}
public static boolean isInstaneOf(Class src ,String className) {
if(src==null) return false;
if(className.equals(src.getName()))return true;
// interfaces
Class[] interfaces = src.getInterfaces();
for(int i=0;i<interfaces.length;i++){
if(isInstaneOf(interfaces[i], className)) return true;
}
return isInstaneOf(src.getSuperclass(), className);
}
public static int _sizeOf(Object o) {
//System.err.println(o.getClass().getName());
ByteArrayOutputStream os = new ByteArrayOutputStream();
ObjectOutputStream oos=null;
try {
oos = new ObjectOutputStream(os);
oos.writeObject(o);
}
catch(Throwable t){}
finally {
Util.closeEL(oos);
}
return os.toByteArray().length;
}
public static class Size {
public int count;
public int size;
public Size(int count, int size) {
this.count=count;
this.size=size;
}
}
}
class Arch {
private static final Arch ARCH_32_BITS=new Arch(32, 4);
private static final Arch ARCH_64_BITS=new Arch(64, 8);
private static final Arch ARCH_UNKNOWN=new Arch(32, 4);
private int bits;
private int wordSize;
private Arch(int bits, int wordSize) {
this.bits = bits;
this.wordSize = wordSize;
}
public int getBits() {
return bits;
}
public int getWordSize() {
return wordSize;
}
public static Arch getVMArchitecture() {
String archString = System.getProperty("sun.arch.data.model");
if (archString != null) {
if (archString.equals("32")) {
return ARCH_32_BITS;
} else if (archString.equals("64")) {
return ARCH_64_BITS;
}
}
return ARCH_UNKNOWN;
}
}