An error occurred while loading the file. Please try again.
-
Dumoulin Nicolas authored2a9651ce
package fr.cemagref.observation.kernel;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
// TODO troduce in english
public class ObservablesHandler {
private java.util.ArrayList<ObserverListener> observers;
private transient ObservableFetcher[] fetchers;
/**
* Constructeur.
*
* @param cl La classe à laquelle on associe le présent ClassObservable
*/
public ObservablesHandler(Class cl) {
// observable attributes initialisation
List<Field> v = new ArrayList<Field>();
List<Method> v2 = new ArrayList<Method>();
// observable methods initialisation
Class inheritanceNavigator = cl;
while (inheritanceNavigator != null) {
for (Field f : inheritanceNavigator.getDeclaredFields()) {
if (f.isAnnotationPresent(Observable.class)) {
v.add(f);
}
}
for (Method m : inheritanceNavigator.getDeclaredMethods()) {
if (m.isAnnotationPresent(Observable.class)) {
v2.add(m);
}
}
inheritanceNavigator = inheritanceNavigator.getSuperclass();
}
fetchers = new ObservableFetcher[v.size() + v2.size()];
for (int i = 0; i < v.size(); i++) {
fetchers[i] = new FieldFetcher(v.get(i));
}
for (int i = 0; i < v2.size(); i++) {
fetchers[v.size() + i] = new MethodFetcher(v2.get(i));
}
// observers intialisation
observers = new ArrayList<ObserverListener>();
}
public Object getValue(int i, Object instance) throws IllegalArgumentException, IllegalAccessException,
InvocationTargetException {
return fetchers[i].fetchValue(instance);
}
/**
* Equivalent à getAttributes()[n], sauf s'il n'y a aucun attribut annoté comme observable (dans
* quel cas null est renvoyé).
*
* @param n Un indice
* @return Le java.lang.reflect.Field correspondant au n-ième attribut, ou null si aucun attribut
* n'est annoté comme observable.
*/
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
public ObservableFetcher getObservableFetcher(int n) {
return fetchers[n];
}
public ObservableFetcher[] getObservableFetchers() {
return fetchers;
}
/** Renvoie l'attribut dont la description est passée en argument.
*
* PERFS : Très mauvaises. A n'utiliser que pour un stockage initial de l'attribut
* afin de l'utiliser intensivement ensuite.
*
* @param desc Une description
* @return L'attribut qui correspond à cette description.
*/
public ObservableFetcher getObservableFetcher(String desc) {
if ((fetchers.length == 0) || (desc == null)) {
return null;
}
desc = desc.trim();
for (int i = 0; i < fetchers.length; i++) {
if (desc.equals(fetchers[i].getDescription().trim())) {
return fetchers[i];
}
}
return null;
}
public ObservableFetcher getObservableFetcherByName(String name) {
if ((fetchers.length == 0) || (name == null)) {
return null;
}
name = name.trim();
for (int i = 0; i < fetchers.length; i++) {
if (name.equals(fetchers[i].getDeclaredName().trim())) {
return fetchers[i];
}
}
return null;
}
/**
* @return Le nombre d'attributes observables.
*/
public int numberOfAttributes() {
return fetchers.length;
}
/**
* Accesseur vers les descriptions des attributes observables, dans le même ordre
* que getAttributes, c'est à dire que getAttributes()[n] ou getAttribute(n) ont la description
* getDescriptions()[n].
*
* Si aucun attribut n'est annoté comme étant observable, renvoie null.
*
* PERFS : Comme les description ne sont pas destinées à être utilisées de façon intensive,
* le tableau est reconstruit à chaque appel de la méthode.
*
* @return Un tableau de descriptions des attributes annotés comme observables.
* null si aucun attribut n'est annoté comme observable.
*/
public String[] getDescriptions() {
String[] t = new String[fetchers.length];
for (int i = 0; i < fetchers.length; i++) {
t[i] = fetchers[i].getDescription();
}
return t;
}
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
/** Equivalent à getAttribute(n).getAnnotation(Observable.class).description() sauf si
* aucun attribut n'est annoté comme étant observabel, dans quel cas on renvoie null.
* @param n
* @return La description du n-ième attribut.
*/
public String getDescription(int n) {
return fetchers[n].getDescription();
}
public String getDeclaredName(int n) {
return fetchers[n].getDeclaredName();
}
/** Ajoute un ObserverListener à la liste.
*
* @param ob L'ObserverListener à ajouter.
*/
public ObserverListener addObserverListener(ObserverListener ob) {
observers.add(ob);
ob.addObservable(this);
return ob;
}
/** Retire un seul observerListener de la liste. Renvoie true s'il a effectivement été trouvé
* et retiré.
* @param ob L'ObserverListener à retirer.
* @return
*/
public boolean removeObserverListener(ObserverListener ob) {
boolean response = observers.remove(ob);
ObservableManager.getObservableManager().fireObservableManagerStructureChanged();
return response;
}
/**
* <code>size</code>
*
* @see java.util.ArrayList#size()
*/
public int getObserversCount() {
return observers.size();
}
/**
* Notifie les changement aux observers déclarés. Pour le moment les appelle l'un après l'autre.
*
* @param instanceEnCours
* @param t
*/
public void fireChanges(Object instanceEnCours, long t) {
for (ObserverListener listener : observers) {
listener.valueChanged(this, instanceEnCours, t);
}
}
public Collection<ObserverListener> getObservers() {
return observers;
}
/**
* @see java.util.ArrayList#clear()
*/
void clearObservers() {
this.observers.clear();
}
/**
* @see java.util.ArrayList#get(int)
*/
public ObserverListener getObserver(int i) {
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
return observers.get(i);
}
public abstract class ObservableFetcher {
public ObservableFetcher(AccessibleObject accessibleObject) {
accessibleObject.setAccessible(true);
}
public abstract Object fetchValue(Object instance) throws IllegalArgumentException, IllegalAccessException,
InvocationTargetException;
public Object fetchValueOrNull(Object instance) {
try {
return fetchValue(instance);
} catch (IllegalArgumentException ex) {
Logger.getLogger(ObservablesHandler.class.getName()).log(Level.SEVERE, null, ex);
return null;
} catch (IllegalAccessException ex) {
Logger.getLogger(ObservablesHandler.class.getName()).log(Level.SEVERE, null, ex);
return null;
} catch (InvocationTargetException ex) {
Logger.getLogger(ObservablesHandler.class.getName()).log(Level.SEVERE, null, ex);
return null;
}
}
/**
* Return the size of each dimension. If a "labelsMethod" is declared, it will be used, otherwise, the given sizes will be returned. See the @Observable documentation.
* @param instance
* @return
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @throws InvocationTargetException
*/
public int[] getSizes(Object instance) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
int[] sizes;
if (getLabelsMethod().length() > 0) {
sizes = new int[getSize().length];
for (int i = 0; i < sizes.length; i++) {
String[] localLabels = (String[]) instance.getClass().getDeclaredMethod(getLabelsMethod(), Integer.class).invoke(instance, i);
sizes[i] = (localLabels).length;
}
} else {
sizes = getSize();
}
return sizes;
}
public List<String>[] getLabels(Object instance) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
List<String>[] labels = new List[getSize().length];
if (getLabelsMethod().length() > 0) {
for (int i = 0; i < getSize().length; i++) {
String[] localLabels = (String[]) instance.getClass().getDeclaredMethod(getLabelsMethod(), Integer.class).invoke(instance, i);
labels[i] = Arrays.asList(localLabels);
}
} else {
for (int i = 0; i < getSize().length; i++) {
labels[i] = new ArrayList<String>(getSize()[i]);
labels[i].add(getDeclaredName() + "[0]");
for (int j = 1; j < getSize()[i]; j++) {
labels[i].add("[" + j + "]");
}
}
}
return labels;
}
public abstract Observable getAnnotation();
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
public abstract String getDeclaredName();
public String getDescription() {
return getAnnotation().description();
}
protected final int[] getSize() {
return getAnnotation().size();
}
public String getLabelsMethod() {
return getAnnotation().labelsMethod();
}
public abstract Class getDeclaredType();
}
public class MethodFetcher extends ObservableFetcher {
private Method method;
public MethodFetcher(Method method) {
super(method);
this.method = method;
}
@Override
public Object fetchValue(Object instance) throws IllegalArgumentException, IllegalAccessException,
InvocationTargetException {
// TODO add the ability to use arguments
return method.invoke(instance, new Object[]{});
}
@Override
public Observable getAnnotation() {
return method.getAnnotation(Observable.class);
}
@Override
public String getDeclaredName() {
return method.getName();
}
@Override
public Class getDeclaredType() {
return method.getReturnType();
}
}
public class FieldFetcher extends ObservableFetcher {
private Field field;
public FieldFetcher(Field field) {
super(field);
this.field = field;
}
@Override
public Object fetchValue(Object instance) throws IllegalArgumentException, IllegalAccessException,
InvocationTargetException {
return field.get(instance);
}
@Override
public Observable getAnnotation() {
return field.getAnnotation(Observable.class);
}
351352353354355356357358359360361362
@Override
public String getDeclaredName() {
return field.getName();
}
@Override
public Class getDeclaredType() {
return field.getType();
}
}
}