package beast.app.draw;
import java.awt.Dimension;
import java.awt.Point;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Scanner;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import beast.app.beauti.BeautiDoc;
import beast.core.BEASTInterface;
import beast.core.Input;
import beast.core.MCMC;
import beast.core.util.Log;
import beast.util.XMLProducer;
/**
* Panel for editing BEASTObjects.
* <p/>
* This dynamically creates a Panel consisting of
* InputEditors associated with the inputs of a BEASTObject.
* *
*/
public class BEASTObjectPanel extends JPanel {
private static final long serialVersionUID = 1L;
/**
* plug in to be edited *
*/
public BEASTInterface m_beastObject;
/**
* (super) class of plug-in, this determines the super-class
* that is allowable if the beastObject class is changed.
*/
Class<?> m_beastObjectClass;
JLabel m_beastObjectButton;
JTextField m_identry;
private boolean m_bOK = false;
/* Set of beastObjects in the system.
* These are the beastObjects that an input can be connected to **/
static public HashMap<String, BEASTInterface> g_plugins = null;
// static public Set<Operator> g_operators = null;
// static public Set<StateNode> g_stateNodes = null;
// static public Set<Loggable> g_loggers = null;
// static public Set<Distribution> g_distributions = null;
public static Point m_position;
/**
* map that identifies the InputEditor to use for a particular type of Input *
*/
// static HashMap<Class<?>, String> g_inputEditorMap;
// static HashMap<Class<?>, String> g_listInputEditorMap;
static {
init();
} // finished registering input editors
public static void init() {
// // register input editors
// g_inputEditorMap = new HashMap<>, String>();
// g_listInputEditorMap = new HashMap<>, String>();
//
//// String [] knownEditors = new String [] {"beast.app.draw.DataInputEditor","beast.app.beauti.AlignmentListInputEditor", "beast.app.beauti.FrequenciesInputEditor", "beast.app.beauti.OperatorListInputEditor", "beast.app.beauti.ParametricDistributionInputEditor", "beast.app.beauti.PriorListInputEditor", "beast.app.beauti.SiteModelInputEditor", "beast.app.beauti.TaxonSetInputEditor", "beast.app.beauti.TipDatesInputEditor", "beast.app.draw.BooleanInputEditor", "beast.app.draw.DoubleInputEditor", "beast.app.draw.EnumInputEditor", "beast.app.draw.IntegerInputEditor", "beast.app.draw.ListInputEditor",
//// "beast.app.draw.ParameterInputEditor", "beast.app.draw.PluginInputEditor", "beast.app.draw.StringInputEditor"};
//// registerInputEditors(knownEditors);
// String[] PACKAGE_DIRS = {"beast.app",};
// for (String packageName : PACKAGE_DIRS) {
// List<String> inputEditors = AddOnManager.find("beast.app.draw.InputEditor", packageName);
// registerInputEditors(inputEditors.toArray(new String[0]));
// }
m_position = new Point(0, 0);
g_plugins = new HashMap<>();
// g_operators = new HashSet<>();
// g_stateNodes = new HashSet<>();
// g_loggers = new HashSet<>();
// g_distributions = new HashSet<>();
}
public BEASTObjectPanel(BEASTInterface beastObject, Class<?> _pluginClass, List<BEASTInterface> beastObjects, BeautiDoc doc) {
//g_plugins = new HashMap<>();
for (BEASTInterface beastObject2 : beastObjects) {
String id = getID(beastObject2);
// ensure IDs are unique
if (g_plugins.containsKey(id)) {
beastObject2.setID(null);
id = getID(beastObject2);
}
registerPlugin(getID(beastObject2), beastObject2, doc);
}
init(beastObject, _pluginClass, true, doc);
}
/**
* add beastObject to beastObject map and update related maps
*
* @return true if it was already registered *
*/
static public boolean registerPlugin(String id, BEASTInterface beastObject, BeautiDoc doc) {
if (doc != null) {
doc.registerPlugin(beastObject);
}
// if (beastObject instanceof Operator) {
// g_operators.add((Operator)beastObject);
// }
// if (beastObject instanceof StateNode) {
// g_stateNodes.add((StateNode)beastObject);
// }
// if (beastObject instanceof Loggable) {
// g_loggers.add((Loggable)beastObject);
// }
// if (beastObject instanceof Distribution) {
// g_distributions.add((Distribution)beastObject);
// }
if (g_plugins.containsKey(id) && g_plugins.get(id) == beastObject) {
return true;
}
g_plugins.put(id, beastObject);
return false;
}
public static void renamePluginID(BEASTInterface beastObject, String oldID, String id, BeautiDoc doc) {
if (doc != null) {
doc.unregisterPlugin(beastObject);
}
g_plugins.remove(oldID);
// g_operators.remove(oldID);
// g_stateNodes.remove(oldID);
// g_loggers.remove(oldID);
// g_distributions.remove(oldID);
registerPlugin(id, beastObject, doc);
}
public BEASTObjectPanel(BEASTInterface beastObject, Class<?> _pluginClass, BeautiDoc doc) {
this(beastObject, _pluginClass, true, doc);
}
public BEASTObjectPanel(BEASTInterface beastObject, Class<?> _pluginClass, boolean isShowHeader, BeautiDoc doc) {
initPlugins(beastObject, doc);
init(beastObject, _pluginClass, isShowHeader, doc);
}
void init(BEASTInterface beastObject, Class<?> _pluginClass, boolean showHeader, BeautiDoc doc) {
try {
m_beastObject = beastObject.getClass().newInstance();
for (Input<?> input : beastObject.listInputs()) {
m_beastObject.setInputValue(input.getName(), input.get());
}
m_beastObject.setID(beastObject.getID());
} catch (Exception e) {
e.printStackTrace();
}
//setModal(true);
//m_beastObject = beastObject;
m_beastObjectClass = _pluginClass;
//setTitle(m_beastObject.getID() + " Editor");
Box mainBox = Box.createVerticalBox();
mainBox.add(Box.createVerticalStrut(5));
if (showHeader) {
/* add beastObject + help button at the top */
Box pluginBox = createPluginBox();
mainBox.add(pluginBox);
mainBox.add(Box.createVerticalStrut(5));
if (doc != null && m_identry != null) {
// we are in Beauti, do not edit IDs
m_identry.setEnabled(false);
}
}
doc.getInputEditorFactory().addInputs(mainBox, m_beastObject, null, null, doc);
mainBox.add(Box.createVerticalStrut(5));
this.add(mainBox);
Dimension dim = mainBox.getPreferredSize();
setSize(dim.width + 10, dim.height + 30);
BEASTObjectPanel.m_position.x += 30;
BEASTObjectPanel.m_position.y += 30;
setLocation(BEASTObjectPanel.m_position);
} // c'tor
public boolean getOK() {
BEASTObjectPanel.m_position.x -= 30;
BEASTObjectPanel.m_position.y -= 30;
return m_bOK;
}
/**
* add all inputs of a beastObject to a box *
*/
public static int countInputs(BEASTInterface beastObject, BeautiDoc doc) {
int inputCount = 0;
try {
if (beastObject == null) {
return 0;
}
List<Input<?>> inputs = beastObject.listInputs();
for (Input<?> input : inputs) {
String fullInputName = beastObject.getClass().getName() + "." + input.getName();
if (!doc.beautiConfig.suppressBEASTObjects.contains(fullInputName)) {
inputCount++;
}
}
} catch (Exception e) {
// ignore
}
return inputCount;
} // addInputs
/**
* create box for manipulating the beastObject, or ask for help *
*/
Box createPluginBox() {
Box box = Box.createHorizontalBox();
//jLabel icon = new JLabel();
box.add(Box.createHorizontalGlue());
JLabel label = new JLabel(m_beastObjectClass.getName().replaceAll(".*\\.", "") + ":");
box.add(label);
// m_pluginButton = new JLabel(m_beastObject.getID());
// m_pluginButton.setToolTipText(m_beastObject.getID() + " is of type " + m_beastObject.getClass().getName() + " Click to change.");
label.setToolTipText(m_beastObject.getID() + " is of type " + m_beastObject.getClass().getName() + " Click to change.");
// m_pluginButton.addActionListener(new ActionListener() {
// @Override
// public void actionPerformed(ActionEvent e) {
// List<String> classes = ClassDiscovery.find(m_pluginClass, "beast");
// String className = (String) JOptionPane.showInputDialog(null,
// "Select another type of " + m_pluginClass.getName().replaceAll(".*\\.", ""),
// "Select",
// JOptionPane.PLAIN_MESSAGE, null,
// classes.toArray(new String[0]),
// null);
// if (className.equals(m_beastObject.getClass().getName())) {
// return;
// }
// try {
// m_beastObject = (BEASTObject) Class.forName(className).newInstance();
// m_pluginButton.setText(className.replaceAll(".*\\.", ""));
// // TODO: replace InputEditors where appropriate.
//
// } catch (Exception ex) {
// JOptionPane.showMessageDialog(null, "Could not change beastObject: " +
// ex.getClass().getName() + " " +
// ex.getMessage()
// );
// }
// }
// });
// box.add(Box.createHorizontalStrut(10));
// box.add(m_pluginButton);
box.add(new JLabel(" " + m_beastObject.getID()));
// m_identry = new JTextField();
// m_identry.setText(m_beastObject.getID());
// m_identry.setToolTipText("Name/ID that uniquely identifies this item");
//
// m_identry.getDocument().addDocumentListener(new DocumentListener() {
// @Override
// public void removeUpdate(DocumentEvent e) {
// processID();
// }
//
// @Override
// public void insertUpdate(DocumentEvent e) {
// processID();
// }
//
// @Override
// public void changedUpdate(DocumentEvent e) {
// processID();
// }
// });
// box.add(m_identry);
Box vbox = Box.createVerticalBox();
vbox.setBorder(BorderFactory.createEmptyBorder());
vbox.add(Box.createVerticalStrut(10));
vbox.add(box);
vbox.add(Box.createVerticalStrut(10));
return vbox;
} // createPluginBox
void processID() {
// PluginPanel.g_plugins.remove(m_beastObject.getID());
// m_beastObject.setID(m_identry.getText());
// PluginPanel.g_plugins.put(m_beastObject.getID(), m_beastObject);
}
/**
* collect all beastObjects that can reach this input (actually, it's parent)
* and add them to the tabu list.
*/
static List<BEASTInterface> listAscendants(BEASTInterface parent, Collection<BEASTInterface> beastObjects) {
/* First, calculate outputs for each beastObject */
HashMap<BEASTInterface, List<BEASTInterface>> outputs = getOutputs(beastObjects);
/* process outputs */
List<BEASTInterface> ascendants = new ArrayList<>();
ascendants.add(parent);
boolean progress = true;
while (progress) {
progress = false;
for (int i = 0; i < ascendants.size(); i++) {
BEASTInterface ascendant = ascendants.get(i);
if (outputs.containsKey(ascendant)) {
for (BEASTInterface parent2 : outputs.get(ascendant)) {
if (!ascendants.contains(parent2)) {
ascendants.add(parent2);
progress = true;
}
}
}
}
}
return ascendants;
}
/* calculate outputs for each beastObject
* and put them as ArrayLists in a Map
* so they can be retrieved indexed by beastObject like this:
* ArrayList<BEASTObject> output = outputs.get(beastObject)*/
static HashMap<BEASTInterface, List<BEASTInterface>> getOutputs(Collection<BEASTInterface> beastObjects) {
HashMap<BEASTInterface, List<BEASTInterface>> outputs = new HashMap<>();
for (BEASTInterface beastObject : beastObjects) {
outputs.put(beastObject, new ArrayList<>());
}
for (BEASTInterface beastObject : beastObjects) {
try {
for (Input<?> input2 : beastObject.listInputs()) {
Object o = input2.get();
if (o != null && o instanceof BEASTInterface) {
List<BEASTInterface> list = outputs.get(o);
// if (list == null) {
// int h = 3;
// h++;
// } else {
list.add(beastObject);
// }
}
if (o != null && o instanceof List<?>) {
for (Object o2 : (List<?>) o) {
if (o2 != null && o2 instanceof BEASTInterface) {
List<BEASTInterface> list = outputs.get(o2);
if (list != null) {
list.add(beastObject);
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return outputs;
} // getOutputs
public void initPlugins(BEASTInterface beastObject, BeautiDoc doc) {
//g_plugins = new HashMap<>();
addPluginToMap(beastObject, doc);
}
static public void addPluginToMap(BEASTInterface beastObject, BeautiDoc doc) {
if (registerPlugin(getID(beastObject), beastObject, doc)) {
return;
}
try {
for (Input<?> input : beastObject.listInputs()) {
if (input.get() != null) {
if (input.get() instanceof BEASTInterface) {
addPluginToMap((BEASTInterface) input.get(), doc);
}
if (input.get() instanceof List<?>) {
for (Object o : (List<?>) input.get()) {
if (o instanceof BEASTInterface) {
addPluginToMap((BEASTInterface) o, doc);
}
}
}
}
}
} catch (Exception e) {
// ignore
Log.warning.println(e.getClass().getName() + " " + e.getMessage());
}
} // addPluginToMap
/**
* return ID of beastObject, if no ID is specified, generate an appropriate ID first
*/
public static String getID(BEASTInterface beastObject) {
if (beastObject.getID() == null || beastObject.getID().length() == 0) {
String id = beastObject.getClass().getName().replaceAll(".*\\.", "");
int i = 0;
while (g_plugins.containsKey(id + "." + i)) {
i++;
}
beastObject.setID(id + "." + i);
}
return beastObject.getID();
}
/**
* rudimentary test *
*/
public static void main(String[] args) {
init();
BEASTObjectPanel pluginPanel = null;
try {
if (args.length == 0) {
pluginPanel = new BEASTObjectPanel(new MCMC(), Runnable.class, null);
} else if (args[0].equals("-x")) {
StringBuilder text = new StringBuilder();
String NL = System.getProperty("line.separator");
Scanner scanner = new Scanner(new File(args[1]));
try {
while (scanner.hasNextLine()) {
text.append(scanner.nextLine() + NL);
}
} finally {
scanner.close();
}
BEASTInterface beastObject = new beast.util.XMLParser().parseBareFragment(text.toString(), false);
pluginPanel = new BEASTObjectPanel(beastObject, beastObject.getClass(), null);
} else if (args.length == 1) {
pluginPanel = new BEASTObjectPanel((BEASTInterface) Class.forName(args[0]).newInstance(), Class.forName(args[0]), null);
} else if (args.length == 2) {
pluginPanel = new BEASTObjectPanel((BEASTInterface) Class.forName(args[0]).newInstance(), Class.forName(args[1]), null);
} else {
throw new IllegalArgumentException("Incorrect number of arguments");
}
} catch (Exception e) {
e.printStackTrace();
System.err.println("Usage: " + BEASTObjectPanel.class.getName() + " [-x file ] [class [type]]\n" +
"where [class] (optional, default MCMC) is a BEASTObject to edit\n" +
"and [type] (optional only if class is specified, default Runnable) the type of the BEASTObject.\n" +
"for example\n" +
"");
System.exit(1);
}
pluginPanel.setVisible(true);
if (pluginPanel.m_bOK) {
BEASTInterface beastObject = pluginPanel.m_beastObject;
String xml = new XMLProducer().modelToXML(beastObject);
System.out.println(xml);
}
} // main
} // class PluginDialog