blob: 61eb1d4d3f7ef0ce9ebe8196e27aff0a4662c19b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.knox.gateway.util.urltemplate;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.regex.Pattern;
abstract class Segment {
static final String ANONYMOUS_PARAM = "";
static final String DEFAULT_PATTERN = "";
static final String STAR_PATTERN = "*";
static final String GLOB_PATTERN = "**";
static final char DOT = '.';
static final char ESC = '\\';
// Note: The order of these is important. The numbers must be from most specific to least.
public static final int STATIC = 1;
public static final int REGEX = 2;
public static final int STAR = 3;
public static final int DEFAULT = 4;
public static final int GLOB = 5;
public static final int UNKNOWN = 6;
// private String paramName; // ?queryName={paramName=value}
private Token token;
private Map<String,Value> values;
// protected Segment( String paramName, String valuePattern ) {
// this.paramName = paramName;
// this.values = new LinkedHashMap<>();
// this.values.put( valuePattern, new Value( valuePattern ) );
// }
protected Segment( Token token ) {
this.token = token;
this.values = new LinkedHashMap<>();
this.values.put( token.effectivePattern, new Value( token ) );
}
// protected Segment( Segment that ) {
// this.paramName = that.paramName;
// this.values = new LinkedHashMap<>();
// for( Value thatValue : that.getValues() ) {
// Value thisValue = new Value( thatValue );
// this.values.put( thisValue.getPattern(), thisValue );
// }
// }
protected Token getToken() {
return token;
}
@Override
public int hashCode() {
return token.parameterName.hashCode();
}
@Override
@SuppressWarnings( "unchecked" )
public boolean equals( Object obj ) {
boolean equal = false;
if( obj instanceof Segment ) {
Segment that = (Segment)obj;
equal = ( this.token.parameterName.equals( that.token.parameterName ) && this.values.size() == that.values.size() );
if( equal ) {
for( String pattern: this.values.keySet() ) {
equal = that.values.containsKey( pattern );
if( !equal ) {
break;
}
}
}
}
return equal;
}
public String getParamName() {
return token.parameterName;
}
public Collection<Value> getValues() {
return values.values();
}
public Value getFirstValue() {
Value first = null;
if( !values.isEmpty() ) {
first = values.values().iterator().next();
}
return first;
}
public boolean matches( Segment that ) {
if( getClass().isInstance( that ) ) {
for( Value thisValue: this.values.values() ) {
for( Value thatValue: that.values.values() ) {
if( thisValue.matches( thatValue ) ) {
return true;
}
}
}
}
return false;
}
void addValue( Token token ) {
Value value = new Value( token );
values.put( token.effectivePattern, value );
}
// void addValue( String valuePattern ) {
// Value value = new Value( valuePattern );
// values.put( valuePattern, value );
// }
public String toString() {
StringBuilder s = new StringBuilder();
s.append( getParamName() );
Collection<Value> values = getValues();
if( values == null ) {
s.append( "null" );
} else if( values.isEmpty() ) {
s.append( "empty" );
} else {
s.append( "[" );
Iterator i = values.iterator();
while( i.hasNext() ) {
s.append( i.next() );
if( i.hasNext() ) {
s.append( "," );
}
}
s.append( "]" );
}
return s.toString();
}
public class Value {
private int type;
private Token token;
private Pattern regex;
private Value( Token token ) {
this.token = token;
this.regex = null;
String effectivePattern = token.effectivePattern;
if( token.isLiteral() ) {
this.type = STATIC;
} else if( DEFAULT_PATTERN.equals( effectivePattern ) ) {
this.type = DEFAULT;
} else if( STAR_PATTERN.equals( effectivePattern ) ) {
this.type = STAR;
} else if( GLOB_PATTERN.equals( effectivePattern ) ) {
type = GLOB;
} else if ( effectivePattern != null && effectivePattern.contains( STAR_PATTERN ) ) {
this.type = REGEX;
this.regex = compileRegex( effectivePattern );
} else {
this.type = STATIC;
}
}
// private Value( String pattern ) {
// this.pattern = pattern;
// this.regex = null;
// if( DEFAULT_PATTERN.equals( pattern ) ) {
// this.type = DEFAULT;
// } else if( STAR_PATTERN.equals( pattern ) ) {
// this.type = STAR;
// } else if( GLOB_PATTERN.equals( pattern ) ) {
// type = GLOB;
// } else if ( pattern != null && pattern.contains( STAR_PATTERN ) ) {
// this.type = REGEX;
// this.regex = compileRegex( pattern );
// } else {
// this.type = STATIC;
// }
// }
private Value( Value that ) {
this.type = that.type;
this.token = that.token;
this.regex = that.regex;
}
Token getToken() {
return token;
}
public int getType() {
return type;
}
public String getPattern() {
return token.originalPattern;
}
String getOriginalPattern() {
return token.originalPattern;
}
String getEffectivePattern() {
return token.effectivePattern;
}
public Pattern getRegex() {
return regex;
}
public String toString() {
return token.effectivePattern;
}
public boolean matches( Value that ) {
boolean matches = getClass().isInstance( that );
if( matches ) {
switch( this.getType() ) {
case( STATIC ):
matches = this.token.originalPattern.equals( that.token.originalPattern );
//matches = matchThisStatic( that ); // See: MatcherTest.testWildcardCharacterInInputTemplate
break;
case( DEFAULT ):
case( STAR ):
case( GLOB ):
matches = true;
//matches = matchThisWildcard( that ); // See: MatcherTest.testWildcardCharacterInInputTemplate
break;
case( REGEX ):
matches = this.regex.matcher( that.token.effectivePattern ).matches();
//matches = matchThisRegex( that ); // See: MatcherTest.testWildcardCharacterInInputTemplate
break;
default:
matches = false;
}
}
return matches;
}
// See: MatcherTest.testWildcardCharacterInInputTemplate
// private boolean matchThisStatic( Value that ) {
// boolean matches = false;
// switch( that.getType() ) {
// case( STATIC ):
// matches = this.pattern.equals( that.pattern );
// break;
// case( DEFAULT ):
// case( STAR ):
// case( GLOB ):
// matches = true;
// break;
// case( REGEX ):
// matches = that.regex.matcher( this.pattern ).matches();
// break;
// }
// return matches;
// }
// See: MatcherTest.testWildcardCharacterInInputTemplate
// private boolean matchThisWildcard( Value that ) {
// boolean matches = false;
// switch( that.getType() ) {
// case( STATIC ):
// matches = true;
// break;
// case( DEFAULT ):
// case( STAR ):
// case( GLOB ):
// matches = true;
// break;
// case( REGEX ):
// matches = true;
// break;
// }
// return matches;
// }
// See: MatcherTest.testWildcardCharacterInInputTemplate
// private boolean matchThisRegex( Value that ) {
// boolean matches = false;
// switch( that.getType() ) {
// case( STATIC ):
// matches = this.regex.matcher( that.pattern ).matches();
// break;
// case( DEFAULT ):
// case( STAR ):
// case( GLOB ):
// matches = true;
// break;
// case( REGEX ):
// matches = this.pattern.equals( that.pattern );
// break;
// }
// return matches;
// }
}
// Escape .\${ and turn * into .*
static final String createRegex( final String segment ) {
StringBuilder regex = new StringBuilder( segment );
int len = regex.length();
for( int i=len-1; i>=0; i-- ) {
char c = regex.charAt( i );
switch( c ) {
case '*':
regex.insert( i, DOT );
break;
case '\\':
case '.':
case '{':
case '}':
case '$':
regex.insert( i, ESC );
break;
default:
// noop
}
}
return regex.toString();
}
// Creates a pattern for a simplified filesystem style wildcard '*' syntax.
static final Pattern compileRegex( String segment ) {
// // Turn '*' into '/' to keep it safe.
// // Chose '/' because that can't exist in a segment.
// segment = segment.replaceAll( "\\*", "/" );
// // Turn '.' into '\.'.
// segment = segment.replaceAll( "\\.", "\\\\." );
// // Turn '$' into '\$'.
// segment = escapeSpecialRegExChar( segment, '$' );
// segment = escapeSpecialRegExChar( segment, '{' );
// // Turn '/' back into '.*'.
// segment = segment.replaceAll( "/", "\\.\\*" );
segment = createRegex( segment );
return Pattern.compile( segment );
}
// private static String escapeSpecialRegExChar( String input, char c ) {
// int i = input.indexOf( c );
// if( i >= 0 ) {
// int inputLength = input.length();
// StringBuilder output = new StringBuilder( inputLength + 1 );
// output.append( input, 0, i );
// output.append( '\\' );
// if( i < inputLength ) {
// output.append( input, i, inputLength );
// }
// input = output.toString();
// }
// return input;
// }
// Escape
}