Files
L-System/src/lsystem/engine/Parser.java

191 lines
6.6 KiB
Java

package lsystem.engine;
import lsystem.screen.Constants;
import lsystem.utils.Pair;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* check if given axiom and rules are correct and parse {@link Rewrite rewritten} word to an {@link Element} object
*/
public class Parser {
private final String axiom;
private final List<String> rules;
private final int nbIterations;
public Parser(String axiom, List<String> rules,int nbIterations) {
this.axiom = axiom;
this.rules = rules;
this.nbIterations = nbIterations;
}
/**
* Check if axiom and rules given by user respect the syntax
* @return true if the syntax is correct
*/
public boolean isCorrect(){
if (nbIterations < 1) {
System.out.println("Erreur, nombre d'itérations insuffisant (plus petit que 1)");
return false;
}
boolean bl = isCorrect(axiom, Type.AXIOM);
for(String rule : this.rules){
bl = bl && isCorrect(rule, Type.RULE);
}
return bl;
}
/**
*
* @param stringToCheck check if the string respect the syntax
* @param type to help the program know if the string is an axiom or a rule to check if is correct
* @return {@code true} if {@code stringToCheck} respect the syntax, {@code false} otherwise
*/
private boolean isCorrect(String stringToCheck, Type type) {
if(type == Type.RULE && !stringToCheck.contains("="))
return false;
char old = ' ';
int bracket = 0;
boolean equalsSymbolFound = false;
for (int i = 0; i < stringToCheck.length(); i++) {
char temp = stringToCheck.charAt(i);
if (temp == '[')
bracket++;
if(temp == ']') {
bracket--;
if(stringToCheck.charAt(i - 1) == '[')
return false;
}
if(temp == '=') {
if(!equalsSymbolFound)
equalsSymbolFound = true;
else
// only one '=' allowed
return false;
}
if(old == '.'){
for(int y = (type == Type.RULE ? 0 : 1); y < 10; y++){
if(temp == Constants.VALID_CHARS[y])
return false;
}
}
old = temp;
for(char validChar : Constants.VALID_CHARS){
if(validChar == ' ')
return false;
if(temp == validChar)
break;
}
}
return bracket == 0;
}
/**
* Used by {@link Rewrite#rewrite(String, List, int)} )}
* @return a list of rules with the left and right sides separated by a {@link lsystem.utils.Pair Pair}
*/
public List<Pair<String, String>> parseRules() {
List<Pair<String, String>> rules = new ArrayList<>();
this.rules.forEach(rule -> {
String[] str = rule.split("=");
rules.add(new Pair<>(str[0], str[1]));
});
return rules;
}
/**
* convert a rewritten string to an organized L-System stored like a tree as an {@link Element} object
* @param word rewritten word
* @return the organized L-System object
* @throws NumberFormatException if an number has been written incorrectly in {@code word} param
*/
public static Element parse(String word) throws NumberFormatException {
char[] numbers = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '+', '-'};
Element root = null;
Element workingElement = null;
String number = "";
List<Element> bracket = new ArrayList<>();
float[] appliedRotation = new float[2];
Element lastCreatedElement = null;
for(int i = 0; i < word.length(); ++i) {
char c = word.charAt(i);
ElementProperties property = Arrays.stream(ElementProperties.values()).filter(p -> p.getChar() == c).findFirst().orElse(null);
if(property != null) {
if(property.getDirection() == -1) {
if(!number.isEmpty()) {
float n = getFloat(number);
number = "";
appliedRotation[0] = n;
}
if(workingElement == null) {
workingElement = new Element(property, null);
lastCreatedElement = workingElement;
root = workingElement;
} else {
Element element = new Element(property, workingElement, appliedRotation);
lastCreatedElement = element;
appliedRotation = new float[]{0f, 0f, 0f};
workingElement.children.add(element);
workingElement = lastCreatedElement;
}
} else {
float n = getFloat(number);
number = "";
appliedRotation[property.getDirection()] = n;
}
} else {
if(c == '[') {
workingElement = lastCreatedElement;
bracket.add(lastCreatedElement);
} else if(c == ']') {
assert workingElement != null && !bracket.isEmpty();
workingElement = bracket.remove(bracket.size() - 1);
} else {
for(char n : numbers) {
if(c == n) {
number += c;
break;
}
}
}
}
}
assert root == null;
return root;
}
/**
* Add some pre condition before execute {@link Float#parseFloat(String)} like if {@code number} give a {@code +}
* or an empty {@link String}, method will return 0.25 degrees and {@code -} will return 25 degrees
* @param number the number which will be converted
* @return the converted float from {@code number} param
* @throws NumberFormatException throw by {@link Float#parseFloat(String)}
*/
private static float getFloat(String number) throws NumberFormatException {
float n;
if(number.equals("") || number.equals("+"))
n = Constants.TWENTY_FIVE_DEGREES;
else if(number.equals("-")) {
n = -Constants.TWENTY_FIVE_DEGREES;
}else{
n = Float.parseFloat(number);
}
return n;
}
public int getNbIterations() {
return nbIterations;
}
public String getAxiom() {
return axiom;
}
}