/*******************************************************************************
* Copyright (c) 2008 Ralf Ebert
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Ralf Ebert - initial API and implementation
*******************************************************************************/
package com.swtxml.util.reflector;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import com.swtxml.util.lang.CollectionUtils;
import com.swtxml.util.lang.Filters;
import com.swtxml.util.lang.IFilter;
public class FieldQuery {
private Visibility visibility;
private Subclasses subclasses;
private List<IFilter<Field>> filters = new ArrayList<IFilter<Field>>();
FieldQuery(Visibility visibility, Subclasses subclasses) {
this.visibility = visibility;
this.subclasses = subclasses;
}
private Collection<Field> getFields(Class<?> type) {
if (visibility == Visibility.PUBLIC && subclasses == Subclasses.INCLUDE) {
return Arrays.asList(type.getFields());
} else if (visibility == Visibility.PUBLIC && subclasses == Subclasses.NONE) {
List<Field> fields = Arrays.asList(type.getDeclaredFields());
for (Iterator<Field> i = fields.iterator(); i.hasNext();) {
if (!Modifier.isPublic(i.next().getModifiers())) {
i.remove();
}
}
return fields;
} else if (visibility == Visibility.PRIVATE && subclasses == Subclasses.INCLUDE) {
return getAllFields(type);
}
throw new UnsupportedOperationException("Querying with " + visibility + " and "
+ subclasses + " not supported at the moment.");
};
public FieldQuery name(final String name) {
filters.add(new IFilter<Field>() {
public boolean match(Field field) {
return field.getName().equals(name);
}
});
return this;
}
public FieldQuery nameStartsWith(final String str) {
filters.add(new IFilter<Field>() {
public boolean match(Field field) {
return field.getName().startsWith(str);
}
});
return this;
}
public FieldQuery annotatedWith(final Class<? extends Annotation> annotationClass) {
filters.add(new IFilter<Field>() {
public boolean match(Field field) {
return field.isAnnotationPresent(annotationClass);
}
});
return this;
}
public FieldQuery type(final Class<?> type) {
filters.add(new IFilter<Field>() {
public boolean match(Field field) {
return type.equals(field.getType());
}
});
return this;
}
public FieldQuery isStatic(final boolean isStatic) {
filters.add(new IFilter<Field>() {
public boolean match(Field field) {
return isStatic == Modifier.isStatic(field.getModifiers());
}
});
return this;
}
public Collection<Field> all(Class<?> type) {
return CollectionUtils.select(getFields(type), Filters.and(filters));
}
public Field exactOne(Class<?> type) {
Collection<Field> results = all(type);
if (results.size() == 1) {
return results.iterator().next();
// TODO: explain filter criteria in exception
} else if (results.isEmpty()) {
throw new ReflectorException("No suitable fields found for query!");
} else {
throw new ReflectorException("Ambiguous fields found for query!");
}
}
/**
* Same as Class.getFields() but with private fields included. Returns all
* fields of <type> and its superclasses.
*/
private Collection<Field> getAllFields(Class<?> type) {
List<Field> fields = new ArrayList<Field>();
while (type != null) {
for (Field field : type.getDeclaredFields()) {
if (!field.isSynthetic()) {
fields.add(field);
}
}
type = type.getSuperclass();
}
return fields;
}
}