/* * Copyright 2004-2005 Graeme Rocher * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.grails.web.mapping; import grails.util.GrailsWebUtil; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import grails.web.mapping.UrlCreator; import grails.core.GrailsControllerClass; import grails.util.GrailsStringUtils; import org.grails.buffer.FastStringWriter; import org.grails.web.servlet.mvc.DefaultRequestStateLookupStrategy; import org.grails.web.servlet.mvc.GrailsRequestStateLookupStrategy; import org.grails.web.servlet.mvc.GrailsWebRequest; import org.grails.web.servlet.mvc.exceptions.ControllerExecutionException; import org.springframework.web.context.request.RequestContextHolder; /** * The default implementation of the UrlCreator interface that constructs URLs in Grails * default pattern of /controllerName/actionName/id. * * @author Graeme Rocher * @since 0.5.5 */ @SuppressWarnings("rawtypes") public class DefaultUrlCreator implements UrlCreator { private static final char SLASH = '/'; private final String controllerName; private final String actionName; public static final String ARGUMENT_ID = "id"; private static final String ENTITY_AMPERSAND = "&"; public DefaultUrlCreator(String controller, String action) { controllerName = controller; actionName = action; } public String createURL(Map parameterValues, String encoding) { if (parameterValues == null) parameterValues = Collections.emptyMap(); GrailsWebRequest webRequest = (GrailsWebRequest) RequestContextHolder.getRequestAttributes(); return createURLWithWebRequest(parameterValues, webRequest, true); } public String createURL(Map parameterValues, String encoding, String fragment) { String url = createURL(parameterValues, encoding); return createUrlWithFragment(encoding, fragment, url); } @SuppressWarnings("unchecked") private String createURLWithWebRequest(Map parameterValues, GrailsWebRequest webRequest, boolean includeContextPath) { GrailsRequestStateLookupStrategy requestStateLookupStrategy = new DefaultRequestStateLookupStrategy(webRequest); final String encoding = requestStateLookupStrategy.getCharacterEncoding(); String id = null; if (parameterValues.containsKey(ARGUMENT_ID)) { Object o = parameterValues.get(ARGUMENT_ID); if (o != null) { id = o.toString(); } } FastStringWriter actualUriBuf = new FastStringWriter(); if (includeContextPath) { actualUriBuf.append(requestStateLookupStrategy.getContextPath()); } if (actionName != null) { if (actionName.indexOf(SLASH) > -1) { actualUriBuf.append(actionName); } else { if (controllerName != null) { appendUrlToken(actualUriBuf, controllerName, encoding); } else { appendUrlToken(actualUriBuf, requestStateLookupStrategy.getControllerName(), encoding); } } appendUrlToken(actualUriBuf, actionName, encoding); } if (id != null) { appendUrlToken(actualUriBuf, id, encoding); } appendRequestParams(actualUriBuf, parameterValues, encoding); return actualUriBuf.toString(); } public String createURL(String controller, String action, Map parameterValues, String encoding) { return createURL(controller, action, null, null, parameterValues, encoding); } public String createURL(String controller, String action, String pluginName, Map parameterValues, String encoding) { return createURL(controller, action, null, pluginName, parameterValues, encoding); } @SuppressWarnings("unchecked") public String createURL(String controller, String action, String namespace, String pluginName, Map parameterValues, String encoding) { return createURLInternal(controller, action, parameterValues, true); } private String createURLInternal(String controller, String action, Map<String, String> parameterValues, boolean includeContextPath) { GrailsWebRequest webRequest = (GrailsWebRequest) RequestContextHolder.getRequestAttributes(); if (parameterValues == null) parameterValues = new HashMap<String, String>(); boolean blankController = GrailsStringUtils.isBlank(controller); boolean blankAction = GrailsStringUtils.isBlank(action); if (!blankController) { parameterValues.put(GrailsControllerClass.CONTROLLER, controller); } if (!blankAction) { parameterValues.put(GrailsControllerClass.ACTION, action); } try { return createURLWithWebRequest(parameterValues, webRequest, includeContextPath); } finally { if (!blankController) { parameterValues.remove(GrailsControllerClass.CONTROLLER); } if (!blankAction) { parameterValues.remove(GrailsControllerClass.ACTION); } } } public String createRelativeURL(String controller, String action, Map parameterValues, String encoding) { return createRelativeURL(controller, action, null, null, parameterValues, encoding); } public String createRelativeURL(String controller, String action, String pluginName, Map parameterValues, String encoding) { return createRelativeURL(controller, action, null, null, parameterValues, encoding); } @SuppressWarnings("unchecked") public String createRelativeURL(String controller, String action, String namespace, String pluginName, Map parameterValues, String encoding) { return createURLInternal(controller, action, parameterValues, false); } public String createRelativeURL(String controller, String action, Map parameterValues, String encoding, String fragment) { return createRelativeURL(controller, action, null, null, parameterValues, encoding, fragment); } @SuppressWarnings("unchecked") public String createRelativeURL(String controller, String action, String namespace, String pluginName, Map parameterValues, String encoding, String fragment) { final String url = createURLInternal(controller, action, parameterValues, false); return createUrlWithFragment(encoding, fragment, url); } public String createURL(String controller, String action, Map parameterValues, String encoding, String fragment) { return createURL(controller, action, null, null, parameterValues, encoding, fragment); } public String createURL(String controller, String action, String namespace, String pluginName, Map parameterValues, String encoding, String fragment) { String url = createURL(controller, action, namespace, pluginName, parameterValues, encoding); return createUrlWithFragment(encoding, fragment, url); } private String createUrlWithFragment(String encoding, String fragment, String url) { if (fragment != null) { try { return url + '#' + URLEncoder.encode(fragment, encoding); } catch (UnsupportedEncodingException ex) { throw new ControllerExecutionException("Error creating URL [" + url + "], problem encoding URL fragment [" + fragment + "]: " + ex.getMessage(),ex); } } return url; } /* * Appends all the request parameters to the URI buffer */ private void appendRequestParams(FastStringWriter actualUriBuf, Map<Object, Object> params, String encoding) { boolean querySeparator = false; for (Map.Entry<Object, Object> entry : params.entrySet()) { Object name = entry.getKey(); if (name.equals(GrailsControllerClass.CONTROLLER) || name.equals(GrailsControllerClass.ACTION) || name.equals(ARGUMENT_ID)) { continue; } if (!querySeparator) { actualUriBuf.append('?'); querySeparator = true; } else { actualUriBuf.append(ENTITY_AMPERSAND); } Object value = entry.getValue(); if (value instanceof Collection) { Collection values = (Collection) value; Iterator valueIterator = values.iterator(); while (valueIterator.hasNext()) { Object currentValue = valueIterator.next(); appendRequestParam(actualUriBuf, name, currentValue, encoding); if (valueIterator.hasNext()) { actualUriBuf.append(ENTITY_AMPERSAND); } } } else if (value != null && value.getClass().isArray()) { Object[] array = (Object[]) value; for (int j = 0; j < array.length; j++) { Object currentValue = array[j]; appendRequestParam(actualUriBuf, name, currentValue, encoding); if (j < (array.length-1)) { actualUriBuf.append(ENTITY_AMPERSAND); } } } else { appendRequestParam(actualUriBuf, name, value, encoding); } } } /* * Appends a request parameters for the given aname and value */ private void appendRequestParam(FastStringWriter actualUriBuf, Object name, Object value, String encoding) { if (value == null) { value = ""; } actualUriBuf.append(urlEncode(name, encoding)) .append('=') .append(urlEncode(value, encoding)); } private String urlEncode(Object obj, String charset) { try { return URLEncoder.encode(obj.toString(), (charset != null) ? charset : GrailsWebUtil.DEFAULT_ENCODING); } catch (UnsupportedEncodingException ex) { throw new ControllerExecutionException( "Error creating URL, cannot URLEncode to the client's character encoding: " + ex.getMessage(), ex); } } /* * Appends a URL token to the buffer */ private void appendUrlToken(FastStringWriter actualUriBuf, Object token, String charset) { actualUriBuf.append(SLASH).append(urlEncode(token, charset)); } }