/* GeoGebra - Dynamic Mathematics for Everyone http://www.geogebra.org This file is part of GeoGebra. 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. */ /** * Used as internal return type in Parser. * Stores a label. */ package org.geogebra.common.kernel.arithmetic; import java.util.Set; import java.util.Vector; import org.geogebra.common.kernel.GTemplate; import org.geogebra.common.kernel.Kernel; import org.geogebra.common.kernel.MacroConstruction; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.util.debug.HasDebugString; import org.geogebra.common.util.debug.Log; /** * Common class for objects obtained from the parser that are not yet processed * to GeoElements. They may also persist in ExpressionNodes of functions * * @author Markus * */ public abstract class ValidExpression implements ExpressionValue, HasDebugString { private Vector<String> labels; private boolean inTree; // used by ExpressionNode /** * @param label * label to be added */ public void addLabel(String label) { initLabels(); // App.printStacktrace(label+":"+(label==null)); labels.add(label); } private void initLabels() { if (labels == null) { labels = new Vector<String>(); } } /** * @param labellist * list of labels to be added */ public void addLabel(Vector<String> labellist) { initLabels(); labels.addAll(labellist); } /** * @return count of labels */ public int labelCount() { if (labels == null) { return 0; } return labels.size(); } /** * @param index * index * @return label */ public String getLabel(int index) { if (index < 0 || index >= labelCount()) { return null; } return labels.get(index); } /** * @return array of all labels */ public String[] getLabels() { int size = labelCount(); if (size == 0) { return null; } String[] ret = new String[size]; for (int i = 0; i < size; i++) { ret[i] = labels.get(i); } return ret; } /** * @return label */ public String getLabel() { return getLabel(0); } /** * @param label * sets given label */ public void setLabel(String label) { initLabels(); labels.clear(); labels.add(label); } /** * @param str * sets all labels */ public void setLabels(String[] str) { initLabels(); labels.clear(); if (str == null) { return; } for (int i = 0; i < str.length; i++) { labels.add(str[i]); } } @Override public boolean isVariable() { return false; } @Override final public boolean isInTree() { return inTree; } @Override final public void setInTree(boolean flag) { inTree = flag; } @Override final public boolean isGeoElement() { return false; } /** * @return true if this is command and it is on top level */ public boolean isTopLevelCommand() { return false; } /** * @return top level command of this expression */ public Command getTopLevelCommand() { return null; } /** * @return label */ public String getLabelForAssignment() { return getLabel(); } /** * Includes the label and assignment operator. E.g. while toString() would * return x^2, this method would return f(x) := x^2 * * @param tpl * String template * @param assignmentType * assignment type * @return assignment in the form L:=R */ public String toAssignmentString(StringTemplate tpl, AssignmentType assignmentType) { return toAssignmentString(toString(tpl), assignmentType); } /** * @param rhs * assignment RHS * @param assignmentType * assignment type * @return rhs with prepended label + assignment operator */ public String toAssignmentString(String rhs, AssignmentType assignmentType) { if (labels == null) { return rhs; } StringBuilder sb = new StringBuilder(); // make sure we do not prepend null when // assignment type is none. switch (assignmentType) { case DEFAULT: sb.append(getLabelForAssignment()); sb.append(unwrap().getAssignmentOperator()); break; case DELAYED: sb.append(getLabelForAssignment()); sb.append(getDelayedAssignmentOperator()); break; case NONE: break; } sb.append(rhs); return sb.toString(); } /** * * @param tpl * string template * @param assignmentType * assignment type * @return assignment in LaTeX */ public final String toAssignmentLaTeXString(StringTemplate tpl, AssignmentType assignmentType) { if (labels == null) { return toLaTeXString(true, tpl); } StringBuilder sb = new StringBuilder(); switch (assignmentType) { case DEFAULT: sb.append(tpl.printVariableName(getLabelForAssignment())); sb.append(getAssignmentOperatorLaTeX()); break; case DELAYED: sb.append(tpl.printVariableName(getLabelForAssignment())); sb.append(getDelayedAssignmentOperatorLaTeX()); break; case NONE: break; } sb.append(toLaTeXString(true, tpl)); return sb.toString(); } /** * @return operator for non-delayed assignment */ @Override public String getAssignmentOperator() { return ":="; } /** * @return operator for delayed assignment */ public String getDelayedAssignmentOperator() { return "::="; } /** * @return operator for non-delayed assignment in LaTeX form */ public String getAssignmentOperatorLaTeX() { return " \\, := \\, "; } /** * @return operator for delayed assignment in LaTeX form */ public String getDelayedAssignmentOperatorLaTeX() { return " \\, ::= \\, "; } /** * @param cmds * commands */ public final void addCommands(Set<Command> cmds) { // do nothing, see Command, ExpressionNode classes } @Override public ExpressionValue evaluate(StringTemplate tpl) { return this; } /** * Evaluates to number (if not numeric, returns undefined MyDouble) * * @return number or undefined double */ @Override public double evaluateDouble() { ExpressionValue ev = evaluate(StringTemplate.defaultTemplate); if (ev instanceof NumberValue) { return ((NumberValue) ev).getDouble(); } return Double.NaN; } /** * Evaluates like function, a complex expression * * @return function */ public Function evaluateComplex() { ExpressionValue ev = evaluate(StringTemplate.defaultTemplate); return (Function) ev; } @SuppressWarnings("deprecation") @Deprecated @Override public final String toString() { return toString(StringTemplate.defaultTemplate); } @Override public abstract String toString(StringTemplate tpl); @Override public abstract String toValueString(StringTemplate tpl); @Override public ExpressionValue traverse(final Traversing t) { return t.process(this); } @Override public boolean inspect(Inspecting t) { return t.check(this); } @Override public String getDebugString() { return debugString(this); } /** * @param s * expression * @return string for debugging (revealing structure) */ public static String debugString(ExpressionValue s) { if (s == null) { return "<null>"; } if (s instanceof ExpressionNode) { return "ExNode(" + debugString(((ExpressionNode) s).getLeft()) + "," + ((ExpressionNode) s).getOperation() + "," + debugString(((ExpressionNode) s).getRight()) + ")"; } if (s instanceof Equation) { return "Eq(" + debugString(((Equation) s).getLHS()) + ",=," + debugString(((Equation) s).getRHS()) + ")"; } if (s instanceof MyList) { StringBuilder sb = new StringBuilder("MyList("); for (int i = 0; i < ((MyList) s).size(); i++) { if (i > 0) { sb.append(","); } sb.append(debugString(((MyList) s).getListElement(i))); } sb.append(')'); return sb.toString(); } if (s instanceof Command) { StringBuilder sb = new StringBuilder("Cmd:"); sb.append(((Command) s).getName()); sb.append("("); for (int i = 0; i < ((Command) s).getArgumentNumber(); i++) { if (i > 0) { sb.append(","); } sb.append(debugString(((Command) s).getArgument(i).unwrap())); } sb.append(')'); return sb.toString(); } if (s.isGeoElement()) { return (((GeoElement) s) .getConstruction() instanceof MacroConstruction ? "Macro" : "") + s.getClass().getName() .replaceAll("org.geogebra.common.kernel.geos.Geo", "G") .replaceAll( "org.geogebra.common.geogebra3D.kernel3D.geos.Geo", "G") + "(" + s.toString(StringTemplate.defaultTemplate) + ")"; } return s.getClass().getName() .replaceAll("org.geogebra.common.kernel.arithmetic.", "") + "(" + s.toString(StringTemplate.defaultTemplate) + ")"; } @Override public ExpressionValue unwrap() { return this; } @Override public abstract ExpressionNode wrap(); @Override public boolean hasCoords() { return false; } @Override public ExpressionValue derivative(FunctionVariable fv, Kernel kernel) { Log.debug("derivative from " + this.getValueType()); return new ExpressionNode(kernel, Double.NaN); } @Override public ExpressionValue integral(FunctionVariable fv, Kernel kernel) { Log.debug("integral from " + this.getValueType()); return null; } @Override public boolean isExpressionNode() { return false; } /** * print expression as value or geo label * * @param x2 * expression * @param values * value or label * @param tpl * template * @return value or geo */ protected static String print(ExpressionValue x2, boolean values, StringTemplate tpl) { if (values) { return x2.toValueString(tpl); } return x2.isGeoElement() ? ((GeoElement) x2).getLabel(tpl) : x2.toString(tpl); } /** * @return deep check for function variable */ public final boolean containsFunctionVariable() { return this.inspect(new Inspecting() { @Override public boolean check(ExpressionValue v) { return v instanceof FunctionVariable; } }); } /** * @param name * variable name * @return deep check for function variable with given name */ public final boolean containsFunctionVariable(final String name) { return this.inspect(new Inspecting() { @Override public boolean check(ExpressionValue v) { return v instanceof FunctionVariable && (name == null || name .equals(((FunctionVariable) v).getSetVarString())); } }); } /** * @param vars * list of acceptable variables * @return whether some other variable is included */ public final boolean containsFunctionVariableOtherThan( final FunctionVariable[] vars) { return this.inspect(new Inspecting() { @Override public boolean check(ExpressionValue v) { return v instanceof FunctionVariable && ExpressionNode.doesNotInclude(vars, v); } }); } /** * Here we just check for number values, overridden in ExpressionNode */ @Override public final boolean evaluatesToNumber(boolean def) { return getValueType() == ValueType.NUMBER || getValueType() == ValueType.BOOLEAN || (def && getValueType() == ValueType.UNKNOWN); } /** * Unlike contains does not stop on lists and equations * * @param needle * subexpression * @return whether expression is included */ public boolean containsDeep(final ExpressionValue needle) { return inspect(new Inspecting() { @Override public boolean check(ExpressionValue v) { return v == needle; } }); } /** * @param tpl * template * @return string representation of this node */ public final String toString(GTemplate tpl) { return toString(tpl.getTemplate()); } @Override public final boolean evaluatesToNonComplex2DVector() { return getValueType() == ValueType.NONCOMPLEX2D; } @Override public boolean evaluatesToVectorNotPoint() { return false; } @Override public final boolean evaluatesTo3DVector() { return getValueType() == ValueType.VECTOR3D; } @Override public final boolean evaluatesToList() { return getValueType() == ValueType.LIST; } @Override public int getListDepth() { return 0; } @Override public boolean evaluatesToText() { return getValueType() == ValueType.TEXT; } @Override public abstract ValueType getValueType(); @Override public ExpressionValue getUndefinedCopy(Kernel kernel) { return new MyDouble(kernel, Double.NaN); } @Override public ExpressionValue toValidExpression() { return this; } @Override public boolean evaluatesToNDVector() { ValueType vt = getValueType(); return vt == ValueType.NONCOMPLEX2D || vt == ValueType.VECTOR3D; } @Override public abstract ValidExpression deepCopy(Kernel kernel); /** * @param string * command name * @return whether top level is a command and name is equal to string */ public boolean isTopLevelCommand(String string) { return false; } }