/*
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.
*/
package org.geogebra.common.kernel.commands;
import java.util.HashMap;
import org.geogebra.common.kernel.Construction;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.Macro;
import org.geogebra.common.kernel.arithmetic.Command;
import org.geogebra.common.kernel.arithmetic.ExpressionValue;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.main.App;
import org.geogebra.common.main.Localization;
import org.geogebra.common.main.MyError;
import org.geogebra.common.util.debug.Log;
/**
* Runs commands and handles string to command processor conversion.
*
*/
public abstract class CommandDispatcher {
/** kernel **/
protected Kernel kernel;
private Construction cons;
private App app;
/**
* stores public (String name, CommandProcessor cmdProc) pairs
*
* NB: Do not put CAS-specific commands in this table! If you ever want to,
* call Markus, so he can give you one million reasons why this is a
* terribly bad idea!
**/
protected HashMap<String, CommandProcessor> cmdTable;
/** Similar to cmdTable, but for CAS */
/** number of visible tables */
public static final int tableCount = 20;
/**
* Returns localized name of given command set
*
* @param index
* number of set (see Commands.TABLE_*)
* @return localized name
*/
public String getSubCommandSetName(int index) {
Localization loc = app.getLocalization();
switch (index) {
case CommandsConstants.TABLE_GEOMETRY:
return loc.getMenu("Type.Geometry");
case CommandsConstants.TABLE_ALGEBRA:
return loc.getMenu("Type.Algebra");
case CommandsConstants.TABLE_TEXT:
return loc.getMenu("Type.Text");
case CommandsConstants.TABLE_LOGICAL:
return loc.getMenu("Type.Logic");
case CommandsConstants.TABLE_FUNCTION:
return loc.getMenu("Type.FunctionsAndCalculus");
case CommandsConstants.TABLE_CONIC:
return loc.getMenu("Type.Conic");
case CommandsConstants.TABLE_LIST:
return loc.getMenu("Type.List");
case CommandsConstants.TABLE_VECTOR:
return loc.getMenu("Type.VectorAndMatrix");
case CommandsConstants.TABLE_TRANSFORMATION:
return loc.getMenu("Type.Transformation");
case CommandsConstants.TABLE_CHARTS:
return loc.getMenu("Type.Chart");
case CommandsConstants.TABLE_STATISTICS:
return loc.getMenu("Type.Statistics");
case CommandsConstants.TABLE_PROBABILITY:
return loc.getMenu("Type.Probability");
case CommandsConstants.TABLE_SPREADSHEET:
return loc.getMenu("Type.Spreadsheet");
case CommandsConstants.TABLE_SCRIPTING:
return loc.getMenu("Type.Scripting");
case CommandsConstants.TABLE_DISCRETE:
return loc.getMenu("Type.DiscreteMath");
case CommandsConstants.TABLE_GEOGEBRA:
return loc.getMenu("Type.GeoGebra");
case CommandsConstants.TABLE_OPTIMIZATION:
return loc.getMenu("Type.OptimizationCommands");
case CommandsConstants.TABLE_CAS:
return loc.getMenu("Type.CAS");
case CommandsConstants.TABLE_3D:
return loc.getMenu("Type.3D");
case CommandsConstants.TABLE_FINANCIAL:
return loc.getMenu("Type.Financial");
// Commands.TABLE_ENGLISH:
default:
return null;
}
}
/** stores internal (String name, CommandProcessor cmdProc) pairs */
private MacroProcessor macroProc;
/**
* Creates new command dispatcher
*
* @param kernel2
* Kernel of current application
*/
public CommandDispatcher(Kernel kernel2) {
this.kernel = kernel2;
cons = kernel2.getConstruction();
app = kernel2.getApplication();
}
/**
* Returns whether the given command name is supported in GeoGebra.
*
* @param cmd
* command name
* @return whether the given command name is supported in GeoGebra.
*/
public boolean isCommandAvailable(String cmd) {
if (cmdTable == null) {
initCmdTable();
}
return cmdTable.containsKey(cmd);
}
/**
* @param c
* Command to be executed
* @param info
* specifies if output GeoElements of this command should get
* labels
* @throws MyError
* in case command execution fails
* @return Geos created by the command
*/
final public GeoElement[] processCommand(Command c, EvalInfo info)
throws MyError {
CommandProcessor cmdProc = getProcessor(c);
return process(cmdProc, c, info);
}
private GeoElement[] process(CommandProcessor cmdProc, Command c,
EvalInfo info) {
if (cmdProc == null) {
throw new MyError(app.getLocalization(),
app.getLocalization().getError("UnknownCommand") + " : "
+ app.getLocalization().getCommand(c.getName()));
}
// switch on macro mode to avoid labeling of output if desired
// Solve[{e^-(x*x/2)=1,x>0},x]
boolean oldMacroMode = cons.isSuppressLabelsActive();
if (info != null && !info.isLabelOutput()) {
cons.setSuppressLabelCreation(true);
}
GeoElement[] ret = null;
try {
ret = cmdProc.process(c, info);
} catch (MyError e) {
throw e;
} catch (Exception e) {
cons.setSuppressLabelCreation(oldMacroMode);
Log.debug(e);
throw new MyError(app.getLocalization(), "CAS.GeneralErrorMessage",
c.getName(), e);
} finally {
cons.setSuppressLabelCreation(oldMacroMode);
}
return ret;
}
private CommandProcessor getProcessor(Command c) throws MyError {
if (!enabled) {
throw new MyError(kernel.getLocalization(), "InvalidInput");
}
if (cmdTable == null) {
initCmdTable();
}
// cmdName
String cmdName = c.getName();
CommandProcessor cmdProc;
//
// // remove CAS variable prefix from command name if present
// cmdName = cmdName.replace(ExpressionNode.GGBCAS_VARIABLE_PREFIX, "");
// MACRO: is there a macro with this command name?
Macro macro = kernel.getMacro(cmdName);
if (macro != null) {
c.setMacro(macro);
cmdProc = macroProc;
// remember macro command used:
// this is needed when a single tool A[] is exported to find
// all other tools that are needed for A[]
cons.addUsedMacro(macro);
}
// STANDARD CASE
else {
// get CommandProcessor object for command name from command table
cmdProc = cmdTable.get(cmdName);
if (cmdProc == null) {
cmdProc = commandTableSwitch(c);
if (cmdProc != null) {
cmdTable.put(cmdName, cmdProc);
}
}
}
return cmdProc;
}
/**
* Fills the string-command map
*/
protected void initCmdTable() {
macroProc = new MacroProcessor(kernel);
// external commands: visible to users
cmdTable = new HashMap<String, CommandProcessor>(500);
for (Commands comm : Commands.values()) {
cmdTable.put(comm.name(), null);
}
// =============================================================
// CAS
// do *after* above loop as we must add only those CAS commands without
// a ggb equivalent
// =============================================================
}
/**
* This method returns a CommandProcessor Object for a corresponding command
* name. This should be called only if that CommandProcessor object is not
* there already in the command table.
*
* @param c
* Command to be processed
* @return Processor for given command
*/
public CommandProcessor commandTableSwitch(Command c) {
String cmdName = c.getName();
try {
Commands command = Commands.valueOf(cmdName);
switch (command) {
// scripting
case RigidPolygon:
case Relation:
case CopyFreeObject:
case DataFunction:
case SetColor:
case SetBackgroundColor:
case SetDynamicColor:
case SetConditionToShowObject:
case SetFilling:
case SetLineThickness:
case SetLineStyle:
case SetPointStyle:
case SetPointSize:
case SetFixed:
case SetTrace:
case Rename:
case HideLayer:
case ShowLayer:
case SetCoords:
case Pan:
case CenterView:
case ZoomIn:
case SetSeed:
case ZoomOut:
case SetActiveView:
case SelectObjects:
case SetLayer:
case SetCaption:
case SetLabelMode:
case SetTooltipMode:
case UpdateConstruction:
case SetValue:
case PlaySound:
case ParseToNumber:
case ParseToFunction:
case StartAnimation:
case StartLogging:
case StopLogging:
case StartRecord:
case SetPerspective:
case Delete:
case Repeat:
case Slider:
case Checkbox:
case InputBox:
case Textfield:
case Button:
case Execute:
case GetTime:
case ShowLabel:
case SetAxesRatio:
case SetVisibleInView:
case ShowAxes:
case ShowGrid:
case SlowPlot:
case ToolImage:
case Turtle:
case TurtleForward:
case TurtleBack:
case TurtleLeft:
case TurtleRight:
case TurtleUp:
case TurtleDown:
case RunClickScript:
case RunUpdateScript:
// case DensityPlot:
return getScriptingDispatcher().dispatch(command, kernel);
// advanced
case ContourPlot:
case IntersectPath:
case IntersectRegion:
case Direction:
case Difference:
case TaylorPolynomial:
case TaylorSeries:
case SecondAxis:
case MinorAxis:
case SemiMinorAxisLength:
case SecondAxisLength:
case Directrix:
case Numerator:
case Denominator:
case ComplexRoot:
case SlopeField:
case Iteration:
case PathParameter:
case Asymptote:
case CurvatureVector:
case Curvature:
case OsculatingCircle:
case IterationList:
case RootList:
case ImplicitCurve:
case ImplicitSurface:
case Roots:
case AffineRatio:
case CrossRatio:
case ClosestPoint:
case IsInRegion:
case PrimeFactors:
case CompleteSquare:
case Union:
case ScientificText:
case VerticalText:
case RotateText:
case Ordinal:
case Parameter:
case Incircle:
case SelectedElement:
case SelectedIndex:
case Unique:
case Zip:
case Intersection:
case PointList:
case ApplyMatrix:
case Invert:
case NInvert:
case Transpose:
case ReducedRowEchelonForm:
case Determinant:
// case MatrixPlot:
case Identity:
case Centroid:
case MajorAxis:
case FirstAxis:
case SemiMajorAxisLength:
case FirstAxisLength:
case AxisStepX:
case AxisStepY:
case ConstructionStep:
case SetConstructionStep:
case Polar:
case LinearEccentricity:
case Excentricity:
case Eccentricity:
case Axes:
case IndexOf:
case Flatten:
case Insert:
case Prove:
case ProveDetails:
case DynamicCoordinates:
case Maximize:
case Minimize:
case AreCollinear:
case AreParallel:
case AreConcyclic:
case ArePerpendicular:
case AreEqual:
case AreCongruent:
case AreConcurrent:
case ToBase:
case FromBase:
case ContinuedFraction:
case AttachCopyToView:
case Divisors:
case DivisorsSum:
case Dimension:
case DivisorsList:
case IsPrime:
case LeftSide:
case RightSide:
case Division:
case MatrixRank:
case CommonDenominator:
case ToPoint:
case ToComplex:
case ToPolar:
case Factors:
case NSolveODE:
case Rate:
case Periods:
case Payment:
case FutureValue:
case PresentValue:
case SVD:
return getAdvancedDispatcher().dispatch(command, kernel);
// basic
case Tangent:
case Length:
case UnitPerpendicularVector:
case UnitOrthogonalVector:
case Sort:
case BarChart:
case Product:
case Join:
case LCM:
case GCD:
case LetterToUnicode:
case UnicodeToLetter:
case Object:
case CountIf:
case Extremum:
case UnitVector:
case Text:
case Vector:
case Dot:
case Cross:
case PolyLine:
case PointIn:
case Line:
case Ray:
case AngleBisector:
case AngularBisector:
case Segment:
case Slope:
case Angle:
case Point:
case Midpoint:
case Intersect:
case Distance:
case Radius:
case Arc:
case Sector:
case CircleArc:
case CircularArc:
case CircleSector:
case CircularSector:
case CircumcircleSector:
case CircumcircularSector:
case CircumcircleArc:
case CircumcircularArc:
case Polygon:
case Area:
case Circumference:
case Perimeter:
case Locus:
case Vertex:
case If:
case Root:
case TurningPoint:
case Polynomial:
case Spline:
// case Nyquist:
case Function:
case Curve:
case CurveCartesian:
case LowerSum:
case LeftSum:
case RectangleSum:
case UpperSum:
case TrapezoidalSum:
case Ellipse:
case Hyperbola:
case Conic:
case Circle:
case Semicircle:
case Parabola:
case Focus:
case Center:
case Element:
case Sequence:
case Reflect:
case Mirror:
case Dilate:
case Rotate:
case Translate:
case Shear:
case Stretch:
case Corner:
case Name:
case Diameter:
case ConjugateDiameter:
case LineBisector:
case PerpendicularBisector:
case OrthogonalLine:
case PerpendicularLine:
case OrthogonalVector:
case PerpendicularVector:
case Random:
case RandomBetween:
case RandomPointIn:
case Sum:
case Binomial:
case BinomialCoefficient:
case Mod:
case Div:
case Min:
case Max:
case Append:
case First:
case Last:
case Remove:
case RemoveUndefined:
case Reverse:
case TableText:
case Take:
case TextToUnicode:
case UnicodeToText:
case FractionText:
case KeepIf:
case IsInteger:
case Defined:
case IsDefined:
case FormulaText:
case LaTeX:
case RoundedPolygon:
case Normalize:
return getBasicDispatcher().dispatch(command, kernel);
case CFactor:
case CIFactor:
case CSolutions:
case CSolve:
case Eliminate:
case GroebnerLex:
case GroebnerDegRevLex:
case GroebnerLexDeg:
case NSolve:
case NSolutions:
case Numeric:
case MixedNumber:
case Rationalize:
case Solutions:
case Solve:
case Substitute:
case ToExponential:
return new CAScmdProcessor(kernel);
// ************** STATS ***************
case ANOVA:
case Bernoulli:
case BinomialDist:
case BoxPlot:
case Cauchy:
case Cell:
case CellRange:
case ChiSquaredTest:
case ChiSquared:
case Classes:
case Column:
case ColumnName:
case CorrelationCoefficient:
case Covariance:
case ContingencyTable:
case DotPlot:
case Erlang:
case Exponential:
case FDistribution:
case FillCells:
case FillColumn:
case FillRow:
case Fit:
case FitImplicit:
case FitExp:
case FitGrowth:
case FitLine:
case FitLineX:
case FitLineY:
case FitLog:
case FitLogistic:
case FitPoly:
case FitPow:
case FitSin:
case Frequency:
case FrequencyPolygon:
case FrequencyTable:
case Gamma:
case GeometricMean:
case HarmonicMean:
case Histogram:
case HistogramRight:
case HyperGeometric:
case InverseBinomial:
case InverseCauchy:
case InverseChiSquared:
case InverseExponential:
case InverseFDistribution:
case InverseGamma:
case InverseHyperGeometric:
case InverseLogNormal:
case InverseLogistic:
case InverseNormal:
case InversePascal:
case InversePoisson:
case InverseTDistribution:
case InverseWeibull:
case InverseZipf:
case LogNormal:
case Logistic:
case Mean:
case MeanX:
case MeanY:
case Median:
case Mode:
case Normal:
case NormalQuantilePlot:
case nPr:
case OrdinalRank:
case PMCC:
case Pascal:
case Percentile:
case Poisson:
case Q1:
case Q3:
case RSquare:
case RandomDiscrete:
case RandomElement:
case RandomPolynomial:
case RandomBinomial:
case RandomNormal:
case RandomPoisson:
case RandomUniform:
case ResidualPlot:
case RootMeanSquare:
case Row:
case SD:
case SDX:
case SDY:
case SXX:
case SXY:
case SYY:
case Sample:
case SampleSD:
case SampleSDX:
case SampleSDY:
case SampleVariance:
case Shuffle:
case SigmaXX:
case SigmaXY:
case SigmaYY:
case Spearman:
case StemPlot:
case StepGraph:
case StickGraph:
case SumSquaredErrors:
case TDistribution:
case TMean2Estimate:
case TMeanEstimate:
case TTest2:
case TTest:
case TTestPaired:
case TiedRank:
case Triangular:
case Uniform:
case Variance:
case Weibull:
case ZMean2Estimate:
case ZMean2Test:
case ZMeanEstimate:
case ZMeanTest:
case ZProportion2Estimate:
case ZProportion2Test:
case ZProportionEstimate:
case ZProportionTest:
case Zipf:
return getStatsDispatcher().dispatch(command, kernel);
case TriangleCenter:
case Barycenter:
case Trilinear:
case Cubic:
case TriangleCurve:
case Voronoi:
case Hull:
case ConvexHull:
case MinimumSpanningTree:
case DelauneyTriangulation:
case TravelingSalesman:
case ShortestDistance:
return getDiscreteDispatcher().dispatch(command, kernel);
case LocusEquation:
case Envelope:
case Expand:
case Factor:
case IFactor:
case Simplify:
case SurdText:
case ParametricDerivative:
case Derivative:
case NDerivative:
case Integral:
case IntegralBetween:
case NIntegral:
case TrigExpand:
case TrigSimplify:
case TrigCombine:
case Limit:
case LimitBelow:
case LimitAbove:
case Degree:
case Coefficients:
case PartialFractions:
case SolveODE:
case ImplicitDerivative:
case NextPrime:
case PreviousPrime:
return getCASDispatcher().dispatch(command, kernel);
default:
Log.error("missing case in CommandDispatcher " + cmdName);
return null;
}
} catch (RuntimeException e) {
Log.warn("command not found / CAS command called:" + cmdName);
}
return null;
}
private CommandDispatcherStats statsDispatcher = null;
private CommandDispatcherStats getStatsDispatcher() {
if (statsDispatcher == null) {
statsDispatcher = new CommandDispatcherStats();
}
return statsDispatcher;
}
/** dispatcher for discrete math */
protected CommandDispatcherInterface discreteDispatcher = null;
/** @return dispatcher for discrete math */
protected CommandDispatcherInterface getDiscreteDispatcher() {
if (discreteDispatcher == null) {
discreteDispatcher = new CommandDispatcherDiscrete();
}
return discreteDispatcher;
}
/** dispatcher for CAS commands */
protected CommandDispatcherInterface casDispatcher = null;
/** @return dispatcher for CAS commands */
protected CommandDispatcherInterface getCASDispatcher() {
if (casDispatcher == null) {
casDispatcher = new CommandDispatcherCAS();
}
return casDispatcher;
}
/** dispatcher for scripting commands */
protected CommandDispatcherInterface scriptingDispatcher = null;
/** @return dispatcher for scripting commands */
protected CommandDispatcherInterface getScriptingDispatcher() {
if (scriptingDispatcher == null) {
scriptingDispatcher = new CommandDispatcherScripting();
}
return scriptingDispatcher;
}
/** dispatcher for advanced commands */
protected CommandDispatcherInterface advancedDispatcher = null;
/** @return dispatcher for advanced commands */
protected CommandDispatcherInterface getAdvancedDispatcher() {
if (advancedDispatcher == null) {
advancedDispatcher = new CommandDispatcherAdvanced();
}
return advancedDispatcher;
}
private CommandDispatcherBasic basicDispatcher = null;
private boolean enabled = true;
private CommandDispatcherBasic getBasicDispatcher() {
if (basicDispatcher == null) {
basicDispatcher = new CommandDispatcherBasic();
}
return basicDispatcher;
}
/**
* A way to process a command to an expression value rather than GeoELement
*
* @param c
* command
* @param info
* flags eg whether output needs label
* @return command result
*/
public ExpressionValue simplifyCommand(Command c, EvalInfo info) {
CommandProcessor cmdProc = getProcessor(c);
if (cmdProc != null) {
ExpressionValue simple = cmdProc.simplify(c);
if (simple != null) {
return simple;
}
}
return process(cmdProc, c, info)[0];
}
/**
* @param enable
* true to enable commands
*/
public void setEnabled(boolean enable) {
this.enabled = enable;
}
/**
* @return whether commands are supported
*/
public boolean isEnabled() {
return enabled;
}
}