001    package railo.runtime.converter;
002    
003    import java.io.File;
004    import java.io.IOException;
005    import java.io.Writer;
006    import java.lang.reflect.Field;
007    import java.lang.reflect.Method;
008    import java.lang.reflect.Modifier;
009    import java.util.Date;
010    import java.util.HashSet;
011    import java.util.Iterator;
012    import java.util.List;
013    import java.util.ListIterator;
014    import java.util.Map;
015    import java.util.Map.Entry;
016    import java.util.Set;
017    
018    import org.w3c.dom.Node;
019    
020    import railo.commons.lang.CFTypes;
021    import railo.runtime.Component;
022    import railo.runtime.ComponentScope;
023    import railo.runtime.ComponentWrap;
024    import railo.runtime.PageContext;
025    import railo.runtime.component.Property;
026    import railo.runtime.exp.ExpressionException;
027    import railo.runtime.exp.PageException;
028    import railo.runtime.java.JavaObject;
029    import railo.runtime.op.Caster;
030    import railo.runtime.op.Decision;
031    import railo.runtime.orm.hibernate.HBMCreator;
032    import railo.runtime.reflection.Reflector;
033    import railo.runtime.text.xml.XMLCaster;
034    import railo.runtime.type.Array;
035    import railo.runtime.type.Collection;
036    import railo.runtime.type.Collection.Key;
037    import railo.runtime.type.KeyImpl;
038    import railo.runtime.type.ObjectWrap;
039    import railo.runtime.type.Query;
040    import railo.runtime.type.Struct;
041    import railo.runtime.type.StructImpl;
042    import railo.runtime.type.UDF;
043    import railo.runtime.type.cfc.ComponentAccess;
044    import railo.runtime.type.dt.DateTime;
045    import railo.runtime.type.dt.DateTimeImpl;
046    import railo.runtime.type.dt.TimeSpan;
047    import railo.runtime.type.util.ArrayUtil;
048    import railo.runtime.type.util.CollectionUtil;
049    import railo.runtime.type.util.ComponentUtil;
050    
051    /**
052     * class to serialize and desirilize WDDX Packes
053     */
054    public final class JSONConverter extends ConverterSupport {
055        
056            private static final Collection.Key REMOTING_FETCH = KeyImpl.intern("remotingFetch");
057    
058            private static final Key TO_JSON = KeyImpl.intern("_toJson");
059        private static final Object NULL = new Object();
060        private static final String NULL_STRING = "";
061    
062            private boolean ignoreRemotingFetch;
063    
064    
065            /**
066         * constructor of the class
067         */
068        public JSONConverter(boolean ignoreRemotingFetch) {
069            this.ignoreRemotingFetch=ignoreRemotingFetch;
070        }
071            
072            
073            /**
074             * serialize Serializable class
075             * @param serializable
076         * @param sb
077             * @param serializeQueryByColumns 
078             * @param done 
079             * @throws ConverterException
080         */
081        
082        private void _serializeClass(PageContext pc,Set test,Class clazz,Object obj, StringBuffer sb, boolean serializeQueryByColumns, Set<Object> done) throws ConverterException {
083            Struct sct=new StructImpl(Struct.TYPE_LINKED);
084            if(test==null)test=new HashSet();
085            
086            // Fields
087            Field[] fields = clazz.getFields();
088            Field field;
089            for(int i=0;i<fields.length;i++){
090                    field=fields[i];
091                    if(obj!=null || (field.getModifiers()&Modifier.STATIC)>0)
092                                    try {
093                                            sct.setEL(field.getName(), testRecusrion(test,field.get(obj)));
094                                    } catch (Exception e) {
095                                            e.printStackTrace();
096                                    }
097            }
098            if(obj !=null){
099                    // setters
100                    Method[] setters=Reflector.getSetters(clazz);
101                    for(int i=0;i<setters.length;i++){
102                            sct.setEL(setters[i].getName().substring(3), NULL);
103                    }
104                    // getters
105                    Method[] getters=Reflector.getGetters(clazz);
106                    for(int i=0;i<getters.length;i++){
107                            try {
108                                    sct.setEL(getters[i].getName().substring(3), testRecusrion(test,getters[i].invoke(obj, ArrayUtil.OBJECT_EMPTY)));
109                                    
110                                    } 
111                            catch (Exception e) {}
112                    }
113            }
114            test.add(clazz);
115            
116            
117            _serializeStruct(pc,test,sct, sb, serializeQueryByColumns, true,done);
118        }
119        
120            
121            private Object testRecusrion(Set test, Object obj) {
122                    if(test.contains(obj.getClass())) return obj.getClass().getName();
123                    return obj;
124            }
125    
126    
127            /**
128             * serialize a Date
129             * @param date Date to serialize
130             * @param sb
131             * @throws ConverterException
132             */
133            private void _serializeDate(Date date, StringBuffer sb) {
134                    _serializeDateTime(new DateTimeImpl(date),sb);
135            }
136            /**
137             * serialize a DateTime
138             * @param dateTime DateTime to serialize
139             * @param sb
140             * @throws ConverterException
141             */
142            private void _serializeDateTime(DateTime dateTime, StringBuffer sb) {
143                    
144                    sb.append('"');
145                    
146                    //sb.append(escape(dateTime.toString()));
147                    sb.append(escape(JSONDateFormat.format(dateTime,null)));
148                    sb.append('"');
149                    
150                    /*try {
151                    sb.append(goIn());
152                        sb.append("createDateTime(");
153                        sb.append(DateFormat.call(null,dateTime,"yyyy,m,d"));
154                        sb.append(' ');
155                        sb.append(TimeFormat.call(null,dateTime,"HH:mm:ss"));
156                        sb.append(')');
157                    } 
158                catch (PageException e) {
159                            throw new ConverterException(e);
160                    }*/
161                //Januar, 01 2000 01:01:01
162            }
163    
164            /**
165             * serialize a Array
166             * @param array Array to serialize
167             * @param sb
168             * @param serializeQueryByColumns 
169             * @param done 
170             * @throws ConverterException
171             */
172            private void _serializeArray(PageContext pc,Set test,Array array, StringBuffer sb, boolean serializeQueryByColumns, Set<Object> done) throws ConverterException {
173                    _serializeList(pc,test,array.toList(),sb,serializeQueryByColumns,done);
174            }
175            
176            /**
177             * serialize a List (as Array)
178             * @param list List to serialize
179             * @param sb
180             * @param serializeQueryByColumns 
181             * @param done 
182             * @throws ConverterException
183             */
184            private void _serializeList(PageContext pc,Set test,List list, StringBuffer sb, boolean serializeQueryByColumns, Set<Object> done) throws ConverterException {
185                    
186                sb.append(goIn());
187                sb.append("[");
188                boolean doIt=false;
189                    ListIterator it=list.listIterator();
190                    while(it.hasNext()) {
191                        if(doIt)sb.append(',');
192                        doIt=true;
193                            _serialize(pc,test,it.next(),sb,serializeQueryByColumns,done);
194                    }
195                    
196                    sb.append(']');
197            }
198            private void _serializeArray(PageContext pc,Set test,Object[] arr, StringBuffer sb, boolean serializeQueryByColumns, Set<Object> done) throws ConverterException {
199                    
200                sb.append(goIn());
201                sb.append("[");
202                for(int i=0;i<arr.length;i++) {
203                        if(i>0)sb.append(',');
204                        _serialize(pc,test,arr[i],sb,serializeQueryByColumns,done);
205                    }
206                    sb.append(']');
207            }
208    
209        /**
210         * serialize a Struct
211         * @param struct Struct to serialize
212         * @param sb
213         * @param serializeQueryByColumns 
214         * @param addUDFs 
215         * @param done 
216         * @throws ConverterException
217         */
218        public void _serializeStruct(PageContext pc,Set test,Struct struct, StringBuffer sb, boolean serializeQueryByColumns, boolean addUDFs, Set<Object> done) throws ConverterException {
219            // Component
220            if(struct instanceof Component){
221                    String res = castToJson(pc, (Component)struct, NULL_STRING);
222                    if(res!=NULL_STRING) {
223                            sb.append(res);
224                            return;
225                    }
226            }
227            
228            
229            sb.append(goIn());
230            sb.append("{");
231            //Key[] keys = struct.keys();
232            //Key key;
233            Iterator<Entry<Key, Object>> it = struct.entryIterator();
234            Entry<Key, Object> e;
235            Object value;
236            boolean doIt=false;
237            while(it.hasNext()) {
238                    e = it.next();
239                    //key=keys[i];
240                    value=e.getValue();
241                    if(!addUDFs && (value instanceof UDF || value==null))continue;
242                    if(doIt)sb.append(',');
243                doIt=true;
244                sb.append('"');
245                sb.append(escape(e.getKey().getString()));
246                sb.append('"');
247                sb.append(':');
248                _serialize(pc,test,value,sb,serializeQueryByColumns,done);
249            }
250            
251            if(struct instanceof Component){
252                    Boolean remotingFetch;
253                    Component cp = (Component)struct;
254                    boolean isPeristent=false;
255                    try {
256                                    ComponentAccess ca = ComponentUtil.toComponentAccess(cp);
257                                    isPeristent=ca.isPersistent();
258                            } catch (ExpressionException ee) {}
259                            
260                    Property[] props = cp.getProperties(false);
261                    ComponentScope scope = cp.getComponentScope();
262                    for(int i=0;i<props.length;i++) {
263                            if(!ignoreRemotingFetch) {
264                                    remotingFetch=Caster.toBoolean(props[i].getDynamicAttributes().get(REMOTING_FETCH,null),null);
265                                    if(remotingFetch==null){
266                                            if(isPeristent  && HBMCreator.isRelated(props[i])) continue;
267                                    }
268                                    else if(!remotingFetch.booleanValue()) continue;
269                            
270                            }
271                            Key key = KeyImpl.getInstance(props[i].getName());
272                    value=scope.get(key,null);
273                    if(!addUDFs && (value instanceof UDF || value==null))continue;
274                    if(doIt)sb.append(',');
275                    doIt=true;
276                    sb.append('"');
277                    sb.append(escape(key.getString()));
278                    sb.append('"');
279                    sb.append(':');
280                    _serialize(pc,test,value,sb,serializeQueryByColumns,done);
281                    }
282            }
283            
284            
285            sb.append('}');
286        }
287        
288        private static String castToJson(PageContext pc,Component cfc, String defaultValue) throws ConverterException {
289                    Object o=cfc.get(TO_JSON,null);
290                    if(!(o instanceof UDF)) return defaultValue;
291                    UDF udf=(UDF) o;
292                    if(udf.getReturnType()!=CFTypes.TYPE_VOID && udf.getFunctionArguments().length==0) {
293                            try {
294                                    return Caster.toString(cfc.call(pc, TO_JSON, new Object[0]));
295                            } catch (PageException e) {
296                                    e.printStackTrace();
297                                    throw toConverterException(e);
298                            }
299                    }
300                    return defaultValue;
301        }
302        
303        
304    
305        /**
306         * serialize a Map (as Struct)
307         * @param map Map to serialize
308         * @param sb
309         * @param serializeQueryByColumns 
310         * @param done 
311         * @throws ConverterException
312         */
313        private void _serializeMap(PageContext pc,Set test,Map map, StringBuffer sb, boolean serializeQueryByColumns, Set<Object> done) throws ConverterException {
314            sb.append(goIn());
315            sb.append("{");
316            
317            Iterator it=map.keySet().iterator();
318            boolean doIt=false;
319            while(it.hasNext()) {
320                Object key=it.next();
321                if(doIt)sb.append(',');
322                doIt=true;
323                sb.append('"');
324                sb.append(escape(key.toString()));
325                sb.append('"');
326                sb.append(':');
327                _serialize(pc,test,map.get(key),sb,serializeQueryByColumns,done);
328            }
329            
330            sb.append('}');
331        }
332        /**
333         * serialize a Component
334         * @param component Component to serialize
335         * @param sb
336         * @param serializeQueryByColumns 
337         * @param done 
338         * @throws ConverterException
339         */
340        private void _serializeComponent(PageContext pc,Set test,Component component, StringBuffer sb, boolean serializeQueryByColumns, Set<Object> done) throws ConverterException {
341            try {
342                            ComponentWrap cw = ComponentWrap.toComponentWrap(Component.ACCESS_PRIVATE,component);
343                    _serializeStruct(pc,test,cw, sb, serializeQueryByColumns,false,done);
344                    } 
345            catch (ExpressionException e) {
346                            throw toConverterException(e);
347                    }
348        }
349        
350    
351        private void _serializeUDF(PageContext pc,Set test,UDF udf, StringBuffer sb,boolean serializeQueryByColumns, Set<Object> done) throws ConverterException {
352                    Struct sct=new StructImpl();
353                    try {
354                            // Meta
355                            Struct meta = udf.getMetaData(pc);
356                            sct.setEL("Metadata", meta);
357                            
358                            // Parameters
359                            sct.setEL("MethodAttributes", meta.get("PARAMETERS"));
360                    } 
361                    catch (PageException e) {
362                            throw toConverterException(e);
363                    }
364                    
365                    sct.setEL("Access", ComponentUtil.toStringAccess(udf.getAccess(),"public"));
366                    sct.setEL("Output", Caster.toBoolean(udf.getOutput()));
367                    sct.setEL("ReturnType", udf.getReturnTypeAsString());
368                    try{
369                            sct.setEL("PagePath", udf.getPageSource().getResource().getAbsolutePath());
370                    }catch(Throwable t){}
371                    
372                    _serializeStruct(pc,test,sct, sb, serializeQueryByColumns, true,done);
373                    // TODO key SuperScope and next?
374            }
375    
376        
377    
378            /**
379             * serialize a Query
380             * @param query Query to serialize
381             * @param sb
382             * @param serializeQueryByColumns 
383             * @param done 
384             * @throws ConverterException
385             */
386            private void _serializeQuery(PageContext pc,Set test,Query query, StringBuffer sb, boolean serializeQueryByColumns, Set<Object> done) throws ConverterException {
387                    
388                    Collection.Key[] _keys = CollectionUtil.keys(query);
389                    sb.append(goIn());
390                    sb.append("{");
391                    
392                    /*
393    
394    {"DATA":[["a","b"],["c","d"]]}
395    {"DATA":{"aaa":["a","c"],"bbb":["b","d"]}}
396                     * */
397                    // Rowcount
398                    if(serializeQueryByColumns){
399                            sb.append("\"ROWCOUNT\":");
400                            sb.append(Caster.toString(query.getRecordcount()));
401                            sb.append(',');
402                    }
403                    
404                    // Columns
405                    sb.append("\"COLUMNS\":[");
406                    String[] cols = query.getColumns();
407                    for(int i=0;i<cols.length;i++) {
408                            if(i>0)sb.append(",\"");
409                            else sb.append('"');
410                sb.append(escape(cols[i].toUpperCase()));
411                sb.append('"');
412                    }
413                    sb.append("],");
414                    
415                    // Data
416                    sb.append("\"DATA\":");
417                    if(serializeQueryByColumns) {
418                            sb.append('{');
419                            boolean oDoIt=false;
420                            int len=query.getRecordcount();
421                            for(int i=0;i<_keys.length;i++) {
422                                if(oDoIt)sb.append(',');
423                                oDoIt=true;
424                                sb.append(goIn());
425                        sb.append('"');
426                        sb.append(escape(_keys[i].getString()));
427                        sb.append('"');
428                                    sb.append(":[");
429                                    boolean doIt=false;
430                                            for(int y=1;y<=len;y++) {
431                                                if(doIt)sb.append(',');
432                                                doIt=true;
433                                                try {
434                                                            _serialize(pc,test,query.getAt(_keys[i],y),sb,serializeQueryByColumns,done);
435                                                    } catch (PageException e) {
436                                                            _serialize(pc,test,e.getMessage(),sb,serializeQueryByColumns,done);
437                                                    }
438                                            }
439                                    
440                                    sb.append(']');
441                            }
442            
443                            sb.append('}');
444                    }
445                    else {
446                            sb.append('[');
447                            boolean oDoIt=false;
448                            int len=query.getRecordcount();
449                            for(int row=1;row<=len;row++) {
450                                if(oDoIt)sb.append(',');
451                                oDoIt=true;
452            
453                                    sb.append("[");
454                                    boolean doIt=false;
455                                            for(int col=0;col<_keys.length;col++) {
456                                                if(doIt)sb.append(',');
457                                                doIt=true;
458                                                try {
459                                                            _serialize(pc,test,query.getAt(_keys[col],row),sb,serializeQueryByColumns,done);
460                                                    } catch (PageException e) {
461                                                            _serialize(pc,test,e.getMessage(),sb,serializeQueryByColumns,done);
462                                                    }
463                                            }
464                                    sb.append(']');
465                            }
466                            sb.append(']');
467                    }
468                    sb.append('}');
469            }
470            
471            /**
472             * serialize a Object to his xml Format represenation
473             * @param object Object to serialize
474             * @param sb StringBuffer to write data
475             * @param serializeQueryByColumns 
476             * @param done 
477             * @throws ConverterException
478             */
479            private void _serialize(PageContext pc,Set test,Object object, StringBuffer sb, boolean serializeQueryByColumns, Set<Object> done) throws ConverterException {
480                    
481                    // NULL
482                    if(object==null || object==NULL) {
483                        sb.append(goIn());
484                        sb.append("null");
485                        return;
486                    }
487                    // String
488                    if(object instanceof String || object instanceof StringBuffer) {
489                        sb.append(goIn());
490                        sb.append('"');
491                        sb.append(escape(object.toString()));
492                        sb.append('"');
493                        return;
494                    }
495                    // Character
496                    if(object instanceof Character) {
497                        sb.append(goIn());
498                        sb.append('"');
499                        sb.append(escape(String.valueOf(((Character)object).charValue())));
500                        sb.append('"');
501                        return;
502                    }
503                    // Number
504                    if(object instanceof Number) {
505                        sb.append(goIn());
506                        sb.append(Caster.toString(((Number)object).doubleValue()));
507                        return;
508                    }
509                    // Boolean
510                    if(object instanceof Boolean) {
511                        sb.append(goIn());
512                        sb.append(Caster.toString(((Boolean)object).booleanValue()));
513                        return;
514                    }
515                    // DateTime
516                    if(object instanceof DateTime) {
517                            _serializeDateTime((DateTime)object,sb);
518                        return;
519                    }
520                    // Date
521                    if(object instanceof Date) {
522                            _serializeDate((Date)object,sb);
523                        return;
524                    }
525            // XML
526            if(object instanceof Node) {
527                    _serializeXML((Node)object,sb);
528                        return;
529            }
530            // Timespan
531            if(object instanceof TimeSpan) {
532                    _serializeTimeSpan((TimeSpan) object,sb);
533                        return;
534            }
535                    // File
536                    if(object instanceof File) {
537                            _serialize(pc,test, ((File)object).getAbsolutePath(), sb, serializeQueryByColumns,done);
538                        return;
539                    }
540                    // String Converter
541                    if(object instanceof ScriptConvertable) {
542                        sb.append(((ScriptConvertable)object).serialize());
543                        return;
544                    }
545                    Object raw = LazyConverter.toRaw(object);
546                    if(done.contains(raw)){
547                            sb.append(goIn());
548                        sb.append("null");
549                        return;
550                    }
551                    
552                    
553                    done.add(raw);
554                    try{
555                            // Component
556                            if(object instanceof Component) {
557                                _serializeComponent(pc,test,(Component)object,sb,serializeQueryByColumns,done);
558                                        return;
559                            }
560                            // UDF
561                            if(object instanceof UDF) {
562                                _serializeUDF(pc,test,(UDF)object,sb,serializeQueryByColumns,done);
563                                        return;
564                            }
565                            // Struct
566                            if(object instanceof Struct) {
567                                    _serializeStruct(pc,test,(Struct)object,sb,serializeQueryByColumns,true,done);
568                                        return;
569                            }
570                            // Map
571                            if(object instanceof Map) {
572                                _serializeMap(pc,test,(Map)object,sb,serializeQueryByColumns,done);
573                                        return;
574                            }
575                                    // Array
576                                    if(object instanceof Array) {
577                                            _serializeArray(pc,test,(Array)object,sb,serializeQueryByColumns,done);
578                                        return;
579                                    }
580                                    // List
581                                    if(object instanceof List) {
582                                            _serializeList(pc,test,(List)object,sb,serializeQueryByColumns,done);
583                                        return;
584                                    }
585                            // Query
586                            if(object instanceof Query) {
587                                _serializeQuery(pc,test,(Query)object,sb,serializeQueryByColumns,done);
588                                        return;
589                            }
590                                    // Native Array
591                                    if(Decision.isNativeArray(object)){
592                                            if(object instanceof char[])
593                                                    _serialize(pc,test,new String((char[])object), sb, serializeQueryByColumns,done);
594                                            else {
595                                                    _serializeArray(pc,test,ArrayUtil.toReferenceType(object,ArrayUtil.OBJECT_EMPTY), sb, serializeQueryByColumns,done);
596                                            }
597                                        return;
598                                                    
599                                    }
600                                    // ObjectWrap
601                                    if(object instanceof ObjectWrap) {
602                                            try {
603                                                    _serialize(pc,test,((ObjectWrap)object).getEmbededObject(), sb, serializeQueryByColumns,done);
604                                            } catch (PageException e) {
605                                                    if(object instanceof JavaObject){
606                                                            _serializeClass(pc,test,((JavaObject)object).getClazz(),null,sb,serializeQueryByColumns,done);
607                                                    }
608                                                    else throw new ConverterException("can't serialize Object of type [ "+Caster.toClassName(object)+" ]");
609                                            }
610                                        return;
611                                    }
612                                    
613                                    _serializeClass(pc,test,object.getClass(),object,sb,serializeQueryByColumns,done);
614                    }
615                    finally{
616                            done.remove(raw);
617                    }
618            }
619    
620            private void _serializeXML(Node node, StringBuffer sb) {
621            node=XMLCaster.toRawNode(node);
622            sb.append(goIn());
623                sb.append('"');
624                sb.append(escape(XMLCaster.toString(node,"")));
625                sb.append('"');
626            
627            }
628    
629    
630            private void _serializeTimeSpan(TimeSpan span, StringBuffer sb) {
631            
632                    sb.append(goIn());
633                        sb.append("createTimeSpan(");
634                        sb.append(span.getDay());
635                        sb.append(',');
636                        sb.append(span.getHour());
637                        sb.append(',');
638                        sb.append(span.getMinute());
639                        sb.append(',');
640                        sb.append(span.getSecond());
641                        sb.append(')');
642                    
643            }
644    
645            
646            public static String escape(String str) {
647                    char[] arr=str.toCharArray();
648                    StringBuffer rtn=new StringBuffer(arr.length);
649                    for(int i=0;i<arr.length;i++) {
650                            if(arr[i] < 128){
651                                    switch(arr[i]) {
652                                            case '\\': rtn.append("\\\\"); break;
653                                            case '/': rtn.append("\\/"); break;
654                                            case '\n': rtn.append("\\n"); break;
655                                            case '\r': rtn.append("\\r"); break;
656                                            case '\f': rtn.append("\\f"); break;
657                                            case '\b': rtn.append("\\b"); break;
658                                            case '\t': rtn.append("\\t"); break;
659                                            case '"' : rtn.append("\\\""); break;
660                                            default : rtn.append(arr[i]); break;
661                                    }
662                            }
663                            else {
664                                    if (arr[i] < 0x10)                   rtn.append("\\u000");
665                                else if (arr[i] < 0x100)         rtn.append( "\\u00");
666                                else if (arr[i] < 0x1000)        rtn.append( "\\u0");
667                                else                                                rtn.append( "\\u");
668                                    rtn.append(Integer.toHexString(arr[i]));
669                            }
670                    }
671                    return rtn.toString();
672            }
673    
674        /**
675             * serialize a Object to his literal Format
676             * @param object Object to serialize
677         * @param serializeQueryByColumns 
678             * @return serialized wddx package
679             * @throws ConverterException
680             */
681            public String serialize(PageContext pc,Object object, boolean serializeQueryByColumns) throws ConverterException {
682                    StringBuffer sb=new StringBuffer();
683                    _serialize(pc,null,object,sb,serializeQueryByColumns,new HashSet<Object>());
684                    return sb.toString();
685            }
686    
687            @Override
688            public void writeOut(PageContext pc, Object source, Writer writer) throws ConverterException, IOException {
689                    writer.write(serialize(pc,source,false));
690                    writer.flush();
691            }
692            
693            
694            /**
695             * @return return current blockquote
696             */
697            private String goIn() {
698                return "";
699            }
700    
701    
702    }