compareTo method

int compareTo(
  1. Pattern other
)

Comparing two patterns for ordering them.

This ordering should be used when registering request handlers to a pipeline, since an incorrect order might cause some rules to be masked by other rules.

The rules for ordering are:

  • literals have priority over optionals;
  • optionals over variables; and
  • variables over wildcards.

If there are multiple patterns that could match the same path, the more specific pattern will have priority over the less specific.

For example, "~/foo/bar" is ordered before "~/foo/:a-variable". This is usually the desired order for rules: the path "/foo/bar" will match that first pattern and the path "/foo/xyz" will match the second pattern. If the order was reversed, the pattern with the variable segment will match both paths (i.e. the "~/foo/bar" pattern will be ignored if the handler for the other pattern processes the request).

A more practical example are the patterns "~/abc/def/" and "~/abc/def/:variable".

Note: the names of variables are significant when comparing them. For example, "~/:a" and "~/:b" will return a non-zero value, but matchesSamePaths will return true.

Implementation

int compareTo(Pattern other) {
  var varNameOrder = 0;

  for (var x = 0; x < _segments.length; x++) {
    if (other._segments.length <= x) {
      // No corresponding segment in the other pattern
      return -1; // the longer pattern (this) has priority
    }

    final seg1 = _segments[x];
    final seg2 = other._segments[x];

    if (wildcard == seg1) {
      if (wildcard == seg2) {
        // both wildcards
      } else if (_isVariable(seg2)) {
        return 1; // wildcard <=> variable
      } else if (_isOptional(seg2)) {
        return 1; // wildcard <=> optional
      } else {
        return 1; // wildcard <=> literal
      }
    } else if (_isVariable(seg1)) {
      if (wildcard == seg2) {
        return -1; // variable <=> wildcard
      } else if (_isVariable(seg2)) {
        // both variables
        // These are not yet significant
        if (varNameOrder == 0) {
          varNameOrder = _variableName(seg1).compareTo(_variableName(seg2));
        }
      } else if (_isOptional(seg2)) {
        return 1; // variable <=> optional
      } else {
        return 1; // variable <=> literal
      }
    } else if (_isOptional(seg1)) {
      if (wildcard == seg2) {
        return -1; // optional <=> wildcard
      } else if (_isVariable(seg2)) {
        return -1; // optional <=> variable
      } else if (_isOptional(seg2)) {
        final cmp = _optionalName(seg1).compareTo(_optionalName(seg2));
        if (cmp != 0) {
          return cmp;
        }
      } else {
        return 1; // optional <=> literal
      }
    } else {
      if (wildcard == seg2) {
        return -1; // literal <=> wildcard
      } else if (_isVariable(seg2)) {
        return -1; // literal <=> variable
      } else if (_isOptional(seg2)) {
        return -1; // literal <=> optional
      } else {
        final cmp = seg1.compareTo(seg2); // both literal
        if (cmp != 0) {
          return cmp;
        }
      }
    }
  }

  // At this point, all the segments in this pattern are the same as in the
  // other pattern.

  if (_segments.length == other._segments.length) {
    // Both patterns are semantically the same, so consider the syntax (i.e.
    // the variable names) to determine the order.
    return varNameOrder;
  } else {
    // The other pattern has more segments
    return 1; // the longer pattern (other) has priority
  }
}