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