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.engine;
016
017import java.io.UnsupportedEncodingException;
018import java.util.Map;
019
020import org.apache.commons.codec.net.URLCodec;
021import org.apache.hivemind.ApplicationRuntimeException;
022import org.apache.hivemind.util.Defense;
023import org.apache.tapestry.IRequestCycle;
024import org.apache.tapestry.Tapestry;
025import org.apache.tapestry.util.QueryParameterMap;
026import org.apache.tapestry.web.WebRequest;
027
028/**
029 * A EngineServiceLink represents a possible action within the client web browser; either clicking a
030 * link or submitting a form, which is constructed primarily from the servlet path, with some
031 * additional query parameters. A full URL for the EngineServiceLink can be generated, or the query
032 * parameters for the EngineServiceLink can be extracted (separately from the servlet path). The
033 * latter case is used when submitting constructing {@link org.apache.tapestry.form.Form forms}.
034 * 
035 * @author Howard Lewis Ship
036 * @since 3.0
037 */
038
039public class EngineServiceLink implements ILink
040{
041    private static final int DEFAULT_HTTP_PORT = 80;
042    private static final int DEFAULT_HTTPS_PORT = 443;
043
044    private final IRequestCycle _cycle;
045
046    private final String _servletPath;
047
048    private final URLCodec _codec;
049
050    private String _encoding;
051
052    private boolean _stateful;
053
054    /** @since 4.0 */
055    private final QueryParameterMap _parameters;
056
057    /** @since 4.0 */
058
059    private final WebRequest _request;
060
061    /**
062     * Creates a new EngineServiceLink.
063     * 
064     * @param cycle
065     *            The {@link IRequestCycle}  the EngineServiceLink is to be created for.
066     * @param servletPath
067     *            The path used to invoke the Tapestry servlet.
068     * @param codec
069     *            A codec for converting strings into URL-safe formats.
070     * @param encoding
071     *            The output encoding for the request.
072     * @param parameters
073     *            The query parameters to be encoded into the url. Keys are strings, values are
074     *            null, string or array of string. The map is retained, not copied.
075     * @param stateful
076     *            if true, the service which generated the EngineServiceLink is stateful and expects
077     *            that the final URL will be passed through {@link IRequestCycle#encodeURL(String)}.
078     */
079
080    public EngineServiceLink(IRequestCycle cycle, String servletPath, String encoding,
081            URLCodec codec, WebRequest request, Map parameters, boolean stateful)
082    {
083        Defense.notNull(cycle, "cycle");
084        Defense.notNull(servletPath, "servletPath");
085        Defense.notNull(encoding, "encoding");
086        Defense.notNull(codec, "codec");
087        Defense.notNull(request, "request");
088        Defense.notNull(parameters, "parameters");
089
090        _cycle = cycle;
091        _servletPath = servletPath;
092        _encoding = encoding;
093        _codec = codec;
094        _request = request;
095        _parameters = new QueryParameterMap(parameters);
096        _stateful = stateful;
097    }
098
099    public String getURL()
100    {
101        return getURL(null, true);
102    }
103
104    public String getURL(String anchor, boolean includeParameters)
105    {
106        return constructURL(new StringBuffer(), anchor, includeParameters);
107    }
108
109    public String getAbsoluteURL()
110    {
111        return getAbsoluteURL(null, null, 0, null, true);
112    }
113
114    public String getURL(String scheme, String server, int port, String anchor,
115            boolean includeParameters)
116    {
117        boolean useAbsolute = EngineUtils.needAbsoluteURL(scheme, server, port, _request);
118
119        return useAbsolute ? getAbsoluteURL(scheme, server, port, anchor, includeParameters)
120                : getURL(anchor, includeParameters);
121    }
122
123    public String getAbsoluteURL(String scheme, String server, int port, String anchor,
124            boolean includeParameters)
125    {
126        StringBuffer buffer = new StringBuffer();
127
128        if (scheme == null)
129            scheme = _request.getScheme();
130
131        buffer.append(scheme);
132        buffer.append("://");
133
134        if (server == null)
135            server = _request.getServerName();
136
137        buffer.append(server);
138
139        if (port == 0)
140            port = _request.getServerPort();
141
142        if (!(scheme.equals("http") && port == DEFAULT_HTTP_PORT))
143        {
144            buffer.append(':');
145            buffer.append(port);
146        }
147
148        // Add the servlet path and the rest of the URL & query parameters.
149        // The servlet path starts with a leading slash.
150
151        return constructURL(buffer, anchor, includeParameters);
152    }
153
154    private String constructURL(StringBuffer buffer, String anchor, boolean includeParameters)
155    {
156        buffer.append(_servletPath);
157
158        if (includeParameters)
159            addParameters(buffer);
160
161        if (anchor != null)
162        {
163            buffer.append('#');
164            buffer.append(anchor);
165        }
166
167        String result = buffer.toString();
168
169        result = _cycle.encodeURL(result);
170
171        return result;
172    }
173
174    private void addParameters(StringBuffer buffer)
175    {
176        String[] names = getParameterNames();
177
178        String sep = "?";
179
180        for (int i = 0; i < names.length; i++)
181        {
182            String name = names[i];
183            String[] values = getParameterValues(name);
184
185            if (values == null)
186                continue;
187
188            for (int j = 0; j < values.length; j++)
189            {
190                buffer.append(sep);
191                buffer.append(name);
192                buffer.append("=");
193                buffer.append(encode(values[j]));
194
195                sep = "&";
196            }
197
198        }
199    }
200
201    private String encode(String value)
202    {
203        try
204        {
205            return _codec.encode(value, _encoding);
206        }
207        catch (UnsupportedEncodingException ex)
208        {
209            throw new ApplicationRuntimeException(Tapestry.format("illegal-encoding", _encoding),
210                    ex);
211        }
212    }
213
214    public String[] getParameterNames()
215    {
216        return _parameters.getParameterNames();
217    }
218
219    public String[] getParameterValues(String name)
220    {
221        return _parameters.getParameterValues(name);
222    }
223}