refcodes-matcher: Build custom matching logic and match path patterns

README

The REFCODES.ORG codes represent a group of artifacts consolidating parts of my work in the past years. Several topics are covered which I consider useful for you, programmers, developers and software engineers.

What is this repository for?

This artifact allows the construction of matchers for building arbitrary complex matching functionality. It is used to test whether matchee instances match certain criteria. Matcher instances may get nested in a hierarchy (using e.g. the OrMatcher, the AndMatcher or the NotMatcher types), having different matchers in combination will do custom matching. Some examples for Matcher implementations are the EqualWithMatcher, the PathMatcher,the LessThanMatcher or the RegExpMatcher. Use the MatcherSugar syntactic sugar mixin for expressively building your matchers.

How do I get set up?

To get up and running, include the following dependency (without the three dots “…”) in your pom.xml:

1
2
3
4
5
6
7
8
9
<dependencies>
	...
	<dependency>
		<artifactId>refcodes-matcher</artifactId>
		<groupId>org.refcodes</groupId>
		<version>3.0.6</version>
	</dependency>
	...
</dependencies>

The artifact is hosted directly at Maven Central. Jump straight to the source codes at Bitbucket. Read the artifact’s javadoc at javadoc.io.

Snippets of interest

Match x₁ ≤ x ≤ x₂

The code snippet below test whether a number x is between the range x₁ ≤ x ≤ x₂ with x₁ = -10 (included) and x₂ = 10 (included):

1
2
3
4
5
6
7
8
import static org.refcodes.matcher.MatcherSugar.*;
import org.refcodes.matcher.*;
...
	Matcher<Integer> theMatcher = and( greaterOrEqualThan( -10 ), lessOrEqualThan( 10 ) ); // "-10 ≤ x ≤ 10
		for ( int i = -12; i <= 12; i++ ) {
			System.out.println( "-10 ≤ " + i + " ≤ 10 --> " + theMatcher.isMatching( i ) );
	}
...

Note that we combine a GreaterOrEqualThanMatcher instance and a LessOrEqualThanMatcher instance within a AndMatcher instance (further nesting is possible, only limited by your ideas).

-10 ≤ -12 ≤ 10 --> false
-10 ≤ -11 ≤ 10 --> false
-10 ≤ -10 ≤ 10 --> true
-10 ≤ -9 ≤ 10 --> true
-10 ≤ -8 ≤ 10 --> true
-10 ≤ -7 ≤ 10 --> true
-10 ≤ -6 ≤ 10 --> true
-10 ≤ -5 ≤ 10 --> true
-10 ≤ -4 ≤ 10 --> true
-10 ≤ -3 ≤ 10 --> true
-10 ≤ -2 ≤ 10 --> true
-10 ≤ -1 ≤ 10 --> true
-10 ≤ 0 ≤ 10 --> true
-10 ≤ 1 ≤ 10 --> true
-10 ≤ 2 ≤ 10 --> true
-10 ≤ 3 ≤ 10 --> true
-10 ≤ 4 ≤ 10 --> true
-10 ≤ 5 ≤ 10 --> true
-10 ≤ 6 ≤ 10 --> true
-10 ≤ 7 ≤ 10 --> true
-10 ≤ 8 ≤ 10 --> true
-10 ≤ 9 ≤ 10 --> true
-10 ≤ 10 ≤ 10 --> true
-10 ≤ 11 ≤ 10 --> false
-10 ≤ 12 ≤ 10 --> false

Match x₁ < x < x₂

The code snippet below test whether a number x is between (excluding) the range x₁ ≤ x ≤ x₂ with x₁ = -10 (excluded) and x₂ = 10 (excluded):

1
2
3
4
5
6
7
8
import static org.refcodes.matcher.MatcherSugar.*;
import org.refcodes.matcher.*;
...
	Matcher<Integer> theMatcher = and( greaterThan( -10 ), lessThan( 10 ) ); // -10 < x < 10
		for ( int i = -12; i <= 12; i++ ) {
			System.out.println( "-10 ≤ " + i + " ≤ 10 --> " + theMatcher.isMatching( i ) );
	}
...

Note that we combine a GreaterOrEqualThanMatcher instance and a LessThanMatcher instance within a AndMatcher instance (further nesting is possible, only limited by your ideas).

