I have a working annotation processor that gathers information of the annotated classes. Everything is there during compilation. But I would like to have access to those results during runtime.
#SupportedSourceVersion(SourceVersion.RELEASE_8)
#AutoService(Processor.class)
public class TestProcessor extends AbstractProcessor {
private final static List<TestInfo> tests = new ArrayList<>();
#Override
public Set getSupportedAnnotationTypes() {
return new LinkedHashSet() {
{
add(Annotation.class.getCanonicalName());
}
};
}
#Override
public boolean process(final Set<? extends TypeElement> annotations,
final RoundEnvironment env) {
System.out.println("Processing!");
if (!env.processingOver()) {
Set<? extends Element> rootE = env.getRootElements();
for (Element e : rootE) {
if (e.getKind() == ElementKind.CLASS) {
TestInfo t = new TestInfo(e.asType().toString());
for (Element se : e.getEnclosedElements()) {
if (se.getKind() == ElementKind.METHOD) {
t.addMethod(se.getSimpleName().toString());
}
}
getTests().add(t);
}
}
getTests().forEach(ti -> {
System.out.println(ti);
});
}
return false;
}
public static TypeElement findEnclosingTypeElement(Element e) {
while (e != null && !(e instanceof TypeElement)) {
e = e.getEnclosingElement();
}
return TypeElement.class.cast(e);
}
/**
* #return the tests
*/
public static List<TestInfo> getTests() {
return tests;
}
}
Is there a way to retrieve the results at runtime? TestProcessor.getTests returns an empty list.
Here's the TestInfo class fyi:
public class TestInfo {
private final String name;
private final List<String> methods = new ArrayList<>();
public TestInfo(String name) {
this.name = name;
}
public void addMethod(String m) {
getMethods().add(m);
}
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #return the methods
*/
public List<String> getMethods() {
return methods;
}
#Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(name).append(methods.toString());
return sb.toString();
}
}
Update: The annotation is marked with retention runtime.
The annotation-processing is in compile time. So you can't get the information in the runtime.
A direct way is to write the information as a resource file in compile time and read it at runtime.
Here is my example:
The annotation:
#Retention(SOURCE)
#Target(TYPE)
public #interface Anno {
}
The processor:
#Override
public boolean processActual(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (roundEnv.processingOver()) {
return false;
}
try {
write(roundEnv);
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
private void write(RoundEnvironment roundEnv) throws IOException, UnsupportedEncodingException {
Filer filer = processingEnv.getFiler();
FileObject resource = filer.createResource(StandardLocation.CLASS_OUTPUT, "", "TestInfo");
OutputStream output = resource.openOutputStream();
PrintStream writer = new PrintStream(output, false, "UTF-8");
roundEnv.getElementsAnnotatedWith(Anno.class)
.stream()
.filter(e -> e.getKind() == ElementKind.CLASS)
.map(e -> e.asType().toString())
.forEach(writer::println);
writer.flush();
}
And the user code:
#Anno
public class Q48177784 {
public static final List<Class<?>> CLASSES;
static {
try {
URL resource = Q48177784.class.getClassLoader().getResource("TestInfo");
CLASSES = Files.readAllLines(Paths.get(resource.toURI()))
.stream()
.map(s -> {
try {
return Class.forName(s);
} catch (ClassNotFoundException e) {
throw new Error(e);
}
})
.collect(Collectors.toList());
} catch (Exception e) {
throw new Error(e);
}
}
public static void main(String[] args) {
System.out.println(CLASSES);
}
}
After build with processor, run the main method:
[class xdean.stackoverflow.Q48177784]
For your case, the only thing you should do is serialize/deserialize your TestInfo
Check out #RetentionPolicy. I think you want to set it to RUNTIME.
Related
I have a java jar file contain interface named IOperator like this :
public interface IOperator {
String execute(Map<String, String> parameters);
boolean isSuccess();
}
now in groovy script (in single file):
public class ClassA {
}
public class ClassB {
}
public class classC implements IOperator {
#Override
String execute(Map<String, String> parameters) {
ClassA classA = new ClassA();
ClassB classB = new ClassB();
return null
}
#Override
boolean isSuccess() {
return false
}
}
Is there any way scan this groovy script to find that specific class implemented IOperator and invoke execute method ?
Note: I want do it in Java code.
Finally i found answer :
public class OperatorManager {
public static void run(File scriptFile) {
try {
GroovyScriptEngineImpl engine = (GroovyScriptEngineImpl) new GroovyScriptEngineFactory().getScriptEngine();
GroovyClassLoader classLoader = new GroovyClassLoader();
classLoader.parseClass(new GroovyCodeSource(scriptFile));
engine.setClassLoader(classLoader);
Class<?>[] loadedClasses = engine.getClassLoader().getLoadedClasses();
Class<?> operatorImplClass = Arrays.stream(loadedClasses)
.filter(IOperator.class::isAssignableFrom)
.findFirst().orElse(null);
if (operatorImplClass != null) {
String result = invokeMethod(operatorImplClass);
System.out.println(result);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static String invokeMethod(Class<?> item) {
try {
Object instance = item.getConstructor().newInstance();
Method execute = item.getDeclaredMethod("execute", Map.class);
return (String) execute.invoke(instance, new Object[]{null});
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
I would like to generate a class from an interface.
My interface is:
#Annotation
interface Object {
String getName();
}
I would expect this:
public final class ObjectGenerated implements Object {
public String getName() {
return "my code";
}
}
The class processor is:
#AutoService(Processor.class)
#SupportedSourceVersion(SourceVersion.RELEASE_8)
public class Processor extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
for (Element element : roundEnvironment.getElementsAnnotatedWith(Annotation.class)) {
if (element.getKind() != ElementKind.INTERFACE) {
messager.printMessage(Diagnostic.Kind.ERROR, "Can be applied to class.");
return false;
}
generateClass(element);
}
return true;
}
private void generateClass(Element element) {
try {
String className = element.getSimpleName().toString();
String pack = processingEnv.getElementUtils().getPackageOf(element).toString();
String fileName = className + "Generated";
TypeSpec.Builder classBuilder = TypeSpec
.classBuilder(fileName)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addSuperinterface(ClassName.get(element.asType()));
for (Element elementMethod : element.getEnclosedElements()) {
MethodSpec intentMethod = MethodSpec
.methodBuilder(elementMethod.getSimpleName().toString())
.addModifiers(Modifier.PUBLIC)
.addStatement("return $S", "my code")
.returns(ClassName.get(elementMethod.asType())) //this generate error
.build();
classBuilder.addMethod(intentMethod);
}
JavaFile.builder(pack, classBuilder.build()).build().writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public Set<String> getSupportedAnnotationTypes() {
return ImmutableSet.of(Annotation.class.getCanonicalName());
}
#Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}
The error is:
java.lang.IllegalArgumentException: Unexpected type mirror: ()java.lang.String
It's possible that I did something wrong.
How can I get the "Type" from the interface in my case is String and use it in the MethodSpec.returns ?
Thanks.
Cast your Element to ExecutableElement and use its getReturnType()
In particular:
for (Element elementMethod : element.getEnclosedElements()) {
if (elementMethod.getKind() != ElementKind.METHOD) {
//skip non-method elements like final fields
continue;
}
ExecutableElement method = (ExecutableElement)elementMethod; //cast
MethodSpec intentMethod = MethodSpec
.methodBuilder(method.getSimpleName().toString())
.addModifiers(Modifier.PUBLIC)
.addStatement("return $S", "my code")
.returns(ClassName.get(method.getReturnType())) //should be ok now
.build();
classBuilder.addMethod(intentMethod);
}
I can use an extractor (Callback<E, Observable[]> extractor) to make a ListProperty fire change events if one of its elements changed one of its properties (update event).
Update Change Event in ObservableList
Is there an equivalent for ObjectProperty<>? I have an SimpleObjectProperty which I want to fire events when properties of it's value (another bean type) change (update change events).
Sample code:
public class TestBean {
public static <T extends TestBean> Callback<T, Observable[]> extractor() {
return (final T o) -> new Observable[] { o.testPropertyProperty() };
}
private final StringProperty testProperty = new SimpleStringProperty();
public final StringProperty testPropertyProperty() {
return this.testProperty;
}
public final String getTestProperty() {
return this.testPropertyProperty().get();
}
public final void setTestProperty(final String testProperty) {
this.testPropertyProperty().set(testProperty);
}
}
public class SomeType {
/**
* How can I listen for changes of TestBean#testProperty?
*/
private final ObjectProperty<TestBean> property = new SimpleObjectProperty<>();
}
I want to receive change events if the value of SomeType#property changes, but also, if SomeType#property#testProperty changes.
I cannot just listen for SomeType#property#testProperty, since I would not be notified when SomeType#property was changed (I would then listen on the wrong object for changes).
I want to receive change events if value of SomeType#property changes, but also, if SomeType#property#testProperty changes.
I cannot just listen for SomeType#property#testProperty, since I would not be notified, when SomeType#property was changed (I would then listen on the wrong object for changes).
This is a limitation of sorts of the current iteration of JavaFX. The built-in way is unreliable and you're better off using 3rd party libraries. See this answer for more information.
For you case, ReactFX can be utilized in a similar way:
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import org.reactfx.value.Val;
import org.reactfx.value.Var;
class TestBean {
private final StringProperty testProperty = new SimpleStringProperty();
public final StringProperty testPropertyProperty() { return testProperty; }
public final String getTestProperty() { return testProperty.get(); }
public final void setTestProperty(String newTestProperty) { testProperty.set(newTestProperty); }
}
public class SomeType {
private final ObjectProperty<TestBean> property = new SimpleObjectProperty<>();
public final ObjectProperty<TestBean> propertyProperty() { return property; }
public final TestBean getProperty() { return property.get(); }
public final void setProperty(TestBean newProperty) { property.set(newProperty); }
public static void main(String[] args) {
SomeType someType = new SomeType();
Var<String> chainedTestProperty = Val.selectVar(someType.propertyProperty(), TestBean::testPropertyProperty);
chainedTestProperty.addListener((obs, oldVal, newVal) -> System.out.println(obs + " " + oldVal + "->" + newVal));
//Tests
someType.setProperty(new TestBean());
someType.getProperty().setTestProperty("s1");
TestBean bean2 = new TestBean();
bean2.setTestProperty("s2");
someType.setProperty(bean2);
someType.setProperty(new TestBean());
}
}
Output:
org.reactfx.value.FlatMappedVar#7aec35a null->s1
org.reactfx.value.FlatMappedVar#7aec35a s1->s2
org.reactfx.value.FlatMappedVar#7aec35a s2->null
The key line
Var<String> chainedTestProperty = Val.selectVar(someType.propertyProperty(), TestBean::testPropertyProperty);
is a sort of listener chaining. The first argument is a property (OvservableValue) of some type Type. The second argument is the "sub"-property of some other type Type2 inside Type, which is given as a function from Type to that property.
Now whenever any "links" in the chain change, you are notified. You can continue to listen to changes in sub-sub-... properties by continuously chaining ovservables this way.
I came up with the following:
public class ObservableValueProperty<T> extends SimpleObjectProperty<T> {
private InvalidationListener listener = null;
private final Callback<T, Observable[]> extractor;
public ObservableValueProperty() {
this(null);
}
public ObservableValueProperty(final Callback<T, Observable[]> extractor) {
this.extractor = extractor;
}
#Override
protected void fireValueChangedEvent() {
super.fireValueChangedEvent();
}
#Override
public void setValue(final T v) {
if (extractor != null) {
final T oldValue = super.get();
if (oldValue != null) {
for (final Observable o : extractor.call(oldValue)) {
o.removeListener(listener);
}
}
listener = o -> fireValueChangedEvent();
for (final Observable o : extractor.call(v)) {
o.addListener(listener);
}
}
super.setValue(v);
}
}
public class ObservableValuePropertyTest4 implements ChangeListener<Object> {
#BeforeClass
public static void setUpBeforeClass() throws Exception {
}
#AfterClass
public static void tearDownAfterClass() throws Exception {
}
#Before
public void setUp() throws Exception {
}
#After
public void tearDown() throws Exception {
}
static class NestedBean {
StringProperty nestedProperty = new SimpleStringProperty("hans");
public static <T extends NestedBean> Callback<T, Observable[]> extractor() {
return (final T o) -> new Observable[] { o.nestedProperty };
}
#Override
public boolean equals(final Object obj) {
if (obj instanceof NestedBean) {
System.err.println(this.nestedProperty.get() + " " + ((NestedBean) obj).nestedProperty.get());
return Objects.equal(this.nestedProperty.get(), ((NestedBean) obj).nestedProperty.get());
}
return false;
}
}
private ObservableValueProperty<NestedBean> p;
private NestedBean nestedBean;
private String newNestedValue = null;
#Test
public void test01() {
p = new ObservableValueProperty<>(NestedBean.extractor());
nestedBean = new NestedBean();
p.setValue(nestedBean);
p.addListener(this);
nestedBean.nestedProperty.set("peter");
assertEquals("peter", newNestedValue);
}
#Override
public void changed(final ObservableValue<? extends Object> observable, final Object oldValue,
final Object newValue) {
System.err.println("Changed");
newNestedValue = nestedBean.nestedProperty.get();
}
}
Unfortunately, this does not fire any change events because of ExpressionHelper$SingleChange:
#Override
protected void fireValueChangedEvent() {
final T oldValue = currentValue;
currentValue = observable.getValue();
final boolean changed = (currentValue == null)? (oldValue != null) : !currentValue.equals(oldValue);
if (changed) {
try {
listener.changed(observable, oldValue, currentValue);
} catch (Exception e) {
Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e);
}
}
}
This checks for equality and only if not equal, notifies all listeners. When I trigger fireValueChangedEvent() the value has already changed, and new- and old values are equal, therefore no notification to listeners.
I had the same problem last week, and after many tries, I found a solution that seems to work as expected:
I created a new class called ObjectXProperty<E>, that has the same interface of an ObjectProperty<E>;
It has constructors that can accept a Callback<E,Observable[]>, our extractor function;
Inside the ObjectXProperty, I use a SimpleObjectProperty that deleguates all methods;
The magic trick lies in the set(E value) methods : I create an ObjectBinding that simply send back the value, but it uses the extractor function to decide when it's become invalidated!
This trick will not be applied if the bind method was used previously on the ObjectXProperty, to let the "real" binding do his job; it will work again if the unbind method is called;
Here's my new class ObjectXProperty<E> :
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.util.Callback;
/**
*
* #author Claude Bouchard - 2017
*/
public class ObjectXProperty<E> extends ObjectProperty<E> {
SimpleObjectProperty<E> p;
Callback<E, Observable[]> extractor;
boolean externalBound = false;
public ObjectXProperty(Callback<E, Observable[]> extractor) {
this.extractor = extractor;
}
public ObjectXProperty(E init, Callback<E, Observable[]> extractor) {
p = new SimpleObjectProperty();
this.extractor = extractor;
set(init);
}
public ObjectXProperty(Object bean, String name, Callback<E, Observable[]> extractor) {
p = new SimpleObjectProperty(bean, name);
this.extractor = extractor;
}
public ObjectXProperty(Object bean, String name, E init, Callback<E, Observable[]> extractor) {
p = new SimpleObjectProperty(bean, name);
this.extractor = extractor;
set(init);
}
#Override
public void set(E value) {
if (!externalBound) {
if (value != null) {
p.bind(Bindings.createObjectBinding(() -> {
return value;
}, extractor.call(value)));
} else {
p.bind(Bindings.createObjectBinding(() -> {
return value;
}, new Observable[]{}));
}
} else {
p.set(value); //As expected, it will throw a java.lang.RuntimeException
}
}
#Override
public E get() {
return p.get();
}
#Override
public void addListener(ChangeListener<? super E> listener) {
p.addListener(listener);
}
#Override
public void removeListener(ChangeListener<? super E> listener) {
p.removeListener(listener);
}
#Override
public void addListener(InvalidationListener listener) {
p.addListener(listener);
}
#Override
public void removeListener(InvalidationListener listener) {
p.removeListener(listener);
}
#Override
public Object getBean() {
return p.getBean();
}
#Override
public String getName() {
return p.getName();
}
#Override
public void bind(ObservableValue<? extends E> observable) {
p.bind(observable);
externalBound = true;
}
#Override
public void unbind() {
p.unbind();
externalBound = false;
set(get()); //to reactivate the extractor on the last value
}
#Override
public boolean isBound() {
return externalBound;
}
}
I think you need to add a listener to your object. This can be done simply. First of all you should write your class with a constructor and with getters this way:
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
public class SomeType {
public ObjectProperty<TestProperty> property;
public SomeType(TestProperty testProperty) {
this.property = new SimpleObjectProperty<>(testProperty);
}
public TestProperty getProperty() {
return property.get();
}
public ObjectProperty<TestProperty> propertyProperty() {
return property;
}
}
Then anywhere you have an instance of SomeType you can chain the properties, so you get the property the property's testProperty() and then simply add a listener to it.
someType.getProperty().testProperty().addListener((observable, oldValue, newValue) -> {
// Do whatever you want if the its value changed.
// You can also use its old or new value.
});
I often use this design in my code to maintain configurable values. Consider this code:
public enum Options {
REGEX_STRING("Some Regex"),
REGEX_PATTERN(Pattern.compile(REGEX_STRING.getString()), false),
THREAD_COUNT(2),
OPTIONS_PATH("options.config", false),
DEBUG(true),
ALWAYS_SAVE_OPTIONS(true),
THREAD_WAIT_MILLIS(1000);
Object value;
boolean saveValue = true;
private Options(Object value) {
this.value = value;
}
private Options(Object value, boolean saveValue) {
this.value = value;
this.saveValue = saveValue;
}
public void setValue(Object value) {
this.value = value;
}
public Object getValue() {
return value;
}
public String getString() {
return value.toString();
}
public boolean getBoolean() {
Boolean booleanValue = (value instanceof Boolean) ? (Boolean) value : null;
if (value == null) {
try {
booleanValue = Boolean.valueOf(value.toString());
}
catch (Throwable t) {
}
}
// We want a NullPointerException here
return booleanValue.booleanValue();
}
public int getInteger() {
Integer integerValue = (value instanceof Number) ? ((Number) value).intValue() : null;
if (integerValue == null) {
try {
integerValue = Integer.valueOf(value.toString());
}
catch (Throwable t) {
}
}
return integerValue.intValue();
}
public float getFloat() {
Float floatValue = (value instanceof Number) ? ((Number) value).floatValue() : null;
if (floatValue == null) {
try {
floatValue = Float.valueOf(value.toString());
}
catch (Throwable t) {
}
}
return floatValue.floatValue();
}
public static void saveToFile(String path) throws IOException {
FileWriter fw = new FileWriter(path);
Properties properties = new Properties();
for (Options option : Options.values()) {
if (option.saveValue) {
properties.setProperty(option.name(), option.getString());
}
}
if (DEBUG.getBoolean()) {
properties.list(System.out);
}
properties.store(fw, null);
}
public static void loadFromFile(String path) throws IOException {
FileReader fr = new FileReader(path);
Properties properties = new Properties();
properties.load(fr);
if (DEBUG.getBoolean()) {
properties.list(System.out);
}
Object value = null;
for (Options option : Options.values()) {
if (option.saveValue) {
Class<?> clazz = option.value.getClass();
try {
if (String.class.equals(clazz)) {
value = properties.getProperty(option.name());
}
else {
value = clazz.getConstructor(String.class).newInstance(properties.getProperty(option.name()));
}
}
catch (NoSuchMethodException ex) {
Debug.log(ex);
}
catch (InstantiationException ex) {
Debug.log(ex);
}
catch (IllegalAccessException ex) {
Debug.log(ex);
}
catch (IllegalArgumentException ex) {
Debug.log(ex);
}
catch (InvocationTargetException ex) {
Debug.log(ex);
}
if (value != null) {
option.setValue(value);
}
}
}
}
}
This way, I can save and retrieve values from files easily. The problem is that I don't want to repeat this code everywhere. Like as we know, enums can't be extended; so wherever I use this, I have to put all these methods there. I want only to declare the values and that if they should be persisted. No method definitions each time; any ideas?
Using an enum to hold configurable values like this looks like an entirely wrong design. Enums are singletons, so effectively you can only have one configuration active at any given time.
An EnumMap sounds more like what you need. It's external to the enum, so you can instantiate as many configurations as you need.
import java.util.*;
public class EnumMapExample {
static enum Options {
DEBUG, ALWAYS_SAVE, THREAD_COUNT;
}
public static void main(String[] args) {
Map<Options,Object> normalConfig = new EnumMap<Options,Object>(Options.class);
normalConfig.put(Options.DEBUG, false);
normalConfig.put(Options.THREAD_COUNT, 3);
System.out.println(normalConfig);
// prints "{DEBUG=false, THREAD_COUNT=3}"
Map<Options,Object> debugConfig = new EnumMap<Options,Object>(Options.class);
debugConfig.put(Options.DEBUG, true);
debugConfig.put(Options.THREAD_COUNT, 666);
System.out.println(debugConfig);
// prints "{DEBUG=true, THREAD_COUNT=666}"
}
}
API links
java.util.EnumMap
A specialized Map implementation for use with enum type keys. All of the keys in an enum map must come from a single enum type that is specified, explicitly or implicitly, when the map is created. Enum maps are represented internally as arrays. This representation is extremely compact and efficient.
i tried doing something similar with enum maps and properties files (please see code below). but my enums were simple and only had one value except for an embedded case. i may have something that is more type safe. i will look around for it.
package p;
import java.util.*;
import java.io.*;
public class GenericAttributes<T extends Enum<T>> {
public GenericAttributes(final Class<T> keyType) {
map = new EnumMap<T, Object>(this.keyType = keyType);
}
public GenericAttributes(final Class<T> keyType, final Properties properties) {
this(keyType);
addStringProperties(properties);
}
public Object get(final T key) {
// what does a null value mean?
// depends on P's semantics
return map.containsKey(key) ? map.get(key) : null;
}
public boolean contains(final T key) {
return map.containsKey(key);
}
public void change(final T key, final Object value) {
remove(key);
put(key, value);
}
public Object put(final T key, final Object value) {
if (map.containsKey(key))
throw new RuntimeException("map already contains: " + key);
else
return map.put(key, value);
}
public Object remove(final T key) {
if (!map.containsKey(key))
throw new RuntimeException("map does not contain: " + key);
return map.remove(key);
}
public String toString() {
return toString(defaultEquals, defaultEndOfLine);
}
// maybe we don;t need this stuff
// we have tests for it though
// it might be useful
public String toString(final String equals, final String endOfLine) {
final StringBuffer sb = new StringBuffer();
for (Map.Entry<T, Object> entry : map.entrySet())
sb.append(entry.getKey()).append(equals).append(entry.getValue()).append(endOfLine);
return sb.toString();
}
public Properties toProperties() {
final Properties p = new Properties();
for (Map.Entry<T, Object> entry : map.entrySet())
p.put(entry.getKey().toString(), entry.getValue().toString());
return p;
}
public void addStringProperties(final Properties properties) {
// keep this for strings, but mostly do work in the enum class
// i.e. static GenericAttributes<PA> fromProperties();
// which would use a fromString()
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
final String key = (String) entry.getKey();
final String value = (String) entry.getValue();
addProperty(key, value);
}
}
public void addProperty(final String key, final Object value) {
try {
final T e = Enum.valueOf(keyType, key);
map.put(e, value);
} catch (IllegalArgumentException e) {
System.err.println(key + " is not an enum from: " + keyType);
}
}
public int size() {
return map.size();
}
public static Properties load(final InputStream inputStream,final Properties defaultProperties) {
final Properties p=defaultProperties!=null?new Properties(defaultProperties):new Properties();
try {
p.load(inputStream);
} catch(IOException e) {
throw new RuntimeException(e);
}
return p;
}
public static Properties load(final File file,final Properties defaultProperties) {
Properties p=null;
try {
final InputStream is=new FileInputStream(file);
p=load(is,defaultProperties);
is.close();
} catch(IOException e) {
throw new RuntimeException(e);
}
return p;
}
public static void store(final OutputStream outputStream, final Properties properties) {
try {
properties.store(outputStream, null);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public static void store(final File file, final Properties properties) {
try {
final OutputStream os = new FileOutputStream(file);
store(os, properties);
os.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
final Class<T> keyType;
static final String defaultEquals = "=", defaultEndOfLine = "\n";
private final EnumMap<T, Object> map;
public static void main(String[] args) {
}
}
package p;
import static org.junit.Assert.*;
import org.junit.*;
import java.io.*;
import java.util.*;
enum A1 {
foo,bar,baz;
}
enum A2 {
x,y,z;
}
public class GenericAttributesTestCase {
#Test public void testGenericAttributes() {
new GenericAttributes<A1>(A1.class);
}
#Test public void testGenericAttributesKeyTypeProperties() {
final Properties expected=gA1.toProperties();
final GenericAttributes<A1> gA=new GenericAttributes<A1>(A1.class,expected);
final Properties actual=gA.toProperties();
assertEquals(expected,actual);
}
#Test public void testGet() {
final A1 key=A1.foo;
emptyGA1.put(key,null);
final Object actual=emptyGA1.get(key);
assertEquals(null,actual);
}
#Test public void testGetInteger() {
// attributes.add(key,integer);
// assertEquals(integer,attributes.get("key"));
}
#Test public void testContains() {
for(A1 a:A1.values())
assertFalse(emptyGA1.contains(a));
}
#Test public void testChange() {
final A1 key=A1.foo;
final Integer value=42;
emptyGA1.put(key,value);
final Integer expected=43;
emptyGA1.change(key,expected);
final Object actual=emptyGA1.get(key);
assertEquals(expected,actual);
}
#Test public void testAdd() {
final A1 key=A1.foo;
final Integer expected=42;
emptyGA1.put(key,expected);
final Object actual=emptyGA1.get(key);
assertEquals(expected,actual);
}
#Test public void testRemove() {
final A1 key=A1.foo;
final Integer value=42;
emptyGA1.put(key,value);
emptyGA1.remove(key);
assertFalse(emptyGA1.contains(key));
}
#Test public void testToString() {
final String actual=gA1.toString();
final String expected="foo=a foo value\nbar=a bar value\n";
assertEquals(expected,actual);
}
#Test public void testToStringEqualsEndOfLine() {
final String equals=",";
final String endOFLine=";";
final String actual=gA1.toString(equals,endOFLine);
final String expected="foo,a foo value;bar,a bar value;";
assertEquals(expected,actual);
}
#Test public void testEmbedded() {
final String equals=",";
final String endOfLine=";";
//System.out.println("toString(\""+equals+"\",\""+endOFLine+"\"):");
final String embedded=gA1.toString(equals,endOfLine);
GenericAttributes<A2> gA2=new GenericAttributes<A2>(A2.class);
gA2.put(A2.x,embedded);
//System.out.println("embedded:\n"+gA2);
// maybe do file={name=a.jpg;dx=1;zoom=.5}??
// no good, key must be used more than once
// so file:a.jpg={} and hack
// maybe file={name=...} will work
// since we have to treat it specially anyway?
// maybe this is better done in ss first
// to see how it grows?
}
#Test public void testFromString() {
// final Attributes a=Attributes.fromString("");
// final String expected="";
// assertEquals(expected,a.toString());
}
#Test public void testToProperties() {
final Properties expected=new Properties();
expected.setProperty("foo","a foo value");
expected.setProperty("bar","a bar value");
final Properties actual=gA1.toProperties();
assertEquals(expected,actual);
}
#Test public void testAddProperties() {
final Properties p=gA1.toProperties();
final GenericAttributes<A1> ga=new GenericAttributes<A1>(A1.class);
ga.addStringProperties(p);
// assertEquals(ga1,ga); // fails since we need to define equals!
// hack, go backwards
final Properties p2=ga.toProperties();
assertEquals(p,p2); // hack until we define equals
}
#Test public void testStore() throws Exception {
final Properties expected=gA1.toProperties();
final ByteArrayOutputStream baos=new ByteArrayOutputStream();
GenericAttributes.store(baos,expected);
baos.close();
final byte[] bytes=baos.toByteArray();
final ByteArrayInputStream bais=new ByteArrayInputStream(bytes);
final Properties actual=GenericAttributes.load(bais,null);
bais.close();
assertEquals(expected,actual);
}
#Test public void testLoad() throws Exception {
final Properties expected=gA1.toProperties();
final ByteArrayOutputStream baos=new ByteArrayOutputStream();
GenericAttributes.store(baos,expected);
baos.close();
final ByteArrayInputStream bais=new ByteArrayInputStream(baos.toByteArray());
final Properties actual=GenericAttributes.load(bais,null);
bais.close();
assertEquals(expected,actual);
}
#Test public void testMain() {
// fail("Not yet implemented");
}
GenericAttributes<A1> gA1=new GenericAttributes<A1>(A1.class);
{
gA1.put(A1.foo,"a foo value");
gA1.put(A1.bar,"a bar value");
}
GenericAttributes<A1> emptyGA1=new GenericAttributes<A1>(A1.class);
}
answering your comment:
seems like i am getting values by using the enum as the key. i am probably confused.
an enum can implement an interface and each set of enums could have an instance of that base class and delegate calls to it (see item 34 of http://java.sun.com/docs/books/effective/toc.html)
i found the other code that went with my generic attributes (please see below), but i can't find any tests for it and am not quite sure what i was doing other than perhaps to add some stronger typing.
my motivation for all of this was to store some attributes for a photo viewer like picasa, i wanted to store a bunch of attributes for a picture in a single line of a property file
package p;
import java.util.*;
public enum GA {
// like properties, seems like this wants to be constructed with a set of default values
i(Integer.class) {
Integer fromString(final String s) {
return new Integer(s);
}
Integer fromNull() {
return zero; // return empty string?
}
},
b(Boolean.class) {
Boolean fromString(final String s) {
return s.startsWith("t")?true:false;
}
Boolean fromNull() {
return false;
}
},
d(Double.class) {
Double fromString(final String s) {
return new Double(s);
}
Double fromNull() {
return new Double(zero);
}
};
GA() {
this(String.class);
}
GA(final Class clazz) {
this.clazz=clazz;
}
abstract Object fromString(String string);
abstract Object fromNull();
static GenericAttributes<GA> fromProperties(final Properties properties) {
final GenericAttributes<GA> pas=new GenericAttributes<GA>(GA.class);
for(Map.Entry<Object,Object> entry:properties.entrySet()) {
final String key=(String)entry.getKey();
final GA pa=valueOf(key);
if(pa!=null) {
final String stringValue=(String)entry.getValue();
Object value=pa.fromString(stringValue);
pas.addProperty(key,value);
} else throw new RuntimeException(key+"is not a member of "+"GA");
}
return pas;
}
// private final Object defaultValue; // lose type?; require cast?
/* private */final Class clazz;
static final Integer zero=new Integer(0);
}
If you are still looking for answers, you could give a try to Properties library which is open-source with MIT license. Using this, you won't have to specify string constants and everything will be determined by an enum defined by you. And, it has some other features too. Highlights of this library are:
All property keys are defined in a single place, i.e. a user defined enum
Property values can contain variables (starting with $ sign, e.g. $PATH) where PATH is a property key in same file
Property value can be obtained as specified data type, so no need to convert string value to required data type
Property value can be obtained as list of specified data types
Property value can be a multi-line text
Can make property keys mandatory or optional
Can specify default value for the property key if value is not available
Is thread safe
You can find sample programs here
When you run a JUnit 4 ParameterizedTest with the Eclipse TestRunner, the graphical representation is rather dumb: for each test you have a node called [0], [1], etc.
Is it possible give the tests [0], [1], etc. explicit names? Implementing a toString method for the tests does not seem to help.
(This is a follow-up question to JUnit test with dynamic number of tests.)
I think there's nothing built in in jUnit 4 to do this.
I've implemented a solution. I've built my own Parameterized class based on the existing one:
public class MyParameterized extends TestClassRunner {
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public static #interface Parameters {
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public static #interface Name {
}
public static Collection<Object[]> eachOne(Object... params) {
List<Object[]> results = new ArrayList<Object[]>();
for (Object param : params)
results.add(new Object[] { param });
return results;
}
// TODO: single-class this extension
private static class TestClassRunnerForParameters extends TestClassMethodsRunner {
private final Object[] fParameters;
private final Class<?> fTestClass;
private Object instance;
private final int fParameterSetNumber;
private final Constructor<?> fConstructor;
private TestClassRunnerForParameters(Class<?> klass, Object[] parameters, int i) throws Exception {
super(klass);
fTestClass = klass;
fParameters = parameters;
fParameterSetNumber = i;
fConstructor = getOnlyConstructor();
instance = fConstructor.newInstance(fParameters);
}
#Override
protected Object createTest() throws Exception {
return instance;
}
#Override
protected String getName() {
String name = null;
try {
Method m = getNameMethod();
if (m != null)
name = (String) m.invoke(instance);
} catch (Exception e) {
}
return String.format("[%s]", (name == null ? fParameterSetNumber : name));
}
#Override
protected String testName(final Method method) {
String name = null;
try {
Method m = getNameMethod();
if (m != null)
name = (String) m.invoke(instance);
} catch (Exception e) {
}
return String.format("%s[%s]", method.getName(), (name == null ? fParameterSetNumber : name));
}
private Constructor<?> getOnlyConstructor() {
Constructor<?>[] constructors = getTestClass().getConstructors();
assertEquals(1, constructors.length);
return constructors[0];
}
private Method getNameMethod() throws Exception {
for (Method each : fTestClass.getMethods()) {
if (Modifier.isPublic((each.getModifiers()))) {
Annotation[] annotations = each.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation.annotationType() == Name.class) {
if (each.getReturnType().equals(String.class))
return each;
else
throw new Exception("Name annotated method doesn't return an object of type String.");
}
}
}
}
return null;
}
}
// TODO: I think this now eagerly reads parameters, which was never the
// point.
public static class RunAllParameterMethods extends CompositeRunner {
private final Class<?> fKlass;
public RunAllParameterMethods(Class<?> klass) throws Exception {
super(klass.getName());
fKlass = klass;
int i = 0;
for (final Object each : getParametersList()) {
if (each instanceof Object[])
super.add(new TestClassRunnerForParameters(klass, (Object[]) each, i++));
else
throw new Exception(String.format("%s.%s() must return a Collection of arrays.", fKlass.getName(), getParametersMethod().getName()));
}
}
private Collection<?> getParametersList() throws IllegalAccessException, InvocationTargetException, Exception {
return (Collection<?>) getParametersMethod().invoke(null);
}
private Method getParametersMethod() throws Exception {
for (Method each : fKlass.getMethods()) {
if (Modifier.isStatic(each.getModifiers())) {
Annotation[] annotations = each.getAnnotations();
for (Annotation annotation : annotations) {
if (annotation.annotationType() == Parameters.class)
return each;
}
}
}
throw new Exception("No public static parameters method on class " + getName());
}
}
public MyParameterized(final Class<?> klass) throws Exception {
super(klass, new RunAllParameterMethods(klass));
}
#Override
protected void validate(MethodValidator methodValidator) {
methodValidator.validateStaticMethods();
methodValidator.validateInstanceMethods();
}
}
To be used like:
#RunWith(MyParameterized.class)
public class ParameterizedTest {
private File file;
public ParameterizedTest(File file) {
this.file = file;
}
#Test
public void test1() throws Exception {}
#Test
public void test2() throws Exception {}
#Name
public String getName() {
return "coolFile:" + file.getName();
}
#Parameters
public static Collection<Object[]> data() {
// load the files as you want
Object[] fileArg1 = new Object[] { new File("path1") };
Object[] fileArg2 = new Object[] { new File("path2") };
Collection<Object[]> data = new ArrayList<Object[]>();
data.add(fileArg1);
data.add(fileArg2);
return data;
}
}
This implies that I instantiate the test class earlier. I hope this won't cause any errors ... I guess I should test the tests :)
JUnit4 now allows specifying a name attribute to the Parameterized annotation, such that you can specify a naming pattern from the index and toString methods of the arguments. E.g.:
#Parameters(name = "{index}: fib({0})={1}")
public static Iterable<Object[]> data() {
return Arrays.asList(new Object[][] { { 0, 0 }, { 1, 1 }, { 2, 1 },
{ 3, 2 }, { 4, 3 }, { 5, 5 }, { 6, 8 } });
}
A code-less though not that comfortable solution is to pass enough context information to identify the test in assert messages. You will still see just testXY[0] failed but the detailed message tells you which one was that.
assertEquals("Not the expected decision for the senator " + this.currentSenatorName + " and the law " + this.votedLaw,
expectedVote, actualVote);
If you use JUnitParams library (as I have described here), the parameterized tests will have their stringified parameters as their own default test names.
Moreover, you can see in their samples, that JUnitParams also allows you to have a custom test name by using #TestCaseName:
#Test
#Parameters({ "1,1", "2,2", "3,6" })
#TestCaseName("factorial({0}) = {1}")
public void custom_names_for_test_case(int argument, int result) { }
#Test
#Parameters({ "value1, value2", "value3, value4" })
#TestCaseName("[{index}] {method}: {params}")
public void predefined_macro_for_test_case_name(String param1, String param2) { }
There's no hint that this feature is or will be implemented. I would request this feature because it's nice to have.