diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..091699910865adf043ee14c299e4b6ffcf2817d8 --- /dev/null +++ b/pom.xml @@ -0,0 +1,107 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <!-- ************************************************************* --> + <!-- *** POM Relationships *************************************** --> + <!-- ************************************************************* --> + + <groupId>fr.cemagref</groupId> + <artifactId>observation</artifactId> + <version>1.0-SNAPSHOT</version> + + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>3.8.1</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.thoughtworks.xstream</groupId> + <artifactId>xstream</artifactId> + <version>1.3.1</version> + </dependency> + <dependency> + <groupId>jfree</groupId> + <artifactId>jfreechart</artifactId> + <version>1.0.13</version> + </dependency> + <dependency> + <groupId>fr.cemagref</groupId> + <artifactId>ohoui</artifactId> + <version>0.1-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>jfree</groupId> + <artifactId>jcommon</artifactId> + <version>1.0.15</version> + </dependency> + </dependencies> + + <!-- ************************************************************* --> + <!-- *** Project Information ************************************* --> + <!-- ************************************************************* --> + + <name>${project.artifactId} ${project.version}</name> + <description></description> + <url>http://trac.clermont.cemagref.fr/projets/LISC/wiki/JavaObservation</url> + + <licenses> + <license> + <name>GPL</name> + <url>http://www.gnu.org/copyleft/gpl.html</url> + <distribution>repo</distribution> + </license> + </licenses> + + <!-- ************************************************************* --> + <!-- *** Build Settings ****************************************** --> + <!-- ************************************************************* --> + + <packaging>jar</packaging> + + <build> + <plugins> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.6</source> + <target>1.6</target> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <configuration> + <overWriteReleases>false</overWriteReleases> + <overWriteSnapshots>false</overWriteSnapshots> + <overWriteIfNewer>true</overWriteIfNewer> + </configuration> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>copy-dependencies</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <distributionManagement> + <repository> + <id>trac.clermont.cemagref.fr.nexus</id> + <url>http://trac.clermont.cemagref.fr/nexus/content/repositories/releases</url> + </repository> + <snapshotRepository> + <id>trac.clermont.cemagref.fr.nexus</id> + <url>http://trac.clermont.cemagref.fr/nexus/content/repositories/snapshots</url> + </snapshotRepository> + </distributionManagement> + +</project> + + + diff --git a/src/main/java/fr/cemagref/observation/gui/Configurable.java b/src/main/java/fr/cemagref/observation/gui/Configurable.java new file mode 100644 index 0000000000000000000000000000000000000000..0db2c6c22e4f28e0e6df036f32e8a6dbe419c3da --- /dev/null +++ b/src/main/java/fr/cemagref/observation/gui/Configurable.java @@ -0,0 +1,14 @@ +package fr.cemagref.observation.gui; + + +/** + * <code>Configurable</code> This interface is implemented by GUI element that are configurables. (element in a list, panel, ...) + */ +public interface Configurable { + + /** + * <code>configure</code> is called when the user want to configure the component. + * + */ + public void configure(); +} diff --git a/src/main/java/fr/cemagref/observation/gui/ControlsFactory.java b/src/main/java/fr/cemagref/observation/gui/ControlsFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..7ab793938eaf74162bee2f664b9a5a0950038ef4 --- /dev/null +++ b/src/main/java/fr/cemagref/observation/gui/ControlsFactory.java @@ -0,0 +1,36 @@ +package fr.cemagref.observation.gui; + +import java.awt.event.ActionListener; +import java.net.URL; + +import javax.swing.ImageIcon; +import javax.swing.JButton; + +public class ControlsFactory { + + public static JButton makeButton(String imageName, + ActionListener listener, + String toolTipText, + String altText) { + // Look for the image. + String imgLocation = "toolbarButtonGraphics/" + + imageName + + ".gif"; + URL imageURL = ControlsFactory.class.getClassLoader().getResource(imgLocation); + // Create and initialize the button. + JButton button = new JButton(); + button.setToolTipText(toolTipText); + button.addActionListener(listener); + if (imageURL != null) { + //image found + button.setIcon(new ImageIcon(imageURL, altText)); + } else { + //no image found + button.setText(altText); + System.err.println("Resource not found: " + imgLocation); + } + + return button; + } + +} diff --git a/src/main/java/fr/cemagref/observation/gui/Drawable.java b/src/main/java/fr/cemagref/observation/gui/Drawable.java new file mode 100644 index 0000000000000000000000000000000000000000..02bf58ede4253925ad7c13faff81f53cc4179121 --- /dev/null +++ b/src/main/java/fr/cemagref/observation/gui/Drawable.java @@ -0,0 +1,18 @@ +package fr.cemagref.observation.gui; + +import javax.swing.JComponent; + +/** + * <code>Drawable</code> A component implements this interface if it could be graphically drawed as a JComponent. + */ +public interface Drawable { + + /** + * <code>getTitle</code> + * + * @return The title of this component. + */ + public String getTitle(); + + public JComponent getDisplay(); +} diff --git a/src/main/java/fr/cemagref/observation/gui/ObserversManagerHandler.java b/src/main/java/fr/cemagref/observation/gui/ObserversManagerHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..26e9cd25d64801a49745ec0411c958d7bf7155e7 --- /dev/null +++ b/src/main/java/fr/cemagref/observation/gui/ObserversManagerHandler.java @@ -0,0 +1,15 @@ +package fr.cemagref.observation.gui; + +import javax.swing.JFrame; + +/** + * <code>ObserversManagerHandler</code> is those who handles the ObservableManager. + */ +public interface ObserversManagerHandler { + /** + * <code>getHandlingFrame</code> is used by dialog box to know which is the parent frame. + */ + public JFrame getHandlingFrame(); + + public void showDrawable(Drawable drawable); +} diff --git a/src/main/java/fr/cemagref/observation/gui/ObserversManagerPanel.java b/src/main/java/fr/cemagref/observation/gui/ObserversManagerPanel.java new file mode 100644 index 0000000000000000000000000000000000000000..5ab47c2ede5d9f3390e7cac2722f5c3adfe658dd --- /dev/null +++ b/src/main/java/fr/cemagref/observation/gui/ObserversManagerPanel.java @@ -0,0 +1,111 @@ +package fr.cemagref.observation.gui; + +import fr.cemagref.observation.kernel.ObservableManager; +import fr.cemagref.observation.kernel.ObservableManager.ClassAndObserver; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.HashSet; + +import javax.swing.JButton; +import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JToolBar; +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +@SuppressWarnings("serial") +public class ObserversManagerPanel extends JPanel implements ListSelectionListener { + + private ObserversManagerHandler observersManagerHandler; + private JList list; + private JButton removeButton; + private JButton confButton; + private JButton showButton; + + private HashSet<ClassAndObserver> observersShowed = new HashSet<ClassAndObserver>(); + + public ObserversManagerPanel(ObserversManagerHandler observersManagerHandler) { + super(); + this.observersManagerHandler = observersManagerHandler; + + this.setLayout(new BorderLayout()); + // The list + list = new JList(ObservableManager.getObservableManager()); + list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); + list.setLayoutOrientation(JList.HORIZONTAL_WRAP); + list.addListSelectionListener(this); + JScrollPane listScroller = new JScrollPane(list); + this.add(listScroller,BorderLayout.CENTER); + + // The toolbar + JToolBar toolBar = new JToolBar(); + toolBar.setFloatable(false); + add(toolBar,BorderLayout.PAGE_END); + + // The "remove" button + removeButton = ControlsFactory.makeButton("general/Delete24",new RemoveButtonListener(),"Remove the selected module","Remove"); + toolBar.add(removeButton); + + // The "conf" button + confButton = ControlsFactory.makeButton("general/Edit24",new ConfButtonListener(),"Configure the selected module","Configure"); + toolBar.add(confButton); + + // The "show" button + showButton = ControlsFactory.makeButton("general/Find24",new ShowButtonListener(),"Show the selected module","Show"); + toolBar.add(showButton); + + // set buttons enabled or not + this.valueChanged(null); + } + + public void valueChanged(ListSelectionEvent e) { + if (list.getSelectedIndex()>=0) { + ClassAndObserver classAndObserver = (ClassAndObserver)list.getSelectedValue(); + removeButton.setEnabled(true); + confButton.setEnabled(classAndObserver.getObserver() instanceof Configurable); + showButton.setEnabled( + // observers must be drawable + classAndObserver.getObserver() instanceof Drawable + // and observers must not be yet displayed + && !observersShowed.contains(classAndObserver) + ); + } else { + removeButton.setEnabled(false); + confButton.setEnabled(false); + showButton.setEnabled(false); + } + + } + + private class RemoveButtonListener implements ActionListener { + public void actionPerformed(ActionEvent e) { + if (JOptionPane.showConfirmDialog(observersManagerHandler.getHandlingFrame(),"Sure ?","Confirmation",JOptionPane.YES_NO_OPTION)==0) { + ClassAndObserver item = (ClassAndObserver)list.getSelectedValue(); + ObservableManager.removeObserverListener(item.getType(),item.getObserver()); + } + } + } + + private class ConfButtonListener implements ActionListener { + public void actionPerformed(ActionEvent e) { + ((Configurable)((ClassAndObserver)list.getSelectedValue()).getObserver()).configure(); + } + } + + private class ShowButtonListener implements ActionListener { + public void actionPerformed(ActionEvent e) { + ClassAndObserver classAndObserver = (ClassAndObserver)list.getSelectedValue(); + if (observersShowed.add(classAndObserver)) { + // force buttons enabling refresh + valueChanged(null); + // get display and add it in handler + observersManagerHandler.showDrawable((Drawable)classAndObserver.getObserver()); + } + } + } +} diff --git a/src/main/java/fr/cemagref/observation/kernel/Observable.java b/src/main/java/fr/cemagref/observation/kernel/Observable.java new file mode 100644 index 0000000000000000000000000000000000000000..c87ab68a61d39ac7ae29899e5cbe2d74b1e4ec03 --- /dev/null +++ b/src/main/java/fr/cemagref/observation/kernel/Observable.java @@ -0,0 +1,22 @@ +/** + * L'interface "annotation" pour marquer les observables. + */ +package fr.cemagref.observation.kernel; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * <code>Observable</code> The annotation used to declare a field as observable + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD, ElementType.METHOD}) +public @interface Observable { + /** + * <code>description</code> is the textual description of the observable. + * + * @return the textual description of the observable. + */ + String description(); +} diff --git a/src/main/java/fr/cemagref/observation/kernel/ObservableManager.java b/src/main/java/fr/cemagref/observation/kernel/ObservableManager.java new file mode 100644 index 0000000000000000000000000000000000000000..76cb0b3ee3a285c7aa6f02ca1ef8c6fc4c4434bb --- /dev/null +++ b/src/main/java/fr/cemagref/observation/kernel/ObservableManager.java @@ -0,0 +1,237 @@ +package fr.cemagref.observation.kernel; + +import java.io.Reader; +import java.util.ArrayList; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.swing.ListModel; +import javax.swing.event.ListDataEvent; +import javax.swing.event.ListDataListener; + +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.io.xml.DomDriver; + +/** + * TODO traduce in english + */ +public class ObservableManager implements ListModel { + + private static ObservableManager om = null; + private Map<Class, ObservablesHandler> observables; + private List<ListDataListener> listDataListeners = new ArrayList<ListDataListener>(); + + /** Le constructeur. Il est private, car l'accès à l'observable manager doit + * se faire exclusivement via la méthode statique getObservableManager(). + */ + private ObservableManager() { + observables = new Hashtable<Class,ObservablesHandler>(); + } + + /** + * The true mean to access Observable manager. + * + * @return L'observable manager du système. + */ + public static ObservableManager getObservableManager() { + return om == null ? om = new ObservableManager() : om; + } + + /** + * clear method clears all class-observers associations + */ + public static void clear() { + for (ObservablesHandler co : om.observables.values()) { + co.clearObservers(); + } + om.fireObservableManagerStructureChanged(); + } + + /** + * Déclare une classe comme étant observable. + * + * Si une classe a déjà été déclarée comme observable, + * l'appel de cette méthode est équvalent à un getObservable(Class). + * + * @param cl La classe déclarée comme observable + * @return Le ClassObservable de cl. + */ + public static ObservablesHandler addObservable(Class cl) { + if (getObservableManager().observables.containsKey(cl)) { + return getObservableManager().observables.get(cl); + } + ObservablesHandler co = new ObservablesHandler(cl); + getObservableManager().observables.put(cl, co); + getObservableManager().fireObservableManagerStructureChanged(); + return co; + } + + /** Permet d'obtenir l'instance de ClassObservable associée à la classe + * donnée en argument. + * + * PERFS : Les classObservable sont stockées dans une Hashtable, + * avec une clé sur Class, c'est à dire que l'algo est de l'ordre + * de N log(N) (où N est le nombre d'observables), avec + * (ce qui n'est pas négligeable), une opération de base qui est Class.equals(Object). + * En conclusion : pour une utilisation très intensive, on gardera une variable + * pointant sur le ClassObservable, pour éviter d'appeler trop souvent cette méthode. + * + * @param cl La classe dont on veut le ClassObservable + * @return Le classObservable, ou null si la classe donnée en argument + * n'a pas été déclarée comme étant observable. + */ + public static ObservablesHandler getObservable(Class cl) { + return getObservableManager().observables.get(cl); + } + + /** Ajoute un ObserverListener à la liste. + * + * @param ob L'ObserverListener à ajouter. + */ + public static ObserverListener addObserverListener(Class cl, ObserverListener ob) { + ObserverListener observerListener = addObservable(cl).addObserverListener(ob); + getObservableManager().fireObservableManagerStructureChanged(); + return observerListener; + } + + /** Retire un seul observerListener de la liste. Renvoie true s'il a effectivement été trouvé + * et retiré. + * @param ob L'ObserverListener à retirer. + * @return + */ + public static boolean removeObserverListener(Class cl, ObserverListener ob) { + boolean response = getObservable(cl).removeObserverListener(ob); + getObservableManager().fireObservableManagerStructureChanged(); + return response; + } + + public static void initObservers() { + for (ObservablesHandler classObservable : getObservableManager().observables.values()) { + for (ObserverListener observer : classObservable.getObservers()) { + observer.init(); + } + } + } + + public static void closeObservers() { + for (ObservablesHandler classObservable : getObservables().values()) { + for (ObserverListener observer : classObservable.getObservers()) { + observer.close(); + } + } + } + + public static Map<Class, ObservablesHandler> getObservables() { + return getObservableManager().observables; + } + + public static void setObservers(Reader observablesReader, XStream xstream) { + Map<Class,ObservablesHandler> observablesReaded = (Map<Class,ObservablesHandler>) xstream.fromXML(observablesReader); + for (Map.Entry<Class,ObservablesHandler> entry : observablesReaded.entrySet()) { + // add to the list of observers those readed from the file + ObservablesHandler clObservable = addObservable(entry.getKey()); + for (ObserverListener observer : entry.getValue().getObservers()) { + // use addObserverListener method garanties safe init + clObservable.addObserverListener(observer); + } + } + } + + public static void setObservers(Reader observablesReader) { + setObservers(observablesReader, new XStream(new DomDriver())); + } + + /** + * @see javax.swing.ListModel#getSize() + */ + public int getSize() { + int size = 0; + for (ObservablesHandler classObservable : observables.values()) { + size += classObservable.getObserversCount(); + } + return size; + } + + /** + * @see javax.swing.ListModel#getElementAt(int) + */ + public Object getElementAt(int index) { + Set<Map.Entry<Class, ObservablesHandler>> entrySet = observables.entrySet(); + Iterator<Map.Entry<Class, ObservablesHandler>> iterator = entrySet.iterator(); + Map.Entry<Class, ObservablesHandler> current = iterator.next(); + int count = current.getValue().getObserversCount(); + while (count <= 0) { + current = iterator.next(); + count = current.getValue().getObserversCount(); + } + while (count <= index) { + current = iterator.next(); + count += current.getValue().getObserversCount(); + } + int realIndex = index - (count - current.getValue().getObserversCount()); + assert realIndex < current.getValue().getObserversCount() : realIndex + " !< " + current.getValue().getObserversCount(); + return new ClassAndObserver(current.getKey(), current.getValue().getObserver(realIndex)); + } + + /** + * @see javax.swing.ListModel#addListDataListener(javax.swing.event.ListDataListener) + */ + public void addListDataListener(ListDataListener l) { + listDataListeners.add(l); + } + + /** + * @see javax.swing.ListModel#removeListDataListener(javax.swing.event.ListDataListener) + */ + public void removeListDataListener(ListDataListener l) { + listDataListeners.remove(l); + } + + void fireObservableManagerStructureChanged() { + for (ListDataListener listDataListener : listDataListeners) { + listDataListener.contentsChanged(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, getSize())); + } + } + + public static class ClassAndObserver { + + private Class type; + private ObserverListener observer; + + public ClassAndObserver(Class type, ObserverListener observer) { + this.type = type; + this.observer = observer; + } + + public Class getType() { + return type; + } + + public ObserverListener getObserver() { + return observer; + } + + public String toString() { + return observer.getClass().getSimpleName() + " -> " + type.getSimpleName(); + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + return obj.hashCode() == this.hashCode(); + } + + /** + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return type.hashCode() + observer.hashCode(); + } + } +} diff --git a/src/main/java/fr/cemagref/observation/kernel/ObservablesHandler.java b/src/main/java/fr/cemagref/observation/kernel/ObservablesHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..98a805408960c0b9a34c6feff4ef11e375827929 --- /dev/null +++ b/src/main/java/fr/cemagref/observation/kernel/ObservablesHandler.java @@ -0,0 +1,289 @@ +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.Collection; +import java.util.List; + +// 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. + */ + 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; + } + + /** 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) { + 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 abstract String getDeclaredName(); + + public abstract String getDescription(); + + 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 String getDescription() { + return method.getAnnotation(Observable.class).description(); + } + + @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 String getDescription() { + return field.getAnnotation(Observable.class).description(); + } + + @Override + public String getDeclaredName() { + return field.getName(); + } + + @Override + public Class getDeclaredType() { + return field.getType(); + } + } +} diff --git a/src/main/java/fr/cemagref/observation/kernel/ObserverListener.java b/src/main/java/fr/cemagref/observation/kernel/ObserverListener.java new file mode 100644 index 0000000000000000000000000000000000000000..597a9ff6129ad6b8de11493fc2da095d89bd8a3c --- /dev/null +++ b/src/main/java/fr/cemagref/observation/kernel/ObserverListener.java @@ -0,0 +1,36 @@ +package fr.cemagref.observation.kernel; + +public interface ObserverListener { + /** + * <code>addObservable</code> is called when an observable is connected to + * this observable. It gives the possibility to make some init tasks. + * + * @param classObservable + * The new observable connected to this observer + */ + public void addObservable(ObservablesHandler classObservable); + + /** + * <code>valueChanged</code> is invoked when the observable has updated + * its value. It's time for the observer to do its job. + * + * @param clObservable + * the observable handler + * @param instance + * the observable object + * @param t + * the current time + */ + public void valueChanged(ObservablesHandler clObservable, Object instance, long t); + + /** + * <code>init</code> Called at begin of simulation + */ + public void init(); + + /** + * <code>close</code> Called at end of simulation + * + */ + public void close(); +} diff --git a/src/main/java/fr/cemagref/observation/observers/CSVObserver.java b/src/main/java/fr/cemagref/observation/observers/CSVObserver.java new file mode 100644 index 0000000000000000000000000000000000000000..3791290d8e03fe3362f7da1fdcde11e5cec8c70c --- /dev/null +++ b/src/main/java/fr/cemagref/observation/observers/CSVObserver.java @@ -0,0 +1,70 @@ +package fr.cemagref.observation.observers; + +import java.lang.reflect.InvocationTargetException; + +import fr.cemagref.observation.kernel.ObservablesHandler; +import fr.cemagref.ohoui.annotations.Description; + +public class CSVObserver extends ConsoleObserver { + + @Description(name="Field separator", tooltip="") + private char separator = ';'; + + /** <code>classObservable</code> is used to display name of attributes as header */ + private transient ObservablesHandler classObservable; + + public CSVObserver(boolean sysout, String outputFile) { + super(sysout, outputFile); + } + + @Override + public void addObservable(ObservablesHandler classObservable) { + ObservablesHandler bak = this.classObservable; + this.classObservable = classObservable; + if (bak != null) // The observable has been changed + this.init(); + } + + @Override + public void valueChanged(ObservablesHandler clObservable, Object instance, long t) { + StringBuffer buf = new StringBuffer(); + StringBuffer sbSeparator = new StringBuffer(" "+this.separator+" "); + // print current Time + buf.append(t); + // print value of each field + for (int i =0;i<clObservable.numberOfAttributes();i++) { + buf.append(sbSeparator); + try { + buf.append(clObservable.getObservableFetcher(i).fetchValue(instance)); + } catch (IllegalArgumentException e) { + buf.append("N/A"); + e.printStackTrace(); + } catch (IllegalAccessException e) { + buf.append("N/A"); + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + return; + } + } + outputStream.println(buf); + } + + @Override + public void init() { + super.init(); + if (classObservable != null) { + // Headers printing + StringBuffer buf = new StringBuffer(); + StringBuffer sbSeparator = new StringBuffer(" "+this.separator+" "); + buf.append("Time"); + for (int i =0;i<classObservable.numberOfAttributes();i++) { + buf.append(sbSeparator); + buf.append(classObservable.getDescription(i)); + } + outputStream.println(buf); + } + } + + +} diff --git a/src/main/java/fr/cemagref/observation/observers/ConsoleObserver.java b/src/main/java/fr/cemagref/observation/observers/ConsoleObserver.java new file mode 100644 index 0000000000000000000000000000000000000000..0a8af0a0bda8819bb912d9e35fc3377ce9c4665a --- /dev/null +++ b/src/main/java/fr/cemagref/observation/observers/ConsoleObserver.java @@ -0,0 +1,102 @@ +package fr.cemagref.observation.observers; + +import java.awt.Dimension; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintStream; +import java.lang.reflect.InvocationTargetException; + +import fr.cemagref.observation.gui.Configurable; +import fr.cemagref.observation.kernel.ObservablesHandler; +import fr.cemagref.observation.kernel.ObserverListener; +import fr.cemagref.ohoui.annotations.Anchor; +import fr.cemagref.ohoui.annotations.Description; +import fr.cemagref.ohoui.annotations.Link; +import fr.cemagref.ohoui.filters.NoTransientField; +import fr.cemagref.ohoui.swing.OhOUI; +import fr.cemagref.ohoui.swing.OhOUIDialog; +import java.io.BufferedOutputStream; +import java.io.FileOutputStream; + +public class ConsoleObserver implements ObserverListener, Configurable { + + @Description(name="Use standard output",tooltip = "") + @Link(action="disable",target="filename") + private boolean sysout = true; + + @Anchor(id="filename") + private File outputFile = new File(""); + + private transient File outputFileBak = outputFile; + protected transient PrintStream outputStream = System.out; + + public ConsoleObserver(boolean sysout, String outputFile) { + this.sysout = sysout; + this.outputFile = new File(outputFile); + this.init(); + } + + @Override + public void addObservable(ObservablesHandler classObservable) { + // nothing to do + } + + @Override + public void valueChanged(ObservablesHandler clObservable, Object instance, long t) { + StringBuffer buf = new StringBuffer(t+" "+instance.getClass().getName() + " :\n"); + for (int i =0;i<clObservable.numberOfAttributes();i++) { + buf.append(" "+clObservable.getDescription(i)+" = "); + try { + buf.append(clObservable.getObservableFetcher(i).fetchValue(instance)); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + return; + } + if (i<clObservable.numberOfAttributes()-1) buf.append("\n"); + } + outputStream.println(buf); + } + + protected void println(CharSequence message) { + if (sysout) { + outputStream.print(this.getClass().getSimpleName()+" :: "); + } + outputStream.println(message); + } + + @Override + public void configure() { + OhOUIDialog dialog = OhOUI.getDialog(null,this,new NoTransientField()); + dialog.setSize(new Dimension(510, 160)); + dialog.setVisible(true); + if (!outputFile.equals(outputFileBak)) init(); + outputFileBak = outputFile; + } + + @Override + public void init() { + if (sysout) { + outputStream = System.out; + } else { + try { + // it's rather better to use a buffetred outputstream, and to disable to default buffer of the PrintStream + outputStream = new PrintStream(new BufferedOutputStream(new FileOutputStream(outputFile), 1024), false); + } catch (FileNotFoundException e) { + // TODO bring info to the user. Or prevent exception when directory of file doesn't exist + e.printStackTrace(); + outputStream = System.out; + } + } + } + + @Override + public void close() { + if (outputStream != System.out) + outputStream.close(); + } + +} diff --git a/src/main/java/fr/cemagref/observation/observers/MatrixCSVObserver.java b/src/main/java/fr/cemagref/observation/observers/MatrixCSVObserver.java new file mode 100644 index 0000000000000000000000000000000000000000..032beaf065d46532ea6ee446b96c9f47357ca605 --- /dev/null +++ b/src/main/java/fr/cemagref/observation/observers/MatrixCSVObserver.java @@ -0,0 +1,69 @@ +package fr.cemagref.observation.observers; + +import java.lang.reflect.InvocationTargetException; + +import fr.cemagref.observation.kernel.ObservablesHandler; +import fr.cemagref.ohoui.annotations.Description; + +public class MatrixCSVObserver extends ConsoleObserver { + + @Description(name = "Field separator", tooltip = "") + private char separator = ';'; + /** <code>classObservable</code> is used to display name of attributes as header */ + private transient ObservablesHandler classObservable; + + public MatrixCSVObserver(boolean sysout, String outputFile) { + super(sysout, outputFile); + } + + @Override + public void addObservable(ObservablesHandler classObservable) { + ObservablesHandler bak = this.classObservable; + this.classObservable = classObservable; + if (bak != null) // The observable has been changed + { + this.init(); + } + } + + @Override + public void valueChanged(ObservablesHandler clObservable, Object instance, long t) { + StringBuilder buf = new StringBuilder(); + StringBuilder sbSeparator = new StringBuilder(" ").append(this.separator).append(" "); + // print current Time + buf.append(t); + // print value of each field + for (int i = 0; i < clObservable.numberOfAttributes(); i++) { + buf.append(sbSeparator); + try { + buf.append(clObservable.getObservableFetcher(i).fetchValue(instance)); + } catch (IllegalArgumentException e) { + buf.append("N/A"); + e.printStackTrace(); + } catch (IllegalAccessException e) { + buf.append("N/A"); + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + return; + } + } + outputStream.println(buf); + } + + @Override + public void init() { + super.init(); + if (classObservable != null) { + // Headers printing + StringBuilder buf = new StringBuilder(); + StringBuilder sbSeparator = new StringBuilder(" " + this.separator + " "); + buf.append("Time"); + for (int i = 0; i < classObservable.numberOfAttributes(); i++) { + buf.append(sbSeparator); + buf.append(classObservable.getDescription(i)); + } + outputStream.println(buf); + } + } +} diff --git a/src/main/java/fr/cemagref/observation/observers/XMLObserver.java b/src/main/java/fr/cemagref/observation/observers/XMLObserver.java new file mode 100644 index 0000000000000000000000000000000000000000..503a6efdc8dbbbaeb0eea9fe4a27dcdbbaab4c53 --- /dev/null +++ b/src/main/java/fr/cemagref/observation/observers/XMLObserver.java @@ -0,0 +1,159 @@ +package fr.cemagref.observation.observers; + +import fr.cemagref.observation.kernel.ObservablesHandler; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Observer that write data in XML format as defined by the Mexico working group. + */ +public class XMLObserver extends ConsoleObserver { + + private static class ObservableDataInfos { + + String tagOpeningWithName; + String tagOpening; + String tagClosing; + int numberOfDimensions; + } + private static transient Map<Class, String> typeForXml; + private transient List<ObservableDataInfos> observableDataInfosList; + + public XMLObserver(boolean sysout, String outputFile) { + super(sysout, outputFile); + } + + @Override + public void init() { + super.init(); + if (typeForXml == null) { + typeForXml = new HashMap<Class, String>(); + typeForXml.put(Double.class, "d"); + typeForXml.put(double.class, "d"); + typeForXml.put(Integer.class, "i"); + typeForXml.put(int.class, "i"); + typeForXml.put(String.class, "s"); + } + if (observableDataInfosList == null) { + observableDataInfosList = new ArrayList<ObservableDataInfos>(); + } + outputStream.print("<outputData>\n"); + } + + @Override + public void addObservable(ObservablesHandler classObservable) { + if (typeForXml == null) { + init(); + } + if (observableDataInfosList == null) { + init(); + } + // Storing the infos for this observable + ObservableDataInfos infos; + for (int i = 0; i < classObservable.numberOfAttributes(); i++) { + infos = new ObservableDataInfos(); + ObservablesHandler.ObservableFetcher fetcher = classObservable.getObservableFetcher(i); + Class observableType = fetcher.getDeclaredType(); + // if the type is an array, going in depth to find the final component type and the number of dimensions + infos.numberOfDimensions = 0; + while (observableType.isArray()) { + infos.numberOfDimensions++; + observableType = observableType.getComponentType(); + } + infos.tagOpening = "<" + typeForXml.get(observableType) + ">"; + infos.tagOpeningWithName = "<" + typeForXml.get(observableType) + " name =\""; + infos.tagClosing = "</" + typeForXml.get(observableType) + ">"; + observableDataInfosList.add(infos); + } + } + + @Override + public void valueChanged(ObservablesHandler clObservable, Object instance, long t) { + for (int i = 0; i < clObservable.numberOfAttributes(); i++) { + try { + //System.out.println("XMLObserver::valueChanged" + clObservable.getObservableFetcher(i).fetchValue(instance) + clObservable.getDeclaredName(i) + clObservable.getDescription(i)); + outputStream.print("<step time =\""); + outputStream.print(t); + outputStream.println("\">"); + printData(observableDataInfosList.get(i), clObservable.getObservableFetcher(i).fetchValue(instance), clObservable.getDeclaredName(i), + clObservable.getDescription(i)); + outputStream.println("</step>"); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + return; + } + } + } + + private void printData(ObservableDataInfos infos, Object data, String declaredName, String description) { + if (infos.numberOfDimensions > 0) { + // Open the matrix tag + outputStream.print("<m name=\""); + outputStream.print(description); + outputStream.print("\">\n"); + // print metadata for each dimensions + Object dimension = data; + int dimensionExtent; + for (int i = 1; i <= infos.numberOfDimensions; i++) { + outputStream.print("<dimension name=\"dim"); + outputStream.print(i); + outputStream.print("\" size=\""); + dimensionExtent = Array.getLength(dimension); + outputStream.print(dimensionExtent); + outputStream.println("\"><label></label></dimension>"); + if (dimensionExtent > 0) { + dimension = Array.get(dimension, 0); + } else { + outputStream.println("</m>"); + return; + } + } + // print the matrix data + for (int i = 0; i < Array.getLength(data); i++) { + printMatrixData(infos, Array.get(data, i), declaredName, description, 0); + } + // close the matrix tag + outputStream.println("</m>"); + } else { + printScalarData(infos, data, description); + } + } + + private void printMatrixData(ObservableDataInfos infos, Object data, String declaredName, String description, int depth) { + if (data.getClass().isArray()) { + for (int i = 0; i < Array.getLength(data); i++) { + printMatrixData(infos, Array.get(data, i), declaredName, description, depth + 1); + } + } else { + printScalarData(infos, data); + } + } + + private void printScalarData(ObservableDataInfos infos, Object data, String description) { + outputStream.print(infos.tagOpeningWithName); + outputStream.print(description); + outputStream.print("\">"); + outputStream.print(data); + outputStream.println(infos.tagClosing); + } + + private void printScalarData(ObservableDataInfos infos, Object data) { + outputStream.print(infos.tagOpening); + outputStream.print(data); + outputStream.println(infos.tagClosing); + } + + @Override + public void close() { + outputStream.print("</outputData>"); + super.close(); + } +} diff --git a/src/main/java/fr/cemagref/observation/observers/XMLObserverSelectable.java b/src/main/java/fr/cemagref/observation/observers/XMLObserverSelectable.java new file mode 100644 index 0000000000000000000000000000000000000000..7982bef9402d706b4b333d43b0d3f6d60fd1c887 --- /dev/null +++ b/src/main/java/fr/cemagref/observation/observers/XMLObserverSelectable.java @@ -0,0 +1,138 @@ +package fr.cemagref.observation.observers; + +import fr.cemagref.observation.kernel.ObservablesHandler; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; + +/** + * Observer that write data in XML format as defined by the Mexico working group. + */ +public class XMLObserverSelectable extends ConsoleObserver { + + protected String observableVariable; + protected transient ObservablesHandler.ObservableFetcher variable; + protected transient String tagOpeningWithName; + protected transient String tagOpening; + protected transient String tagClosing; + protected transient int numberOfDimensions; + private static transient Map<Class, String> typeForXml; + + public XMLObserverSelectable(boolean sysout, String outputFile) { + super(sysout, outputFile); + } + + @Override + public void init() { + super.init(); + if (typeForXml == null) { + typeForXml = new HashMap<Class, String>(); + typeForXml.put(Double.class, "d"); + typeForXml.put(double.class, "d"); + typeForXml.put(Integer.class, "i"); + typeForXml.put(int.class, "i"); + typeForXml.put(String.class, "s"); + } + } + + @Override + public void addObservable(ObservablesHandler classObservable) { + if (typeForXml == null) { + init(); + } + // Storing the infos for this observable + variable = classObservable.getObservableFetcherByName(observableVariable); + if (variable != null) { + Class observableType = variable.getDeclaredType(); + // if the type is an array, going in depth to find the final component type and the number of dimensions + numberOfDimensions = 0; + while (observableType.isArray()) { + numberOfDimensions++; + observableType = observableType.getComponentType(); + } + tagOpening = "<" + typeForXml.get(observableType) + ">"; + tagOpeningWithName = "<" + typeForXml.get(observableType) + " name =\""; + tagClosing = "</" + typeForXml.get(observableType) + ">"; + } + } + + @Override + public void valueChanged(ObservablesHandler clObservable, Object instance, long t) { + if (variable != null) { + try { + outputStream.print("<step time =\""); + outputStream.print(t); + outputStream.println("\">"); + printData(variable.fetchValue(instance), variable.getDeclaredName(), + variable.getDescription()); + outputStream.println("</step>"); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + return; + } + } + } + + private void printData(Object data, String declaredName, String description) { + if (numberOfDimensions > 0) { + // Open the matrix tag + outputStream.print("<m name=\""); + outputStream.print(description); + outputStream.print("\">\n"); + // print metadata for each dimensions + Object dimension = data; + int dimensionExtent; + for (int i = 1; i <= numberOfDimensions; i++) { + outputStream.print("<dimension name=\"dim"); + outputStream.print(i); + outputStream.print("\" size=\""); + dimensionExtent = Array.getLength(dimension); + outputStream.print(dimensionExtent); + outputStream.println("\"><label></label></dimension>"); + if (dimensionExtent > 0) { + dimension = Array.get(dimension, 0); + } else { + outputStream.println("</m>"); + return; + } + } + // print the matrix data + for (int i = 0; i < Array.getLength(data); i++) { + printMatrixData(Array.get(data, i), declaredName, description, 0); + } + // close the matrix tag + outputStream.println("</m>"); + } else { + printScalarData(data, description); + } + } + + private void printMatrixData(Object data, String declaredName, String description, int depth) { + if (data.getClass().isArray()) { + for (int i = 0; i < Array.getLength(data); i++) { + printMatrixData(Array.get(data, i), declaredName, description, depth + 1); + } + } else { + printScalarData(data); + } + } + + private void printScalarData(Object data, String description) { + outputStream.print(tagOpeningWithName); + outputStream.print(description); + outputStream.print("\">"); + outputStream.print(data); + outputStream.println(tagClosing); + } + + private void printScalarData(Object data) { + outputStream.print(tagOpening); + outputStream.print(data); + outputStream.println(tagClosing); + } +} diff --git a/src/main/java/fr/cemagref/observation/observers/jfreechart/GraphType.java b/src/main/java/fr/cemagref/observation/observers/jfreechart/GraphType.java new file mode 100644 index 0000000000000000000000000000000000000000..3fd8307b530d0de142e800ce3e125466ec57bf3f --- /dev/null +++ b/src/main/java/fr/cemagref/observation/observers/jfreechart/GraphType.java @@ -0,0 +1,30 @@ +package fr.cemagref.observation.observers.jfreechart; + +public enum GraphType { + + POINT(1,"Points"), + LINE(2,"Lignes"), + AREA(3,"Aires"), + AREASTEP(4,"Aires en escalier"), + STEP(5,"Escalier"); + + private int code; + private String description; + + private GraphType(int i,String description) { + code=i; + this.description=description; + } + + public int code() { + return code; + } + + public String getDescription() { + return description; + } + + public String toString() { + return getDescription(); + } +} diff --git a/src/main/java/fr/cemagref/observation/observers/jfreechart/TemporalChart.java b/src/main/java/fr/cemagref/observation/observers/jfreechart/TemporalChart.java new file mode 100644 index 0000000000000000000000000000000000000000..d3f463e9d34937e164619a906b9201e0556117bc --- /dev/null +++ b/src/main/java/fr/cemagref/observation/observers/jfreechart/TemporalChart.java @@ -0,0 +1,128 @@ +package fr.cemagref.observation.observers.jfreechart; + +import javax.swing.JComboBox; +import javax.swing.JComponent; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.data.xy.XYSeriesCollection; + +import fr.cemagref.observation.gui.Configurable; +import fr.cemagref.observation.gui.Drawable; +import fr.cemagref.observation.kernel.ObservablesHandler; +import fr.cemagref.observation.kernel.ObserverListener; +import fr.cemagref.ohoui.filters.NoTransientField; +import fr.cemagref.ohoui.swing.OUIPanel; +import fr.cemagref.ohoui.swing.OhOUI; +import fr.cemagref.ohoui.swing.OhOUIDialog; + +public abstract class TemporalChart implements ObserverListener, Configurable, Drawable { + + private transient ChartPanel chartPanel; + private transient JFreeChart jfchart; + protected transient XYSeriesCollection dataset; + protected GraphType graphType = GraphType.LINE; + + /** + * <code>variable</code> is the variable to represent + */ + protected transient ObservablesHandler.ObservableFetcher variable; + protected transient ObservablesHandler classObservable; + private String title = "", xAxisLabel = "Time", yAxisLabel = ""; + + public TemporalChart() { + graphTypeUpdated(); + } + + public TemporalChart(ObservablesHandler.ObservableFetcher variable) { + this(); + this.variable = variable; + variableUpdated(); + } + + protected void variableUpdated() { + if (variable != null) { + yAxisLabel = variable.getDescription(); + title = "Observation of " + yAxisLabel; + } + } + + protected void graphTypeUpdated() { + PlotOrientation orientation = PlotOrientation.VERTICAL; + boolean legend = true; + boolean tooltips = true; + boolean urls = false; + + if(graphType.equals(GraphType.POINT)){ + jfchart=ChartFactory.createScatterPlot(title,xAxisLabel,yAxisLabel,dataset,orientation,legend,tooltips,urls); + }else if(graphType.equals(GraphType.LINE)){ + jfchart = ChartFactory.createXYLineChart(title,xAxisLabel,yAxisLabel,dataset,orientation,legend,tooltips,urls); + }else if(graphType.equals(GraphType.AREA)){ + jfchart = ChartFactory.createXYAreaChart(title,xAxisLabel,yAxisLabel,dataset,orientation,legend,tooltips,urls); + }else if(graphType.equals(GraphType.STEP)){ + jfchart = ChartFactory.createXYStepChart(title,xAxisLabel,yAxisLabel,dataset,orientation,legend,tooltips,urls); + }else if(graphType.equals(GraphType.AREASTEP)){ + jfchart = ChartFactory.createXYStepAreaChart(title,xAxisLabel,yAxisLabel,dataset,orientation,legend,tooltips,urls); + } + if (chartPanel == null) + chartPanel = new ChartPanel(jfchart); + else + chartPanel.setChart(jfchart); + } + + public JFreeChart getJfchart() { + return jfchart; + } + + public void configure() { + ObservablesHandler.ObservableFetcher variableBak = variable; + OhOUIDialog dialog = OhOUI.getDialog(null,this,new NoTransientField()); + JComboBox comboBox = new JComboBox(classObservable.getDescriptions()); + if (variable != null) + comboBox.setSelectedItem(variable.getDescription()); + OUIPanel ouiPanel = OUIPanel.makeLabelComponentOUIPanel(variable, comboBox, "Variable", ""); + dialog.getContentPane().add(ouiPanel.getPanel(),0); + dialog.pack(); + dialog.setVisible(true); + variable = classObservable.getObservableFetcher((String)comboBox.getSelectedItem()); + if (variable != null) + if ( ! variable.equals(variableBak)) + variableUpdated(); + graphTypeUpdated(); + } + + /** + * @see fr.cemagref.observation.kernel.ObserverListener#addObservable(fr.cemagref.observation.kernel.ObservablesHandler) + */ + public void addObservable(ObservablesHandler classObservable) { + this.classObservable = classObservable; + } + + /** + * @see fr.cemagref.observation.gui.Drawable#getDisplay() + */ + public JComponent getDisplay() { + return chartPanel; + } + + /** + * @see fr.cemagref.observation.gui.Drawable#getTitle() + */ + public String getTitle() { + return title; + } + + /** + * @see fr.cemagref.observation.kernel.ObserverListener#init() + */ + public void init() { + if (classObservable != null) + if (classObservable.numberOfAttributes() > 0) + variable = classObservable.getObservableFetcher(0); + // TODO classObservable.getAttributes().length *must* be > 0 + } + + +} diff --git a/src/main/java/fr/cemagref/observation/observers/jfreechart/TemporalMeanChart.java b/src/main/java/fr/cemagref/observation/observers/jfreechart/TemporalMeanChart.java new file mode 100644 index 0000000000000000000000000000000000000000..c91ee25bd5dfc93b5f2e80e82de545c0970c5335 --- /dev/null +++ b/src/main/java/fr/cemagref/observation/observers/jfreechart/TemporalMeanChart.java @@ -0,0 +1,109 @@ +package fr.cemagref.observation.observers.jfreechart; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +import fr.cemagref.observation.kernel.ObservablesHandler; + +/** + * <code>TemporalMeanChart</code> allow to draw a chart representing the mean + * of several temporal series + */ +public class TemporalMeanChart extends TemporalChart { + + /** + * <code>serie</code> store the temporal values of the mean, ready to draw + */ + private transient XYSeries serie; + + /** + * <code>temporalValues</code> + */ + private transient Map<Long, List<Double>> temporalValues = new HashMap<Long, List<Double>>(); + + /** + * This constructor init the chart with a line drawstyle. + * + * @param variable + * the variable to represent + */ + public TemporalMeanChart(ObservablesHandler.ObservableFetcher variable) { + super(variable); + init(); + } + + /** + * <code>mean</code> computes the mean of the value of the list passed as + * arguments + */ + private double mean(List<Double> list) { + double mean = 0; + for (Double d : list) { + mean += d; + } + mean = mean / list.size(); + return mean; + } + + /** + * @see fr.cemagref.observation.kernel.ObserverListener#valueChanged(fr.cemagref.observation.kernel.ObservablesHandler, + * java.lang.Object, int) + */ + @Override + public void valueChanged(ObservablesHandler clObservable, Object instance, long t) { + // we fetch the value of the observable + double value; + try { + value = (Double)variable.fetchValue(instance); + } catch (IllegalArgumentException e1) { + e1.printStackTrace(); + return; + } catch (IllegalAccessException e1) { + e1.printStackTrace(); + return; + } catch (InvocationTargetException e) { + e.printStackTrace(); + return; + } + + int index = serie.indexOf(t); + if (index < 0) { + // This is the first value for this time step + List<Double> list = new ArrayList<Double>(); + list.add(value); + temporalValues.put(t, list); + // For instance, the mean is this first value + serie.add(t, value); + } else { + // we add this new value to the list + List<Double> list = temporalValues.get(t); + list.add(value); + // and we update the mean + serie.getDataItem(index).setY(mean(list)); + } + } + + /** + * @see fr.cemagref.observation.kernel.ObserverListener#init() + */ + @Override + public void init() { + super.init(); + serie = new XYSeries("values"); + dataset = new XYSeriesCollection(serie); + graphTypeUpdated(); + } + + /** + * @see fr.cemagref.observation.kernel.ObserverListener#close() + */ + public void close() { + } + +} diff --git a/src/main/java/fr/cemagref/observation/observers/jfreechart/TemporalSerieChart.java b/src/main/java/fr/cemagref/observation/observers/jfreechart/TemporalSerieChart.java new file mode 100644 index 0000000000000000000000000000000000000000..f86001a3a9e61c26c58ded618eeef64b4eccf3c0 --- /dev/null +++ b/src/main/java/fr/cemagref/observation/observers/jfreechart/TemporalSerieChart.java @@ -0,0 +1,75 @@ +package fr.cemagref.observation.observers.jfreechart; + +import java.lang.reflect.InvocationTargetException; + +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +import fr.cemagref.observation.kernel.ObservablesHandler; + +public class TemporalSerieChart extends TemporalChart { + + public TemporalSerieChart() { + super(); + init(); + } + + public TemporalSerieChart(ObservablesHandler.ObservableFetcher variable) { + super(variable); + init(); + } + + /** + * @see fr.cemagref.observation.kernel.ObserverListener#valueChanged(fr.cemagref.observation.kernel.ObservablesHandler, java.lang.Object, int) + */ + @Override + public void valueChanged(ObservablesHandler clObservable, Object instance, long t) { + // we fetch the value of the observable + double value; + try { + Object objectValue = variable.fetchValue(instance); + if (objectValue instanceof Integer) + value = (Integer)objectValue; + else + value = (Double)objectValue; + } catch (IllegalArgumentException e1) { + e1.printStackTrace(); + return; + } catch (IllegalAccessException e1) { + e1.printStackTrace(); + return; + } catch (InvocationTargetException e) { + e.printStackTrace(); + return; + } + + int index = dataset.indexOf(instance.hashCode()); + XYSeries serie; + if (index >= 0) { + serie = dataset.getSeries(index); + } else { + // This is the first value of the observable + serie = new XYSeries(instance.hashCode()); + dataset.addSeries(serie); + } + serie.add(t, value); + } + + /** + * @see fr.cemagref.observation.kernel.ObserverListener#init() + */ + @Override + public void init() { + super.init(); + dataset = new XYSeriesCollection(); + graphTypeUpdated(); + } + + /** + * @see fr.cemagref.observation.kernel.ObserverListener#close() + */ + @Override + public void close() { + } + +} diff --git a/src/main/java/fr/cemagref/observation/observers/jfreechart/XYSeriesChart.java b/src/main/java/fr/cemagref/observation/observers/jfreechart/XYSeriesChart.java new file mode 100644 index 0000000000000000000000000000000000000000..e96691e5c73cc17f6cdbc42c6fab2f96fbc1b229 --- /dev/null +++ b/src/main/java/fr/cemagref/observation/observers/jfreechart/XYSeriesChart.java @@ -0,0 +1,210 @@ +package fr.cemagref.observation.observers.jfreechart; + +import com.thoughtworks.xstream.XStream; +import javax.swing.JComboBox; +import javax.swing.JComponent; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.data.xy.XYSeriesCollection; + +import fr.cemagref.observation.gui.Configurable; +import fr.cemagref.observation.gui.Drawable; +import fr.cemagref.observation.kernel.ObservablesHandler; +import fr.cemagref.observation.kernel.ObservablesHandler.ObservableFetcher; +import fr.cemagref.observation.kernel.ObserverListener; +import fr.cemagref.ohoui.filters.NoTransientField; +import fr.cemagref.ohoui.swing.OUIPanel; +import fr.cemagref.ohoui.swing.OhOUI; +import fr.cemagref.ohoui.swing.OhOUIDialog; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; +import org.jfree.data.xy.XYDataset; + +public class XYSeriesChart implements ObserverListener, Configurable, Drawable { + + private transient ChartPanel chartPanel; + private transient JFreeChart jfchart; + protected transient XYDataset dataset; + /** + * <code>variable</code> is the variable to represent + */ + protected transient List<ObservableFetcher> variableXY; + protected transient ObservablesHandler classObservable; + protected transient List<Integer> xIndex = null; + protected transient List<Integer> yIndex = null; + protected GraphType graphType = GraphType.LINE; + private String title = "", xAxisLabel = "x", yAxisLabel = "y"; + + public XYSeriesChart() { + graphTypeUpdated(); + } + + public XYSeriesChart(List<ObservableFetcher> xy) { + this(); + this.variableXY = xy; + variableUpdated(); + } + + protected void variableUpdated() { + if ((xIndex != null) && (xIndex.size() > 0)) { + xAxisLabel = variableXY.get(xIndex.get(0)).getDescription(); + } + if ((yIndex != null) && (yIndex.size() > 0)) { + yAxisLabel = variableXY.get(yIndex.get(1)).getDescription(); + } + title = "Observation of " + yAxisLabel; + } + + protected void graphTypeUpdated() { + PlotOrientation orientation = PlotOrientation.VERTICAL; + boolean legend = true; + boolean tooltips = true; + boolean urls = false; + + if (graphType.equals(GraphType.POINT)) { + jfchart = ChartFactory.createScatterPlot(title, xAxisLabel, yAxisLabel, dataset, orientation, legend, tooltips, urls); + } else if (graphType.equals(GraphType.LINE)) { + jfchart = ChartFactory.createXYLineChart(title, xAxisLabel, yAxisLabel, dataset, orientation, legend, tooltips, urls); + } else if (graphType.equals(GraphType.AREA)) { + jfchart = ChartFactory.createXYAreaChart(title, xAxisLabel, yAxisLabel, dataset, orientation, legend, tooltips, urls); + } else if (graphType.equals(GraphType.STEP)) { + jfchart = ChartFactory.createXYStepChart(title, xAxisLabel, yAxisLabel, dataset, orientation, legend, tooltips, urls); + } else if (graphType.equals(GraphType.AREASTEP)) { + jfchart = ChartFactory.createXYStepAreaChart(title, xAxisLabel, yAxisLabel, dataset, orientation, legend, tooltips, urls); + } + if (chartPanel == null) { + chartPanel = new ChartPanel(jfchart); + } else { + chartPanel.setChart(jfchart); + } + } + + public JFreeChart getJfchart() { + return jfchart; + } + + @Override + public void configure() { + List<ObservableFetcher> variableBak = variableXY; + OhOUIDialog dialog = OhOUI.getDialog(null, this, new NoTransientField()); +// panel for X variable + JComboBox comboBoxX = new JComboBox(classObservable.getDescriptions()); + if (variableXY != null) { + comboBoxX.setSelectedItem(variableXY.get(0).getDescription()); + } + OUIPanel ouiPanelX = OUIPanel.makeLabelComponentOUIPanel(variableXY, comboBoxX, "Variable for X axis", ""); + dialog.getContentPane().add(ouiPanelX.getPanel(), 0); + +// panel for Y variable + JComboBox comboBoxY = new JComboBox(classObservable.getDescriptions()); + if (variableXY != null) { + comboBoxY.setSelectedItem(variableXY.get(1).getDescription()); + } + OUIPanel ouiPanelY = OUIPanel.makeLabelComponentOUIPanel(variableXY, comboBoxY, "Variable for Y axis", ""); + dialog.getContentPane().add(ouiPanelY.getPanel(), 1); + + dialog.pack(); + dialog.setVisible(true); + variableXY = new ArrayList<ObservableFetcher>(); + variableXY.add(classObservable.getObservableFetcher((String) comboBoxX.getSelectedItem())); + variableXY.add(classObservable.getObservableFetcher((String) comboBoxY.getSelectedItem())); + if (variableXY != null) { + if (!variableXY.equals(variableBak)) { + variableUpdated(); + } + } + graphTypeUpdated(); + } + + /** + * @see fr.cemagref.observation.kernel.ObserverListener#addObservable(fr.cemagref.observation.kernel.ObservablesHandler) + */ + @Override + public void addObservable(ObservablesHandler classObservable) { + this.classObservable = classObservable; + } + + /** + * @see fr.cemagref.observation.gui.Drawable#getDisplay() + */ + @Override + public JComponent getDisplay() { + return chartPanel; + } + + /** + * @see fr.cemagref.observation.gui.Drawable#getTitle() + */ + @Override + public String getTitle() { + return title; + } + + /** + * @see fr.cemagref.observation.kernel.ObserverListener#init() + */ + @Override + public void init() { + if (classObservable != null) { + if (classObservable.numberOfAttributes() > 0) { + variableXY = new ArrayList<ObservableFetcher>(); + + variableXY.add(classObservable.getObservableFetcher(0)); + variableXY.add(classObservable.getObservableFetcher(1)); + } + } + // TODO classObservable.getAttributes().length *must* be > 0 + dataset = new XYSeriesCollection(); + graphTypeUpdated(); + } + + @Override + public void valueChanged(ObservablesHandler clObservable, Object instance, long t) { + // we fetch the value of the observable + List<Object> value = new ArrayList<Object>(); + try { + for (ObservableFetcher va : variableXY) { + Object objectValue = va.fetchValue(instance); + if (objectValue instanceof Integer) { + value.add((Integer) objectValue); + } else { + value.add((Double) objectValue); + } + } + } catch (IllegalArgumentException e1) { + e1.printStackTrace(); + return; + } catch (IllegalAccessException e1) { + e1.printStackTrace(); + return; + } catch (InvocationTargetException e) { + e.printStackTrace(); + return; + } + + /* TODO a revoir + int index = dataset.indexOf(instance.hashCode()); + XYSeries serie; + if (index >= 0) { + serie = dataset.getSeries(index); + } else { + // This is the first value of the observable + serie = new XYSeries(instance.hashCode()); + dataset.addSeries(serie); + } + serie.add(t, value); + * */ + } + + @Override + public void close() { + } + + public static void main(String[] args) { + System.out.println(new XStream().toXML(new XYSeriesChart())); + } +}