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
015package org.apache.tapestry.html;
016
017import java.util.HashMap;
018import java.util.Iterator;
019import java.util.Map;
020
021import org.apache.hivemind.ApplicationRuntimeException;
022import org.apache.hivemind.Resource;
023import org.apache.tapestry.AbstractComponent;
024import org.apache.tapestry.IAsset;
025import org.apache.tapestry.IBinding;
026import org.apache.tapestry.IMarkupWriter;
027import org.apache.tapestry.IRequestCycle;
028import org.apache.tapestry.IScript;
029import org.apache.tapestry.PageRenderSupport;
030import org.apache.tapestry.TapestryUtils;
031import 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
041public 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}