001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.functions.conversion; 020 021import java.util.Iterator; 022 023import lucee.commons.lang.StringUtil; 024import lucee.runtime.PageContext; 025import lucee.runtime.exp.FunctionException; 026import lucee.runtime.exp.PageException; 027import lucee.runtime.ext.function.Function; 028import lucee.runtime.functions.BIF; 029import lucee.runtime.interpreter.JSONExpressionInterpreter; 030import lucee.runtime.op.Caster; 031import lucee.runtime.type.Array; 032import lucee.runtime.type.ArrayImpl; 033import lucee.runtime.type.Collection; 034import lucee.runtime.type.Collection.Key; 035import lucee.runtime.type.KeyImpl; 036import lucee.runtime.type.QueryImpl; 037import lucee.runtime.type.Struct; 038import lucee.runtime.type.util.CollectionUtil; 039import lucee.runtime.type.util.KeyConstants; 040import lucee.runtime.type.util.ListUtil; 041 042/** 043 * Decodes Binary Data that are encoded as String 044 */ 045public final class DeserializeJSON extends BIF implements Function { 046 047 private static final Key ROWCOUNT = KeyImpl.intern("ROWCOUNT"); 048 049 public static Object call(PageContext pc, String JSONVar) throws PageException { 050 return call(pc,JSONVar,true); 051 } 052 public static Object call(PageContext pc, String JSONVar,boolean strictMapping) throws PageException { 053 Object result = new JSONExpressionInterpreter().interpret(pc, JSONVar); 054 if(!strictMapping) return toQuery(result); 055 return result; 056 } 057 058 @Override 059 public Object invoke(PageContext pc, Object[] args) throws PageException { 060 if (args.length == 1) { 061 return call(pc, Caster.toString(args[0])); 062 } else if (args.length == 2) { 063 return call(pc, Caster.toString(args[0]), Caster.toBoolean(args[1])); 064 } 065 throw new FunctionException(pc, "deserializeJSON", 0, 1, args.length - 1); 066 } 067 068 //{"COLUMNS":["AAA","BBB"],"DATA":[["a","b"],["c","d"]]} 069 //{"ROWCOUNT":2,"COLUMNS":["AAA","BBB"],"DATA":{"aaa":["a","c"],"bbb":["b","d"]}} 070 private static Object toQuery(Object obj) throws PageException { 071 if(obj instanceof Struct) { 072 Struct sct=(Struct) obj; 073 Key[] keys = CollectionUtil.keys(sct); 074 075 // Columns 076 Key[] columns = null; 077 if(contains(keys,KeyConstants._COLUMNS)) 078 columns = toColumns(sct.get(KeyConstants._COLUMNS,null)); 079 else if(contains(keys,KeyConstants._COLUMNLIST)) 080 columns = toColumnlist(sct.get(KeyConstants._COLUMNLIST,null)); 081 082 // rowcount 083 int rowcount = -1; 084 if(contains(keys,ROWCOUNT)) 085 rowcount = toRowCount(sct.get(ROWCOUNT,null)); 086 else if(contains(keys,KeyConstants._RECORDCOUNT)) 087 rowcount = toRowCount(sct.get(KeyConstants._RECORDCOUNT,null)); 088 089 090 if(columns!=null) { 091 if(keys.length==2 && contains(keys,KeyConstants._DATA)) { 092 093 Array[] data = toData(sct.get(KeyConstants._DATA,null),columns); 094 if(data!=null) { 095 return new QueryImpl(columns,data,"query"); 096 } 097 } 098 099 else if(keys.length==3 && rowcount!=-1 && contains(keys,KeyConstants._DATA)) { 100 Array[] data = toData(sct.get(KeyConstants._DATA,null),columns,rowcount); 101 if(data!=null) { 102 return new QueryImpl(columns,data,"query"); 103 } 104 } 105 } 106 return toQuery(sct,keys); 107 } 108 /*else if(obj instanceof Query) { 109 return toQuery((Query) obj); 110 }*/ 111 else if(obj instanceof Collection) { 112 Collection coll=(Collection) obj; 113 return toQuery(coll, CollectionUtil.keys(coll)); 114 } 115 116 return obj; 117 118 } 119 120 /*private static Object toQuery(Query qry) throws DatabaseException { 121 int rows=qry.getRecordcount(); 122 String[] columns = qry.getColumns(); 123 Object src,trg; 124 for(int row=1;row<=rows;row++) { 125 for(int col=0;col<columns.length;col++) { 126 trg=toQuery(src=qry.getAt(columns[col], row, null)); 127 if(src!=trg) qry.setAtEL(columns[col], row, trg); 128 } 129 } 130 return qry; 131 }*/ 132 133 private static Collection toQuery(Collection coll, Key[] keys) throws PageException { 134 Object src,trg; 135 for(int i=0;i<keys.length;i++) { 136 trg=toQuery(src=coll.get(keys[i],null)); 137 if(src!=trg) coll.setEL(keys[i], trg); 138 } 139 return coll; 140 } 141 142 private static int toRowCount(Object obj) { 143 return Caster.toIntValue(obj,-1); 144 } 145 146 private static Array[] toData(Object obj, Key[] columns, int rowcount) throws PageException { 147 if(columns==null || rowcount==-1) return null; 148 149 Struct sct = Caster.toStruct(obj,null,false); 150 if(sct!=null && sct.size()==columns.length) { 151 Array[] datas=new Array[columns.length]; 152 Array col; 153 int colLen=-1; 154 for(int i=0;i<columns.length;i++) { 155 col=Caster.toArray(sct.get(columns[i],null),null); 156 if(col==null || colLen!=-1 && colLen!=col.size()) return null; 157 datas[i]=(Array) toQuery(col,CollectionUtil.keys(col)); 158 colLen=col.size(); 159 } 160 return datas; 161 } 162 return null; 163 } 164 165 private static Array[] toData(Object obj,Key[] columns) throws PageException { 166 if(columns==null) return null; 167 168 Array arr = Caster.toArray(obj,null); 169 if(arr!=null) { 170 Array[] datas=new Array[columns.length]; 171 for(int i=0;i<datas.length;i++) { 172 datas[i]=new ArrayImpl(); 173 } 174 175 Array data; 176 Iterator<Object> it = arr.valueIterator(); 177 while(it.hasNext()) { 178 data=Caster.toArray(it.next(),null); 179 if(data==null || data.size()!=datas.length) return null; 180 for(int i=0;i<datas.length;i++) { 181 datas[i].appendEL(toQuery(data.get(i+1,null))); 182 } 183 } 184 return datas; 185 } 186 return null; 187 } 188 189 190 private static Key[] toColumns(Object obj) { 191 Array arr = Caster.toArray(obj,null); 192 if(arr!=null) { 193 Key[] columns=new Key[arr.size()]; 194 String column; 195 int index=0; 196 Iterator<Object> it = arr.valueIterator(); 197 while(it.hasNext()) { 198 column=Caster.toString(it.next(),null); 199 if(StringUtil.isEmpty(column)) return null; 200 columns[index++]=KeyImpl.getInstance(column); 201 } 202 return columns; 203 } 204 return null; 205 } 206 207 private static Key[] toColumnlist(Object obj) throws PageException { 208 String list = Caster.toString(obj,null); 209 if(StringUtil.isEmpty(list)) return null; 210 return toColumns(ListUtil.trimItems(ListUtil.listToArrayRemoveEmpty(list, ','))); 211 } 212 213 214 /*private static boolean contains(Key[] haystack, Key[] needle) { 215 Key h; 216 outer:for(int i=0;i<haystack.length;i++) { 217 h=haystack[i]; 218 for(int y=0;y<needle.length;y++) { 219 if(h.equalsIgnoreCase(needle[y])) continue outer; 220 } 221 return false; 222 } 223 return true; 224 }*/ 225 226 227 private static boolean contains(Key[] haystack, Key needle) { 228 for(int i=0;i<haystack.length;i++) { 229 if(haystack[i].equalsIgnoreCase(needle)) return true; 230 } 231 return false; 232 } 233}