Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simple reflection tool #8

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions src/main/java/org/fest/reflect/simple/SimpleInjector.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package org.fest.reflect.simple;

import static java.text.MessageFormat.format;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
* Provides simple injection capabilities when needed in tests. To be used by {@link SimpleReflection}.
*
* @author Marek Dominiak
*/
public class SimpleInjector {
private final Object valueToSet;

static SimpleInjector set(Object value) {
return new SimpleInjector(value);
}

private SimpleInjector(Object value) {
this.valueToSet = value;
}

/**
* Sets {@link #valueToSet} into target object if it is possible.
* Rules for setting:
* <ul>
* <li>there must be EXACTLY ONE field in "target" object which is of type which is assignable from "valueToSet" type</li>
* </ul>
* @param target object into which we try to inject
* @throws IllegalStateException when there are none or more than one fields of "source" object in "target" type.
*/
public void in(Object target) {
Class<? extends Object> targetClass = target.getClass();
Field[] fields = targetClass.getDeclaredFields();
List<Field> matchingFields = getAllFieldsAssignableFrom(fields);
checkThatNumberOfFieldsOfValueTypeIsValid(targetClass, matchingFields);
Field fieldToInjectTo = matchingFields.get(0);
setValueOnField(fieldToInjectTo, target);
}

private List<Field> getAllFieldsAssignableFrom(Field[] fields) {
List<Field> result = new ArrayList<Field>();
for (Field field : fields) {
if (field.getType().isAssignableFrom(valueToSet.getClass())) {
result.add(field);
}
}
return result;
}

private void checkThatNumberOfFieldsOfValueTypeIsValid(Class<? extends Object> targetClass,
List<Field> matchingFields) {
if (matchingFields.isEmpty()) {
String errorMessage = format("There must be exactly ONE field of type {0} (or assignable from {0}) in target class {1}!\nBut found none.", valueToSet.getClass().getName(), targetClass.getName());
throw new IllegalStateException(errorMessage);
}

if (matchingFields.size() > 1) {
String errorMessageListOfFields = "" + matchingFields.size() + ":\n";
for (Field field : matchingFields) {
errorMessageListOfFields += field.getName() + "\n";
}
String errorMessage = format("There must be exactly ONE field of type {0} (or assignable from {0}) in target class {1}!\nBut found {2}", valueToSet.getClass().getName(), targetClass.getName(), errorMessageListOfFields);
throw new IllegalStateException(errorMessage);
}
}

private void setValueOnField(Field found, Object target) {
found.setAccessible(true);
try {
found.set(target, valueToSet);
} catch (final IllegalAccessException ex) {
throw new IllegalStateException("Unexpected reflection exception - " + ex.getClass().getName() + ": "
+ ex.getMessage());
}
}
}
58 changes: 58 additions & 0 deletions src/main/java/org/fest/reflect/simple/SimpleReflection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Created on Oct 13, 2012
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS"
* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
* Copyright @2006-2012 the original author or authors.
*/
package org.fest.reflect.simple;

import org.fest.reflect.core.Reflection;

