package Converters; import java.util.ArrayList; import java.util.LinkedList; import java.util.Queue; import java.util.UUID; public class TreeVariable extends Variable { //Used for Enumerations and Structures private TreeVariable trunk; private ArrayList branches; private UUID uuid = UUID.randomUUID(); //Used for Linking /* Constructor */ public TreeVariable(String name, Ctype type, TreeVariable trunk) { super(type, name); this.branches = new ArrayList(); this.trunk = trunk; } /* Getters and Setters */ public String getUUIDString( ) { return uuid.toString(); } public String getHTMLID( ) { if (getType() != Ctype.Struct) { return super.getHTMLID(); } else { return uuid.toString(); //Not used by the ESP32 getter and setter } } public TreeVariable getTrunk() { return this.trunk; } public void setTrunk(TreeVariable trunk) { this.trunk = trunk; } public ArrayList getBranches( ) { return this.branches; } //Overrides the Variable.setArraySize() public void setArraySize( int arraySize ) { ArrayList array = new ArrayList(); setVarNum(); //Change the location of the variable number for(int i = 0; i < arraySize; i++) { array.add(this.deepCopy()); } super.setArray(array); super.setPlainArraySize(arraySize); //ONLY sets the arraySize, NOTHING with varNums or spans // if (arraySize > 0) { // IDPrefix.removeAllScatch(this); // } } /* Class Methods */ public void addBranch(Variable branch) { this.branches.add(branch); } public String generateJSEnumHelper(String prefix) { return generateJSEnumHelper( null, prefix ); } public String generateJSEnumHelper( String functionName, String prefix ) { if (super.getType() != Ctype.Enum) { return ""; } //make sure its an enum String enumDef = prefix + "const " + this.getDisplayName() + " = Object.freeze({\n"; String funcDef = prefix + "//returns a string\n"; if (functionName == null) { funcDef = prefix + "function parse_" + this.getName() + "(value, id) {\n"; } else { funcDef = prefix + "function " + functionName + "(value, id) {\n"; } // String funcDef = "//returns a string\n" // + "function parseStatus(statusValue) {\n"; funcDef += prefix + " switch (value) {\n"; for(int i = 0; i < this.branches.size(); i++) { //Iterate over the branches if (branches.get(i) instanceof EnumItemVariable) { EnumItemVariable enumItem = (EnumItemVariable) branches.get(i); enumDef += prefix + "\t\"%s\": %d,\n".formatted(enumItem.getDisplayName(), enumItem.getEnumValue()); funcDef += prefix + "\tcase %s[\"%s\"]:\n".formatted(this.getDisplayName(), enumItem.getDisplayName()) + prefix + "\t\treturn \"%s\";\n".formatted(enumItem.getDisplayName()); } }//for enumDef += prefix + "});\n\n"; funcDef += prefix + "\tdefault:\n" + prefix + "\t\treturn \"\";\n" + prefix + "\t}\n" + prefix + "}\n"; return enumDef + funcDef; } /** * Generates the HTML code that displays the value and each branch as drop down lists. * @param selected: true if the title should be hidden, or indented again */ public String generateHTML( String tabs, boolean selected ) { if (this.getHTMLType() == null) { System.out.println("WARNING: generateHTML() for '" + super.getName() + "' Has no set HTML type."); return ""; } else if (htmlIsHidden()) { return ""; } else { String htmlString = ""; if (super.getArraySize() > 0) { htmlString += tabs + Variable.getIndentation( ) + "
\n"; //Creates details view with a css class htmlString += tabs + Variable.getIndentation( ) + " " + this.getFormatedName() + " (" + this.getArraySize() + ")" + "\n"; htmlString += tabs + Variable.getIndentation( ) + "
    \n"; //Crate Ordered list for each item in the array ArrayList array = getArray(); for(int i = 0; i < array.size(); i++) { //Add in special list open and close ability htmlString += tabs + Variable.getIndentation( ) + "
    \n"; //before the list item so the ordered list number is closer to the html code htmlString += tabs + Variable.getIndentation( ) + "
  1. \n"; //Start of the list Item htmlString += array.get(i).generateHTML(tabs + " ", true); htmlString += tabs + Variable.getIndentation( ) + "
  2. \n"; //End of the list Item htmlString += tabs + Variable.getIndentation( ) + "
    \n"; }//for htmlString += tabs + Variable.getIndentation( ) + "
\n"; //end of ordered list htmlString += tabs + Variable.getIndentation( ) + "
\n"; } else { if (!selected && super.getType() != Ctype.Enum) { htmlString = tabs + Variable.getIndentation( ) + "

" + this.getFormatedName() + "

\n"; } if (super.getType() != Ctype.Enum && !selected) htmlString += tabs + Variable.getIndentation( ) + "
\n"; htmlString += this.getHTMLType().getVarHTML(this, tabs + ""); //This does not have an array and is plainly displayed if (super.getType() != Ctype.Enum && !selected) htmlString += tabs + Variable.getIndentation( ) + "
\n"; } return htmlString; } } //generateHTML public String generateCGetCase( String tabs, VariableDirectoryPath path ) { return generateCGetCase(tabs, path, -1); } public String generateCGetCase( String tabs, VariableDirectoryPath path, int arrayIndex ) { if (super.getHTMLType() == null) { System.out.println("WARN: 'generateCGetCase()' for " + getName() + " has null HTML type."); return ""; } if (super.getType() == null) { System.out.println("WARN: 'generateCGetCase()' for " + getName() + " has null CType type."); return ""; } String caseString = ""; switch(super.getType()) { case Struct: //NOTE: This does not actually allow the struct to be copied but the components inside of it if (this.getArraySize() > 0) { for(int i = 0; i < this.getArraySize(); i++) { for(int j = 0; j < branches.size(); j++) { caseString += ((TreeVariable) this.getArray().get(i)).branches.get(j).generateCGetCase(tabs, path.add(this, arrayIndex), i); } }//for } else { for(int i = 0; i < branches.size(); i++) { caseString += branches.get(i).generateCGetCase(tabs, path.add(this, arrayIndex)); } } break; case Enum: caseString += super.generateCGetCase(tabs, path, arrayIndex); //The super class handles enums break; default: break; } return caseString; } public void resetVarNum() { switch (super.getType()) { case Struct: if (this.getArraySize() > 0) { for(int i = 0; i < this.getArraySize(); i++) { for(int j = 0; j < branches.size(); j++) { ((TreeVariable) this.getArray().get(i)).branches.get(j).resetVarNum(); } }//for } else { for(int i = 0; i < branches.size(); i++) { branches.get(i).resetVarNum(); } } break; case Enum: if (this.getArraySize() > 0) { for(int i = 0; i < this.getArraySize(); i++) { this.getArray().get(i).setVarNum(); }//for } else { super.setVarNum(); } break; default: } } // public String generateArduinoJSONSet( String tabs, VariableDirectoryPath path ) { // return this.generateArduinoJSONSet(tabs, path, -1); // } public String generateArduinoJSONSet( String tabs, VariableDirectoryPath path, int arrayIndex, boolean forLiveData ) { if (super.getHTMLType() == null) { System.out.println("WARN: 'generateCGetCase()' for " + getName() + " has null HTML type."); return ""; } if (super.getType() == null) { System.out.println("WARN: 'generateCGetCase()' for " + getName() + " has null CType type."); return ""; } String caseString = ""; switch(super.getType()) { case Struct: //NOTE: This does not actually allow the struct to be copied but the components inside of it if (this.getArraySize() > 0) { for(int i = 0; i < this.getArraySize(); i++) { for(int j = 0; j < branches.size(); j++) { caseString += ((TreeVariable) this.getArray().get(i)).branches.get(j).generateArduinoJSONSet(tabs, path.add(this, arrayIndex), i, forLiveData); } }//for } else { for(int i = 0; i < branches.size(); i++) { caseString += branches.get(i).generateArduinoJSONSet(tabs, path.add(this, arrayIndex), forLiveData); } } break; case Enum: caseString += super.generateArduinoJSONSet(tabs, path, arrayIndex, forLiveData); //The super class handles enums break; default: break; } return caseString; } private class NumberLink { Variable controller = null; Variable slave = null; private String pendingName = null; private boolean pendingIsSlave = true; public NumberLink(Variable var, String pendingName, boolean pendingIsSlave) { this.pendingIsSlave = pendingIsSlave; this.pendingName = pendingName; if (pendingIsSlave) { this.controller = var; } else { this.slave = var; } } public NumberLink(Variable controller, Variable slave) { this.controller = controller; this.slave = slave; } public Variable getController() { return controller; } public Variable getSlave() { return slave; } public String getControllerName() { if (controller != null) return controller.getName(); else return null; } public String getSlaveName() { if (controller != null) return controller.getName(); else return null; } public void setSlave(Variable slave) { this.slave = slave; } public String getPendingName() { return pendingName; } public boolean isPendingSlave() { return this.pendingIsSlave; } } /** * This function is only for Structures */ public String generateLinkJavaScript(String spacer) { if (getType() != Ctype.Struct) { return ""; } String js = ""; if (getArraySize() > 0) { ArrayList array = getArray(); for(int i = 0; i < array.size(); i++) { js += ((TreeVariable) array.get(i)).generateLinkJavaScript(spacer); } } else { //This is not an Array Queue queue = new LinkedList<>(); queue.addAll(branches); Variable next = null; ArrayList pendingLinks = new ArrayList<>(); ArrayList confirmedLinks = new ArrayList<>(); while ((next = queue.poll()) != null) { if (next.getArraySize() > 0) { for(int i = 0; i < pendingLinks.size(); i++) { NumberLink link = pendingLinks.get(i); if (link.isPendingSlave() && link.getPendingName().equals(next.getName())) { link.setSlave(next); confirmedLinks.add(link); pendingLinks.remove(i); break; //exit for loop } }//for loop } else { String linkName = next.getLinkedName(); if (linkName != null) { pendingLinks.add(new NumberLink(next, linkName, true)); } } //check if the next is a Structure, then check its children if (next.getType() == Ctype.Struct) { js += ((TreeVariable) next).generateLinkJavaScript(spacer); } } for(int i = 0; i < confirmedLinks.size(); i++) { Variable controller = confirmedLinks.get(i).getController(); Variable slave = confirmedLinks.get(i).getSlave(); String uuidName = "item" + controller.getHTMLID().replace("-", ""); js += spacer + "const " + uuidName + " = document.getElementById(\"" + controller.getHTMLID() + "\");\n" + spacer + uuidName + ".addEventListener(\"change\", (event) => {\n" + spacer + "\tconst newValue = Number(event.target.value);\n"; ArrayList array = slave.getArray(); for(int j = 0; j < array.size(); j++) { // js += spacer + "\tdocument.getElementById(\"" + array.get(j).getHTMLID() + "\").style.visibility = newValue > " + j + " ? 'visible' : 'hidden';\n"; js += spacer + "\tdocument.getElementById(\"" + array.get(j).getHTMLID() + "\").hidden = !(newValue > " + j + ");\n"; } js += spacer + "});\n" + spacer + uuidName + ".dispatchEvent(new Event('change', { bubbles: true }));\n\n"; } } return js; } // public TreeVariable makeCopy( ) { // TreeVariable newTree = new TreeVariable(super.getName(), super.getType(), this.getTrunk()); // newTree.setArraySize(getArraySize()); // newTree.setSuperName(getSuperName()); // newTree.setLinkedName(getLinkedName()); // newTree.setDisplayName(getDisplayName()); // newTree.setHTMLType(getHTMLType()); // newTree.setDecimalPlaces(getDecimalPlaces()); // newTree.branches = this.branches; // // return newTree; // } public Variable deepCopy() { TreeVariable copy = new TreeVariable(getName(), getType(), trunk); copy.setSuperName(getSuperName()); copy.setLinkedName(getLinkedName()); copy.setDisplayName(getDisplayName()); copy.setHTMLType(getHTMLType()); copy.setDecimalPlaces(getDecimalPlaces()); // if (getArraySize() > 0) { //// getPrefixID().resetCounter(); // CPath.resetSubPrefixes(this); // } copy.setPrefixID(getPrefixID()); for(int i = 0; i < this.branches.size(); i++) { copy.branches.add(branches.get(i).deepCopy()); } copy.setArraySize(super.getArraySize()); return copy; } public String toString() { return "TreeVariable: " + getName() + ""; } }