package org.geogebra.common.gui.inputfield; import java.util.ArrayList; import org.geogebra.common.gui.inputfield.DynamicTextElement.DynamicTextType; import org.geogebra.common.kernel.StringTemplate; import org.geogebra.common.kernel.algos.AlgoDependentText; import org.geogebra.common.kernel.arithmetic.ExpressionNode; import org.geogebra.common.kernel.arithmetic.ExpressionValue; import org.geogebra.common.kernel.arithmetic.MyStringBuffer; import org.geogebra.common.kernel.geos.GeoElement; import org.geogebra.common.kernel.geos.GeoText; import org.geogebra.common.main.App; import org.geogebra.common.util.StringUtil; import org.geogebra.common.util.lang.Unicode; /** * Utility class with methods for converting a GeoText string into a list of * DynamicTextElements and vice-versa. * * A GeoText string is composed of static and dynamic substrings separated by * quotes. Dynamic substrings reference the labels of other GeoElements. In raw * form GeoText strings are difficult for users to handle correctly, so GeoGebra * text editors simplify the process by inserting these dynamic strings into * special editing containers (e.g. an embedded text field). * * @author G. Sturr * */ public class DynamicTextProcessor { private App app; private ArrayList<DynamicTextElement> dList; /** * Constructor * * @param app */ public DynamicTextProcessor(App app) { this.app = app; dList = new ArrayList<DynamicTextElement>(); } /** * Converts the string of a GeoText into a list of DynamicTextElements * * * @param geo * GeoText * @return list of DynamicTextElements representing the string a the given * GeoText */ public ArrayList<DynamicTextElement> buildDynamicTextList(GeoText geo) { dList.clear(); if (geo == null) { return dList; } if (geo.isIndependent()) { dList.add(new DynamicTextElement(geo.getTextString(), DynamicTextType.STATIC)); return dList; } if (!(geo.getParentAlgorithm() instanceof AlgoDependentText)) { return null; } // if dependent text then get the root ExpressionNode root = ((AlgoDependentText) geo.getParentAlgorithm()) .getRoot(); // parse the root and set the text content this.splitString(root, dList); return dList; } /** * Parses an expression node into substrings and stores these in a list of * DynamicTextElements. * * @param en * node to be parsed * @param dList * list of DynamicTextElements derived from the given node */ private void splitString(ExpressionNode en, ArrayList<DynamicTextElement> dList) { ExpressionValue left = en.getLeft(); ExpressionValue right = en.getRight(); StringTemplate tpl = StringTemplate.defaultTemplate; if (en.isLeaf()) { if (left.isGeoElement()) { DynamicTextElement d = createDynamicTextElement( ((GeoElement) left).getLabel(tpl)); // add at end dList.add(d); } else if (left.isExpressionNode()) { splitString((ExpressionNode) left, dList); } else if (left instanceof MyStringBuffer) { DynamicTextElement d = createDynamicTextElement( left.toString(tpl).replaceAll("\"", "")); dList.add(d); } else { DynamicTextElement d = createDynamicTextElement( left.toString(tpl)); dList.add(d); } } // STANDARD case: no leaf else { if (right != null && !en.containsMyStringBuffer()) { // neither left nor right are free texts, eg a+3 in // (a+3)+"hello" // so no splitting needed dList.add(createDynamicTextElement(en.toString(tpl))); return; } // expression node if (left.isGeoElement()) { dList.add(createDynamicTextElement( ((GeoElement) left).getLabel(tpl))); } else if (left.isExpressionNode()) { this.splitString((ExpressionNode) left, dList); } else if (left instanceof MyStringBuffer) { dList.add(new DynamicTextElement( left.toString(tpl).replaceAll("\"", ""), DynamicTextType.STATIC)); } else { dList.add(createDynamicTextElement(left.toString(tpl))); } if (right != null) { if (right.isGeoElement()) { dList.add(createDynamicTextElement( ((GeoElement) right).getLabel(tpl))); } else if (right.isExpressionNode()) { this.splitString((ExpressionNode) right, dList); } else if (right instanceof MyStringBuffer) { dList.add(new DynamicTextElement( right.toString(tpl).replaceAll("\"", ""), DynamicTextType.STATIC)); } else { dList.add(createDynamicTextElement(right.toString(tpl))); } } } } /** * Creates a DynamicTextElement instance from a given string. The string is * processed to remove unnecessary prefixes and evaluated to determine its * dynamic text type. * * @param text * text to put in the dynamic field * * @return DynamicText instance */ private DynamicTextElement createDynamicTextElement(String text) { String contentString = text; DynamicTextType type = DynamicTextType.VALUE; String prefix; if (contentString.endsWith("]")) { if (contentString.startsWith( prefix = app.getLocalization().getCommand("LaTeX") + "[")) { // strip off outer command contentString = contentString.substring(prefix.length(), contentString.length() - 1); // check for second argument in LaTeX[str, false] int commaIndex = contentString.lastIndexOf(','); int bracketCount = 0; for (int i = commaIndex + 1; i < contentString.length(); i++) { if (contentString.charAt(i) == '[') { bracketCount++; } else if (contentString.charAt(i) == ']') { bracketCount--; } } if (bracketCount != 0 || commaIndex == -1) { // no second argument type = DynamicTextType.FORMULA_TEXT; } } else if (contentString.startsWith( prefix = app.getLocalization().getCommand("Name") + "[")) { // strip off outer command contentString = contentString.substring(prefix.length(), contentString.length() - 1); type = DynamicTextType.DEFINITION; } } return new DynamicTextElement(contentString, type); } /** * Converts a list of DynamicTextElements into a GeoText string. * * @param latex * boolean * @return GeoText string, e.g. "value is " + a */ public String buildGeoGebraString(ArrayList<DynamicTextElement> list, boolean latex) { if (list == null || list.size() == 0) { return ""; } char currentQuote = Unicode.OPEN_DOUBLE_QUOTE; StringBuilder sb = new StringBuilder(); String text; DynamicTextType mode; for (int i = 0; i < list.size(); i++) { text = list.get(i).text; mode = list.get(i).type; if (mode == DynamicTextType.STATIC) { for (int k = 0; k < text.length(); k++) { currentQuote = StringUtil.processQuotes(sb, text.substring(k, k + 1), currentQuote); } } else { if (mode == DynamicTextType.DEFINITION) { sb.append("\"+"); sb.append("Name["); sb.append(text); sb.append(']'); sb.append("+\""); } else if (latex || mode == DynamicTextType.FORMULA_TEXT) { sb.append("\"+"); sb.append("LaTeX["); // internal name for FormulaText[ ] sb.append(text); sb.append(']'); sb.append("+\""); } else if (mode == DynamicTextType.VALUE) { // brackets needed for eg "hello"+(a+3) sb.append("\"+("); sb.append(text); sb.append(")+\""); } } } // add quotes at start and end so it parses to a text sb.insert(0, '"'); sb.append('"'); return sb.toString(); } }