compareTo method

int compareTo(
  1. Pattern other
)

Comparing two patterns for ordering them.

This is used for sorting Handles annotations, to define the order of the rules that are automatically registered with a pipeline (if the priority of the registrations are the same).

For ordering, 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
  }
}