/*
* Open Source Physics software is free software as described near the bottom of this code file.
*
* For additional information and documentation on Open Source Physics please see:
* <http://www.opensourcephysics.org/>
*/
package org.opensourcephysics.tools;
import java.beans.PropertyChangeEvent;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.opensourcephysics.controls.XML;
import org.opensourcephysics.controls.XMLControl;
/**
* A FunctionEditor for UserFunctions.
*
* @author Douglas Brown
*/
public class UserFunctionEditor extends FunctionEditor {
private UserFunction[] mainFunctions = new UserFunction[0];
private String[] defaultVariableNames = new String[] {"x"}; //$NON-NLS-1$
protected boolean parametersValid = true;
/**
* Constructor.
*/
public UserFunctionEditor() {
super();
}
/**
* Returns the main user functions.
*
* @return UserFunction[]
*/
public UserFunction[] getMainFunctions() {
return mainFunctions;
}
/**
* Sets the main user functions.
*
* @param functions UserFunction[]
*/
public void setMainFunctions(UserFunction[] functions) {
// remove existing main functions
UserFunction[] f = getMainFunctions();
for(UserFunction u : f) {
objects.remove(u);
}
// add new main functions
for(UserFunction u : functions) {
addObject(u, false);
}
// for (int i = 0; i < functions.length; i++) {
// boolean found = false;
// for (int row = 0; row < objects.size(); row++) {
// Object next = objects.get(row);
// if (getName(next).equals(functions[i].getName())) {
// objects.remove(next);
// objects.add(row, functions[i]);
// found = true;
// }
// }
// if (!found) addObject(functions[i], false);
// }
mainFunctions = functions;
setDefaultVariables(functions[0].getIndependentVariables());
}
/**
* Returns supporting functions
*
* @return an array of UserFunctions
*/
public UserFunction[] getSupportFunctions() {
ArrayList<Object> temp = new ArrayList<Object>();
for(Iterator<Object> it = objects.iterator(); it.hasNext(); ) {
Object next = it.next();
if(!isMainFunction(next)) {
temp.add(next);
}
}
return temp.toArray(new UserFunction[0]);
}
/**
* Returns the name of the object.
*
* @param obj the object
* @return the name
*/
public String getName(Object obj) {
return (obj==null)? null: ((UserFunction)obj).getName();
}
/**
* Returns the expression of the object.
*
* @param obj the object
* @return the expression
*/
public String getExpression(Object obj) {
return (obj==null)? null: ((UserFunction)obj).getInputString();
}
/**
* Returns the description of the object.
*
* @param obj the object
* @return the description
*/
public String getDescription(Object obj) {
return (obj==null)? null: ((UserFunction)obj).getDescription();
}
/**
* Sets the description of the object.
*
* @param obj the object
* @param desc the description
*/
public void setDescription(Object obj, String desc) {
if (obj!=null) {
if (desc!=null && desc.trim().equals("")) { //$NON-NLS-1$
desc = null;
}
((UserFunction)obj).setDescription(desc);
super.setDescription(obj, desc);
}
}
/**
* Determines if an object's name is editable.
*
* @param obj the object
* @return true if the name is editable
*/
public boolean isNameEditable(Object obj) {
return((UserFunction) obj).isNameEditable();
}
/**
* Determines if an object's expression is editable.
*
* @param obj the object
* @return true if the expression is editable
*/
public boolean isExpressionEditable(Object obj) {
UserFunction f = (UserFunction)obj;
if (f.polynomial!=null) return false;
return true;
}
/**
* Evaluates all current objects.
*/
public void evaluateAll() {
super.evaluateAll();
ParamEditor paramEditor = getParamEditor();
if(!parametersValid&&(paramEditor!=null)) {
paramEditor.evaluateAll();
}
for(int i = 0; i<evaluate.size(); i++) {
UserFunction f = (UserFunction) evaluate.get(i);
if(!parametersValid&&(paramEditor!=null)) {
f.setParameters(paramEditor.getNames(), paramEditor.getValues(), paramEditor.getDescriptions());
}
f.setExpression(f.getInputString(), f.getIndependentVariables());
}
parametersValid = true;
}
/**
* Adds an object.
*
* @param obj the object
* @param postEdit true to post an undoable edit
*/
public Object addObject(Object obj, int row, boolean postEdit, boolean firePropertyChange) {
obj = super.addObject(obj, row, postEdit, firePropertyChange);
if(obj!=null) {
firePropertyChange("function", null, obj); //$NON-NLS-1$
}
return obj;
}
/**
* Removes an object.
*
* @param obj the object to remove
* @param postEdit true to post an undoable edit
* @return the removed object
*/
public Object removeObject(Object obj, boolean postEdit) {
obj = super.removeObject(obj, postEdit);
if(obj!=null) {
firePropertyChange("function", obj, null); //$NON-NLS-1$
}
return obj;
}
/**
* Returns a tooltip for the object.
*
* @param obj the object
* @return the tooltip
*/
public String getTooltip(Object obj) {
return (obj==null)? null: ((UserFunction)obj).getDescription();
}
/**
* Responds to property change events.
*
* @param e the event
*/
public void propertyChange(PropertyChangeEvent e) {
String propName = e.getPropertyName();
if (propName.equals("param_description")) { //$NON-NLS-1$
// parameter description has changed
for (UserFunction f: getMainFunctions()) {
f.setParameters(paramEditor.getNames(), paramEditor.getValues(), paramEditor.getDescriptions());
}
for (UserFunction f: getSupportFunctions()) {
f.setParameters(paramEditor.getNames(), paramEditor.getValues(), paramEditor.getDescriptions());
}
}
else if (propName.equals("edit")) { //$NON-NLS-1$
// parameter has changed
UserFunction[] mainFunctions = getMainFunctions();
if (mainFunctions.length>0) {
String newName = (String)e.getOldValue();
String oldName = null;
Object obj = e.getNewValue();
UserFunction func = getMainFunctions()[0];
if (func.polynomial!=null && obj!=null) {
if (obj instanceof DefaultEdit) {
DefaultEdit edit = (DefaultEdit)obj;
if (edit.editType!=FunctionEditor.NAME_EDIT) {
super.propertyChange(e);
return;
}
oldName = (String)edit.undoObj;
}
else if (obj instanceof String) {
oldName = (String)obj;
}
if (oldName!=null) {
for (UserFunction f: getMainFunctions()) {
f.setParameters(paramEditor.getNames(), paramEditor.getValues(), paramEditor.getDescriptions());
f.replaceParameterNameInExpression(oldName, newName);
}
for (UserFunction f: getSupportFunctions()) {
f.setParameters(paramEditor.getNames(), paramEditor.getValues(), paramEditor.getDescriptions());
f.replaceParameterNameInExpression(oldName, newName);
}
}
}
}
}
super.propertyChange(e);
}
//_________________________ protected methods ___________________________
/**
* Determines if an object is important. Important objects cannot be cut
* even if they are editable.
*
* @param obj the object
* @return true if important
*/
protected boolean isImportant(Object obj) {
for(int i = 0; i<mainFunctions.length; i++) {
if(mainFunctions[i]==obj) {
return true;
}
}
return false;
}
/**
* Informs an object about other objects referenced in its expression.
*/
protected void setReferences(Object obj, List<Object> ref) {
UserFunction f = (UserFunction) obj;
UserFunction[] references = ref.toArray(new UserFunction[0]);
f.setReferences(references);
}
/**
* Sets the default variable names.
*
* @param varNames the names
*/
protected void setDefaultVariables(String[] varNames) {
defaultVariableNames = varNames;
}
/**
* Returns true if a name is forbidden or in use.
*
* @param obj the object (may be null)
* @param name the proposed name for the object
* @return true if disallowed
*/
protected boolean isDisallowedName(Object obj, String name) {
boolean disallowed = super.isDisallowedName(obj, name);
if(obj!=null) {
String var = ((UserFunction) obj).getIndependentVariable();
disallowed = disallowed||var.equals(name);
}
if(disallowed) {
return true;
}
// added following line so leaving object name unchanged is not disallowed
if (obj!=null && getName(obj).equals(name)) return false;
if (functionPanel instanceof FitFunctionPanel) {
FitFunctionPanel fitPanel = (FitFunctionPanel) functionPanel;
if(fitPanel.functionTool!=null) {
String s = fitPanel.functionTool.getUniqueName(name);
disallowed = !name.equals(s);
for(DatasetCurveFitter next : fitPanel.functionTool.curveFitters) {
if(disallowed) {
return true;
}
disallowed = next.fitMap.keySet().contains(name);
}
}
}
return disallowed;
}
/**
* Returns a String with the names of variables available for expressions.
*/
protected String getVariablesString(String separator) {
StringBuffer vars = new StringBuffer(""); //$NON-NLS-1$
int init = vars.length();
boolean firstItem = true;
UserFunction f = (UserFunction) getSelectedObject();
if(f!=null) {
String[] s = f.getIndependentVariables();
for(int i = 0; i<s.length; i++) {
if(!firstItem) {
vars.append(" "); //$NON-NLS-1$
}
vars.append(s[i]);
firstItem = false;
}
}
List<String> namesToSkip = new ArrayList<String>();
namesToSkip.add(getName(getSelectedObject()));
for(int i = 0; i<mainFunctions.length; i++) {
namesToSkip.add(getName(mainFunctions[i]));
}
for(int i = 0; i<names.length; i++) {
if(namesToSkip.contains(names[i])) {
continue;
}
if(!firstItem) {
vars.append(" "); //$NON-NLS-1$
}
vars.append(names[i]);
firstItem = false;
}
// add parameters, if any
String[] paramNames = paramEditor.getNames();
for(int i = 0; i<paramNames.length; i++) {
if(!firstItem) {
vars.append(" "); //$NON-NLS-1$
}
vars.append(paramNames[i]);
firstItem = false;
}
if(vars.length()==init) {
return ToolsRes.getString("FunctionPanel.Instructions.Help"); //$NON-NLS-1$
}
return ToolsRes.getString("FunctionPanel.Instructions.ValueCell") //$NON-NLS-1$
+separator+vars.toString();
}
/**
* Returns true if the object's expression is invalid.
*/
protected boolean isInvalidExpression(Object obj) {
UserFunction f = (UserFunction) obj;
return !f.getExpression().equals(f.getInputString());
}
/**
* Creates an object with specified name and expression.
* This modifies and returns the input UserFunction unless null.
*
* @param name the name
* @param expression the expression
* @param obj ignored
* @return the object
*/
protected Object createObject(String name, String expression, Object obj) {
UserFunction f = (UserFunction) obj;
if((f!=null)&&f.getName().equals(name)&&f.getInputString().equals(expression)) {
return f;
}
if(f==null) {
f = new UserFunction(name);
f.setParameters(paramEditor.getNames(), paramEditor.getValues(), paramEditor.getDescriptions());
f.setExpression(expression, defaultVariableNames);
} else if(!f.getName().equals(name)) {
f.setNameEditable(true);
f.setName(name);
} else {
f.setParameters(paramEditor.getNames(), paramEditor.getValues(), paramEditor.getDescriptions());
f.setExpression(expression, f.getIndependentVariables());
}
return f;
}
private boolean isMainFunction(Object obj) {
for(int i = 0; i<mainFunctions.length; i++) {
if(obj==mainFunctions[i]) {
return true;
}
}
return false;
}
//__________________________ static methods ___________________________
/**
* Returns an ObjectLoader to save and load data for this class.
*
* @return the object loader
*/
public static XML.ObjectLoader getLoader() {
return new Loader();
}
/**
* A class to save and load data for this class.
*/
static class Loader implements XML.ObjectLoader {
public void saveObject(XMLControl control, Object obj) {
UserFunctionEditor editor = (UserFunctionEditor) obj;
UserFunction[] functions = editor.getMainFunctions();
control.setValue("main_functions", functions); //$NON-NLS-1$
functions = editor.getSupportFunctions();
if(functions.length>0) {
control.setValue("support_functions", functions); //$NON-NLS-1$
}
}
public Object createObject(XMLControl control) {
return new UserFunctionEditor();
}
public Object loadObject(XMLControl control, Object obj) {
UserFunctionEditor editor = (UserFunctionEditor) obj;
UserFunction[] functions = (UserFunction[]) control.getObject("main_functions"); //$NON-NLS-1$
editor.setMainFunctions(functions);
int row = functions.length;
functions = (UserFunction[]) control.getObject("support_functions"); //$NON-NLS-1$
if(functions!=null) {
for(int i = 0; i<functions.length; i++) {
editor.addObject(functions[i], row+i, false, false);
}
}
return obj;
}
}
}
/*
* Open Source Physics software is free software; you can redistribute
* it and/or modify it under the terms of the GNU General Public License (GPL) as
* published by the Free Software Foundation; either version 2 of the License,
* or(at your option) any later version.
* Code that uses any portion of the code in the org.opensourcephysics package
* or any subpackage (subdirectory) of this package must must also be be released
* under the GNU GPL license.
*
* This software 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; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA
* or view the license online at http://www.gnu.org/copyleft/gpl.html
*
* Copyright (c) 2007 The Open Source Physics project
* http://www.opensourcephysics.org
*/