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.helpers;
020
021import java.io.InputStream;
022import java.util.Stack;
023
024import lucee.commons.io.IOUtil;
025import lucee.commons.io.res.Resource;
026import lucee.runtime.PageContext;
027import lucee.runtime.engine.ThreadLocalPageContext;
028import lucee.runtime.exp.PageException;
029import lucee.runtime.exp.PageRuntimeException;
030import lucee.runtime.op.Caster;
031import lucee.runtime.text.xml.XMLUtil;
032import lucee.runtime.type.Struct;
033import lucee.runtime.type.StructImpl;
034import lucee.runtime.type.UDF;
035import lucee.runtime.type.util.ArrayUtil;
036
037import org.xml.sax.Attributes;
038import org.xml.sax.InputSource;
039import org.xml.sax.SAXException;
040import org.xml.sax.SAXParseException;
041import org.xml.sax.XMLReader;
042import org.xml.sax.helpers.DefaultHandler;
043
044
045
046/**
047 * Sax Parse with callback to CFC Methods
048 */
049public final class XMLEventParser extends DefaultHandler {
050        
051        private UDF startDocument;
052        private UDF startElement;
053        private UDF body;
054        private UDF endElement;
055        private UDF endDocument;
056        private UDF error;
057        
058        private Stack<StringBuilder> bodies=new Stack<StringBuilder>();
059        private PageContext pc;
060        private Struct att;
061        /**
062         * Field <code>DEFAULT_SAX_PARSER</code>
063         */
064        public final static String DEFAULT_SAX_PARSER="org.apache.xerces.parsers.SAXParser";
065        
066        /**
067         * constructor of the class
068         * @param pc
069         * @param startDocument
070         * @param startElement
071         * @param body
072         * @param endElement
073         * @param endDocument
074         * @param error
075         */
076        public XMLEventParser(
077                        PageContext pc,
078                        UDF startDocument,
079                        UDF startElement,
080                        UDF body,
081                        UDF endElement,
082                        UDF endDocument,
083                        UDF error) {
084                
085                this.pc=pc;
086                this.startDocument=startDocument;
087                this.startElement=startElement;
088                this.body=body;
089                this.endElement=endElement;
090                this.endDocument=endDocument;
091                this.error=error;
092
093        }
094
095        /**
096         * start execution of the parser
097         * @param xmlFile
098         * @throws PageException
099         */
100        public void start(Resource xmlFile) throws PageException {
101                start(xmlFile,DEFAULT_SAX_PARSER);
102        }
103        
104        /**
105         * start execution of the parser
106         * @param xmlFile
107         * @param saxParserCass
108         * @throws PageException
109         */
110        public void start(Resource xmlFile,String saxParserCass) throws PageException {
111                InputStream is=null;
112                try {
113                        XMLReader xmlReader = XMLUtil.createXMLReader(saxParserCass);
114                        xmlReader.setContentHandler(this);
115                        xmlReader.setErrorHandler(this);
116                        xmlReader.parse(new InputSource(is=IOUtil.toBufferedInputStream(xmlFile.getInputStream())));
117                } catch (Exception e) {
118                        throw Caster.toPageException(e); 
119                }       
120                finally {
121                        IOUtil.closeEL(is);
122                }
123                
124        }
125
126        @Override
127        public void characters(char[] ch, int start, int length) throws SAXException {
128                bodies.peek().append(ch,start,length);
129        }
130
131        @Override
132        public void error(SAXParseException e) throws SAXException {
133                error(Caster.toPageException(e));
134        }
135        @Override
136        public void fatalError(SAXParseException e) throws SAXException {
137                error(Caster.toPageException(e));
138        }
139        @Override
140        public void startElement(String uri, String localName, String qName,
141                        Attributes attributes) throws SAXException {
142                bodies.add(new StringBuilder());
143                att = toStruct(attributes);
144                call(startElement,new Object[]{uri,localName,qName,att});
145        }
146
147        @Override
148        public void endElement(String uri, String localName, String qName) throws SAXException {
149                call(body,new Object[]{bodies.pop().toString()});
150                call(endElement,new Object[]{uri,localName,qName,att});
151        }
152        
153        @Override
154        public void startDocument() throws SAXException {
155                call(startDocument,ArrayUtil.OBJECT_EMPTY);
156        }
157        
158        @Override
159        public void endDocument() throws SAXException {
160                call(endDocument,ArrayUtil.OBJECT_EMPTY);
161        }
162        
163        /**
164         * call a user defined function
165         * @param udf
166         * @param arguments
167         */
168        private void call(UDF udf, Object[] arguments) {
169                try {
170                        udf.call(pc,arguments,false);
171                } catch (PageException pe) {
172                        error(pe);
173                }
174        }
175        
176        /**
177         * call back error function if a error occour
178         * @param pe
179         */
180        private void error(PageException pe) {
181                if(error==null) throw new PageRuntimeException(pe);
182                try {
183                        pc=ThreadLocalPageContext.get(pc);
184                        error.call(pc,new Object[]{pe.getCatchBlock(pc.getConfig())},false);
185                } 
186                catch (PageException e) {}
187        }
188        
189        /**
190         * cast a Attributes object to a Struct
191         * @param att
192         * @return Attributes as Struct
193         */
194        private Struct toStruct(Attributes att) {
195                int len=att.getLength();
196                Struct sct=new StructImpl();
197                for(int i=0;i<len;i++) {
198                        sct.setEL(att.getQName(i),att.getValue(i));
199                }
200                return sct;
201        }
202}