-10 < -12 < 10 --> false
-10 < -11 < 10 --> false
-10 < -10 < 10 --> false
-10 < -9 < 10 --> true
-10 < -8 < 10 --> true
-10 < -7 < 10 --> true
-10 < -6 < 10 --> true
-10 < -5 < 10 --> true
-10 < -4 < 10 --> true
-10 < -3 < 10 --> true
-10 < -2 < 10 --> true
-10 < -1 < 10 --> true
-10 < 0 < 10 --> true
-10 < 1 < 10 --> true
-10 < 2 < 10 --> true
-10 < 3 < 10 --> true
-10 < 4 < 10 --> true
-10 < 5 < 10 --> true
-10 < 6 < 10 --> true
-10 < 7 < 10 --> true
-10 < 8 < 10 --> true
-10 < 9 < 10 --> true
-10 < 10 < 10 --> false
-10 < 11 < 10 --> false
-10 < 12 < 10 --> false

Match x ≤ x₁ or x ≥ x₂

The code snippet below test whether a number x is outside the range x₁ ≤ x ≤ x₂ with x₁ = -10 (included) and x₂ = 10 (included):

1
2
3
4
5
6
7
8
import static org.refcodes.matcher.MatcherSugar.*;
import org.refcodes.matcher.*;
...
	Matcher<Integer> theMatcher = or( lessOrEqualThan( -10 ), greaterOrEqualThan( 10 ) ); // -10 ≰ x ≰ 10
		for ( int i = -12; i <= 12; i++ ) {
			System.out.println( "-10 ≤ " + i + " ≤ 10 --> " + theMatcher.isMatching( i ) );
	}
...

Note that we combine a GreaterOrEqualThanMatcher instance and a LessOrEqualThanMatcher instance within a OrMatcher instance (further nesting is possible, only limited by your ideas).

-10 ≰ -12 ≰ 10 --> true
-10 ≰ -11 ≰ 10 --> true
-10 ≰ -10 ≰ 10 --> true
-10 ≰ -9 ≰ 10 --> false
-10 ≰ -8 ≰ 10 --> false
-10 ≰ -7 ≰ 10 --> false
-10 ≰ -6 ≰ 10 --> false
-10 ≰ -5 ≰ 10 --> false
-10 ≰ -4 ≰ 10 --> false
-10 ≰ -3 ≰ 10 --> false
-10 ≰ -2 ≰ 10 --> false
-10 ≰ -1 ≰ 10 --> false
-10 ≰ 0 ≰ 10 --> false
-10 ≰ 1 ≰ 10 --> false
-10 ≰ 2 ≰ 10 --> false
-10 ≰ 3 ≰ 10 --> false
-10 ≰ 4 ≰ 10 --> false
-10 ≰ 5 ≰ 10 --> false
-10 ≰ 6 ≰ 10 --> false
-10 ≰ 7 ≰ 10 --> false
-10 ≰ 8 ≰ 10 --> false
-10 ≰ 9 ≰ 10 --> false
-10 ≰ 10 ≰ 10 --> true
-10 ≰ 11 ≰ 10 --> true
-10 ≰ 12 ≰ 10 --> true

Match x < x₁ or x > x₂

The code snippet below test whether a number x is outside the range x₁ < x < x₂ with x₁ = -10 (excluded) and x₂ = 10 (excluded):

1
2
3
4
5
6
7
8
import static org.refcodes.matcher.MatcherSugar.*;
import org.refcodes.matcher.*;
...
	Matcher<Integer> theMatcher = or( lessThan( -10 ), greaterThan( 10 ) ); // -10 ≮ x ≮ 10
		for ( int i = -12; i <= 12; i++ ) {
			System.out.println( "-10 ≤ " + i + " ≤ 10 --> " + theMatcher.isMatching( i ) );
	}
...

Note that we combine a GreaterThanMatcher instance and a LessThanMatcher instance within a OrMatcher instance (further nesting is possible, only limited by your ideas).

