diff --git a/nb-configuration.xml b/nb-configuration.xml new file mode 100644 index 0000000000000000000000000000000000000000..29f974656b191f1ed5e7d332dc04185a5950e25a --- /dev/null +++ b/nb-configuration.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project-shared-configuration> + <!-- +This file contains additional configuration written by modules in the NetBeans IDE. +The configuration is intended to be shared among all the users of project and +therefore it is assumed to be part of version control checkout. +Without this configuration present, some functionality in the IDE may be limited or fail altogether. +--> + <properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1"> + <!-- +Properties that influence various parts of the IDE, especially code formatting and the like. +You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up. +That way multiple projects can share the same settings (useful for formatting rules for example). +Any value defined here will override the pom.xml file value but is only applicable to the current project. +--> + <org-netbeans-modules-html-editor-lib.default-html-public-id>HTML5</org-netbeans-modules-html-editor-lib.default-html-public-id> + </properties> +</project-shared-configuration> diff --git a/pom.xml b/pom.xml index efe854aea380d4f0cccab76eb66bb82cbfb1c2de..cba28d3e62266772b855069d36bc8de64f63d044 100644 --- a/pom.xml +++ b/pom.xml @@ -62,8 +62,8 @@ <packaging>jar</packaging> <scm> - <connection>scm:svn:http://trac.clermont.cemagref.fr/svn/LISC/Observation/tags/observation-0.1.1</connection> - <url>http://trac.clermont.cemagref.fr/projets/LISC/browser/Observation/tags/observation-0.1.1</url> + <connection>scm:svn:http://trac.clermont.cemagref.fr/svn/LISC/Observation/tags/observation-0.1.4</connection> + <url>http://trac.clermont.cemagref.fr/projets/LISC/browser/Observation/tags/observation-0.1.4</url> </scm> <build> <plugins> diff --git a/src/main/java/fr/cemagref/observation/gui/ObserversManagerPanel.java b/src/main/java/fr/cemagref/observation/gui/ObserversManagerPanel.java index 5ab47c2ede5d9f3390e7cac2722f5c3adfe658dd..0a5f8aa7160fbb5ad9194f1d9689d1f4a14e467c 100644 --- a/src/main/java/fr/cemagref/observation/gui/ObserversManagerPanel.java +++ b/src/main/java/fr/cemagref/observation/gui/ObserversManagerPanel.java @@ -21,90 +21,103 @@ import javax.swing.event.ListSelectionListener; @SuppressWarnings("serial") public class ObserversManagerPanel extends JPanel implements ListSelectionListener { + private ObservableManager observableManager; 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) { + + public ObserversManagerPanel(ObservableManager observableManager, ObserversManagerHandler observersManagerHandler) { super(); + this.observableManager = observableManager; this.observersManagerHandler = observersManagerHandler; - + this.setLayout(new BorderLayout()); // The list - list = new JList(ObservableManager.getObservableManager()); + list = new JList(observableManager); list.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); list.setLayoutOrientation(JList.HORIZONTAL_WRAP); list.addListSelectionListener(this); JScrollPane listScroller = new JScrollPane(list); - this.add(listScroller,BorderLayout.CENTER); - + this.add(listScroller, BorderLayout.CENTER); + // The toolbar JToolBar toolBar = new JToolBar(); toolBar.setFloatable(false); - add(toolBar,BorderLayout.PAGE_END); + add(toolBar, BorderLayout.PAGE_END); // The "remove" button - removeButton = ControlsFactory.makeButton("general/Delete24",new RemoveButtonListener(),"Remove the selected module","Remove"); + 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"); + 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"); + 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 reloadObservers(ObservableManager observableManager) { + this.observableManager = observableManager; + list.setModel(observableManager); + this.valueChanged(null); + } + @Override public void valueChanged(ListSelectionEvent e) { - if (list.getSelectedIndex()>=0) { - ClassAndObserver classAndObserver = (ClassAndObserver)list.getSelectedValue(); + 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) - ); + && !observersShowed.contains(classAndObserver)); } else { removeButton.setEnabled(false); confButton.setEnabled(false); showButton.setEnabled(false); } - + } private class RemoveButtonListener implements ActionListener { + + @Override 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()); + 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 { + + @Override public void actionPerformed(ActionEvent e) { - ((Configurable)((ClassAndObserver)list.getSelectedValue()).getObserver()).configure(); + ((Configurable) ((ClassAndObserver) list.getSelectedValue()).getObserver()).configure(); } } - + private class ShowButtonListener implements ActionListener { + + @Override public void actionPerformed(ActionEvent e) { - ClassAndObserver classAndObserver = (ClassAndObserver)list.getSelectedValue(); + 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()); + 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 index c87ab68a61d39ac7ae29899e5cbe2d74b1e4ec03..91d7afc0455a95c953c4b3a53d71432634c19861 100644 --- a/src/main/java/fr/cemagref/observation/kernel/Observable.java +++ b/src/main/java/fr/cemagref/observation/kernel/Observable.java @@ -1,6 +1,3 @@ -/** - * L'interface "annotation" pour marquer les observables. - */ package fr.cemagref.observation.kernel; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -19,4 +16,25 @@ public @interface Observable { * @return the textual description of the observable. */ String description(); + + /** + * Indicates the size of each dimension of the observable. If a labelsMethod + * is provided, the size will be ignored, but the number of dimensions is needed. + * + * @return + */ + int[] size() default {}; + + /** + * Indicates the name of the method to invoke for having the labels of the data. + * It is usefull, when the size of the data vector isn't a constant, but can be + * retrieved programmaticaly. + * + * The target method must accept a single <tt>Integer</tt> argument that is the dimension + * concerned. For example, if your observed data is a matrix (dimension 2), you should + * give "{0,0}" as size, and your method will return an array of labels for each dimension. + * + * @return + */ + String labelsMethod() default ""; } diff --git a/src/main/java/fr/cemagref/observation/kernel/ObservableManager.java b/src/main/java/fr/cemagref/observation/kernel/ObservableManager.java index 76cb0b3ee3a285c7aa6f02ca1ef8c6fc4c4434bb..77d26fc2b2b6bb245a916ef019b8e82e26388e6a 100644 --- a/src/main/java/fr/cemagref/observation/kernel/ObservableManager.java +++ b/src/main/java/fr/cemagref/observation/kernel/ObservableManager.java @@ -2,7 +2,6 @@ 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; @@ -14,40 +13,26 @@ import javax.swing.event.ListDataListener; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.io.xml.DomDriver; +import java.util.HashMap; -/** - * 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; + public ObservableManager() { + observables = new HashMap<Class, ObservablesHandler>(); } /** * clear method clears all class-observers associations */ - public static void clear() { - for (ObservablesHandler co : om.observables.values()) { + public void clear() { + for (ObservablesHandler co : observables.values()) { co.clearObservers(); } - om.fireObservableManagerStructureChanged(); + fireObservableManagerStructureChanged(); } /** @@ -59,13 +44,13 @@ public class ObservableManager implements ListModel { * @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); + public ObservablesHandler addObservable(Class cl) { + if (observables.containsKey(cl)) { + return observables.get(cl); } ObservablesHandler co = new ObservablesHandler(cl); - getObservableManager().observables.put(cl, co); - getObservableManager().fireObservableManagerStructureChanged(); + observables.put(cl, co); + fireObservableManagerStructureChanged(); return co; } @@ -83,17 +68,17 @@ public class ObservableManager implements ListModel { * @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); + public ObservablesHandler getObservable(Class cl) { + return observables.get(cl); } /** Ajoute un ObserverListener à la liste. * * @param ob L'ObserverListener à ajouter. */ - public static ObserverListener addObserverListener(Class cl, ObserverListener ob) { + public ObserverListener addObserverListener(Class cl, ObserverListener ob) { ObserverListener observerListener = addObservable(cl).addObserverListener(ob); - getObservableManager().fireObservableManagerStructureChanged(); + fireObservableManagerStructureChanged(); return observerListener; } @@ -102,21 +87,21 @@ public class ObservableManager implements ListModel { * @param ob L'ObserverListener à retirer. * @return */ - public static boolean removeObserverListener(Class cl, ObserverListener ob) { - boolean response = getObservable(cl).removeObserverListener(ob); - getObservableManager().fireObservableManagerStructureChanged(); + public boolean removeObserverListener(Class cl, ObserverListener ob) { + boolean response = getObservable(cl).removeObserverListener(this, ob); + fireObservableManagerStructureChanged(); return response; } - public static void initObservers() { - for (ObservablesHandler classObservable : getObservableManager().observables.values()) { + public void initObservers() { + for (ObservablesHandler classObservable : observables.values()) { for (ObserverListener observer : classObservable.getObservers()) { observer.init(); } } } - public static void closeObservers() { + public void closeObservers() { for (ObservablesHandler classObservable : getObservables().values()) { for (ObserverListener observer : classObservable.getObservers()) { observer.close(); @@ -124,13 +109,13 @@ public class ObservableManager implements ListModel { } } - public static Map<Class, ObservablesHandler> getObservables() { - return getObservableManager().observables; + public Map<Class, ObservablesHandler> getObservables() { + return 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()) { + public 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()) { @@ -140,13 +125,14 @@ public class ObservableManager implements ListModel { } } - public static void setObservers(Reader observablesReader) { + public void setObservers(Reader observablesReader) { setObservers(observablesReader, new XStream(new DomDriver())); } /** * @see javax.swing.ListModel#getSize() */ + @Override public int getSize() { int size = 0; for (ObservablesHandler classObservable : observables.values()) { @@ -158,6 +144,7 @@ public class ObservableManager implements ListModel { /** * @see javax.swing.ListModel#getElementAt(int) */ + @Override public Object getElementAt(int index) { Set<Map.Entry<Class, ObservablesHandler>> entrySet = observables.entrySet(); Iterator<Map.Entry<Class, ObservablesHandler>> iterator = entrySet.iterator(); @@ -179,6 +166,7 @@ public class ObservableManager implements ListModel { /** * @see javax.swing.ListModel#addListDataListener(javax.swing.event.ListDataListener) */ + @Override public void addListDataListener(ListDataListener l) { listDataListeners.add(l); } @@ -186,6 +174,7 @@ public class ObservableManager implements ListModel { /** * @see javax.swing.ListModel#removeListDataListener(javax.swing.event.ListDataListener) */ + @Override public void removeListDataListener(ListDataListener l) { listDataListeners.remove(l); } @@ -214,8 +203,9 @@ public class ObservableManager implements ListModel { return observer; } + @Override public String toString() { - return observer.getClass().getSimpleName() + " -> " + type.getSimpleName(); + return type.getSimpleName() + " -> " + observer.toString() ; } /** diff --git a/src/main/java/fr/cemagref/observation/kernel/ObservablesHandler.java b/src/main/java/fr/cemagref/observation/kernel/ObservablesHandler.java index 98a805408960c0b9a34c6feff4ef11e375827929..5809a30e7e725f71d6fbcbf01ed9c6e8e8333a0c 100644 --- a/src/main/java/fr/cemagref/observation/kernel/ObservablesHandler.java +++ b/src/main/java/fr/cemagref/observation/kernel/ObservablesHandler.java @@ -5,8 +5,11 @@ 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 { @@ -163,9 +166,9 @@ public class ObservablesHandler { * @param ob L'ObserverListener à retirer. * @return */ - public boolean removeObserverListener(ObserverListener ob) { + public boolean removeObserverListener(ObservableManager observableManager, ObserverListener ob) { boolean response = observers.remove(ob); - ObservableManager.getObservableManager().fireObservableManagerStructureChanged(); + observableManager.fireObservableManagerStructureChanged(); return response; } @@ -217,9 +220,78 @@ public class ObservablesHandler { 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(); + public abstract String getDeclaredName(); - public abstract String getDescription(); + public String getDescription() { + return getAnnotation().description(); + } + + protected final int[] getSize() { + return getAnnotation().size(); + } + + public String getLabelsMethod() { + return getAnnotation().labelsMethod(); + } public abstract Class getDeclaredType(); } @@ -241,8 +313,8 @@ public class ObservablesHandler { } @Override - public String getDescription() { - return method.getAnnotation(Observable.class).description(); + public Observable getAnnotation() { + return method.getAnnotation(Observable.class); } @Override @@ -272,8 +344,8 @@ public class ObservablesHandler { } @Override - public String getDescription() { - return field.getAnnotation(Observable.class).description(); + public Observable getAnnotation() { + return field.getAnnotation(Observable.class); } @Override diff --git a/src/main/java/fr/cemagref/observation/kernel/ObserverListener.java b/src/main/java/fr/cemagref/observation/kernel/ObserverListener.java index 597a9ff6129ad6b8de11493fc2da095d89bd8a3c..3cb877962335e2f39a8a87a21de27da3135e8438 100644 --- a/src/main/java/fr/cemagref/observation/kernel/ObserverListener.java +++ b/src/main/java/fr/cemagref/observation/kernel/ObserverListener.java @@ -1,36 +1,38 @@ package fr.cemagref.observation.kernel; -public interface ObserverListener { +public abstract class 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 + * + * @param classObservable The new observable connected to this observer */ - public void addObservable(ObservablesHandler classObservable); + public abstract 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 + * <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); + public abstract void valueChanged(ObservablesHandler clObservable, Object instance, long t); /** * <code>init</code> Called at begin of simulation */ - public void init(); + public abstract void init(); /** * <code>close</code> Called at end of simulation - * + * */ - public void close(); + public abstract void close(); + + @Override + public String toString() { + return this.getClass().getSimpleName(); + } } diff --git a/src/main/java/fr/cemagref/observation/observers/CSVObserver.java b/src/main/java/fr/cemagref/observation/observers/CSVObserver.java index 3791290d8e03fe3362f7da1fdcde11e5cec8c70c..5f321d350c7f13a50bb8ad588a2d0b0b1e4c7955 100644 --- a/src/main/java/fr/cemagref/observation/observers/CSVObserver.java +++ b/src/main/java/fr/cemagref/observation/observers/CSVObserver.java @@ -4,67 +4,119 @@ import java.lang.reflect.InvocationTargetException; import fr.cemagref.observation.kernel.ObservablesHandler; import fr.cemagref.ohoui.annotations.Description; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; 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 */ + @Description(name = "Field separator", tooltip = "") + protected char separator = ';'; + private List<String> observables; + protected transient List<ObservablesHandler.ObservableFetcher> fetchers; + /** + * <code>classObservable</code> is used to display name of attributes as + * header + */ private transient ObservablesHandler classObservable; + public CSVObserver() { + } + public CSVObserver(boolean sysout, String outputFile) { super(sysout, outputFile); } - + + public CSVObserver(boolean sysout, String outputFile, String... obsvervables) { + super(sysout, outputFile); + this.observables = Arrays.asList(obsvervables); + } + + public List<String> getObservables() { + return observables; + } + @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; + if (isInDates(t)) { + StringBuffer buf = new StringBuffer(); + StringBuffer sbSeparator = new StringBuffer(" " + this.separator + " "); + // print current Time + buf.append(t); + // print value of each field + for (ObservablesHandler.ObservableFetcher fetcher : fetchers) { + buf.append(sbSeparator); + Object value = getValue(fetcher, instance); + buf.append(value == null ? "N/A" : value); } + outputStream.println(buf); + } + } + + /** + * + * @param fetcher + * @param instance + * @return the value returned by the fetcher or null if a problem has + * occured due to the introspection. + */ + protected Object getValue(ObservablesHandler.ObservableFetcher fetcher, Object instance) { + try { + return fetcher.fetchValue(instance); + } catch (IllegalArgumentException ex) { + Logger.getLogger(CSVObserver.class.getName()).log(Level.SEVERE, null, ex); + return null; + } catch (IllegalAccessException ex) { + Logger.getLogger(CSVObserver.class.getName()).log(Level.SEVERE, null, ex); + return null; + } catch (InvocationTargetException ex) { + Logger.getLogger(CSVObserver.class.getName()).log(Level.SEVERE, null, ex); + return null; } - outputStream.println(buf); } @Override public void init() { super.init(); if (classObservable != null) { + fetchers = new ArrayList<ObservablesHandler.ObservableFetcher>(); + if (observables != null) { + for (String name : observables) { + fetchers.add(classObservable.getObservableFetcherByName(name)); + } + } else { + fetchers.addAll(Arrays.asList(classObservable.getObservableFetchers())); + } // Headers printing StringBuffer buf = new StringBuffer(); - StringBuffer sbSeparator = new StringBuffer(" "+this.separator+" "); + StringBuffer sbSeparator = new StringBuffer(" " + this.separator + " "); buf.append("Time"); - for (int i =0;i<classObservable.numberOfAttributes();i++) { + for (ObservablesHandler.ObservableFetcher fetcher : fetchers) { buf.append(sbSeparator); - buf.append(classObservable.getDescription(i)); + buf.append(fetcher.getDescription()); } outputStream.println(buf); } } - - + + @Override + public String toString() { + if (observables != null) { + return observables.toString() + " " + super.toString(); + } else { + return super.toString(); + } + } } diff --git a/src/main/java/fr/cemagref/observation/observers/ConsoleObserver.java b/src/main/java/fr/cemagref/observation/observers/ConsoleObserver.java index 0a8af0a0bda8819bb912d9e35fc3377ce9c4665a..963cb4791df9c8bf63e7908621b8d0cc8da43008 100644 --- a/src/main/java/fr/cemagref/observation/observers/ConsoleObserver.java +++ b/src/main/java/fr/cemagref/observation/observers/ConsoleObserver.java @@ -17,63 +17,76 @@ import fr.cemagref.ohoui.swing.OhOUI; import fr.cemagref.ohoui.swing.OhOUIDialog; import java.io.BufferedOutputStream; import java.io.FileOutputStream; +import java.util.List; -public class ConsoleObserver implements ObserverListener, Configurable { +public class ConsoleObserver extends ObserverListener implements Configurable { - @Description(name="Use standard output",tooltip = "") - @Link(action="disable",target="filename") + @Description(name = "Use standard output", tooltip = "") + @Link(action = "disable", target = "filename") private boolean sysout = true; - - @Anchor(id="filename") + @Anchor(id = "filename") private File outputFile = new File(""); - private transient File outputFileBak = outputFile; protected transient PrintStream outputStream = System.out; + private List<Integer> dates; + + public ConsoleObserver() { + } 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 } + protected boolean isInDates(long date) { + return dates == null || dates.contains((int) date); + } + @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 (isInDates(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"); + } } - if (i<clObservable.numberOfAttributes()-1) buf.append("\n"); + outputStream.println(buf); } - outputStream.println(buf); } protected void println(CharSequence message) { if (sysout) { - outputStream.print(this.getClass().getSimpleName()+" :: "); + outputStream.print(this.getClass().getSimpleName() + " :: "); } outputStream.println(message); } - + @Override public void configure() { - OhOUIDialog dialog = OhOUI.getDialog(null,this,new NoTransientField()); + OhOUIDialog dialog = OhOUI.getDialog(null, this, new NoTransientField()); dialog.setSize(new Dimension(510, 160)); dialog.setVisible(true); - if (!outputFile.equals(outputFileBak)) init(); + if (!outputFile.equals(outputFileBak)) { + init(); + } outputFileBak = outputFile; } @@ -83,20 +96,29 @@ public class ConsoleObserver implements ObserverListener, Configurable { outputStream = System.out; } else { try { + File outputDir = outputFile.getParentFile(); + if (!outputDir.exists()) { + outputDir.mkdirs(); + } // 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 + // 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) + if (outputStream != System.out) { outputStream.close(); + } } + @Override + public String toString() { + return "in " + outputFile; + } } diff --git a/src/main/java/fr/cemagref/observation/observers/jfreechart/TemporalChart.java b/src/main/java/fr/cemagref/observation/observers/jfreechart/TemporalChart.java index 709447d5e1771e4a623e0b0dbd6826c464e78845..1b1d528361a9ac079e1dc5d1a3ba2b4d5fd8ee35 100644 --- a/src/main/java/fr/cemagref/observation/observers/jfreechart/TemporalChart.java +++ b/src/main/java/fr/cemagref/observation/observers/jfreechart/TemporalChart.java @@ -18,18 +18,19 @@ 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 { +public abstract class TemporalChart extends ObserverListener implements Configurable, Drawable { private transient ChartPanel chartPanel; private transient JFreeChart jfchart; protected transient XYSeriesCollection dataset; protected GraphType graphType = GraphType.LINE; + protected String variableName; /** * <code>variable</code> is the variable to represent */ protected transient ObservablesHandler.ObservableFetcher variable; protected transient ObservablesHandler classObservable; - private String title = "", xAxisLabel = "Time", yAxisLabel = ""; + private String titlePrefix="Observation of ", title = "", xAxisLabel = "Time", yAxisLabel = ""; public TemporalChart() { graphTypeUpdated(); @@ -43,11 +44,20 @@ public abstract class TemporalChart implements ObserverListener, Configurable, D protected void variableUpdated() { if (variable != null) { + variableName = variable.getDeclaredName(); yAxisLabel = variable.getDescription(); - title = "Observation of " + yAxisLabel; + title = titlePrefix + yAxisLabel; } } + /** + * Modify the prefix used for updating the title. By default, it is "Observation of ". + * @param prefix + */ + protected void setTitlePrefix(String prefix) { + this.titlePrefix = prefix; + } + protected void graphTypeUpdated() { PlotOrientation orientation = PlotOrientation.VERTICAL; boolean legend = true; @@ -128,11 +138,20 @@ public abstract class TemporalChart implements ObserverListener, Configurable, D public void init() { if (variable == null) { if (classObservable != null) { - if (classObservable.numberOfAttributes() > 0) { + if (variableName!=null) { + variable = classObservable.getObservableFetcherByName(variableName); + } else if (classObservable.numberOfAttributes() > 0) { variable = classObservable.getObservableFetcher(0); } } // TODO classObservable.getAttributes().length *must* be > 0 } } + + @Override + public String toString() { + return "Chart: " + title; + } + + } diff --git a/src/main/java/fr/cemagref/observation/observers/jfreechart/TwoObservablesChart.java b/src/main/java/fr/cemagref/observation/observers/jfreechart/TwoObservablesChart.java new file mode 100644 index 0000000000000000000000000000000000000000..9abd330566dd98d29706313a76c3109942c48071 --- /dev/null +++ b/src/main/java/fr/cemagref/observation/observers/jfreechart/TwoObservablesChart.java @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2010 Cemagref + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package fr.cemagref.observation.observers.jfreechart; + +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; +import java.lang.reflect.InvocationTargetException; +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.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +/** + * + * @author Nicolas Dumoulin <nicolas.dumoulin@cemagref.fr> + */ +public class TwoObservablesChart extends ObserverListener implements Configurable, Drawable { + + private transient ChartPanel chartPanel; + private transient JFreeChart jfchart; + protected transient XYSeriesCollection dataset; + protected GraphType graphType = GraphType.LINE; + protected String variableXName; + protected String variableYName; + protected transient ObservablesHandler.ObservableFetcher variableX; + protected transient ObservablesHandler.ObservableFetcher variableY; + protected transient ObservablesHandler classObservable; + private String titlePrefix = "Observation of ", title = "", xAxisLabel = "", yAxisLabel = ""; + + public TwoObservablesChart() { + super(); + graphTypeUpdated(); + } + + public TwoObservablesChart(ObservablesHandler.ObservableFetcher variableX, ObservablesHandler.ObservableFetcher variableY) { + this(); + this.variableX = variableX; + this.variableY = variableY; + variablesUpdated(); + init(); + dataset = new XYSeriesCollection(); + graphTypeUpdated(); + } + + /** + * @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) { + System.err.println("Value changed"); + // we fetch the value of the observable + double valueX; + double valueY; + try { + Object objectValue = variableX.fetchValue(instance); + if (objectValue instanceof Integer) { + valueX = (Integer) objectValue; + } else { + valueX = (Double) objectValue; + } + objectValue = variableY.fetchValue(instance); + if (objectValue instanceof Integer) { + valueY = (Integer) objectValue; + } else { + valueY = (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); + } + System.err.println("hc : " + instance.hashCode()); + System.err.println("Appending (" + valueX + "," + valueY + ") on " + serie); + serie.add(valueX, valueY); + } + + protected void variablesUpdated() { + if (variableX != null) { + variableXName = variableX.getDeclaredName(); + xAxisLabel = variableX.getDescription(); + } + if (variableY != null) { + variableYName = variableY.getDeclaredName(); + yAxisLabel = variableY.getDescription(); + title = titlePrefix + yAxisLabel; + } + } + + /** + * Modify the prefix used for updating the title. By default, it is "Observation of ". + * @param prefix + */ + protected void setTitlePrefix(String prefix) { + this.titlePrefix = prefix; + } + + 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() { + OhOUIDialog dialog = OhOUI.getDialog(null, this, new NoTransientField()); + // X + ObservablesHandler.ObservableFetcher variableXBak = variableX; + JComboBox comboBoxX = new JComboBox(classObservable.getDescriptions()); + if (variableX != null) { + comboBoxX.setSelectedItem(variableX.getDescription()); + } + OUIPanel ouiPanel = OUIPanel.makeLabelComponentOUIPanel(variableX, comboBoxX, "Variable for X", ""); + dialog.getContentPane().add(ouiPanel.getPanel(), 0); + // Y + ObservablesHandler.ObservableFetcher variableYBak = variableY; + JComboBox comboBoxY = new JComboBox(classObservable.getDescriptions()); + if (variableY != null) { + comboBoxY.setSelectedItem(variableY.getDescription()); + } + ouiPanel = OUIPanel.makeLabelComponentOUIPanel(variableY, comboBoxY, "Variable for Y", ""); + dialog.getContentPane().add(ouiPanel.getPanel(), 1); + // pack + dialog.pack(); + dialog.setVisible(true); + variableX = classObservable.getObservableFetcher((String) comboBoxX.getSelectedItem()); + if (variableX != null) { + if (!variableX.equals(variableXBak)) { + variablesUpdated(); + } + } + variableY = classObservable.getObservableFetcher((String) comboBoxY.getSelectedItem()); + if (variableY != null) { + if (!variableY.equals(variableYBak)) { + variablesUpdated(); + } + } + graphTypeUpdated(); + } + + @Override + public void addObservable(ObservablesHandler classObservable) { + this.classObservable = classObservable; + } + + @Override + public JComponent getDisplay() { + return chartPanel; + } + + @Override + public String getTitle() { + return title; + } + + @Override + public void init() { + if (variableX == null) { + if (classObservable != null) { + if (variableXName != null) { + variableX = classObservable.getObservableFetcherByName(variableXName); + } else if (classObservable.numberOfAttributes() > 0) { + variableX = classObservable.getObservableFetcher(0); + } + } + dataset = new XYSeriesCollection(); + graphTypeUpdated(); + } + if (variableY == null) { + if (classObservable != null) { + if (variableYName != null) { + variableY = classObservable.getObservableFetcherByName(variableYName); + } else if (classObservable.numberOfAttributes() > 1) { + variableY = classObservable.getObservableFetcher(1); + } + } + 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 index e96691e5c73cc17f6cdbc42c6fab2f96fbc1b229..c5e571acb1300a3e6c597c352794949942fc324e 100644 --- a/src/main/java/fr/cemagref/observation/observers/jfreechart/XYSeriesChart.java +++ b/src/main/java/fr/cemagref/observation/observers/jfreechart/XYSeriesChart.java @@ -24,7 +24,7 @@ import java.util.ArrayList; import java.util.List; import org.jfree.data.xy.XYDataset; -public class XYSeriesChart implements ObserverListener, Configurable, Drawable { +public class XYSeriesChart extends ObserverListener implements Configurable, Drawable { private transient ChartPanel chartPanel; private transient JFreeChart jfchart; diff --git a/src/main/javadoc/overview.html b/src/main/javadoc/overview.html new file mode 100644 index 0000000000000000000000000000000000000000..10ed0f5b3058728f94b7db70adadd6f997f06ce1 --- /dev/null +++ b/src/main/javadoc/overview.html @@ -0,0 +1,22 @@ +<html> + <body> + <p>Observation is a tool for declaring variables and method that can then + be connected to an observer that will process these data on demand.</p> + + <h2>Declaration of an observable</h2> + By declaring an observable, you will explicitly say that a field or a method + can be retrieved to get an information on your system. Declaring an observable is + accomplished by adding the Observable annotation on a field or a method, as in this example: + <pre> +public class Individual { + @Observable(description = "age in weeks") + private short age; + // … + </pre> + <h2>Use an observer</h2> + <h2>Create your own observer</h2> + <h2>Notification of an observable update</h2> + It can be done by invoking the method fireChanges of the Observable object, for example: + <pre>ObservableManager.getObservable(Individual.class).fireChanges(instance,time);</pre> + </body> +</html> \ No newline at end of file