/*
* This file is part of Bracket Properties
* Copyright 2011-2013 David R. Smith
* All Rights Reserved
*
*/
package asia.redact.bracket.properties.serializer;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import net.iharder.Base64;
import org.apache.log4j.Logger;
import asia.redact.bracket.properties.Properties;
import asia.redact.bracket.util.SerialIDGenerator;
/**
* Given the information on the render method calls, cause properties to be written which represent the objects in the graph.
*
* @author Dave
*
*/
public class PropertiesRenderer implements Renderer {
final static Logger log = Logger.getLogger(PropertiesRenderer.class);
final Properties props;
final PackageMap packages;
public PropertiesRenderer() {
props = Properties.Factory.getInstance();
packages = new PackageMap();
}
public PropertiesRenderer(Properties props) {
this.props=props;
packages = new PackageMap();
}
private String bcnKey(Class<?> cls){
int val = packages.addPackage(cls);
if(val == -1) return cls.getName();
StringBuffer buf = new StringBuffer("$");
buf.append(val);
buf.append(".");
buf.append(cls.getSimpleName());
return buf.toString();
}
public void renderInitialKeyId(Token root){
props.put(Renderer.INITIAL_KEY_NAME, String.valueOf(root.getId()));
}
public void renderLastKeyId(long last){
props.put(Renderer.NEXT_KEY_NAME, String.valueOf(last));
}
@Override
public void renderLoadable(Token token) {
StringBuffer key = new StringBuffer(String.valueOf(token.getId()));
key.append(".");
key.append("fromString");
String value = token.getObject().toString();
props.put(key.toString(), value);
}
public void renderImmediateMapValue(Token parent, Token child, int ct){
StringBuffer key = new StringBuffer(String.valueOf(parent.getId()));
key.append(".");
key.append(ct);
key.append(".");
key.append("v");
String value = String.valueOf(child.getObject());
props.put(key.toString(), value);
}
public void renderIndirectMapValue(Token parent, Token child, int ct){
StringBuffer key = new StringBuffer(String.valueOf(parent.getId()));
key.append(".");
key.append(ct);
key.append(".");
key.append("v");
String value = "$"+String.valueOf(child.getId());
props.put(key.toString(), value);
}
public void renderImmediate(Token parent, Token child){
StringBuffer key = new StringBuffer(String.valueOf(parent.getId()));
key.append(".");
key.append(child.getKey());
String value = String.valueOf(child.getObject());
props.put(key.toString(), value);
}
public void renderIndirect(Token parent, Token child){
StringBuffer key = new StringBuffer(String.valueOf(parent.getId()));
key.append(".");
key.append(child.getKey());
String value = "$"+String.valueOf(child.getId());
props.put(key.toString(), value);
}
public void renderImmediateMapKey(Token parent, Token child, int ct){
StringBuffer key = new StringBuffer(String.valueOf(parent.getId()));
key.append(".");
key.append(ct);
key.append(".");
key.append("k");
String value = String.valueOf(child.getObject());
props.put(key.toString(), value);
}
public void renderIndirectMapKey(Token parent, Token child, int ct){
StringBuffer key = new StringBuffer(String.valueOf(parent.getId()));
key.append(".");
key.append(ct);
key.append(".");
key.append("k");
String value = "$"+String.valueOf(child.getId());
props.put(key.toString(), value);
}
public void renderParentClass(Token parentToken){
TokenType tt = parentToken.getTokenType();
if(tt == TokenType.A){
// put the length of the array as the third argument
StringBuffer value = new StringBuffer();
value.append(tt.name());
value.append("|");
value.append(bcnKey(parentToken.getObject().getClass()));
value.append("|");
value.append(parentToken.getArrayLength());
props.put(String.valueOf(parentToken.getId()), value.toString());
}else if(tt == TokenType.LI){
// in third position, put the class name of the List type (if list not empty) or the token NONE
StringBuffer buf = new StringBuffer();
buf.append(tt.name());
buf.append("|");
buf.append(bcnKey(parentToken.getObject().getClass()));
buf.append("|");
int sz = ((List<?>)parentToken.getObject()).size();
if(sz > 0) {
Object item = ((List<?>)parentToken.getObject()).get(0);
String itemClassName = bcnKey(item.getClass());
buf.append(itemClassName);
}else{
buf.append("NONE");
}
props.put(String.valueOf(parentToken.getId()), buf.toString());
}else if(tt == TokenType.M){
// in third and fourth position put the key and value type respectively or NONE and NONE
StringBuffer buf = new StringBuffer();
buf.append(tt.name());
buf.append("|");
buf.append(bcnKey(parentToken.getObject().getClass()));
buf.append("|");
int sz = ((Map<?,?>)parentToken.getObject()).size();
if(sz>0){
Set<?> set = ((Map<?,?>)parentToken.getObject()).entrySet();
Iterator<?> iter = set.iterator();
Entry<?,?> entry = (Entry<?, ?>) iter.next();
Object key= entry.getKey();
Object value = entry.getValue();
Class<?> keyClass = key.getClass();
Class<?> valueClass = value.getClass();
buf.append(bcnKey(keyClass));
buf.append("|");
buf.append(bcnKey(valueClass));
}else{
buf.append("NONE|NONE");
}
props.put(String.valueOf(parentToken.getId()), buf.toString());
}else{
//normal case, no third argument
props.put(String.valueOf(parentToken.getId()), tt.name()+"|"+bcnKey(parentToken.getObject().getClass()));
}
}
public void renderValue(Token parentToken){
TokenType tt = parentToken.getTokenType();
if(parentToken.getKey() != null && parentToken.getKey().equals("root")){
props.put(String.valueOf(SerialIDGenerator.INSTANCE.increment()), tt.name()+"|"+String.valueOf(parentToken.getObject()));
}else{
props.put(String.valueOf(parentToken.getId()), tt.name()+"|"+String.valueOf(parentToken.getObject()));
}
}
public void renderImmediateListItem(Token parent, Token child, int ct){
StringBuffer key = new StringBuffer(String.valueOf(parent.getId()));
key.append(".");
key.append(ct);
String value = String.valueOf(child.getObject());
props.put(key.toString(), value);
}
public void renderIndirectListItem(Token parent, Token child, int ct){
StringBuffer key = new StringBuffer(String.valueOf(parent.getId()));
key.append(".");
key.append(ct);
String value = "$"+String.valueOf(child.getId());
props.put(key.toString(), value);
}
/**
* char [] array is stored as a String
*
*/
public void renderCharArray(Token token){
char [] array = (char[]) token.getObject();
props.put(token.getId()+".string", new String(array));
}
public void renderByteArray(Token token){
byte [] array = (byte[]) token.getObject();
String str = Base64.encodeBytes(array);
log.debug(token.getId()+".base64"+"="+str);
props.put(token.getId()+".base64", str);
}
public void renderIntArray(Token token){
int[] data = (int[]) token.getObject();
ByteBuffer byteBuffer = ByteBuffer.allocate(data.length * 4);
IntBuffer intBuffer = byteBuffer.asIntBuffer();
intBuffer.put(data);
byte[] array = byteBuffer.array();
String str = Base64.encodeBytes(array);
props.put(token.getId()+".base64", str);
}
public void renderShortArray(Token token){
short[] data = (short[]) token.getObject();
ByteBuffer byteBuffer = ByteBuffer.allocate(data.length * 2);
ShortBuffer intBuffer = byteBuffer.asShortBuffer();
intBuffer.put(data);
byte[] array = byteBuffer.array();
String str = Base64.encodeBytes(array);
props.put(token.getId()+".base64", str);
}
public void renderLongArray(Token token){
long[] data = (long[]) token.getObject();
ByteBuffer byteBuffer = ByteBuffer.allocate(data.length * 8);
LongBuffer longBuffer = byteBuffer.asLongBuffer();
longBuffer.put(data);
byte[] array = byteBuffer.array();
String str = Base64.encodeBytes(array);
props.put(token.getId()+".base64", str);
}
public void renderDoubleArray(Token token){
double[] data = (double[]) token.getObject();
ByteBuffer byteBuffer = ByteBuffer.allocate(data.length * 8);
DoubleBuffer dBuffer = byteBuffer.asDoubleBuffer();
dBuffer.put(data);
byte[] array = byteBuffer.array();
String str = Base64.encodeBytes(array);
props.put(token.getId()+".base64", str);
}
public void renderFloatArray(Token token){
float[] data = (float[]) token.getObject();
ByteBuffer byteBuffer = ByteBuffer.allocate(data.length * 4);
FloatBuffer dBuffer = byteBuffer.asFloatBuffer();
dBuffer.put(data);
byte[] array = byteBuffer.array();
String str = Base64.encodeBytes(array);
props.put(token.getId()+".base64", str);
}
public void renderBooleanArray(Token token){
throw new UnsupportedOperationException("Nor ready for boolean array yet...");
}
public Properties getProps() {
return props;
}
@Override
public void renderClassMap() {
packages.printToProperties(props);
}
}