Route class
Maps Map of variables to String Url component and vice versa.
class Route {
RegExp _matchExp;
String _absolutePart = '';
List<String> _variables = [];
List _urlParts = [];
bool _anyTail = false;
get isAbsolute => _absolutePart.isNotEmpty;
/**
* Constructs [Route] using [String] pattern.
*
* Url is dividid into absolute and patter part.
* Pattern is divided to parts by slash '/' character. The slash must be the
* very first character of the pattern. Each part can be either static or
* placeholder. Static part can contain arbitrary number of [a-zA-Z0-9_-]
* characters. Placeholder part consists of variable name enclosed in curly
* braces. Variable name consists of any characters expect curly braces
* with the first character being not an underscore.
*/
Route(String url) {
String pattern;
if(url.startsWith(new RegExp(r'^(ht|f)tp(s?)://'))) {
int endOfAbsolutePart = url.indexOf('/', url.indexOf('://') + 3);
if(endOfAbsolutePart == -1) {
throw new FormatException('Absolute Url pattern has to have pattern part');
}
_absolutePart = url.substring(0, endOfAbsolutePart);
pattern = url.substring(endOfAbsolutePart);
}
else {
pattern = url;
}
if (pattern.isEmpty || pattern[0] != '/') {
throw new FormatException("Url pattern has to begin with '/' character.");
}
if(pattern[pattern.length - 1] == '*'){
_anyTail = true;
pattern = pattern.substring(0, pattern.length - 1);
}
RegExp exp = new RegExp(r"^(?:([\w-.]*)|{([^_{}][^{}]*)})$");
var matcherParts = new List();
var parts = pattern.split('/');
for (var part in parts) {
var match = exp.firstMatch(part);
if (match == null) {
throw new FormatException(
"""Only alphanumeric characters, dash '-' and underscore '_'
are allowed in the URL."""
);
}
if (match.group(1) != null) {
var group = match.group(1);
matcherParts.add(group);
_urlParts.add({'value': group, 'isVariable': false});
} else {
var group = match.group(2);
matcherParts.add("([^/]*)");
_variables.add(group);
_urlParts.add({'value': group, 'isVariable': true});
}
}
var tailRegExp = r"";
if(_anyTail){
tailRegExp = r"(.*)";
}
_matchExp = new RegExp(r"^" + _absolutePart + matcherParts.join('/') + tailRegExp + r"$");
}
/**
* Matches the [url] against the [Route] pattern and returns [Map] of matched.
* This is the inverse function to [Route.path].
*/
Map match(String url) {
var match = _matchExp.firstMatch(url);
// If the [url] does not match, returns [null].
if (match == null) {
return null;
}
// Decode [url] parameters and fill them into [Map].
Map result = new Map();
for (var i = 0; i < _variables.length; i++) {
result[_variables[i]] = Uri.decodeComponent(match.group(i + 1));
}
//Matched url after prefix
if(_anyTail){
result[PARAM_TAIL] = Uri.decodeComponent(match.group(_variables.length + 1));
}
return result;
}
/**
* Constructs the [url] using the [Route] pattern and values in [variables].
* This is theiInverse function to [Route.match].
* Accepts both [Map] as parameters.
* All [args] of [Route] must be provided.
*/
String path(Map args) {
var parts = [];
for (var part in this._urlParts) {
var value = part['value'];
if (part['isVariable']) {
value = args[value];
if (value == null) {
throw new ArgumentError("Missing value for ${part['value']}.");
}
}
parts.add(Uri.encodeComponent(value));
}
if (_anyTail) {
parts.removeLast();
parts.add(args[PARAM_TAIL]);
}
return _absolutePart + parts.join('/');
}
}
Constructors
new Route(String url) #
Constructs Route using String pattern.
Url is dividid into absolute and patter part.
Pattern is divided to parts by slash '/' character. The slash must be the
very first character of the pattern. Each part can be either static or
placeholder. Static part can contain arbitrary number of a-zA-Z0-9_-
characters. Placeholder part consists of variable name enclosed in curly
braces. Variable name consists of any characters expect curly braces
with the first character being not an underscore.
Route(String url) {
String pattern;
if(url.startsWith(new RegExp(r'^(ht|f)tp(s?)://'))) {
int endOfAbsolutePart = url.indexOf('/', url.indexOf('://') + 3);
if(endOfAbsolutePart == -1) {
throw new FormatException('Absolute Url pattern has to have pattern part');
}
_absolutePart = url.substring(0, endOfAbsolutePart);
pattern = url.substring(endOfAbsolutePart);
}
else {
pattern = url;
}
if (pattern.isEmpty || pattern[0] != '/') {
throw new FormatException("Url pattern has to begin with '/' character.");
}
if(pattern[pattern.length - 1] == '*'){
_anyTail = true;
pattern = pattern.substring(0, pattern.length - 1);
}
RegExp exp = new RegExp(r"^(?:([\w-.]*)|{([^_{}][^{}]*)})$");
var matcherParts = new List();
var parts = pattern.split('/');
for (var part in parts) {
var match = exp.firstMatch(part);
if (match == null) {
throw new FormatException(
"""Only alphanumeric characters, dash '-' and underscore '_'
are allowed in the URL."""
);
}
if (match.group(1) != null) {
var group = match.group(1);
matcherParts.add(group);
_urlParts.add({'value': group, 'isVariable': false});
} else {
var group = match.group(2);
matcherParts.add("([^/]*)");
_variables.add(group);
_urlParts.add({'value': group, 'isVariable': true});
}
}
var tailRegExp = r"";
if(_anyTail){
tailRegExp = r"(.*)";
}
_matchExp = new RegExp(r"^" + _absolutePart + matcherParts.join('/') + tailRegExp + r"$");
}
Properties
final isAbsolute #
get isAbsolute => _absolutePart.isNotEmpty;
Methods
Map match(String url) #
Matches the url against the Route pattern and returns Map of matched. This is the inverse function to Route.path.
Map match(String url) {
var match = _matchExp.firstMatch(url);
// If the [url] does not match, returns [null].
if (match == null) {
return null;
}
// Decode [url] parameters and fill them into [Map].
Map result = new Map();
for (var i = 0; i < _variables.length; i++) {
result[_variables[i]] = Uri.decodeComponent(match.group(i + 1));
}
//Matched url after prefix
if(_anyTail){
result[PARAM_TAIL] = Uri.decodeComponent(match.group(_variables.length + 1));
}
return result;
}
String path(Map args) #
Constructs the url using the Route pattern and values in variables.
This is theiInverse function to Route.match.
Accepts both Map as parameters.
All
args of Route must be provided.
String path(Map args) {
var parts = [];
for (var part in this._urlParts) {
var value = part['value'];
if (part['isVariable']) {
value = args[value];
if (value == null) {
throw new ArgumentError("Missing value for ${part['value']}.");
}
}
parts.add(Uri.encodeComponent(value));
}
if (_anyTail) {
parts.removeLast();
parts.add(args[PARAM_TAIL]);
}
return _absolutePart + parts.join('/');
}