/**
* Provides a &quot;fluent&quot; API that makes usage of the Java Reflection API
* to help achieve some common case scenarios when setting/retrieving state of
* the objects in tests. If some more advanced scenarios are needed use {@link Reflection} instead.
* <p>
* Here are some examples:
* <pre>
* // import static {@link org.fest.reflect.simple.SimpleReflection org.fest.reflect.simple.SimpleReflection}.*;
*
* // Retrieves the value of the field &quot;logger&quot; from object userService
* UserService userService = ...
* Logger logger = SimpleReflection.get(Logger.class).from(userService);
*
* // Sets the value of the field 'logger' to &quot;Yoda&quot;
* UserService userService = ...
* Logger logger = ...
* Logger logger = SimpleReflection.set(logger).to(userService);
* </pre>
*
* @author Marek Dominiak
*/
public class SimpleReflection {
/**
* Begins the setting/injecting of the valueToSet to some object.
* @param valueToSet
* @return fluent API interface to set/inject valueToSet to some object.
*/
public static SimpleInjector set(Object valueToSet) {
return SimpleInjector.set(valueToSet);
}

/**
* Begins the getting/retrieving of the field value of the provided type from some object.
* @param fieldValueType
* @return fluent API interface to get/retrieve field value of the provided type from some object.
*/
public static <T> SimpleRetriever<T> get(Class<T> fieldValueType) {
return new SimpleRetriever<T>(fieldValueType);
}
}
71 changes: 71 additions & 0 deletions src/main/java/org/fest/reflect/simple/SimpleRetriever.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.fest.reflect.simple;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
* Provides simple retrieving capabilities when needed in tests. To be used by {@link SimpleReflection}.
*
* @author Marek Dominiak
*/
public class SimpleRetriever<T> {

private final Class<T> typeToRetrieve;

public SimpleRetriever(Class<T> typeToRetrieve) {
this.typeToRetrieve = typeToRetrieve;
}

/**
* Retrieves the value of field of type {@link #typeToRetrieve} from the "source" object.
*
* Rules for retrieving:
* <ul>
* <li>there must be EXACTLY ONE field in "source" object which is of type which is assignable from {@link #typeToRetrieve} type</li>
* </ul>
* @param source object from which we are retrieving the value
* @throws IllegalStateException when there are none or more than one fields of {@link #typeToRetrieve} type in "source" object type.
*/
public T from(Object source) {
Field[] declaredFields = source.getClass().getDeclaredFields();
List<Field> allPossibleFields = getAllFieldsAssignableFrom(declaredFields);
checkThatThereIsOnlyOneFieldWithCompatibleType(source, allPossibleFields);
return retrieveFieldValueFromBean(source, allPossibleFields.get(0));
}

private void checkThatThereIsOnlyOneFieldWithCompatibleType(Object source, List<Field> allPossibleFields) {
if (allPossibleFields.size() > 1) {
throw new IllegalStateException("Can't retrieve value of type: " + typeToRetrieve.getName()
+ " because there are more fields of this type in source bean (" + source.getClass().getName()
+ ")");
}
if (allPossibleFields.size() == 0) {
throw new IllegalStateException("Can't retrieve value of type: " + typeToRetrieve.getName()
+ " because there none fields of this type (or compatible type) in source bean ("
+ source.getClass().getName() + ")");
}
}

@SuppressWarnings("unchecked")
private T retrieveFieldValueFromBean(Object source, Field field) {
field.setAccessible(true);
Object object = null;
try {
object = field.get(source);
} catch (Exception e) {
throw new IllegalStateException("Cannot retrieve value " + e.getMessage());
}
return (T) object;
}

private List<Field> getAllFieldsAssignableFrom(Field[] fields) {
List<Field> result = new ArrayList<Field>();
for (Field field : fields) {
if (field.getType().isAssignableFrom(typeToRetrieve)) {
result.add(field);
}
}
return result;
}
}
40 changes: 40 additions & 0 deletions src/main/java/org/fest/reflect/simple/package.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<!--
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing permissions and limitations under
the License.

Copyright @2007-2012 the original author or authors.
-->
</head>
<body bgcolor="white">
<p>
Provides a &quot;fluent&quot; API that
makes usage of the Java Reflection API to help achieve some common case scenarios when setting/retrieving state of the objects in tests.
</p>
<p>
Here are some examples:
<pre>
// import static org.fest.reflect.simple.SimpleReflection.*;

// Retrieves the value of the field "logger" from object userService
UserService userService = ...
Logger logger = SimpleReflection.get(Logger.class).from(userService);

// Sets the value of the field 'logger' to "Yoda"
UserService userService = ...
Logger logger = ...
Logger logger = SimpleReflection.set(logger).to(userService);
</pre>
</p>
</body>
</html>
Loading