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('/'); }