001    // Copyright 2004, 2005 The Apache Software Foundation
002    //
003    // Licensed under the Apache License, Version 2.0 (the "License");
004    // you may not use this file except in compliance with the License.
005    // You may obtain a copy of the License at
006    //
007    //     http://www.apache.org/licenses/LICENSE-2.0
008    //
009    // Unless required by applicable law or agreed to in writing, software
010    // distributed under the License is distributed on an "AS IS" BASIS,
011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012    // See the License for the specific language governing permissions and
013    // limitations under the License.
014    
015    package org.apache.tapestry.html;
016    
017    import java.util.HashMap;
018    import java.util.Iterator;
019    import java.util.Map;
020    
021    import org.apache.hivemind.ApplicationRuntimeException;
022    import org.apache.hivemind.Resource;
023    import org.apache.tapestry.AbstractComponent;
024    import org.apache.tapestry.IAsset;
025    import org.apache.tapestry.IBinding;
026    import org.apache.tapestry.IMarkupWriter;
027    import org.apache.tapestry.IRequestCycle;
028    import org.apache.tapestry.IScript;
029    import org.apache.tapestry.PageRenderSupport;
030    import org.apache.tapestry.TapestryUtils;
031    import org.apache.tapestry.engine.IScriptSource;
032    
033    /**
034     * Works with the {@link Body}component to add a script (and perhaps some initialization) to the
035     * HTML response. [ <a href="../../../../../ComponentReference/Script.html">Component Reference
036     * </a>]
037     * 
038     * @author Howard Lewis Ship
039     */
040    
041    public abstract class Script extends AbstractComponent
042    {
043        /**
044         * Injected
045         * 
046         * @since 4.0
047         */
048    
049        public abstract IScriptSource getScriptSource();
050    
051        /**
052         * A Map of input and output symbols visible to the body of the Script.
053         * 
054         * @since 2.2
055         */
056    
057        private Map _symbols;
058    
059        /**
060         * Constructs the symbols {@link Map}. This starts with the contents of the symbols parameter
061         * (if specified) to which is added any informal parameters. If both a symbols parameter and
062         * informal parameters are bound, then a copy of the symbols parameter's value is made (that is,
063         * the {@link Map}provided by the symbols parameter is read, but not modified).
064         */
065    
066        private Map getInputSymbols()
067        {
068            Map result = new HashMap();
069    
070            Map baseSymbols = getBaseSymbols();
071    
072            if (baseSymbols != null)
073                result.putAll(baseSymbols);
074    
075            // Now, iterate through all the binding names (which includes both
076            // formal and informal parmeters). Skip the formal ones and
077            // access the informal ones.
078    
079            Iterator i = getBindingNames().iterator();
080            while (i.hasNext())
081            {
082                String bindingName = (String) i.next();
083    
084                // Skip formal parameters
085    
086                if (getSpecification().getParameter(bindingName) != null)
087                    continue;
088    
089                IBinding binding = getBinding(bindingName);
090    
091                Object value = binding.getObject();
092    
093                result.put(bindingName, value);
094            }
095    
096            return result;
097        }
098    
099        /**
100         * Gets the {@link IScript}for the correct script.
101         */
102    
103        private IScript getParsedScript()
104        {
105            IAsset scriptAsset = getScriptAsset();
106            String scriptPath = getScriptPath();
107            
108            //only one of the two is allowed
109            if (scriptAsset != null && scriptPath != null)
110                    throw new ApplicationRuntimeException(HTMLMessages.multiAssetParameterError(getBinding("scriptAsset"), 
111                                    getBinding("scriptPath")));
112            
113            if (scriptPath == null && scriptAsset == null)
114                    throw new ApplicationRuntimeException(HTMLMessages.noScriptPathError());
115            
116            IScriptSource source = getScriptSource();
117            
118            Resource scriptLocation = null;
119            if (scriptPath != null) {
120                    
121                    // If the script path is relative, it should be relative to the Script component's
122                // container (i.e., relative to a page in the application).
123                    
124                    Resource rootLocation = getContainer().getSpecification().getSpecificationLocation();
125                    scriptLocation = rootLocation.getRelativeResource(scriptPath);
126            } else
127                    scriptLocation = scriptAsset.getResourceLocation();
128            
129            try
130            {
131                return source.getScript(scriptLocation);
132            }
133            catch (RuntimeException ex)
134            {
135                throw new ApplicationRuntimeException(ex.getMessage(), this, getBinding("script")
136                        .getLocation(), ex);
137            }
138    
139        }
140    
141        protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
142        {
143            if (!cycle.isRewinding())
144            {
145                PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this);
146    
147                _symbols = getInputSymbols();
148    
149                getParsedScript().execute(cycle, pageRenderSupport, _symbols);
150            }
151    
152            // Render the body of the Script;
153            renderBody(writer, cycle);
154        }
155    
156        public abstract String getScriptPath();
157    
158        public abstract IAsset getScriptAsset();
159        
160        // Parameter
161    
162        public abstract Map getBaseSymbols();
163    
164        /**
165         * Returns the complete set of symbols (input and output) from the script execution. This is
166         * visible to the body of the Script, but is cleared after the Script finishes rendering.
167         * 
168         * @since 2.2
169         */
170    
171        public Map getSymbols()
172        {
173            return _symbols;
174        }
175    
176        protected void cleanupAfterRender(IRequestCycle cycle)
177        {
178            _symbols = null;
179    
180            super.cleanupAfterRender(cycle);
181        }
182    
183    }