
# AntPathMatcher

`AntPathMatcher` tests paths such as `config/dev/app.properties` against Ant-style patterns such as `config/**/*.properties`, and extracts values from them.

## Why use it

Resource patterns (`resources:mail/*.txt`) and `@PropertySource` locations already use it, so most applications never create one. Use it directly when your own code must match paths or dotted names: which uploaded files are stylesheets, which keys belong to a group, which route pattern fits best.

The syntax:

- `?` matches one character.
- `*` matches zero or more characters within one segment.
- `**` matches zero or more whole segments.
- `{name}` captures part of a segment; `{name:regex}` captures it only when the regular expression matches.

## Example

```dart
import 'package:spring_core_dart/spring_core_dart.dart';

const log = SpringLog('neon.static');

final matcher = AntPathMatcher();

// Which requested files are stylesheets.
bool isStylesheet(String path) => matcher.match('/static/**/*.css', path);

void main() {
  log.info(isStylesheet('/static/css/app.css')); // true

  // Pull the variable out of a path.
  final vars = matcher.extractUriTemplateVariables(
    '/users/{id:[0-9]+}',
    '/users/42',
  );
  log.info(vars['id']); // 42

  // Pick the most specific of several matching patterns.
  final patterns = ['/**', '/hotels/*', '/hotels/{id}']
    ..sort(matcher.getPatternComparator('/hotels/1'));
  log.info(patterns.first); // /hotels/{id}

  // The same matcher works for dotted names.
  final dots = AntPathMatcher(pathSeparator: '.');
  log.info(dots.match('app.*.enabled', 'app.cache.enabled')); // true
}
```

## Parameters

Constructor parameters:

| Name | Type | Default | Meaning |
|---|---|---|---|
| `pathSeparator` | `String` | `'/'` | The text between segments. |
| `caseSensitive` | `bool` | `true` | Whether segments are compared case-sensitively. |
| `trimTokens` | `bool` | `false` | Whether whitespace around segments is ignored. |

Methods of `PathMatcher`:

| Name | Returns | Meaning |
|---|---|---|
| `isPattern(path)` | `bool` | Whether the text contains `*`, `?` or a `{name}` variable. |
| `match(pattern, path)` | `bool` | Whether the whole path matches. |
| `matchStart(pattern, path)` | `bool` | Whether the path matches the start of the pattern, so a longer path could match. |
| `extractPathWithinPattern(pattern, path)` | `String` | The part matched by the wildcards: `/docs/*` and `/docs/a/b.html` give `a/b.html`. |
| `extractUriTemplateVariables(pattern, path)` | `Map<String, String>` | The `{name}` values; throws `StateError` when the path does not match. |
| `getPatternComparator(path)` | `Comparator<String>` | Sorts patterns from most to least specific for the path. |
| `combine(pattern1, pattern2)` | `String` | Joins two patterns: `/hotels` and `{hotel}` give `/hotels/{hotel}`. |

## In Spring (Java)

```java
var matcher = new AntPathMatcher();
matcher.match("/static/**/*.css", "/static/css/app.css");
matcher.extractUriTemplateVariables("/users/{id:[0-9]+}", "/users/42");

var dots = new AntPathMatcher(".");
dots.match("app.*.enabled", "app.cache.enabled");
```

Differences that matter:

- Options are named constructor parameters instead of setters (`setCaseSensitive`, `setTrimTokens`).
- `{name:regex}` uses Dart's `RegExp` syntax. In a resource location, a `\` is cleaned to `/` (as Spring does), so write character classes such as `[0-9]` instead of `\d` there.

## See also

- [Resource](resource.md)
- [@PropertySource](../config/property-source.md)
- [Resources](index.md)
