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.tag;
020
021import java.io.IOException;
022
023import javax.xml.parsers.FactoryConfigurationError;
024
025import lucee.runtime.converter.ConverterException;
026import lucee.runtime.converter.JSConverter;
027import lucee.runtime.converter.WDDXConverter;
028import lucee.runtime.exp.ApplicationException;
029import lucee.runtime.exp.ExpressionException;
030import lucee.runtime.exp.PageException;
031import lucee.runtime.ext.tag.TagImpl;
032import lucee.runtime.op.Caster;
033
034/**
035* Serializes and de-serializes CFML data structures to the XML-based WDDX format. 
036*   Generates JavaScript statements to instantiate JavaScript objects equivalent to the contents of a 
037*   WDDX packet or some CFML data structures.
038*
039*
040*
041**/
042public final class Wddx extends TagImpl {
043
044        /** The value to be processed. */
045        private Object input;
046
047        /** Specifies the action taken by the cfwddx tag. */
048        private String action;
049
050        /** The name of the variable to hold the output of the operation. This attribute is required for 
051        **              action = 'WDDX2CFML'. For all other actions, if this attribute is not provided, the result of the 
052        **              WDDX processing is outputted in the HTML stream. */
053        private String output;
054
055        private boolean validate;
056
057        /** The name of the top-level JavaScript object created by the deserialization process. The object 
058        **              created is an instance of the WddxRecordset object, explained in WddxRecordset Object. */
059        private String toplevelvariable;
060
061        /** Indicates whether to output time-zone information when serializing CFML to WDDX. If time-zone 
062        **              information is taken into account, the hour-minute offset, as represented in the ISO8601 format, is 
063        **              calculated in the date-time output. If time-zone information is not taken into account, the local 
064        **              time is output. The default is Yes. */
065        private boolean usetimezoneinfo;
066
067        private boolean xmlConform;
068
069        @Override
070        public void release()   {
071                super.release();
072                input=null;
073                action=null;
074                output=null;
075                validate=false;
076                toplevelvariable=null;
077                usetimezoneinfo=false;
078                xmlConform=false;
079        }
080        
081
082        /** set the value input
083        *  The value to be processed.
084        * @param input value to set
085        **/
086        public void setInput(Object input)      {
087                this.input=input;
088        }
089
090        /** set the value action
091        *  Specifies the action taken by the cfwddx tag.
092        * @param action value to set
093        **/
094        public void setAction(String action)    {
095                this.action=action.toLowerCase();
096        }
097
098        /** set the value output
099        *  The name of the variable to hold the output of the operation. This attribute is required for 
100        *               action = 'WDDX2CFML'. For all other actions, if this attribute is not provided, the result of the 
101        *               WDDX processing is outputted in the HTML stream.
102        * @param output value to set
103        **/
104        public void setOutput(String output)    {
105                this.output=output;
106        }
107
108        /** set the value validate
109        *  
110        * @param validate value to set
111        **/
112        public void setValidate(boolean validate)       {
113                this.validate=validate;
114        }
115
116        /** set the value toplevelvariable
117        *  The name of the top-level JavaScript object created by the deserialization process. The object 
118        *               created is an instance of the WddxRecordset object, explained in WddxRecordset Object.
119        * @param toplevelvariable value to set
120        **/
121        public void setToplevelvariable(String toplevelvariable)        {
122                this.toplevelvariable=toplevelvariable;
123        }
124
125        /** set the value usetimezoneinfo
126        *  Indicates whether to output time-zone information when serializing CFML to WDDX. If time-zone 
127        *               information is taken into account, the hour-minute offset, as represented in the ISO8601 format, is 
128        *               calculated in the date-time output. If time-zone information is not taken into account, the local 
129        *               time is output. The default is Yes.
130        * @param usetimezoneinfo value to set
131        **/
132        public void setUsetimezoneinfo(boolean usetimezoneinfo) {
133                this.usetimezoneinfo=usetimezoneinfo;
134        }
135        
136        /**
137         * sets if generated code is xml or wddx conform
138         * @param xmlConform
139         */
140        public void setXmlconform(boolean xmlConform)   {
141                this.xmlConform=xmlConform;
142        }
143
144
145        @Override
146        public int doStartTag() throws PageException    {
147                try {
148                        doIt();
149                        
150                } catch (Exception e) {
151                        throw Caster.toPageException(e);
152                }
153                return SKIP_BODY;
154        }
155        private void doIt() throws ExpressionException, PageException, ConverterException, IOException, FactoryConfigurationError       {
156        // cfml > wddx
157                if(action.equals("cfml2wddx")) {
158                        if(output!=null) pageContext.setVariable(output,cfml2wddx(input));
159                        else pageContext.forceWrite(cfml2wddx(input));
160                }
161                
162        // wddx > cfml
163                else if(action.equals("wddx2cfml")) {
164                        if(output==null) throw new ApplicationException("at tag cfwddx the attribute output is required if you set action==wddx2cfml");
165                        pageContext.setVariable(output,wddx2cfml(Caster.toString(input)));
166                }
167                
168        // cfml > js
169                else if(action.equals("cfml2js")) {
170                        if(output!=null) pageContext.setVariable(output,cfml2js(input));
171                        else pageContext.forceWrite(cfml2js(input));
172                }
173                
174        // wddx > js
175                else if(action.equals("wddx2js")) {
176                        if(output!=null) pageContext.setVariable(output,wddx2js(Caster.toString(input)));
177                        else pageContext.forceWrite(wddx2js(Caster.toString(input)));
178                }
179                
180                
181                else throw new ExpressionException("invalid attribute action for tag cfwddx, attributes are [cfml2wddx, wddx2cfml,cfml2js, wddx2js].");
182
183        }
184        
185        private String cfml2wddx(Object input) throws ConverterException {
186                WDDXConverter converter =new WDDXConverter(pageContext.getTimeZone(),xmlConform,true);
187                if(!usetimezoneinfo)converter.setTimeZone(null);
188                return converter.serialize(input);
189        }
190        private Object wddx2cfml(String input) throws ConverterException, IOException, FactoryConfigurationError {
191                WDDXConverter converter =new WDDXConverter(pageContext.getTimeZone(),xmlConform,true);
192                converter.setTimeZone(pageContext.getTimeZone());
193                return converter.deserialize(input,validate);
194        }
195        private String cfml2js(Object input) throws ConverterException {
196                if(toplevelvariable==null)missingTopLevelVariable();
197                JSConverter converter =new JSConverter();
198                return converter.serialize(input,toplevelvariable);
199        }
200        private String wddx2js(String input) throws ConverterException, IOException, FactoryConfigurationError {
201                if(toplevelvariable==null)missingTopLevelVariable();
202                JSConverter converter =new JSConverter();
203                return converter.serialize(wddx2cfml(input),toplevelvariable);
204        }
205        
206        
207        private ApplicationException missingTopLevelVariable() {
208                return new ApplicationException("at tag cfwddx the attribute topLevelVariable is required if you set action equal wddx2js or cfml2js");
209        }
210
211
212        @Override
213        public int doEndTag()   {
214                return EVAL_PAGE;
215        }
216}