-10 ≮ -12 ≮ 10 --> true
-10 ≮ -11 ≮ 10 --> true
-10 ≮ -10 ≮ 10 --> false
-10 ≮ -9 ≮ 10 --> false
-10 ≮ -8 ≮ 10 --> false
-10 ≮ -7 ≮ 10 --> false
-10 ≮ -6 ≮ 10 --> false
-10 ≮ -5 ≮ 10 --> false
-10 ≮ -4 ≮ 10 --> false
-10 ≮ -3 ≮ 10 --> false
-10 ≮ -2 ≮ 10 --> false
-10 ≮ -1 ≮ 10 --> false
-10 ≮ 0 ≮ 10 --> false
-10 ≮ 1 ≮ 10 --> false
-10 ≮ 2 ≮ 10 --> false
-10 ≮ 3 ≮ 10 --> false
-10 ≮ 4 ≮ 10 --> false
-10 ≮ 5 ≮ 10 --> false
-10 ≮ 6 ≮ 10 --> false
-10 ≮ 7 ≮ 10 --> false
-10 ≮ 8 ≮ 10 --> false
-10 ≮ 9 ≮ 10 --> false
-10 ≮ 10 ≮ 10 --> false
-10 ≮ 11 ≮ 10 --> true
-10 ≮ 12 ≮ 10 --> true

Match a path with * and **

The code snippet below test whether path matches the pattern “/*/acme/**” where * is a wildcard for one path element and ** is a wildcard for multiple path elements:

1
2
3
4
5
6
7
8
9
10
11
import static org.refcodes.matcher.MatcherSugar.*;
import org.refcodes.matcher.*;
...
		Matcher<String> theMatcher = new PathMatcher( "/*/acme/**" );
		String thePath = "/foo/acme/atari/bar";
		System.out.println( "\"" + thePath + "\" -?-> \"/*/acme/**\" --> " + theMatcher.isMatching( thePath ) );
		thePath = "/bar/acme/commodore/bar";
		System.out.println( "\"" + thePath + "\" -?-> \"/*/acme/**\" --> " + theMatcher.isMatching( thePath ) );
		thePath = "/foo/evil/bad/bar";
		System.out.println( "\"" + thePath + "\" -?-> \"/*/acme/**\" --> " + theMatcher.isMatching( thePath ) );
...

Note that we directly use a PathMatcher instance (further nesting is possible, only limited by your ideas).

"/foo/acme/atari/bar" -?-> "/*/acme/**" --> true
"/bar/acme/commodore/bar" -?-> "/*/acme/**" --> true
"/foo/evil/bad/bar" -?-> "/*/acme/**" --> false

Get path variables with ${...}

The code snippet below matches the pattern “/acme/**/${tail}” against a path where ${tail} is a path variable which we want to determine:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import static org.refcodes.matcher.MatcherSugar.*;
import org.refcodes.matcher.*;
...
		PathMatcher theMatcher = new PathMatcher( "/acme/**/${tail}" );
		String thePath = "/acme/foo/atari";
		String theVar = theMatcher.toWildcardReplacement( thePath, "tail" );
		System.out.println( "\"" + thePath + "\" -?-> \"/acme/**/${tail}\" --> " + theVar );
		thePath = "/acme/foo/commodore";
		theVar = theMatcher.toWildcardReplacement( thePath, "tail" );
		System.out.println( "\"" + thePath + "\" -?-> \"/acme/**/${tail}\" --> " + theVar );
		thePath = "/acme/foo/what/ever/path/sinclair";
		theVar = theMatcher.toWildcardReplacement( thePath, "tail" );
		System.out.println( "\"" + thePath + "\" -?-> \"/acme/**/${tail}\" --> " + theVar );
		thePath = "/foo/what/ever/path/ghost";
		theVar = theMatcher.toWildcardReplacement( thePath, "tail" );
		System.out.println( "\"" + thePath + "\" -?-> \"/acme/**/${tail}\" --> " + theVar );
...

Note that the path variable ${tail} used here can be of any name (such as ${head}, ${var}, ${...}) and you may use multiple path variables and the path variables can be placed anywhere in the path pattern as path element.

"/acme/foo/atari" -?-> "/acme/**/${tail}" --> atari
"/acme/foo/commodore" -?-> "/acme/**/${tail}" --> commodore
"/acme/foo/what/ever/path/sinclair" -?-> "/acme/**/${tail}" --> sinclair
"/foo/what/ever/path/ghost" -?-> "/acme/**/${tail}" --> null

Examples

See the example source codes of this artifact for further information on the usage of this artifact.

Contribution guidelines

  • Report issues
  • Finding bugs
  • Helping fixing bugs
  • Making code and documentation better
  • Enhance the code

Who do I talk to?

  • Siegfried Steiner (steiner@refcodes.org)

Terms and conditions

The REFCODES.ORG group of artifacts is published under some open source licenses; covered by the refcodes-licensing (org.refcodes group) artifact - evident in each artifact in question as of the pom.xml dependency included in such artifact.