/*******************************************************************************
* Breakout Cave Survey Visualizer
*
* Copyright (C) 2014 James Edwards
*
* jedwards8 at fastmail dot fm
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*******************************************************************************/
package org.andork.util;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class Sizer implements ObjectTraverser.Visitor {
private static class Instance {
final Object obj;
protected Instance(Object obj) {
super();
this.obj = obj;
}
@Override
public boolean equals(Object o) {
if (o instanceof Instance) {
return obj == ((Instance) o).obj;
}
return false;
}
@Override
public int hashCode() {
return System.identityHashCode(obj);
}
}
protected Map<Class<?>, Integer> typeCounts = null;
protected Map<Field, Integer> fieldCounts = null;
protected Set<Instance> instances = new HashSet<Instance>();
protected int referenceSize = 32;
protected int booleanSize = 32;
protected int byteSize = 32;
protected int shortSize = 32;
protected int charSize = 32;
protected int intSize = 32;
protected int floatSize = 32;
protected int longSize = 64;
protected int doubleSize = 64;
protected int referenceCount = 0;
protected int booleanCount = 0;
protected int byteCount = 0;
protected int shortCount = 0;
protected int charCount = 0;
protected int intCount = 0;
protected int floatCount = 0;
protected int longCount = 0;
protected int doubleCount = 0;
protected boolean sizingActive = true;
protected long size = 0;
public Sizer enableFieldCounts() {
fieldCounts = new HashMap<Field, Integer>();
return this;
}
public Sizer enableTypeCounts() {
typeCounts = new HashMap<Class<?>, Integer>();
return this;
}
@Override
public void exitObject(ObjectTraverser traverser, Object obj, Object parent, Field parentField, int index) {
}
public Map<Field, Integer> getFieldCounts() {
return Collections.unmodifiableMap(fieldCounts);
}
public Map<Class<?>, Integer> getTypeCounts() {
return Collections.unmodifiableMap(typeCounts);
}
private void reset() {
instances.clear();
if (typeCounts != null) {
typeCounts.clear();
}
if (fieldCounts != null) {
fieldCounts.clear();
}
referenceCount = 0;
booleanCount = 0;
byteCount = 0;
shortCount = 0;
charCount = 0;
intCount = 0;
floatCount = 0;
longCount = 0;
doubleCount = 0;
size = 0;
}
public long sizeOf(Object obj) {
reset();
try {
new ObjectTraverser().traverse(obj, this);
} catch (Exception ex) {
throw new RuntimeException(ex);
}
return size;
}
@Override
public boolean visitObject(ObjectTraverser traverser, Object obj, Object parent, Field parentField, int index) {
if (parentField != null && Modifier.isStatic(parentField.getModifiers())) {
return false;
}
if (sizingActive && parent != null) {
referenceCount++;
size += referenceSize;
}
if (obj != null && instances.add(new Instance(obj))) {
Class<?> oclass = obj.getClass();
if (sizingActive && typeCounts != null) {
Integer count = typeCounts.get(oclass);
if (count == null) {
count = 0;
}
typeCounts.put(oclass, count + 1);
}
if (sizingActive && oclass.isArray() && oclass.getComponentType().isPrimitive()) {
int length = Array.getLength(obj);
if (oclass.equals(boolean[].class)) {
booleanCount += length;
size += booleanSize * length;
} else if (oclass.equals(byte[].class)) {
byteCount += length;
size += byteSize * length;
} else if (oclass.equals(short[].class)) {
shortCount += length;
size += shortSize * length;
} else if (oclass.equals(char[].class)) {
charCount += length;
size += charSize * length;
} else if (oclass.equals(int[].class)) {
intCount += length;
size += intSize;
} else if (oclass.equals(float[].class)) {
floatCount += length;
size += floatSize * length;
} else if (oclass.equals(long[].class)) {
longCount += length;
size += longSize * length;
} else if (oclass.equals(double[].class)) {
doubleCount += length;
size += doubleSize * length;
}
}
return true;
}
return false;
}
@Override
public void visitPrimitive(ObjectTraverser traverser, Object prim, Object parent, Field parentField) {
if (Modifier.isStatic(parentField.getModifiers())) {
return;
}
if (!sizingActive) {
return;
}
if (typeCounts != null) {
Integer count = typeCounts.get(parentField.getType());
if (count == null) {
count = 0;
}
typeCounts.put(parentField.getType(), count + 1);
}
if (prim instanceof Boolean) {
booleanCount++;
size += booleanSize;
} else if (prim instanceof Byte) {
byteCount++;
size += byteSize;
} else if (prim instanceof Short) {
shortCount++;
size += shortSize;
} else if (prim instanceof Character) {
charCount++;
size += charSize;
} else if (prim instanceof Integer) {
intCount++;
size += intSize;
} else if (prim instanceof Float) {
floatCount++;
size += floatSize;
} else if (prim instanceof Long) {
longCount++;
size += longSize;
} else if (prim instanceof Double) {
doubleCount++;
size += doubleSize;
}
}
}