/* ========================
* JSynoptic : a free Synoptic editor
* ========================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 2001-2003, by :
* Corporate:
* Astrium SAS
* EADS CRC
* Individual:
* Nicolas Brodu
*
* $Id: ExpressionDataSourceProvider.java,v 1.6 2007/09/04 13:57:19 ogor Exp $
*
* Changes
* -------
* 27-Jan-2004 : Creation date (NB);
*
*/
package jsynoptic.data;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Iterator;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.Vector;
import javax.swing.DefaultListCellRenderer;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import jsynoptic.parser.ExpressionNode;
import jsynoptic.parser.ExpressionParser;
import jsynoptic.parser.ParseException;
import jsynoptic.ui.JSynoptic;
import jsynoptic.ui.Run;
import simtools.data.DataInfo;
import simtools.data.DataSource;
import simtools.data.DataSourceCollection;
import simtools.data.DataSourcePool;
import simtools.data.DataSourceProvider;
import simtools.data.DuplicateIdException;
import simtools.ui.ResourceFinder;
import simtools.ui.SourceTree;
/**
* Used in serializing/deserializing expression data sources
*/
public class ExpressionDataSourceProvider implements DataSourceProvider {
public static ResourceBundle resources = ResourceFinder.get(ExpressionDataSourceProvider.class);
private boolean conflictsOK = false; // need to be put as a field
/*
* (non-Javadoc)
*
* @see simtools.data.DataSourceProvider#getOptionalInformation(simtools.data.DataSource,
* simtools.data.DataSourceCollection)
*/
public Object getOptionalInformation(DataSource ds, DataSourceCollection dsc) {
// Only care for expression sources
if (!(ds instanceof ExpressionDataSource)) {
return null;
}
ExpressionDataSource eds = (ExpressionDataSource) ds;
Vector info = new Vector();
info.add(DataInfo.getLabel(ds)); // Name already included in the id
info.add(DataInfo.getComment(ds)); // Expression
Set variables = eds.getVariables();
info.add(new Integer(variables.size()));
for (Iterator it = eds.getVariables().iterator(); it.hasNext();) {
VariableAssociation va = (VariableAssociation) it.next();
// Store source Id => can find it back at reload thanks to
// dependencies mecanism
// And also this takes in account possible Id translation done by
// the source pool
info.add(DataInfo.getId(va.ds));
// Store the name or alias for this source
info.add(va.var);
}
return info;
}
/*
* (non-Javadoc)
*
* @see simtools.data.DataSourceProvider#provide(java.lang.String,
* java.lang.String, java.lang.Object, simtools.data.DataSourcePool)
*/
public DataSource provide(String id, String dscId, Object optionalInformation, DataSourcePool pool) {
// First make sure this provider minds its own business
if (id == null) {
return null;
}
if (!id.startsWith(ExpressionDataSource.EXPRESSION_DATA_SOURCE_MARKER)) {
return null;
}
// Now use the options
if (optionalInformation == null) {
return null; // this class didn't serialize that!
}
if (!(optionalInformation instanceof Vector)) {
return null; // this class didn't serialize that!
}
Vector info = (Vector) optionalInformation;
String name = (String) info.get(0);
String expression = (String) info.get(1);
int numvar = ((Integer) info.get(2)).intValue();
final ConflictListModel clm = new ConflictListModel();
// Thanks to the source dependencies mecanism, these ids should exist in
// the pool
// if pool is null don't know what to do => will try to parse the
// expression nonetheless
if (pool != null) {
for (int i = 0; i < numvar; ++i) {
DataSource ds;
try {
ds = pool.getDataSourceWithId((String) info.get(i * 2 + 3));
} catch (DuplicateIdException e) {
ds = null;
}
// and if the source does not exist, the conflict resolvng
// mecanism will take care of that too
String var = (String) info.get(i * 2 + 4);
// source and var may mismatch => build a list of potential
// conflicts to resolve after this loop
clm.addElement(new VariableConflict(ds, var));
}
}
// If there are conflicts, solve them...
if (!clm.resolved()) {
if (JSynoptic.gui != null) {
conflictsOK = false;
final JList list = new JList(clm);
list.setCellRenderer(new ConflictListCellRenderer());
JButton changeSourceAlias;
JPanel treePane = new JPanel(new BorderLayout());
final SourceTree st = SourceTree.getFromPool("PropertiesPanel0");
treePane.add(new JScrollPane(st), BorderLayout.CENTER);
JPanel bpanel = new JPanel(new GridLayout(1, 1));
bpanel.add(changeSourceAlias = new JButton(resources.getString("changeSourceAlias")));
treePane.add(bpanel, BorderLayout.SOUTH);
JSplitPane pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JScrollPane(list), treePane);
list.setVisibleRowCount(10);
JPanel bbox = new JPanel(new FlowLayout());
final JButton bok;
bbox.add(bok = new JButton("OK"));
// bbox.add(bcancel = new JButton("Cancel"));
final JDialog dialog = new JDialog(JSynoptic.gui.getOwner(), resources
.getString("conflictResolutionDialog"), true);
dialog.getContentPane().add(pane, BorderLayout.CENTER);
dialog.getContentPane().add(bbox, BorderLayout.SOUTH);
bok.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
conflictsOK = true;
dialog.setVisible(false);
}
});
/*
* bcancel.addActionListener(new ActionListener() { public void
* actionPerformed(ActionEvent e) { conflictsOK = false;
* dialog.hide(); } });
*/
changeSourceAlias.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Object o = st.getSelectedSourceOrCollection();
if (!(o instanceof DataSource)) {
return;
}
VariableConflict vc = (VariableConflict) list.getSelectedValue();
((DataSource) o).getInformation().alias = vc.var;
list.repaint();
if (clm.resolved()) {
bok.setEnabled(true);
}
TreeNode tn = (TreeNode) (st.getSelectionPath().getLastPathComponent());
((DefaultTreeModel) (st.getModel())).nodeChanged(tn);
}
});
st.addTreeSelectionListener(new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
Object o = st.getSelectedSourceOrCollection();
if ((o instanceof DataSource) && e.isAddedPath()) {
VariableConflict vc = (VariableConflict) list.getSelectedValue();
vc.ds = (DataSource) o;
list.repaint();
if (clm.resolved()) {
bok.setEnabled(true);
}
TreeNode tn = (TreeNode) (st.getSelectionPath().getLastPathComponent());
((DefaultTreeModel) (st.getModel())).nodeChanged(tn);
}
}
});
list.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
Object sel = list.getSelectedValue();
if (sel == null) {
return;
}
st.setSelectedValue(((VariableConflict) sel).ds); // call
// the
// tree
// selection
// listener
// in
// turn
}
});
list.setSelectedIndex(0);
bok.setEnabled(false);
dialog.pack();
dialog.setVisible(true);
if (!conflictsOK) {
return null; // cancel => do not create the source
}
}
}
ExpressionNode node = null;
try {
ExpressionParser ep = new ExpressionParser(expression);
// Add the data sources as variables, once conflicts are resolved
for (int i = 0; i < clm.size(); ++i) {
VariableConflict vc = (VariableConflict) clm.get(i);
ep.addVariable(new VariableAssociation(vc.ds, vc.var));
}
// Add known plugins => may bring in more mathematical functions
for (Iterator it = Run.plugins.iterator(); it.hasNext();) {
ep.addClass(it.next().getClass());
}
node = ep.parse();
} catch (ParseException pe) {
// Also catches duplicate variable names
JOptionPane.showMessageDialog(JSynoptic.gui.getOwner(), pe.getLocalizedMessage(), resources
.getString("CantRestoreExpression")
+ expression, JOptionPane.ERROR_MESSAGE);
return null;
} catch (Error err) {
JOptionPane.showMessageDialog(JSynoptic.gui.getOwner(), err.getLocalizedMessage(), resources
.getString("CantRestoreExpression")
+ expression, JOptionPane.ERROR_MESSAGE);
return null;
}
// Finally provide the source
ExpressionDataSource eds = new ExpressionDataSource(new DataInfo(name, name, expression), node);
if (pool != null) {
pool.addDataSource(eds);
}
return eds;
}
protected static class VariableConflict {
public DataSource ds;
public String var;
public VariableConflict(DataSource ds, String var) {
this.ds = ds;
this.var = var;
}
public String toString() {
String ret = var;
for (int i = var.length(); i < 20; ++i) {
ret += " ";
}
return ret;
}
public boolean resolved() {
String alias = DataInfo.getAlias(ds);
if ((alias == null) && var.equals(DataInfo.getLabel(ds))) {
return true;
}
if (var.equals(alias)) {
return true;
}
if (ds != null) {
ds.getInformation().alias = var;
return true;
}
return false;
}
}
protected static class ConflictListModel extends DefaultListModel {
public boolean resolved() {
boolean ret = true;
// all conflicts must be resolved to return true
for (int i = 0; i < size(); ++i) {
ret &= ((VariableConflict) this.get(i)).resolved();
}
return ret;
}
}
public static class ConflictListCellRenderer extends DefaultListCellRenderer {
/*
* (non-Javadoc)
*
* @see javax.swing.ListCellRenderer#getListCellRendererComponent(javax.swing.JList,
* java.lang.Object, int, boolean, boolean)
*/
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
boolean resolved = ((VariableConflict) list.getModel().getElementAt(index)).resolved();
if (isSelected) {
if (resolved) {
setBackground(new Color(0x88FF88));
} else {
setBackground(new Color(0xFF8888));
}
} else {
if (resolved) {
setBackground(Color.green);
} else {
setBackground(Color.red);
}
}
return this;
}
}
}