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
035     * initialization) to the HTML response. [ <a
036     * href="../../../../../ComponentReference/Script.html">Component Reference
037     * </a>]
038     * 
039     * @author Howard Lewis Ship
040     */
041    
042    public abstract class Script extends AbstractComponent
043    {
044        /**
045         * A Map of input and output symbols visible to the body of the Script.
046         * 
047         * @since 2.2
048         */
049    
050        private Map _symbols;
051    
052        /**
053         * Injected.
054         * 
055         * @since 4.0
056         */
057    
058        public abstract IScriptSource getScriptSource();
059        
060        /**
061         * Constructs the symbols {@link Map}. This starts with the contents of the
062         * symbols parameter (if specified) to which is added any informal
063         * parameters. If both a symbols parameter and informal parameters are
064         * bound, then a copy of the symbols parameter's value is made (that is, the
065         * {@link Map}provided by the symbols parameter is read, but not modified).
066         */
067    
068        private Map getInputSymbols()
069        {
070            Map result = new HashMap();
071    
072            Map baseSymbols = getBaseSymbols();
073    
074            if (baseSymbols != null) result.putAll(baseSymbols);
075    
076            // Now, iterate through all the binding names (which includes both
077            // formal and informal parmeters). Skip the formal ones and
078            // access the informal ones.
079    
080            Iterator i = getBindingNames().iterator();
081            while(i.hasNext())
082            {
083                String bindingName = (String) i.next();
084    
085                // Skip formal parameters
086    
087                if (getSpecification().getParameter(bindingName) != null) 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
111                        .multiAssetParameterError(getBinding("scriptAsset"),
112                                getBinding("scriptPath")));
113    
114            if (scriptPath == null && scriptAsset == null)
115                throw new ApplicationRuntimeException(HTMLMessages
116                        .noScriptPathError());
117    
118            IScriptSource source = getScriptSource();
119    
120            Resource scriptLocation = null;
121            if (scriptPath != null)
122            {
123    
124                // If the script path is relative, it should be relative to the
125                // Script component's
126                // container (i.e., relative to a page in the application).
127    
128                Resource rootLocation = getContainer().getSpecification()
129                        .getSpecificationLocation();
130                scriptLocation = rootLocation.getRelativeResource(scriptPath);
131            }
132            else scriptLocation = scriptAsset.getResourceLocation();
133    
134            try
135            {
136                return source.getScript(scriptLocation);
137            }
138            catch (RuntimeException ex)
139            {
140                throw new ApplicationRuntimeException(ex.getMessage(), this,
141                        getBinding("script").getLocation(), ex);
142            }
143    
144        }
145    
146        protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
147        {
148            if (!cycle.isRewinding())
149            {
150                PageRenderSupport pageRenderSupport = TapestryUtils
151                        .getPageRenderSupport(cycle, this);
152                
153                _symbols = getInputSymbols();
154                
155                getParsedScript().execute(this, cycle, pageRenderSupport, _symbols);
156            }
157    
158            // Render the body of the Script;
159            renderBody(writer, cycle);
160        }
161    
162        public abstract String getScriptPath();
163    
164        public abstract IAsset getScriptAsset();
165    
166        // Parameter
167    
168        public abstract Map getBaseSymbols();
169    
170        /**
171         * Returns the complete set of symbols (input and output) from the script
172         * execution. This is visible to the body of the Script, but is cleared
173         * after the Script finishes rendering.
174         * 
175         * @since 2.2
176         */
177    
178        public Map getSymbols()
179        {
180            return _symbols;
181        }
182    
183        protected void cleanupAfterRender(IRequestCycle cycle)
184        {
185            _symbols = null;
186    
187            super.cleanupAfterRender(cycle);
188        }
189    
190    }