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