@ -18,9 +18,9 @@ package org.springframework.web.util;
@@ -18,9 +18,9 @@ package org.springframework.web.util;
import java.io.Serializable ;
import java.net.URI ;
import java.util.ArrayList ;
import java.util.Collections ;
import java.util.LinkedHashMap ;
import java.util.LinkedList ;
import java.util.List ;
import java.util.Map ;
import java.util.regex.Matcher ;
@ -37,19 +37,13 @@ import org.springframework.util.Assert;
@@ -37,19 +37,13 @@ import org.springframework.util.Assert;
*
* @author Arjen Poutsma
* @author Juergen Hoeller
* @author Rossen Stoyanchev
* @since 3 . 0
* @see < a href = "http://bitworking.org/projects/URI-Templates/" > URI Templates < / a >
* /
@SuppressWarnings ( "serial" )
public class UriTemplate implements Serializable {
/** Captures URI template variable names. */
private static final Pattern NAMES_PATTERN = Pattern . compile ( "\\{([^/]+?)\\}" ) ;
/** Replaces template variables in the URI template. */
private static final String DEFAULT_VARIABLE_PATTERN = "(.*)" ;
private final UriComponents uriComponents ;
private final List < String > variableNames ;
@ -64,11 +58,13 @@ public class UriTemplate implements Serializable {
@@ -64,11 +58,13 @@ public class UriTemplate implements Serializable {
* @param uriTemplate the URI template string
* /
public UriTemplate ( String uriTemplate ) {
Parser parser = new Parser ( uriTemplate ) ;
Assert . hasText ( uriTemplate , "'uriTemplate' must not be null" ) ;
this . uriTemplate = uriTemplate ;
this . variableNames = parser . getVariableNames ( ) ;
this . matchPattern = parser . getMatchPattern ( ) ;
this . uriComponents = UriComponentsBuilder . fromUriString ( uriTemplate ) . build ( ) ;
TemplateInfo info = TemplateInfo . parse ( uriTemplate ) ;
this . variableNames = Collections . unmodifiableList ( info . getVariableNames ( ) ) ;
this . matchPattern = info . getMatchPattern ( ) ;
}
@ -169,60 +165,81 @@ public class UriTemplate implements Serializable {
@@ -169,60 +165,81 @@ public class UriTemplate implements Serializable {
/ * *
* Static inner class to parse URI template strings into a matching regular expression .
* Helper to extract variable names and regex for matching to actual URLs .
* /
private static class Parser {
private final List < String > variableNames = new LinkedList < String > ( ) ;
private final StringBuilder patternBuilder = new StringBuilder ( ) ;
private Parser ( String uriTemplate ) {
Assert . hasText ( uriTemplate , "'uriTemplate' must not be null" ) ;
Matcher matcher = NAMES_PATTERN . matcher ( uriTemplate ) ;
int end = 0 ;
while ( matcher . find ( ) ) {
this . patternBuilder . append ( quote ( uriTemplate , end , matcher . start ( ) ) ) ;
String match = matcher . group ( 1 ) ;
int colonIdx = match . indexOf ( ':' ) ;
if ( colonIdx = = - 1 ) {
this . patternBuilder . append ( DEFAULT_VARIABLE_PATTERN ) ;
this . variableNames . add ( match ) ;
}
else {
if ( colonIdx + 1 = = match . length ( ) ) {
throw new IllegalArgumentException (
"No custom regular expression specified after ':' in \"" + match + "\"" ) ;
}
String variablePattern = match . substring ( colonIdx + 1 , match . length ( ) ) ;
this . patternBuilder . append ( '(' ) ;
this . patternBuilder . append ( variablePattern ) ;
this . patternBuilder . append ( ')' ) ;
String variableName = match . substring ( 0 , colonIdx ) ;
this . variableNames . add ( variableName ) ;
}
end = matcher . end ( ) ;
}
this . patternBuilder . append ( quote ( uriTemplate , end , uriTemplate . length ( ) ) ) ;
int lastIdx = this . patternBuilder . length ( ) - 1 ;
if ( lastIdx > = 0 & & this . patternBuilder . charAt ( lastIdx ) = = '/' ) {
this . patternBuilder . deleteCharAt ( lastIdx ) ;
}
private static class TemplateInfo {
private final List < String > variableNames ;
private final Pattern pattern ;
private TemplateInfo ( List < String > vars , Pattern pattern ) {
this . variableNames = vars ;
this . pattern = pattern ;
}
private String quote ( String fullPath , int start , int end ) {
if ( start = = end ) {
return "" ;
}
return Pattern . quote ( fullPath . substring ( start , end ) ) ;
public List < String > getVariableNames ( ) {
return this . variableNames ;
}
public Pattern getMatchPattern ( ) {
return this . pattern ;
}
private List < String > getVariableNames ( ) {
return Collections . unmodifiableList ( this . variableNames ) ;
private static TemplateInfo parse ( String uriTemplate ) {
int level = 0 ;
List < String > variableNames = new ArrayList < String > ( ) ;
StringBuilder pattern = new StringBuilder ( ) ;
StringBuilder builder = new StringBuilder ( ) ;
for ( int i = 0 ; i < uriTemplate . length ( ) ; i + + ) {
char c = uriTemplate . charAt ( i ) ;
if ( c = = '{' ) {
level + + ;
if ( level = = 1 ) {
pattern . append ( quote ( builder ) ) ;
builder = new StringBuilder ( ) ;
continue ;
}
}
else if ( c = = '}' ) {
level - - ;
if ( level = = 0 ) {
String variable = builder . toString ( ) ;
int idx = variable . indexOf ( ':' ) ;
if ( idx = = - 1 ) {
pattern . append ( "(.*)" ) ;
variableNames . add ( variable ) ;
}
else {
if ( idx + 1 = = variable . length ( ) ) {
throw new IllegalArgumentException (
"No custom regular expression specified after ':' " +
"in \"" + variable + "\"" ) ;
}
String regex = variable . substring ( idx + 1 , variable . length ( ) ) ;
pattern . append ( '(' ) ;
pattern . append ( regex ) ;
pattern . append ( ')' ) ;
variableNames . add ( variable . substring ( 0 , idx ) ) ;
}
builder = new StringBuilder ( ) ;
continue ;
}
}
if ( i + 1 = = uriTemplate . length ( ) ) {
if ( c ! = '/' ) {
builder . append ( c ) ;
}
pattern . append ( quote ( builder ) ) ;
}
builder . append ( c ) ;
}
return new TemplateInfo ( variableNames , Pattern . compile ( pattern . toString ( ) ) ) ;
}
private Pattern getMatchPattern ( ) {
return Pattern . compile ( this . patternBuilder . toString ( ) ) ;
private static String quote ( StringBuilder builder ) {
return builder . length ( ) ! = 0 ? Pattern . quote ( b uilder. toString ( ) ) : "" ;
}
}