001    /* 
002     * This file is part of the Echo Point Project.  This project is a collection
003     * of Components that have extended the Echo Web Application Framework.
004     *
005     * Version: MPL 1.1/GPL 2.0/LGPL 2.1
006     *
007     * The contents of this file are subject to the Mozilla Public License Version
008     * 1.1 (the "License"); you may not use this file except in compliance with
009     * the License. You may obtain a copy of the License at
010     * http://www.mozilla.org/MPL/
011     *
012     * Software distributed under the License is distributed on an "AS IS" basis,
013     * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
014     * for the specific language governing rights and limitations under the
015     * License.
016     *
017     * Alternatively, the contents of this file may be used under the terms of
018     * either the GNU General Public License Version 2 or later (the "GPL"), or
019     * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
020     * in which case the provisions of the GPL or the LGPL are applicable instead
021     * of those above. If you wish to allow use of your version of this file only
022     * under the terms of either the GPL or the LGPL, and not to allow others to
023     * use your version of this file under the terms of the MPL, indicate your
024     * decision by deleting the provisions above and replace them with the notice
025     * and other provisions required by the GPL or the LGPL. If you do not delete
026     * the provisions above, a recipient may use your version of this file under
027     * the terms of any one of the MPL, the GPL or the LGPL.
028     */
029    package echopoint.template.ui;
030    
031    import java.io.InputStream;
032    import java.io.InputStreamReader;
033    import java.io.BufferedReader;
034    import java.io.IOException;
035    
036    import javax.xml.parsers.DocumentBuilder;
037    import javax.xml.parsers.DocumentBuilderFactory;
038    import javax.xml.parsers.ParserConfigurationException;
039    
040    import nextapp.echo.webcontainer.Connection;
041    
042    import org.w3c.dom.Document;
043    import org.w3c.dom.Element;
044    import org.xml.sax.InputSource;
045    
046    import echopoint.template.TemplateCompilerHints;
047    import echopoint.template.TemplateDataSource;
048    
049    /**
050     * <code>XHTMLTemplateCompiler</code> can compile source XHTML template data
051     * into.. well a XHTML DOM Element.
052     */
053    
054    public class XHTMLTemplateCompiler implements TemplateCompiler {
055    
056        /**
057         * ThreadLocal cache of validating <code>DocumentBuilder</code> instances.
058         */
059        private static final ThreadLocal nonvalidatingDocumentBuilders = new ThreadLocal() {
060    
061            /**
062             * @see java.lang.ThreadLocal#initialValue()
063             */
064            protected Object initialValue() {
065                return buildDocumentBuilder(null);
066            }
067        };
068    
069        /**
070         * Called to return a <code>DocumentBuilder</code> based on the compiler
071         * hints.
072         *
073         * @param compilerHints -
074         *                      the hints for the factory used to build the
075         *                      <code>DocumentBuilder</code> which can be null.
076         * @return a <code>DocumentBuilder</code>
077         */
078        protected static final DocumentBuilder buildDocumentBuilder(TemplateCompilerHints compilerHints) {
079            try {
080                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
081                if (compilerHints == null) {
082                    factory.setNamespaceAware(true);
083                    factory.setValidating(false);
084                    factory.setAttribute("http://apache.org/xml/features/nonvalidating/load-external-dtd", Boolean.valueOf(false));
085                } else {
086                    factory.setCoalescing(compilerHints.isCoalescing());
087                    factory.setExpandEntityReferences(compilerHints.isExpandEntityReferences());
088                    factory.setIgnoringComments(compilerHints.isIgnoringComments());
089                    factory.setIgnoringElementContentWhitespace(compilerHints.isIgnoringElementContentWhitespace());
090                    factory.setNamespaceAware(compilerHints.isNamespaceAware());
091                    factory.setValidating(compilerHints.isValidating());
092                    // custom attributes
093                    String[] attrNames = compilerHints.getAttributeNames();
094                    if (attrNames != null) {
095                        for (int i = 0; i < attrNames.length; i++) {
096                            Object value = compilerHints.getAttributeValue(attrNames[i]);
097                            factory.setAttribute(attrNames[i], value);
098                        }
099                    }
100                }
101                DocumentBuilder builder = factory.newDocumentBuilder();
102                return builder;
103            } catch (ParserConfigurationException ex) {
104                throw new RuntimeException(ex);
105            }
106        }
107    
108        /**
109         * Retrieves a thread-specific non validating <code>DocumentBuilder</code>.
110         *
111         * @return the <code>DocumentBuilder</code> serving the current thread.
112         */
113        protected static DocumentBuilder getNonValidatingDocumentBuilder() {
114            return (DocumentBuilder) nonvalidatingDocumentBuilders.get();
115        }
116    
117        /**
118         * @see echopoint.template.ui.TemplateCompiler#compileTemplateDataIntoXHTML(nextapp.echo.webcontainer.RenderContext,
119         *      echopoint.template.TemplateDataSource)
120         */
121        public Element compileTemplateDataIntoXHTML(Connection c, TemplateDataSource tds) throws Exception {
122            return compileXHTML(tds.getInputStream(), tds);
123        }
124    
125        public String templateDataAsString(Connection c, TemplateDataSource tds) throws Exception {
126            BufferedReader reader = new BufferedReader(new InputStreamReader(tds.getInputStream()));
127            StringBuilder sb = new StringBuilder();
128    
129            String line = null;
130            try {
131                while ((line = reader.readLine()) != null) {
132                    sb.append(line + "\n");
133                }
134            } finally {
135                    tds.getInputStream().close();
136            }
137    
138            return sb.toString();
139    
140        }
141    
142    
143        /**
144         * This does the actual compiling of a stream of XHTML into a XHTML DOM
145         * Element.
146         *
147         * @param inputStream       -
148         *                          this must be a stream of XHTML data
149         * @param tds -
150         *                          -
151         * @return
152         * @throws Exception
153         */
154        protected Element compileXHTML(InputStream inputStream, TemplateDataSource tds) throws Exception {
155            long startMS = System.currentTimeMillis();
156    
157            String characterEncoding = tds.getCharacterEncoding();
158            TemplateCompilerHints compilerHints = tds.getCompilerHints();
159    
160            InputStreamReader isr;
161            if (characterEncoding != null) {
162                isr = new InputStreamReader(inputStream, characterEncoding);
163            } else {
164                isr = new InputStreamReader(inputStream);
165            }
166    
167            InputSource inputSource = new InputSource();
168            inputSource.setCharacterStream(isr);
169            inputSource.setByteStream(inputStream);
170            inputSource.setEncoding(characterEncoding);
171    
172            try {
173                DocumentBuilder docBuilder = null;
174                if (compilerHints == null) {
175                    docBuilder = getNonValidatingDocumentBuilder();
176                } else {
177                    docBuilder = buildDocumentBuilder(compilerHints);
178                }
179                Document document = docBuilder.parse(inputSource);
180                Element documentElement = document.getDocumentElement();
181    
182                long compileMS = System.currentTimeMillis() - startMS;
183                if (compileMS == 0)
184                    ;
185                return documentElement;
186                    } catch (RuntimeException rte) {
187                            throw rte;
188                    }
189            }
190    
191    }