package org.opensourcephysics.tools;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import javax.swing.Box;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JToolBar;
import javax.swing.filechooser.FileFilter;
import org.opensourcephysics.controls.ListChooser;
import org.opensourcephysics.controls.XML;
import org.opensourcephysics.controls.XMLControl;
import org.opensourcephysics.controls.XMLControlElement;
import org.opensourcephysics.controls.XMLProperty;
import org.opensourcephysics.display.Dataset;
import org.opensourcephysics.display.OSPRuntime;
import org.opensourcephysics.display.TeXParser;
/**
* This is a FunctionTool used by DatasetCurveFitter to build, save and load custom fit functions.
* Some methods are tailored for use with DataTool since that is its main application.
*
* @author Doug Brown
*
*/
public class FitBuilder extends FunctionTool {
static JFileChooser chooser;
static java.io.FileFilter xmlFilter;
static String[] preferredAutoloadSearchPaths;
static Collection<String> initialAutoloadSearchPaths = new TreeSet<String>();
static Map<String, String[]> autoloadMap = new TreeMap<String, String[]>();
static {
xmlFilter = new java.io.FileFilter() {
// accept only *.xml files.
public boolean accept(File f) {
if(f==null || f.isDirectory()) return false;
String ext = XML.getExtension(f.getName());
if (ext!=null && "xml".equals(ext.toLowerCase())) return true; //$NON-NLS-1$
return false;
}
};
// load preferred autoload search paths and excluded functions from OSPRuntime.preferences
preferredAutoloadSearchPaths = (String[])OSPRuntime.getPreference("autoload_search_paths"); //$NON-NLS-1$
String[][] autoloadData = (String[][])OSPRuntime.getPreference("autoload_exclusions"); //$NON-NLS-1$
if (autoloadData!=null) {
for (String[] next: autoloadData) {
String filePath = XML.forwardSlash(next[0]);
String[] functions = new String[next.length-1];
System.arraycopy(next, 1, functions, 0, functions.length);
autoloadMap.put(filePath, functions);
}
}
}
protected JButton newFitButton, deleteFitButton, cloneFitButton, loadButton, saveButton, autoloadButton;
protected Component parent;
protected TreeSet<String> addedFits = new TreeSet<String>();
protected String defaultFitName;
protected AutoloadManager autoloadManager;
/**
* Constructor
*
* @param c a component to determine the dialog owner
*/
public FitBuilder(Component c) {
super(c);
parent = c;
newFitButton = new JButton(ToolsRes.getString("DatasetCurveFitter.Button.NewFit.Text")); //$NON-NLS-1$
newFitButton.setToolTipText(ToolsRes.getString("DatasetCurveFitter.Button.NewFit.Tooltip")); //$NON-NLS-1$
newFitButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// create new user function and function panel
String name = getUniqueName(ToolsRes.getString("DatasetCurveFitter.NewFit.Name")); //$NON-NLS-1$
UserFunction f = new UserFunction(name);
Dataset dataset = null;
DatasetCurveFitter fitter = getSelectedCurveFitter();
if (fitter!=null) {
dataset = fitter.getData();
}
String var = (dataset==null)? "x": //$NON-NLS-1$
TeXParser.removeSubscripting(dataset.getColumnName(0));
f.setExpression("0", new String[] {var}); //$NON-NLS-1$
addFitFunctionPanel(f);
}
});
deleteFitButton = new JButton(ToolsRes.getString("DatasetCurveFitter.Button.DeleteFit.Text")); //$NON-NLS-1$
deleteFitButton.setToolTipText(ToolsRes.getString("DatasetCurveFitter.Button.DeleteFit.Tooltip")); //$NON-NLS-1$
deleteFitButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// remove selected fit and panel
String name = getSelectedName();
removePanel(name);
}
});
cloneFitButton = new JButton(ToolsRes.getString("DatasetCurveFitter.Button.Clone.Text")); //$NON-NLS-1$
cloneFitButton.setToolTipText(ToolsRes.getString("DatasetCurveFitter.Button.Clone.Tooltip")); //$NON-NLS-1$
cloneFitButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
final Map<String, KnownFunction> fits = new HashMap<String, KnownFunction>();
final ArrayList<String> fitnames = new ArrayList<String>();
for (DatasetCurveFitter fitter: curveFitters) {
for (int i = 0; i < fitter.fitDropDown.getItemCount(); i++) {
String name = fitter.fitDropDown.getItemAt(i).toString();
if (!fitnames.contains(name)) {
fitnames.add(name);
fits.put(name, fitter.fitMap.get(name));
}
}
}
// inner popup menu listener class
ActionListener listener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (String name: fitnames) {
if (name.equals(e.getActionCommand())) {
DatasetCurveFitter fitter = getSelectedCurveFitter();
if (fitter!=null) {
KnownFunction f = fits.get(name);
UserFunction uf = fitter.createClone(f, name);
UserFunctionEditor editor = new UserFunctionEditor();
editor.setMainFunctions(new UserFunction[] {uf});
FitFunctionPanel panel = new FitFunctionPanel(editor);
addPanel(uf.getName(), panel);
}
}
}
}
};
// create popup menu and add existing fit function items
JPopupMenu popup = new JPopupMenu();
for (String name: fitnames) {
JMenuItem item = new JMenuItem(name);
item.setActionCommand(name);
item.addActionListener(listener);
popup.add(item);
}
// show the popup below the button
popup.show(cloneFitButton, 0, cloneFitButton.getHeight());
}
});
String imageFile = "/org/opensourcephysics/resources/tools/images/open.gif"; //$NON-NLS-1$
Icon openIcon = ResourceLoader.getIcon(imageFile);
loadButton = new JButton(openIcon);
loadButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
loadFits();
}
});
imageFile = "/org/opensourcephysics/resources/tools/images/save.gif"; //$NON-NLS-1$
Icon saveIcon = ResourceLoader.getIcon(imageFile);
saveButton = new JButton(saveIcon);
saveButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
saveFits();
}
});
// create autoloadButton
autoloadButton = new JButton();
autoloadButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
AutoloadManager manager = getAutoloadManager();
manager.refreshAutoloadData();
manager.setVisible(true);
}
});
setToolbarComponents(new Component[] {loadButton, saveButton,
new JToolBar.Separator(), newFitButton, cloneFitButton, deleteFitButton,
Box.createHorizontalGlue(), autoloadButton});
// autoload fits
for (String dir: getInitialSearchPaths()) {
autoloadFits(dir);
}
}
/**
* Gets the DatasetCurveFitter currently selected in the DataTool.
*
* @return the selected DataSetCurveFitter
*/
public DatasetCurveFitter getSelectedCurveFitter() {
Window win = this.getOwner();
if (win!=null && win instanceof DataTool) {
DataTool dataTool = (DataTool)win;
DataToolTab tab = dataTool.getSelectedTab();
if (tab!=null) {
return tab.curveFitter;
}
}
return null;
}
/**
* Refreshes the dropdown with names of the available fits.
*
* @param name the selected fit name
*/
public void refreshDropdown(String name) {
if (name==null) {
name = defaultFitName;
}
deleteFitButton.setEnabled(!getPanelNames().isEmpty());
if (getPanelNames().isEmpty()) {
String label = ToolsRes.getString("FitFunctionPanel.Label"); //$NON-NLS-1$
dropdownLabel.setText(label+":"); //$NON-NLS-1$
}
super.refreshDropdown(name);
}
/**
* Adds a fit function unless already added or loaded.
*
* @param f the fit function to add
* @return true if added now or previously
*/
public boolean addFitFunction(KnownFunction f) {
if (f instanceof UserFunction) {
String name = f.getName();
if (addedFits.contains(name)) return true;
// if a panel exists with the same original name, don't add this fit
for (String next: getPanelNames()) {
FitFunctionPanel panel = (FitFunctionPanel)getPanel(next);
if (name.equals(panel.originalName)) {
return false;
}
}
FitFunctionPanel panel = addFitFunctionPanel((UserFunction)f);
panel.originalName = name;
addedFits.add(name);
}
else if (f instanceof KnownPolynomial) {
UserFunction uf = new UserFunction((KnownPolynomial)f);
return addFitFunction(uf);
}
return true;
}
/**
* Loads fit functions from an XML file chosen by the user.
*
* @return the path to the file opened, or null if none
*/
private String loadFits() {
if (chooser==null) {
chooser = OSPRuntime.getChooser();
for (FileFilter filter: chooser.getChoosableFileFilters()) {
if (filter.getDescription().toLowerCase().indexOf("xml")>-1) { //$NON-NLS-1$
chooser.setFileFilter(filter);
break;
}
}
}
int result = chooser.showOpenDialog(FitBuilder.this);
if(result==JFileChooser.APPROVE_OPTION) {
OSPRuntime.chooserDir = chooser.getCurrentDirectory().toString();
String fileName = chooser.getSelectedFile().getAbsolutePath();
return loadFits(fileName, false);
}
return null;
}
/**
* Loads fit functions from an XML file.
*
* @param path the path to the XML file
* @param loadAll true to load all fit functions defined in the file, false to let the user choose
* @return the path to the file opened, or null if failed
*/
private String loadFits(String path, boolean loadAll) {
if (path==null) return loadFits();
XMLControl control = new XMLControlElement(path);
if (control.failedToRead()) {
JOptionPane.showMessageDialog(FitBuilder.this,
ToolsRes.getString("Dialog.Invalid.Message"), //$NON-NLS-1$
ToolsRes.getString("Dialog.Invalid.Title"), //$NON-NLS-1$
JOptionPane.ERROR_MESSAGE);
return null;
}
Class<?> type = control.getObjectClass();
if (FitBuilder.class.isAssignableFrom(type)) {
// choose fits to load
if (loadAll || chooseFitFunctions(control, "Load")) { //$NON-NLS-1$
control.loadObject(this);
}
}
else {
JOptionPane.showMessageDialog(FitBuilder.this,
ToolsRes.getString("DatasetCurveFitter.FitBuilder.Dialog.WrongType.Message"), //$NON-NLS-1$
ToolsRes.getString("DatasetCurveFitter.FitBuilder.Dialog.WrongType.Title"), //$NON-NLS-1$
JOptionPane.ERROR_MESSAGE);
}
return path;
}
/**
* Saves fit functions to an XML file chosen by the user.
*
* @return the path to the file saved, or null if cancelled or failed
*/
private String saveFits() {
XMLControl control = new XMLControlElement(this);
// choose fits to save
if (chooseFitFunctions(control, "Save")) { //$NON-NLS-1$
if (chooser==null) {
chooser = OSPRuntime.getChooser();
for (FileFilter filter: chooser.getChoosableFileFilters()) {
if (filter.getDescription().toLowerCase().indexOf("xml")>-1) { //$NON-NLS-1$
chooser.setFileFilter(filter);
break;
}
}
}
FontSizer.setFonts(chooser, FontSizer.getLevel());
int result = chooser.showSaveDialog(FitBuilder.this);
if (result==JFileChooser.APPROVE_OPTION) {
OSPRuntime.chooserDir = chooser.getCurrentDirectory().toString();
File file = chooser.getSelectedFile();
// check to see if file already exists
if(file.exists()) {
int isSelected = JOptionPane.showConfirmDialog(FitBuilder.this,
ToolsRes.getString("Tool.Dialog.ReplaceFile.Message")+" "+file.getName()+"?", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
ToolsRes.getString("Tool.Dialog.ReplaceFile.Title"), //$NON-NLS-1$
JOptionPane.YES_NO_CANCEL_OPTION);
if(isSelected!=JOptionPane.YES_OPTION) {
return null;
}
}
return saveFits(file.getAbsolutePath(), control);
}
}
return null;
}
/**
* Saves fit functions to an XML file.
*
* @param path the file path
* @param control a FitBuilder XMLControl
* @return the path to the file saved
*/
private String saveFits(String path, XMLControl control) {
if (path==null) return saveFits();
// add .xml extension if none but don't replace other extensions
if(XML.getExtension(path)==null) {
path += ".xml"; //$NON-NLS-1$
}
if (control==null || control.getObjectClass()!=this.getClass()) {
control = new XMLControlElement(this);
}
control.write(path);
return path;
}
/**
* Loads fit functions from all FitBuilder XMLControl files found in a specified directory.
*
* @param dirPath the directory path
*/
private void autoloadFits(String dirPath) {
if (dirPath==null) return;
File dir = new File(dirPath);
if (!dir.exists()) return;
File[] files = dir.listFiles(xmlFilter);
if (files!=null) {
for (File file: files) {
XMLControl control = new XMLControlElement(file.getPath());
if (control.failedToRead()) {
continue;
}
Class<?> type = control.getObjectClass();
if (type!=null && FitBuilder.class.isAssignableFrom(type)) {
// copy the control for modification if any functions are autoload_off
XMLControl copyControl = new XMLControlElement(control);
String filePath = XML.forwardSlash(file.getAbsolutePath());
eliminateExcludedFunctions(copyControl, filePath);
copyControl.loadObject(this);
}
}
}
}
/**
* Finds fit functions in all FitBuilder XMLControl files in a specified directory.
* Each fit function is String[] {String name, String expression, String description}
*
* @param dirPath the directory path
* @return a map of filePath to list of fit functions
*/
private Map<String, ArrayList<String[]>> findFitFunctions(String dirPath) {
Map<String, ArrayList<String[]>> results = new TreeMap<String, ArrayList<String[]>>();
if (dirPath==null) return results;
File dir = new File(dirPath);
if (!dir.exists()) return results;
File[] files = dir.listFiles(xmlFilter);
if (files!=null) {
for (File file: files) {
XMLControl control = new XMLControlElement(file.getPath());
if (control.failedToRead()) {
continue;
}
Class<?> type = control.getObjectClass();
if (type!=null && FitBuilder.class.isAssignableFrom(type)) {
ArrayList<String[]> functions = new ArrayList<String[]>();
// look through XMLControl for fit functions
for (Object next: control.getPropertyContent()) {
if (next instanceof XMLProperty
&& ((XMLProperty)next).getPropertyName().equals("functions")) { //$NON-NLS-1$
// found FitFunctionPanels
XMLControl[] panels = ((XMLProperty)next).getChildControls();
for (XMLControl panelControl: panels) {
String name = panelControl.getString("name"); //$NON-NLS-1$
String expression = panelControl.getString("description"); //$NON-NLS-1$
String[] data = new String[] {name, expression};
functions.add(data);
}
break;
}
}
// add entry to the results map
results.put(file.getName(), functions);
}
}
}
return results;
}
/**
* Eliminates unwanted function entries from a FitBuilder XMLControl.
* Typical control:
*
* <object class="org.opensourcephysics.tools.FitBuilder">
* <property name="functions" type="collection" class="java.util.ArrayList">
* <property name="item" type="object">
* <object class="org.opensourcephysics.tools.FitFunctionPanel">
* <property name="name" type="string">NaturalLog</property>
* <property name="description" type="string">y = ln(x)</property>
* <property name="user_parameters" type="array" class="[Lorg.opensourcephysics.tools.Parameter;"/>
* <property name="function_editor" type="object">
* <object class="org.opensourcephysics.tools.UserFunctionEditor">
* <property name="main_functions" type="array" class="[Lorg.opensourcephysics.tools.UserFunction;">
* <property name="[0]" type="object">
* <object class="org.opensourcephysics.tools.UserFunction">
* <property name="name" type="string">NaturalLog</property>
* <property name="name_editable" type="boolean">true</property>
* <property name="parameter_names" type="array" class="[Ljava.lang.String;"/>
* <property name="parameter_values" type="array" class="[D"/>
* <property name="parameter_descriptions" type="array" class="[Ljava.lang.String;"/>
* <property name="variables" type="array" class="[Ljava.lang.String;">
* <property name="[0]" type="string">x</property>
* </property>
* <property name="expression" type="string">ln(x)</property>
* </object>
* </property>
* </property>
* </object>
* </property>
* <property name="autoload_off_NaturalLog" type="boolean">true</property>
* </object>
* </property>
* </property>
* </object>
*
* @param fitBuilderControl the XMLControl to modify
* @param filePath the path to the XML file read by the XMLControl
*/
protected void eliminateExcludedFunctions(XMLControl fitBuilderControl, String filePath) {
for (Object obj: fitBuilderControl.getPropertyContent()) {
if (obj instanceof XMLProperty
&& ((XMLProperty)obj).getPropertyName().equals("functions")) { //$NON-NLS-1$
// found fitFunctionPanels
XMLProperty prop = (XMLProperty)obj;
List<Object> items = prop.getPropertyContent();
ArrayList<Object> toRemove = new ArrayList<Object>();
XMLControl[] panels = prop.getChildControls();
for (int i=0; i<panels.length; i++) {
XMLControl panelControl = panels[i];
String name = panelControl.getString("name"); //$NON-NLS-1$
if (isFunctionExcluded(filePath, name)) {
toRemove.add(items.get(i));
}
}
for (Object next: toRemove) {
items.remove(next);
}
}
}
}
/**
* Determines if a named function is excluded from autoloading.
*
* @param filePath the path to the file defining the function
* @param functionName the function name
* @return true if the function is excluded
*/
private boolean isFunctionExcluded(String filePath, String functionName) {
String[] functions = autoloadMap.get(filePath);
if (functions==null) return false;
for (String name: functions) {
if (name.equals("*")) return true; //$NON-NLS-1$
if (name.equals(functionName)) return true;
}
return false;
}
/**
* Refreshes the GUI.
*/
protected void refreshGUI() {
super.refreshGUI();
setTitle(ToolsRes.getString("DatasetCurveFitter.FitBuilder.Title")); //$NON-NLS-1$
if (getPanelNames().isEmpty()) {
String label = ToolsRes.getString("FitFunctionPanel.Label"); //$NON-NLS-1$
dropdownLabel.setText(label+":"); //$NON-NLS-1$
}
if (saveButton!=null) {
saveButton.setEnabled(!getPanelNames().isEmpty());
loadButton.setToolTipText(ToolsRes.getString("DatasetCurveFitter.FitBuilder.Button.Load.Tooltip")); //$NON-NLS-1$
saveButton.setToolTipText(ToolsRes.getString("DatasetCurveFitter.FitBuilder.Button.Save.Tooltip")); //$NON-NLS-1$
FitFunctionPanel panel = (FitFunctionPanel)getSelectedPanel();
deleteFitButton.setEnabled(!getPanelNames().isEmpty() && panel.originalName==null);
newFitButton.setText(ToolsRes.getString("DatasetCurveFitter.Button.NewFit.Text")); //$NON-NLS-1$
newFitButton.setToolTipText(ToolsRes.getString("DatasetCurveFitter.Button.NewFit.Tooltip")); //$NON-NLS-1$
deleteFitButton.setText(ToolsRes.getString("DatasetCurveFitter.Button.DeleteFit.Text")); //$NON-NLS-1$
deleteFitButton.setToolTipText(ToolsRes.getString("DatasetCurveFitter.Button.DeleteFit.Tooltip")); //$NON-NLS-1$
DatasetCurveFitter fitter = getSelectedCurveFitter();
cloneFitButton.setEnabled(fitter!=null);
autoloadButton.setText(ToolsRes.getString("FitBuilder.Button.Autoload")+"..."); //$NON-NLS-1$ //$NON-NLS-2$
autoloadButton.setToolTipText(ToolsRes.getString("FitBuilder.Button.Autoload.Tooltip")); //$NON-NLS-1$
}
}
/**
* Displays a dialog with a list of fit functions to load or save.
*
* @param control a FitBuilder XMLControl
* @param description a description of the purpose (ie load or save)
* @return true if not cancelled by the user
*/
protected boolean chooseFitFunctions(XMLControl control, String description) {
ListChooser listChooser = new ListChooser(
ToolsRes.getString("DatasetCurveFitter.FitBuilder."+description+".Title"), //$NON-NLS-1$ //$NON-NLS-2$
ToolsRes.getString("DatasetCurveFitter.FitBuilder."+description+".Message"), //$NON-NLS-1$ //$NON-NLS-2$
this);
// choose the elements and load the function tool
ArrayList<XMLControl> originals = new ArrayList<XMLControl>();
ArrayList<XMLControl> choices = new ArrayList<XMLControl>();
ArrayList<String> names = new ArrayList<String>();
ArrayList<String> expressions = new ArrayList<String>();
for (Object next: control.getPropertyContent()) {
if (next instanceof XMLProperty) {
XMLProperty prop = (XMLProperty)next;
for (Object obj: prop.getPropertyContent()) {
if (obj instanceof XMLProperty) {
XMLProperty f = (XMLProperty)obj;
XMLControl function = f.getChildControls()[0];
originals.add(function);
choices.add(function);
names.add(function.getString("name")); //$NON-NLS-1$
String desc = function.getString("description"); //$NON-NLS-1$
expressions.add(desc);
}
}
}
}
// select all by default
boolean[] selected = new boolean[choices.size()];
for (int i = 0; i<selected.length; i++) {
selected[i] = true;
}
if (listChooser.choose(choices, names, expressions, selected)) {
// compare choices with originals and remove unwanted object content
for (XMLControl next: originals) {
if (!choices.contains(next)) {
XMLProperty prop = next.getParentProperty();
XMLProperty parent = prop.getParentProperty();
parent.getPropertyContent().remove(prop);
}
}
return true;
}
return false;
}
/**
* Adds a fit function panel.
*
* @param f the fit function to add
* @return the fit function panel
*/
protected FitFunctionPanel addFitFunctionPanel(UserFunction f) {
UserFunctionEditor editor = new UserFunctionEditor();
editor.setMainFunctions(new UserFunction[] {f});
FitFunctionPanel panel = new FitFunctionPanel(editor);
return (FitFunctionPanel)addPanel(f.getName(), panel);
}
/**
* Gets the autoload manager, creating it the first time called.
*
* @return the autoload manageer
*/
protected AutoloadManager getAutoloadManager() {
if (autoloadManager==null) {
autoloadManager = new AutoloadManager(this);
// center on screen
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
int x = (dim.width - autoloadManager.getBounds().width) / 2;
int y = (dim.height - autoloadManager.getBounds().height) / 2;
autoloadManager.setLocation(x, y);
}
autoloadManager.setFontLevel(FontSizer.getLevel());
return autoloadManager;
}
//-------------------------------- static methods ---------------------------
public static String localize(String functionName) {
String s = ToolsRes.getString("Function."+functionName+".Name"); //$NON-NLS-1$ //$NON-NLS-2$
if (!s.startsWith("!")) return s; //$NON-NLS-1$
return functionName;
}
/**
* Gets the autoload search paths.
*
* @return the search paths
*/
protected static Collection<String> getInitialSearchPaths() {
if (initialAutoloadSearchPaths.isEmpty()) {
if (preferredAutoloadSearchPaths!=null) {
for (String next: preferredAutoloadSearchPaths) {
initialAutoloadSearchPaths.add(next);
}
}
else {
for (String next: OSPRuntime.getDefaultSearchPaths()) {
initialAutoloadSearchPaths.add(next);
}
}
}
return initialAutoloadSearchPaths;
}
//-------------------------------- inner classes ---------------------------
/**
* An AutoloadManager for DataFunctions.
*/
class AutoloadManager extends AbstractAutoloadManager {
/**
* Constructor for a dialog.
*
* @param dialog the dialog (DataBuilder)
*/
public AutoloadManager(JDialog dialog) {
super(dialog);
}
@Override
public void setVisible(boolean vis) {
super.setVisible(vis);
if (!vis) {
// save non-default search paths in preferredAutoloadSearchPaths
Collection<String> searchPaths = getSearchPaths();
Collection<String> defaultPaths = OSPRuntime.getDefaultSearchPaths();
boolean isDefault = searchPaths.size()==defaultPaths.size();
for (String next: searchPaths) {
isDefault = isDefault && defaultPaths.contains(next);
}
if (isDefault) {
preferredAutoloadSearchPaths = null;
}
else {
preferredAutoloadSearchPaths = searchPaths.toArray(new String[searchPaths.size()]);
}
OSPRuntime.setPreference("autoload_search_paths", preferredAutoloadSearchPaths); //$NON-NLS-1$
// clean up autoloadMap of excluded autoloadable functions
for (Iterator<String> it = autoloadMap.keySet().iterator(); it.hasNext();) {
String filePath = it.next();
String parentPath = XML.getDirectoryPath(filePath);
boolean keep = false;
for (String dir: searchPaths) {
keep = keep || parentPath.equals(dir);
}
if (!keep || !new File(filePath).exists()) {
it.remove();
}
}
// save excluded autoloadable functions
if (autoloadMap.isEmpty()) {
OSPRuntime.setPreference("autoload_exclusions", null); //$NON-NLS-1$
}
else {
String[][] autoloadData = new String[autoloadMap.size()][];
int i = 0;
for (String filePath: autoloadMap.keySet()) {
String[] functions = autoloadMap.get(filePath);
String[] fileAndFunctions = new String[functions.length+1];
fileAndFunctions[0] = filePath;
System.arraycopy(functions, 0, fileAndFunctions, 1, functions.length);
autoloadData[i] = fileAndFunctions;
i++;
}
OSPRuntime.setPreference("autoload_exclusions", autoloadData); //$NON-NLS-1$
}
OSPRuntime.savePreferences();
}
}
/**
* Sets the selection state of a function.
*
* @param filePath the path to the file defining the function
* @param function the function {name, expression, optional descriptor}
* @param select true to select the function
*/
protected void setFunctionSelected(String filePath, String[] function, boolean select) {
String[] oldExclusions = autoloadMap.get(filePath);
String[] newExclusions = null;
if (!select) {
// create or add entry to newExclusions
if (oldExclusions==null) {
newExclusions = new String[] {function[0]};
}
else {
int n = oldExclusions.length;
newExclusions = new String[n+1];
System.arraycopy(oldExclusions, 0, newExclusions, 0, n);
newExclusions[n] = function[0];
}
}
else if (oldExclusions!=null){
// remove entry
int n = oldExclusions.length;
if (n>1) {
ArrayList<String> exclusions = new ArrayList<String>();
for (String f: oldExclusions) {
if (f.equals(function[0])) continue;
exclusions.add(f);
}
newExclusions = exclusions.toArray(new String[exclusions.size()]);
}
}
autoloadMap.remove(filePath);
if (newExclusions!=null) {
autoloadMap.put(filePath, newExclusions);
}
refreshAutoloadData();
// reload fits
for (String dir: getSearchPaths()) {
autoloadFits(dir);
}
}
/**
* Gets the selection state of a function.
*
* @param filePath the path to the file defining the function
* @param function the function {name, expression, optional descriptor}
* @return true if the function is selected
*/
protected boolean isFunctionSelected(String filePath, String[] function) {
String[] functions = autoloadMap.get(filePath);
if (functions==null) return true;
for (String name: functions) {
if (name.equals("*")) return false; //$NON-NLS-1$
if (name.equals(function[0])) return false;
}
return true;
}
/**
* Sets the selection state of a file.
*
* @param filePath the path to the file
* @param select true to select the file
*/
protected void setFileSelected(String filePath, boolean select) {
autoloadMap.remove(filePath);
if (!select) {
String[] function = new String[] {"*"}; //$NON-NLS-1$
autoloadMap.put(filePath, function);
}
refreshAutoloadData();
// reload fits
for (String dir: getSearchPaths()) {
autoloadFits(dir);
}
}
/**
* Gets the selection state of a file.
*
* @param filePath the path to the file
* @return TristateCheckBox.SELECTED, NOT_SELECTED or PART_SELECTED
*/
protected TristateCheckBox.State getFileSelectionState(String filePath) {
String[] functions = autoloadMap.get(filePath);
if (functions==null) return TristateCheckBox.SELECTED;
if (functions[0].equals("*")) return TristateCheckBox.NOT_SELECTED; //$NON-NLS-1$
return TristateCheckBox.PART_SELECTED;
}
/**
* Gets the collection of search paths.
*
* @return the search paths
*/
@Override
public Collection<String> getSearchPaths() {
Collection<String> paths = super.getSearchPaths();
if (paths.isEmpty() && !initialized) {
initialized = true;
for (String next: getInitialSearchPaths()) {
paths.add(next);
addSearchPath(next);
}
}
return paths;
}
/**
* Refreshes the autoload data.
*/
@Override
protected void refreshAutoloadData() {
final Map<String, Map<String, ArrayList<String[]>>> data
= new TreeMap<String, Map<String, ArrayList<String[]>>>();
for (String path: getSearchPaths()) {
Map<String, ArrayList<String[]>> functionMap = findFitFunctions(path);
data.put(path, functionMap);
}
setAutoloadData(data);
}
/**
* Refreshes the GUI.
*/
protected void refreshGUI() {
refreshAutoloadData();
super.refreshGUI();
String title = FitBuilder.this.getTitle()+" "+getTitle(); //$NON-NLS-1$
setTitle(title);
setInstructions(ToolsRes.getString("FitBuilder.Instructions.SelectToAutoload") //$NON-NLS-1$
+ "\n\n"+ToolsRes.getString("FitBuilder.Instructions.WhereDefined") //$NON-NLS-1$ //$NON-NLS-2$
+" "+ToolsRes.getString("FitBuilder.Instructions.HowToAddFunction") //$NON-NLS-1$ //$NON-NLS-2$
+" "+ToolsRes.getString("FitBuilder.Instructions.HowToAddDirectory")); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}