/*
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;
import java.util.HashSet;
import org.geogebra.common.kernel.geos.GeoElement;
import org.geogebra.common.util.StringUtil;
/**
* Construction for macros.
*/
public class MacroConstruction extends Construction {
private Construction parentCons;
private HashSet<String> reservedLabels;
private boolean globalVariableLookup = false;
/**
* Creates new macro construction
*
* @param kernel
* Kernel
*/
public MacroConstruction(MacroKernel kernel) {
super(kernel, kernel.getParentKernel().getConstruction());
parentCons = kernel.getParentKernel().getConstruction();
reservedLabels = new HashSet<String>();
// allow using reserved function names in marco constructions
super.setFileLoading(true);
}
/**
* Set construction via XML string.
*
* @param xmlString
* XML string of the construction
* @throws Exception
* if there is a problem while reading XML
*/
public void loadXML(String xmlString) throws Exception {
if (undoManager == null) {
undoManager = kernel.getApplication().getUndoManager(this);
}
this.setFileLoading(true);
undoManager.processXML(xmlString);
this.setFileLoading(false);
}
/**
* Adds label to the list of reserved labels. Such labels will not be looked
* up in the parent construction in lookup();
*
* @param label
* reserved label
*/
public void addReservedLabel(String label) {
if (label != null) {
reservedLabels.add(label);
}
}
/**
* Returns a GeoElement for the given label. Note: construction index is
* ignored here. If no geo is found for the specified label a lookup is made
* in the parent construction.
*
* @return may return null
*/
@Override
public final GeoElement lookupLabel(String label, boolean autoCreate) {// package
// private
if (label == null) {
return null;
}
String label1 = label;
// local var handling
if (localVariableTable != null) {
GeoElement localGeo = localVariableTable.get(label1);
if (localGeo != null) {
return localGeo;
}
}
// global var handling
GeoElement geo = geoTableVarLookup(label1);
// STANDARD CASE: variable name found
if (geo != null) {
return geo;
}
label1 = Kernel.removeCASVariablePrefix(label1);
geo = geoTableVarLookup(label1);
if (geo != null) {
// geo found for name that starts with TMP_VARIABLE_PREFIX or
// GGBCAS_VARIABLE_PREFIX
return geo;
}
/*
* SPREADSHEET $ HANDLING In the spreadsheet we may have variable names
* like "A$1" for the "A1" to deal with absolute references. Let's
* remove all "$" signs from label and try again.
*/
if (label1.indexOf('$') > -1) {
StringBuilder labelWithout$ = new StringBuilder(
label1.length() - 1);
for (int i = 0; i < label1.length(); i++) {
char ch = label1.charAt(i);
if (ch != '$') {
labelWithout$.append(ch);
}
}
String labelString = labelWithout$.toString();
// allow automatic creation of elements
geo = lookupLabel(labelString, autoCreate);
if (geo != null) {
// geo found for name that includes $ signs
return geo;
}
if (labelString.charAt(0) >= '0' && labelString.charAt(0) <= '9') {
int cell = 0;
try {
cell = Integer.parseInt(labelWithout$.toString());
} catch (Exception e) {
e.printStackTrace();
}
if (cell > 0) {
return this.getCasCell(cell - 1);
}
}
}
// try upper case version for spreadsheet label like a1
if (autoCreate) {
if (StringUtil.isLetter(label1.charAt(0)) // starts with letter
&& StringUtil.isDigit(label1.charAt(label1.length() - 1))) // ends
// with
// digit
{
String upperCaseLabel = label1.toUpperCase();
geo = geoTableVarLookup(upperCaseLabel);
if (geo != null) {
return geo;
}
}
}
if (globalVariableLookup && !isReservedLabel(label1)) {
// try parent construction
geo = parentCons.lookupLabel(label1, autoCreate);
}
return geo;
}
private boolean isReservedLabel(String label) {
return reservedLabels.contains(label);
}
/**
* Returns true if geos of parent costruction can be referenced
*
* @return true if geos of parent costruction can be referenced
*/
public boolean isGlobalVariableLookup() {
return globalVariableLookup;
}
/**
* Set to true if geos of parent costruction should be referenced
*
* @param globalVariableLookup
* true if geos of parent costruction should be referenced
*/
public void setGlobalVariableLookup(boolean globalVariableLookup) {
this.globalVariableLookup = globalVariableLookup;
}
}