001    package railo.runtime.functions.conversion;
002    
003    import java.util.Iterator;
004    
005    import railo.commons.lang.StringUtil;
006    import railo.runtime.PageContext;
007    import railo.runtime.exp.PageException;
008    import railo.runtime.ext.function.Function;
009    import railo.runtime.interpreter.JSONExpressionInterpreter;
010    import railo.runtime.op.Caster;
011    import railo.runtime.type.Array;
012    import railo.runtime.type.ArrayImpl;
013    import railo.runtime.type.Collection;
014    import railo.runtime.type.Collection.Key;
015    import railo.runtime.type.KeyImpl;
016    import railo.runtime.type.QueryImpl;
017    import railo.runtime.type.Struct;
018    import railo.runtime.type.util.CollectionUtil;
019    import railo.runtime.type.util.KeyConstants;
020    import railo.runtime.type.util.ListUtil;
021    
022    /**
023     * Decodes Binary Data that are encoded as String
024     */
025    public final class DeserializeJSON implements Function {
026    
027            private static final Key ROWCOUNT = KeyImpl.intern("ROWCOUNT");
028    
029            public static Object call(PageContext pc, String JSONVar) throws PageException {
030                return call(pc,JSONVar,true);
031            }
032            public static Object call(PageContext pc, String JSONVar,boolean strictMapping) throws PageException {
033                    Object result = new JSONExpressionInterpreter().interpret(pc, JSONVar);
034                    if(!strictMapping) return toQuery(result);
035                    return result;
036            }
037    
038            //{"COLUMNS":["AAA","BBB"],"DATA":[["a","b"],["c","d"]]}
039            //{"ROWCOUNT":2,"COLUMNS":["AAA","BBB"],"DATA":{"aaa":["a","c"],"bbb":["b","d"]}}
040            private static Object toQuery(Object obj) throws PageException {
041                    if(obj instanceof Struct) {
042                            Struct sct=(Struct) obj;
043                            Key[] keys = CollectionUtil.keys(sct);
044                            
045                            // Columns
046                            Key[] columns = null;
047                            if(contains(keys,KeyConstants._COLUMNS)) 
048                                    columns = toColumns(sct.get(KeyConstants._COLUMNS,null));
049                            else if(contains(keys,KeyConstants._COLUMNLIST)) 
050                                    columns = toColumnlist(sct.get(KeyConstants._COLUMNLIST,null));
051                            
052                            // rowcount
053                            int rowcount = -1;
054                            if(contains(keys,ROWCOUNT))
055                                    rowcount = toRowCount(sct.get(ROWCOUNT,null));
056                            else if(contains(keys,KeyConstants._RECORDCOUNT))
057                                    rowcount = toRowCount(sct.get(KeyConstants._RECORDCOUNT,null));
058                                    
059                            
060                            if(columns!=null) {
061                                    if(keys.length==2 && contains(keys,KeyConstants._DATA)) {
062                                            
063                                            Array[] data = toData(sct.get(KeyConstants._DATA,null),columns);
064                                            if(data!=null) {
065                                                    return new QueryImpl(columns,data,"query");
066                                            }
067                                    }
068                                    
069                                    else if(keys.length==3 && rowcount!=-1 && contains(keys,KeyConstants._DATA)) {
070                                            Array[] data = toData(sct.get(KeyConstants._DATA,null),columns,rowcount);
071                                            if(data!=null) {
072                                                    return new QueryImpl(columns,data,"query");
073                                            }
074                                    }
075                            }
076                            return toQuery(sct,keys);
077                    }
078                    /*else if(obj instanceof Query) {
079                            return toQuery((Query) obj);
080                    }*/     
081                    else if(obj instanceof Collection) {
082                            Collection coll=(Collection) obj;
083                            return toQuery(coll, CollectionUtil.keys(coll));
084                    }
085                    
086                    return obj;
087                    
088            }
089    
090            /*private static Object toQuery(Query qry) throws DatabaseException {
091                    int rows=qry.getRecordcount();
092                    String[] columns = qry.getColumns();
093                    Object src,trg;
094                    for(int row=1;row<=rows;row++) {
095                            for(int col=0;col<columns.length;col++) {
096                                    trg=toQuery(src=qry.getAt(columns[col], row, null));
097                                    if(src!=trg) qry.setAtEL(columns[col], row, trg);
098                            }
099                    }
100                    return qry;
101            }*/
102            
103            private static Collection toQuery(Collection coll, Key[] keys) throws PageException {
104                    Object src,trg;
105                    for(int i=0;i<keys.length;i++) {
106                            trg=toQuery(src=coll.get(keys[i],null));
107                            if(src!=trg) coll.setEL(keys[i], trg);
108                    }
109                    return coll;
110            }
111            
112            private static int toRowCount(Object obj) {
113                    return Caster.toIntValue(obj,-1);
114            }
115    
116            private static Array[] toData(Object obj, Key[] columns, int rowcount) throws PageException {
117                    if(columns==null || rowcount==-1) return null;
118    
119                    Struct sct = Caster.toStruct(obj,null,false);
120                    if(sct!=null && sct.size()==columns.length) {
121                            Array[] datas=new Array[columns.length];
122                            Array col;
123                            int colLen=-1;
124                            for(int i=0;i<columns.length;i++) {
125                                    col=Caster.toArray(sct.get(columns[i],null),null);
126                                    if(col==null || colLen!=-1 && colLen!=col.size()) return null;
127                                    datas[i]=(Array) toQuery(col,CollectionUtil.keys(col));
128                                    colLen=col.size();
129                            }
130                            return datas;
131                    }
132                    return null;
133            }
134            
135            private static Array[] toData(Object obj,Key[] columns) throws PageException {
136                    if(columns==null) return null;
137                    
138                    Array arr = Caster.toArray(obj,null);
139                    if(arr!=null) {
140                            Array[] datas=new Array[columns.length];
141                            for(int i=0;i<datas.length;i++) {
142                                    datas[i]=new ArrayImpl();
143                            }
144                            
145                            Array data;
146                            Iterator<Object> it = arr.valueIterator();
147                            while(it.hasNext()) {
148                                    data=Caster.toArray(it.next(),null);
149                                    if(data==null || data.size()!=datas.length) return null;
150                                    for(int i=0;i<datas.length;i++) {
151                                            datas[i].appendEL(toQuery(data.get(i+1,null)));
152                                    }
153                            }
154                            return datas;
155                    }
156                    return null;
157            }
158    
159            
160            private static Key[] toColumns(Object obj) {
161                    Array arr = Caster.toArray(obj,null);
162                    if(arr!=null) {
163                            Key[] columns=new Key[arr.size()];
164                            String column;
165                            int index=0;
166                            Iterator<Object> it = arr.valueIterator();
167                            while(it.hasNext()) {
168                                    column=Caster.toString(it.next(),null);
169                                    if(StringUtil.isEmpty(column)) return null;
170                                    columns[index++]=KeyImpl.getInstance(column);
171                            }
172                            return columns;
173                    }
174                    return null;
175            }
176            
177            private static Key[] toColumnlist(Object obj) throws PageException {
178                    String list = Caster.toString(obj,null);
179                    if(StringUtil.isEmpty(list)) return null;
180                    return toColumns(ListUtil.trimItems(ListUtil.listToArrayRemoveEmpty(list, ',')));
181            }
182            
183            
184            /*private static boolean contains(Key[] haystack, Key[] needle) {
185                    Key h;
186                    outer:for(int i=0;i<haystack.length;i++) {
187                            h=haystack[i];
188                            for(int y=0;y<needle.length;y++) {
189                                    if(h.equalsIgnoreCase(needle[y])) continue outer;
190                            }
191                            return false;
192                    }
193                    return true;
194            }*/
195            
196            
197            private static boolean contains(Key[] haystack, Key needle) {
198                    for(int i=0;i<haystack.length;i++) {
199                            if(haystack[i].equalsIgnoreCase(needle)) return true;
200                    }
201                    return false;
202            }
203    }