2015-05-29 14:58:41 -07:00
|
|
|
import {
|
|
|
|
RegExp,
|
|
|
|
RegExpWrapper,
|
|
|
|
StringWrapper,
|
|
|
|
isPresent,
|
|
|
|
BaseException
|
|
|
|
} from 'angular2/src/facade/lang';
|
|
|
|
import {
|
|
|
|
Map,
|
|
|
|
MapWrapper,
|
|
|
|
List,
|
|
|
|
ListWrapper,
|
|
|
|
StringMap,
|
|
|
|
StringMapWrapper
|
|
|
|
} from 'angular2/src/facade/collection';
|
2015-04-17 09:59:56 -07:00
|
|
|
|
|
|
|
import {PathRecognizer} from './path_recognizer';
|
|
|
|
|
2015-05-15 02:05:57 -07:00
|
|
|
/**
|
|
|
|
* `RouteRecognizer` is responsible for recognizing routes for a single component.
|
2015-05-29 14:58:41 -07:00
|
|
|
* It is consumed by `RouteRegistry`, which knows how to recognize an entire hierarchy of
|
|
|
|
* components.
|
2015-05-15 02:05:57 -07:00
|
|
|
*/
|
2015-04-17 09:59:56 -07:00
|
|
|
export class RouteRecognizer {
|
2015-05-29 14:58:41 -07:00
|
|
|
names: Map<string, PathRecognizer>;
|
|
|
|
redirects: Map<string, string>;
|
|
|
|
matchers: Map<RegExp, PathRecognizer>;
|
2015-04-17 09:59:56 -07:00
|
|
|
|
|
|
|
constructor() {
|
|
|
|
this.names = MapWrapper.create();
|
|
|
|
this.matchers = MapWrapper.create();
|
|
|
|
this.redirects = MapWrapper.create();
|
|
|
|
}
|
|
|
|
|
2015-05-29 14:58:41 -07:00
|
|
|
addRedirect(path: string, target: string): void { MapWrapper.set(this.redirects, path, target); }
|
2015-04-17 09:59:56 -07:00
|
|
|
|
2015-05-29 14:58:41 -07:00
|
|
|
addConfig(path: string, handler: any, alias: string = null): void {
|
2015-04-17 09:59:56 -07:00
|
|
|
var recognizer = new PathRecognizer(path, handler);
|
2015-05-15 02:05:57 -07:00
|
|
|
MapWrapper.forEach(this.matchers, (matcher, _) => {
|
|
|
|
if (recognizer.regex.toString() == matcher.regex.toString()) {
|
2015-05-29 14:58:41 -07:00
|
|
|
throw new BaseException(
|
|
|
|
`Configuration '${path}' conflicts with existing route '${matcher.path}'`);
|
2015-05-15 02:05:57 -07:00
|
|
|
}
|
|
|
|
});
|
2015-04-17 09:59:56 -07:00
|
|
|
MapWrapper.set(this.matchers, recognizer.regex, recognizer);
|
|
|
|
if (isPresent(alias)) {
|
|
|
|
MapWrapper.set(this.names, alias, recognizer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-15 02:05:57 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a URL, returns a list of `RouteMatch`es, which are partial recognitions for some route.
|
|
|
|
*
|
|
|
|
*/
|
2015-05-29 14:58:41 -07:00
|
|
|
recognize(url: string): List<RouteMatch> {
|
2015-05-15 02:05:57 -07:00
|
|
|
var solutions = ListWrapper.create();
|
|
|
|
|
2015-04-17 09:59:56 -07:00
|
|
|
MapWrapper.forEach(this.redirects, (target, path) => {
|
2015-05-29 14:58:41 -07:00
|
|
|
// TODO: "/" redirect case
|
2015-04-17 09:59:56 -07:00
|
|
|
if (StringWrapper.startsWith(url, path)) {
|
|
|
|
url = target + StringWrapper.substring(url, path.length);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
MapWrapper.forEach(this.matchers, (pathRecognizer, regex) => {
|
|
|
|
var match;
|
|
|
|
if (isPresent(match = RegExpWrapper.firstMatch(regex, url))) {
|
2015-05-29 14:58:41 -07:00
|
|
|
// TODO(btford): determine a good generic way to deal with terminal matches
|
2015-05-15 02:05:57 -07:00
|
|
|
var matchedUrl = '/';
|
|
|
|
var unmatchedUrl = '';
|
|
|
|
if (url != '/') {
|
|
|
|
matchedUrl = match[0];
|
|
|
|
unmatchedUrl = StringWrapper.substring(url, match[0].length);
|
2015-04-29 15:47:12 -07:00
|
|
|
}
|
2015-05-15 02:05:57 -07:00
|
|
|
ListWrapper.push(solutions, new RouteMatch({
|
2015-05-29 14:58:41 -07:00
|
|
|
specificity: pathRecognizer.specificity,
|
|
|
|
handler: pathRecognizer.handler,
|
|
|
|
params: pathRecognizer.parseParams(url),
|
|
|
|
matchedUrl: matchedUrl,
|
|
|
|
unmatchedUrl: unmatchedUrl
|
|
|
|
}));
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return solutions;
|
|
|
|
}
|
|
|
|
|
2015-05-29 14:58:41 -07:00
|
|
|
hasRoute(name: string): boolean { return MapWrapper.contains(this.names, name); }
|
2015-04-17 09:59:56 -07:00
|
|
|
|
2015-05-29 14:58:41 -07:00
|
|
|
generate(name: string, params: any): string {
|
2015-04-17 09:59:56 -07:00
|
|
|
var pathRecognizer = MapWrapper.get(this.names, name);
|
2015-05-14 15:24:35 +02:00
|
|
|
return isPresent(pathRecognizer) ? pathRecognizer.generate(params) : null;
|
2015-04-17 09:59:56 -07:00
|
|
|
}
|
|
|
|
}
|
2015-05-15 02:05:57 -07:00
|
|
|
|
|
|
|
export class RouteMatch {
|
2015-05-29 14:58:41 -07:00
|
|
|
specificity: number;
|
|
|
|
handler: StringMap<string, any>;
|
|
|
|
params: StringMap<string, string>;
|
|
|
|
matchedUrl: string;
|
|
|
|
unmatchedUrl: string;
|
|
|
|
constructor({specificity, handler, params, matchedUrl, unmatchedUrl}: {
|
|
|
|
specificity?: number,
|
|
|
|
handler?: StringMap<string, any>,
|
|
|
|
params?: StringMap<string, string>,
|
|
|
|
matchedUrl?: string,
|
|
|
|
unmatchedUrl?: string
|
|
|
|
} = {}) {
|
2015-05-15 02:05:57 -07:00
|
|
|
this.specificity = specificity;
|
|
|
|
this.handler = handler;
|
|
|
|
this.params = params;
|
|
|
|
this.matchedUrl = matchedUrl;
|
|
|
|
this.unmatchedUrl = unmatchedUrl;
|
|
|
|
}
|
|
|
|
}
|