package railo.runtime.net.amf;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import org.w3c.dom.Node;
import railo.commons.lang.CFTypes;
import railo.commons.lang.StringUtil;
import railo.runtime.Component;
import railo.runtime.ComponentWrap;
import railo.runtime.Page;
import railo.runtime.PageContext;
import railo.runtime.PageContextImpl;
import railo.runtime.PageSourceImpl;
import railo.runtime.component.ComponentLoader;
import railo.runtime.component.Property;
import railo.runtime.config.ConfigWeb;
import railo.runtime.engine.ThreadLocalPageContext;
import railo.runtime.exp.ApplicationException;
import railo.runtime.exp.PageException;
import railo.runtime.img.Image;
import railo.runtime.op.Caster;
import railo.runtime.op.Decision;
import railo.runtime.op.Duplicator;
import railo.runtime.text.xml.XMLCaster;
import railo.runtime.type.Array;
import railo.runtime.type.ArrayImpl;
import railo.runtime.type.Collection;
import railo.runtime.type.Collection.Key;
import railo.runtime.type.KeyImpl;
import railo.runtime.type.Query;
import railo.runtime.type.Struct;
import railo.runtime.type.StructImpl;
import railo.runtime.type.UDF;
import railo.runtime.type.cfc.ComponentAccess;
import railo.runtime.type.dt.DateTimeImpl;
import railo.runtime.type.util.ArrayUtil;
import railo.runtime.type.util.CollectionUtil;
import railo.runtime.type.wrap.ArrayAsList;
import railo.runtime.type.wrap.ListAsArray;
import railo.runtime.type.wrap.MapAsStruct;
import flex.messaging.io.amf.ASObject;
/**
* Cast a CFML object to AMF Objects and the other way
*/
public class ClassicAMFCaster implements AMFCaster {
private static final Collection.Key REMOTING_FETCH = KeyImpl.intern("remotingFetch");
//private static ClassicAMFCaster singelton;
protected boolean forceCFCLower;
protected boolean forceStructLower;
protected boolean forceQueryLower;
private int methodAccessLevel;
@Override
public void init(Map arguments){
forceCFCLower=Caster.toBooleanValue(arguments.get("force-cfc-lowercase"),false);
forceQueryLower=Caster.toBooleanValue(arguments.get("force-query-lowercase"),false);
forceStructLower=Caster.toBooleanValue(arguments.get("force-struct-lowercase"),false);
// method access level
String str=Caster.toString(arguments.get("method-access-level"),"remote");
if("private".equalsIgnoreCase(str))methodAccessLevel=Component.ACCESS_PRIVATE;
else if("package".equalsIgnoreCase(str))methodAccessLevel=Component.ACCESS_PACKAGE;
else if("public".equalsIgnoreCase(str))methodAccessLevel=Component.ACCESS_PUBLIC;
else methodAccessLevel=Component.ACCESS_REMOTE;
}
@Override
public Object toAMFObject(Object cf) throws PageException {
if(cf instanceof Node) return toAMFObject((Node)cf);
if(cf instanceof List) return toAMFObject((List)cf);
if(cf instanceof Array) return toAMFObject(ArrayAsList.toList((Array)cf));
if(cf instanceof Component) return toAMFObject((Component)cf);
if(cf instanceof Query) return toAMFObject((Query)cf);
if(cf instanceof Image) return toAMFObject((Image)cf);
if(cf instanceof Map) return toAMFObject((Map)cf);
if(cf instanceof Object[]) return toAMFObject((Object[])cf);
return cf;
}
protected Object toAMFObject(Node node) {
return XMLCaster.toRawNode(node);
}
protected Object toAMFObject(Query query) throws PageException {
List<ASObject> result = new ArrayList<ASObject>();
int len=query.getRecordcount();
Collection.Key[] columns=CollectionUtil.keys(query);
ASObject row;
for(int r=1;r<=len;r++) {
result.add(row = new ASObject());
for(int c=0;c<columns.length;c++) {
row.put(toString(columns[c],forceQueryLower), toAMFObject(query.getAt(columns[c],r)) );
}
}
return result;
}
protected Object toAMFObject(Image img) throws PageException {
try{
return img.getImageBytes(null);
}
catch(Throwable t){
return img.getImageBytes("png");
}
}
protected ASObject toAMFObject(Component cfc) throws PageException {
ASObject aso = new ASObject();
aso.setType(cfc.getCallName());
Component c=cfc;
if(cfc instanceof ComponentAccess)c=ComponentWrap.toComponentWrap(methodAccessLevel,cfc);
Property[] prop = cfc.getProperties(false);
Object v; UDF udf;
if(prop!=null)for(int i=0;i<prop.length;i++) {
boolean remotingFetch = Caster.toBooleanValue(prop[i].getDynamicAttributes().get(REMOTING_FETCH,Boolean.TRUE),true);
if(!remotingFetch) continue;
v=cfc.get(prop[i].getName(),null);
if(v==null){
v=c.get("get"+prop[i].getName(),null);
if(v instanceof UDF){
udf=(UDF) v;
if(udf.getReturnType()==CFTypes.TYPE_VOID) continue;
if(udf.getFunctionArguments().length>0) continue;
try {
v=c.call(ThreadLocalPageContext.get(), udf.getFunctionName(), ArrayUtil.OBJECT_EMPTY);
} catch (PageException e) {
continue;
}
}
}
aso.put(toString(prop[i].getName(),forceCFCLower), toAMFObject(v));
}
return aso;
}
protected Object toAMFObject(Map map) throws PageException {
if(forceStructLower && map instanceof Struct) toAMFObject((Struct)map);
map=(Map) Duplicator.duplicate(map,false);
Iterator it = map.entrySet().iterator();
Map.Entry entry;
while(it.hasNext()) {
entry=(Entry) it.next();
entry.setValue(toAMFObject(entry.getValue()));
}
return MapAsStruct.toStruct(map, false);
}
protected Object toAMFObject(Struct src) throws PageException {
Struct trg=new StructImpl();
//Key[] keys = src.keys();
Iterator<Entry<Key, Object>> it = src.entryIterator();
Entry<Key, Object> e;
while(it.hasNext()) {
e = it.next();
trg.set(KeyImpl.init(toString(e.getKey(),forceStructLower)), toAMFObject(e.getValue()));
}
return trg;
}
protected Object toAMFObject(List list) throws PageException {
Object[] trg=new Object[list.size()];
ListIterator it = list.listIterator();
while(it.hasNext()) {
trg[it.nextIndex()]=toAMFObject(it.next());
}
return trg;
}
protected Object toAMFObject(Object[] src) throws PageException {
Object[] trg=new Object[src.length];
for(int i=0;i<src.length;i++){
trg[i]=toAMFObject(src[i]);
}
return trg;
}
@Override
public Object toCFMLObject(Object amf) throws PageException {
if(amf instanceof Node) return toCFMLObject((Node)amf);
if(amf instanceof List) return toCFMLObject((List)amf);
if(Decision.isNativeArray(amf)) {
if(amf instanceof byte[]) return amf;
if(amf instanceof char[]) return new String((char[])amf);
return toCFMLObject(Caster.toNativeArray(amf));
}
if(amf instanceof ASObject) return toCFMLObject((ASObject)amf);
if(amf instanceof Map) return toCFMLObject((Map)amf);
if(amf instanceof Date) return new DateTimeImpl((Date)amf);
if(amf == null) return "";
return amf;
}
protected Object toCFMLObject(Node node) {
return XMLCaster.toXMLStruct(node, true);
}
protected Object toCFMLObject(Object[] arr) throws PageException {
Array trg=new ArrayImpl();
for(int i=0;i<arr.length;i++){
trg.setEL(i+1, toCFMLObject(arr[i]));
}
return trg;
}
protected Object toCFMLObject(List list) throws PageException {
ListIterator it = list.listIterator();
while(it.hasNext()) {
//arr.setE(it.nextIndex()+1, toCFMLObject(it.next()));
list.set(it.nextIndex(),toCFMLObject(it.next()));
}
return ListAsArray.toArray(list);
}
protected Object toCFMLObject(Map map) throws PageException {
Iterator it = map.entrySet().iterator();
Map.Entry entry;
while(it.hasNext()) {
entry=(Entry) it.next();
entry.setValue(toCFMLObject(entry.getValue()));
}
return MapAsStruct.toStruct(map, false);
}
protected Object toCFMLObject(ASObject aso) throws PageException {
if(!StringUtil.isEmpty(aso.getType())){
PageContext pc = ThreadLocalPageContext.get();
ConfigWeb config = pc.getConfig();
String name="/"+aso.getType().replace('.', '/')+".cfc";
Page p = PageSourceImpl.loadPage(pc, ((PageContextImpl)pc).getPageSources(name), null) ;
if(p==null)throw new ApplicationException("Could not find a Component with name ["+aso.getType()+"]");
Component cfc = ComponentLoader.loadComponent(pc, p, p.getPageSource(), aso.getType(), false);
ComponentWrap cw=ComponentWrap.toComponentWrap(config.getComponentDataMemberDefaultAccess(),cfc);
Iterator it = aso.entrySet().iterator();
Map.Entry entry;
while(it.hasNext()){
entry = (Entry) it.next();
cw.set(KeyImpl.toKey(entry.getKey()), toCFMLObject(entry.getValue()));
}
return cfc;
}
return toCFMLObject((Map)aso);
}
protected String toString(Object key, boolean forceLower) {
if(key instanceof Key) return toString((Key)key, forceLower);
return toString(Caster.toString(key,""), forceLower);
}
protected String toString(Key key, boolean forceLower) {
if(forceLower) return key.getLowerString();
return key.getString();
}
protected String toString(String key, boolean forceLower) {
if(forceLower) return key.toLowerCase();
return key;
}
}