Skip to content

Commit

Permalink
Added matchers to check the visibility (public/private) of various re…
Browse files Browse the repository at this point in the history
…flective elements.

This is helpful, for example, when enforcing the scope of a public-facing API with a test,
and provides stronger documentation for the future than mere comments.
  • Loading branch information
jbrown committed Apr 12, 2020
1 parent 5dd6a62 commit ce29997
Show file tree
Hide file tree
Showing 11 changed files with 801 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.hamcrest.visibility;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;

import java.lang.reflect.Member;

abstract class AbstractVisibilityMatcher<T> extends BaseMatcher<T>
{
/**
* Should only ever be one of the values returned from
* {@link VisibilityUtils#describeVisibility(Member)} and {@link VisibilityUtils#describeVisibility(Class)}
**/
private final String expectedVisibility;

AbstractVisibilityMatcher(String expectedVisibility)
{
this.expectedVisibility = expectedVisibility;
}

@Override public void describeTo(Description description)
{
description.appendText("is ").appendText(expectedVisibility);
}

@Override public void describeMismatch(Object item, Description description)
{
if (item == null)
{
description.appendText("was null");
}
else if (item instanceof Class)
{
description.appendText("was a ")
.appendText(VisibilityUtils.describeVisibility((Class<?>) item))
.appendText(" class");
}
else if (item instanceof Member)
{
description.appendText("was a ")
.appendText(VisibilityUtils.describeVisibility((Member) item))
.appendText(" ")
.appendText(item.getClass().getName());
}
else
{
description.appendText("was "+item.getClass().getName()+" instead of a reflective element like a Class<T>, Constructor<T>, or Method");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package org.hamcrest.visibility;

import org.hamcrest.Matcher;

import java.lang.reflect.Member;

public class IsPackageProtected<T> extends AbstractVisibilityMatcher<T>
{
public IsPackageProtected()
{
super(VisibilityUtils.PACKAGE_PROTECTED_DESCRIPTION);
}

@Override
public boolean matches(Object actual)
{
if (actual == null)
{
return false;
}
if (actual instanceof Class)
{
return VisibilityUtils.isPackageProtected((Class<?>) actual);
}
if (actual instanceof Member)
{
return VisibilityUtils.isPackageProtected((Member) actual);
}
return false;
}

public static <T> Matcher<T> isPackageProtected()
{
return new IsPackageProtected<>();
}
}
35 changes: 35 additions & 0 deletions hamcrest/src/main/java/org/hamcrest/visibility/IsPrivate.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.hamcrest.visibility;

import org.hamcrest.Matcher;

import java.lang.reflect.Member;

public class IsPrivate<T> extends AbstractVisibilityMatcher<T>
{
public IsPrivate(){
super(VisibilityUtils.PRIVATE_DESCRIPTION);
}

@Override
public boolean matches(Object actual)
{
if (actual == null)
{
return false;
}
if (actual instanceof Class)
{
return VisibilityUtils.isPrivate((Class<?>) actual);
}
if (actual instanceof Member)
{
return VisibilityUtils.isPrivate((Member) actual);
}
return false;
}

public static <T> Matcher<T> isPrivate()
{
return new IsPrivate<>();
}
}
35 changes: 35 additions & 0 deletions hamcrest/src/main/java/org/hamcrest/visibility/IsProtected.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.hamcrest.visibility;

import org.hamcrest.Matcher;

import java.lang.reflect.Member;

public class IsProtected<T> extends AbstractVisibilityMatcher<T>
{
public IsProtected(){
super(VisibilityUtils.PROTECTED_DESCRIPTION);
}

@Override
public boolean matches(Object actual)
{
if (actual == null)
{
return false;
}
if (actual instanceof Class)
{
return VisibilityUtils.isProtected((Class<?>) actual);
}
if (actual instanceof Member)
{
return VisibilityUtils.isProtected((Member) actual);
}
return false;
}

public static <T> Matcher<T> isProtected()
{
return new IsProtected<>();
}
}
35 changes: 35 additions & 0 deletions hamcrest/src/main/java/org/hamcrest/visibility/IsPublic.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.hamcrest.visibility;

import org.hamcrest.Matcher;

import java.lang.reflect.Member;

public class IsPublic<T> extends AbstractVisibilityMatcher<T>
{
public IsPublic(){
super(VisibilityUtils.PUBLIC_DESCRIPTION);
}

@Override
public boolean matches(Object actual)
{
if (actual == null)
{
return false;
}
if (actual instanceof Class)
{
return VisibilityUtils.isPublic((Class<?>) actual);
}
if (actual instanceof Member)
{
return VisibilityUtils.isPublic((Member) actual);
}
return false;
}

public static <T> Matcher<T> isPublic()
{
return new IsPublic<>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package org.hamcrest.visibility;

import java.lang.reflect.Member;
import java.lang.reflect.Modifier;

class VisibilityUtils
{

static final String PUBLIC_DESCRIPTION = "public";
static final String PROTECTED_DESCRIPTION = "protected";
static final String PACKAGE_PROTECTED_DESCRIPTION = "package-protected (no modifiers)";
static final String PRIVATE_DESCRIPTION = "private";

static boolean isPublic(Class<?> clazz)
{
return clazz != null && Modifier.isPublic(clazz.getModifiers());
}

static boolean isProtected(Class<?> clazz)
{
return clazz != null && Modifier.isProtected(clazz.getModifiers());
}

static boolean isPackageProtected(Class<?> clazz)
{
return clazz != null && !isPublic(clazz) && !isProtected(clazz) && !isPrivate(clazz);
}

static boolean isPrivate(Class<?> clazz)
{
return clazz != null && Modifier.isPrivate(clazz.getModifiers());
}


static boolean isPublic(Member member)
{
return member != null && Modifier.isPublic(member.getModifiers());
}

static boolean isProtected(Member member)
{
return member != null && Modifier.isProtected(member.getModifiers());
}

static boolean isPackageProtected(Member member)
{
return member != null && !isPublic(member) && !isProtected(member) && !isPrivate(member);
}

static boolean isPrivate(Member member)
{
return member != null && Modifier.isPrivate(member.getModifiers());
}


static String describeVisibility(Class<?> clazz)
{
if (isPublic(clazz))
{
return PUBLIC_DESCRIPTION;
}
if (isProtected(clazz))
{
return PROTECTED_DESCRIPTION;
}
if (isPackageProtected(clazz))
{
return PACKAGE_PROTECTED_DESCRIPTION;
}
if (isPrivate(clazz))
{
return PRIVATE_DESCRIPTION;
}
throw new IllegalArgumentException("Encountered unexpected visibility! This should NEVER happen.");
}

static String describeVisibility(Member member)
{
if (isPublic(member))
{
return PUBLIC_DESCRIPTION;
}
if (isProtected(member))
{
return PROTECTED_DESCRIPTION;
}
if (isPackageProtected(member))
{
return PACKAGE_PROTECTED_DESCRIPTION;
}
if (isPrivate(member))
{
return PRIVATE_DESCRIPTION;
}
throw new IllegalArgumentException("Encountered unexpected visibility! This should NEVER happen.");
}
}
9 changes: 9 additions & 0 deletions hamcrest/src/main/java/org/hamcrest/visibility/package.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<p>Matchers that perform checks on the visibility of reflective elements, such as Class&lt;?&gt; objects</p>
</body>
</html>
Loading

0 comments on commit ce29997

Please sign in to comment.