@Override public String toString() { return urlData.getUrlPattern(); }
public String[] getLogicalMappings() { return urlData.getLogicalUrls(); }
@SuppressWarnings({"unchecked"}) private String createURLInternal(Map paramValues, String encoding, boolean includeContextPath) { if (encoding == null) encoding = "utf-8"; String contextPath = ""; if (includeContextPath) { GrailsWebRequest webRequest = (GrailsWebRequest) RequestContextHolder.getRequestAttributes(); if (webRequest != null) { contextPath = webRequest.getAttributes().getApplicationUri(webRequest.getCurrentRequest()); } } if (paramValues == null) paramValues = Collections.EMPTY_MAP; StringBuilder uri = new StringBuilder(contextPath); Set usedParams = new HashSet(); String[] tokens = urlData.getTokens(); int paramIndex = 0; for (int i = 0; i < tokens.length; i++) { String token = tokens[i]; if (i == tokens.length - 1 && urlData.hasOptionalExtension()) { token += OPTIONAL_EXTENSION_WILDCARD; } Matcher m = OPTIONAL_EXTENSION_WILDCARD_PATTERN.matcher(token); if (m.find()) { if (token.startsWith(CAPTURED_WILDCARD)) { ConstrainedProperty prop = constraints[paramIndex++]; String propName = prop.getPropertyName(); Object value = paramValues.get(propName); usedParams.add(propName); if (value != null) { token = token.replaceFirst(DOUBLE_WILDCARD_PATTERN.pattern(), value.toString()); } else if (prop.isNullable()) { break; } } uri.append(SLASH); ConstrainedProperty prop = constraints[paramIndex++]; String propName = prop.getPropertyName(); Object value = paramValues.get(propName); usedParams.add(propName); if (value != null) { String ext = "." + value; uri.append( token .replace(OPTIONAL_EXTENSION_WILDCARD + '?', ext) .replace(OPTIONAL_EXTENSION_WILDCARD, ext)); } else { uri.append( token .replace(OPTIONAL_EXTENSION_WILDCARD + '?', "") .replace(OPTIONAL_EXTENSION_WILDCARD, "")); } continue; } if (token.endsWith("?")) { token = token.substring(0, token.length() - 1); } m = DOUBLE_WILDCARD_PATTERN.matcher(token); if (m.find()) { StringBuffer buf = new StringBuffer(); do { ConstrainedProperty prop = constraints[paramIndex++]; String propName = prop.getPropertyName(); Object value = paramValues.get(propName); usedParams.add(propName); if (value == null && !prop.isNullable()) { throw new UrlMappingException( "Unable to create URL for mapping [" + this + "] and parameters [" + paramValues + "]. Parameter [" + prop.getPropertyName() + "] is required, but was not specified!"); } else if (value == null) { m.appendReplacement(buf, ""); } else { m.appendReplacement(buf, Matcher.quoteReplacement(value.toString())); } } while (m.find()); m.appendTail(buf); try { String v = buf.toString(); if (v.indexOf(SLASH) > -1 && CAPTURED_DOUBLE_WILDCARD.equals(token)) { // individually URL encode path segments if (v.startsWith(SLASH)) { // get rid of leading slash v = v.substring(SLASH.length()); } String[] segs = v.split(SLASH); for (String segment : segs) { uri.append(SLASH).append(encode(segment, encoding)); } } else if (v.length() > 0) { // original behavior uri.append(SLASH).append(encode(v, encoding)); } else { // Stop processing tokens once we hit an empty one. break; } } catch (UnsupportedEncodingException e) { throw new ControllerExecutionException( "Error creating URL for parameters [" + paramValues + "], problem encoding URL part [" + buf + "]: " + e.getMessage(), e); } } else { uri.append(SLASH).append(token); } } populateParameterList(paramValues, encoding, uri, usedParams); if (LOG.isDebugEnabled()) { LOG.debug( "Created reverse URL mapping [" + uri.toString() + "] for parameters [" + paramValues + "]"); } return uri.toString(); }
@SuppressWarnings("unchecked") private UrlMappingInfo createUrlMappingInfo(String uri, Matcher m) { boolean hasOptionalExtension = urlData.hasOptionalExtension(); Map params = new HashMap(); Errors errors = new MapBindingResult(params, "urlMapping"); int groupCount = m.groupCount(); String lastGroup = null; for (int i = 0; i < groupCount; i++) { lastGroup = m.group(i + 1); // if null optional.. ignore if (i == groupCount - 1 && hasOptionalExtension) { ConstrainedProperty cp = constraints[constraints.length - 1]; cp.validate(this, lastGroup, errors); if (errors.hasErrors()) { return null; } params.put(cp.getPropertyName(), lastGroup); break; } else { if (lastGroup == null) continue; int j = lastGroup.indexOf('?'); if (j > -1) { lastGroup = lastGroup.substring(0, j); } if (constraints.length > i) { ConstrainedProperty cp = constraints[i]; cp.validate(this, lastGroup, errors); if (errors.hasErrors()) { return null; } params.put(cp.getPropertyName(), lastGroup); } } } for (Object key : parameterValues.keySet()) { params.put(key, parameterValues.get(key)); } if (controllerName == null) { controllerName = createRuntimeConstraintEvaluator(GrailsControllerClass.CONTROLLER, constraints); } if (actionName == null) { actionName = createRuntimeConstraintEvaluator(GrailsControllerClass.ACTION, constraints); } if (namespace == null) { namespace = createRuntimeConstraintEvaluator(NAMESPACE, constraints); } if (viewName == null) { viewName = createRuntimeConstraintEvaluator(GrailsControllerClass.VIEW, constraints); } if (redirectInfo == null) { redirectInfo = createRuntimeConstraintEvaluator("redirect", constraints); } DefaultUrlMappingInfo info; if (forwardURI != null && controllerName == null) { info = new DefaultUrlMappingInfo(forwardURI, getHttpMethod(), urlData, servletContext); } else if (viewName != null && controllerName == null) { info = new DefaultUrlMappingInfo(viewName, params, urlData, servletContext); } else { info = new DefaultUrlMappingInfo( redirectInfo, controllerName, actionName, namespace, pluginName, getViewName(), getHttpMethod(), getVersion(), params, urlData, servletContext); } if (parseRequest) { info.setParsingRequest(parseRequest); } return info; }
private void parse(UrlMappingData data, ConstrainedProperty[] constraints) { Assert.notNull(data, "Argument [data] cannot be null"); String[] urls = data.getLogicalUrls(); urlData = data; patterns = new Pattern[urls.length]; for (int i = 0; i < urls.length; i++) { String url = urls[i]; Integer slashCount = org.springframework.util.StringUtils.countOccurrencesOf(url, "/"); List<Pattern> tokenCountPatterns = patternByTokenCount.get(slashCount); if (tokenCountPatterns == null) { tokenCountPatterns = new ArrayList<Pattern>(); patternByTokenCount.put(slashCount, tokenCountPatterns); } Pattern pattern = convertToRegex(url); if (pattern == null) { throw new IllegalStateException( "Cannot use null pattern in regular expression mapping for url [" + data.getUrlPattern() + "]"); } tokenCountPatterns.add(pattern); this.patterns[i] = pattern; } if (constraints != null) { String[] tokens = data.getTokens(); int pos = 0; int currentToken = 0; int tokensLength = tokens.length - 1; int constraintUpperBound = constraints.length; if (data.hasOptionalExtension()) { constraintUpperBound--; constraints[constraintUpperBound].setNullable(true); } for (int i = 0; i < constraintUpperBound; i++) { ConstrainedProperty constraint = constraints[i]; if (currentToken > tokensLength) break; String token = tokens[currentToken]; int shiftLength = 3; pos = token.indexOf(CAPTURED_WILDCARD, pos); while (pos == -1) { boolean isLastToken = currentToken == tokensLength - 1; if (currentToken < tokensLength) { token = tokens[++currentToken]; // special handling for last token to deal with optional extension if (isLastToken) { if (token.startsWith(CAPTURED_WILDCARD + '?')) { constraint.setNullable(true); } if (token.endsWith(OPTIONAL_EXTENSION_WILDCARD + '?')) { constraints[constraints.length - 1].setNullable(true); } } else { pos = token.indexOf(CAPTURED_WILDCARD, pos); } } else { break; } } if (pos != -1 && pos + shiftLength < token.length() && token.charAt(pos + shiftLength) == '?') { constraint.setNullable(true); } // Move on to the next place-holder. pos += shiftLength; if (token.indexOf(CAPTURED_WILDCARD, pos) == -1) { currentToken++; pos = 0; } } } }