Skip to content

Instantly share code, notes, and snippets.

@MasterTuto
Created October 26, 2023 05:14
Show Gist options
  • Save MasterTuto/b0ad969af27748c52e469dca43cbd413 to your computer and use it in GitHub Desktop.
Save MasterTuto/b0ad969af27748c52e469dca43cbd413 to your computer and use it in GitHub Desktop.
package main;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.print.DocFlavor.STRING;
class Teste {
Map<String, String> SENIOR_ENTITY = Map.of(
"<selfid>", "id"
);
Map<String, String> MID_ENTITY = Map.of(
"<selfid>", "id",
"senior", "seniorid"
);
Map<String, String> JUNIOR_ENTITY = Map.of(
"<selfid>", "id",
"midlevel", "midlevelid"
);
Map<String, Map<String, String>> ENTITIES = Map.of(
"senior", SENIOR_ENTITY,
"midlevel", MID_ENTITY,
"junior", JUNIOR_ENTITY
);
List<String> hiearchy = List.of("senior", "midlevel", "junior");
void test() {
// tests of single entities
testSpecificPath();
testTreeOfSingleEntity();
testForestOfSingleEntity();
testTreeOfTwoEntities();
testForestOfTwoEntities();
}
void testSpecificPath() {
String selector = ".senior#7 .midlevel + .midlevel#2[name='John'] ";
String desiredOutput =
"select midlevel2 from midlevel2" +
"left outer join";
// assert toSql(selector, ENTITIES) == desiredOutput;
}
void testTreeOfSingleEntity() {
}
void testForestOfSingleEntity() {
String selector = ".senior#123[a=2];";
String desiredOutput =
"select * from senior \n" +
" left outer join midlevel on midlevel.seniorid = senior.id \n" +
" left outer join junior on junior.midlevelid = midlevel.id;\n";
test(selector, desiredOutput);
}
void testTreeOfTwoEntities() {
}
void testForestOfTwoEntities() {
}
void test(String cssSelector, String desiredOutput) {
System.out.println("\nINPUT:\n");
System.out.println(cssSelector);
System.out.println("========================================");
System.out.println("\nEXPECTED OUTPUT:\n");
System.out.println(desiredOutput);
System.out.println("========================================");
System.out.println("\nACTUAL OUTPUT:\n");
System.out.println(toSql(cssSelector, ENTITIES, hiearchy));
System.out.println();
}
String toSql(String cssSelector, Map<String, Map<String, String>> parenting, List<String> hierarchy) {
var parsedSelector = new CSSSelector(cssSelector);
return toSql(parsedSelector, parenting, hierarchy);
}
String toSql(CSSSelector parsedSelector, Map<String, Map<String, String>> parenting, List<String> hierarchy) {
StringBuilder outputQuery = new StringBuilder();
var selectPart = "select * from %s\n";
var leftOuterPart = "\tleft outer join %s on %s.%s = %s.%s\n";
var wherePart = "\twhere 1=1\n";
var conditionPart = "\tand %s.%s = '%s'\n";
for (var rule: parsedSelector.getRules()) {
var members = rule.getMembers();
var firstMember = members.peek();
var newMembers = hierarchy.stream()
.dropWhile(e -> !e.equals(firstMember.getName()))
.map((s) -> {
var members2 = new CSSDOMMember(s);
return members2;
});
var listedNewMembers = Stream.concat(Stream.of(firstMember), newMembers).collect(Collectors.toList());
boolean isFirstMember = true;
String previousName = null;
StringBuilder conditionsBuilder = new StringBuilder();
for (var member: listedNewMembers) {
var name = member.getName();
if (isFirstMember)
outputQuery.append(String.format(selectPart, name));
else {
var mappig = parenting.get(name);
var parent = mappig.get(previousName);
if (parent != null)
outputQuery.append(String.format(leftOuterPart, name, name, parent, previousName, parenting.get(previousName).get("<selfid>")));
}
var idColumn = parenting.get(name).get("<selfid>");
var idValue = member.getIdName();
if (idValue != null) {
conditionsBuilder.append(String.format(conditionPart, name, idColumn, idValue));
}
var conditions = member.getConditions();
for (var condition: conditions) {
conditionsBuilder.append(String.format(conditionPart, name, condition.getName(), condition.getValue()));
}
isFirstMember = false;
previousName = name;
}
outputQuery.append(wherePart);
if (conditionsBuilder.length() > 0) {
outputQuery.append(conditionsBuilder);
}
outputQuery.append(";\n");
}
return outputQuery.toString();
}
Optional<String> getNameFromStream(Stream<String> stream, String nameToFind) {
return stream.dropWhile(name -> !name.equalsIgnoreCase(nameToFind)).findFirst();
}
enum CSSState {
CLASS,
CLASSNAME,
ID,
IDNAME,
ATTRIBUTEOPEN,
ATTRIBUTENAME,
ATTRIBUTEEQUALS,
ATTRIBUTEVALUE,
ATTRIBUTECLOSE,
ENDSELECTOR,
ENDRULE,
WHITESPACE
}
class Condition {
private String name;
private String value;
Condition(String name) {
this.name = name;
}
public String getName() {
return name;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
class CSSDOMMember {
private String name;
private Deque<Condition> conditions = new LinkedList<>();
private String idName;
CSSDOMMember(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void addCondition(String attributeName) {
var newCondition = new Condition(attributeName);
conditions.addLast(newCondition);
}
public Condition getNewestCondition() {
return conditions.getLast();
}
public String getIdName() {
return idName;
}
public void setIdName(String idName) {
this.idName = idName;
}
public Deque<Condition> getConditions() {
return conditions;
}
}
class CSSRule {
private Deque<CSSDOMMember> members = new LinkedList<>();
private void addMember(String name) {
var member = new CSSDOMMember(name);
members.addLast(member);
}
private CSSDOMMember getNewestMember() {
return members.getLast();
}
public Deque<CSSDOMMember> getMembers() {
return members;
}
}
class CSSSelector {
private Deque<CSSRule> rules = new LinkedList<>();
private String selector;
CSSSelector(String selector) {
this.selector = selector;
parse();
}
private void parse() {
var state = CSSState.WHITESPACE;
CSSState previousState = null;
String currentValue = "";
rules.addLast(new CSSRule());
for (int tokenIndex = 0; tokenIndex < this.selector.length(); tokenIndex++) {
var token = this.selector.charAt(tokenIndex);
if (!state.equals(CSSState.WHITESPACE))
previousState = state;
switch (state) {
case CLASS:
assertToken(token, ".", "Invalid class token '.' ", tokenIndex);
break;
case CLASSNAME:
assertPrintable(token);
currentValue += token;
break;
case ATTRIBUTENAME:
assertPrintable(token);
currentValue += token;
break;
case IDNAME:
assertPrintable(token);
currentValue += token;
break;
case ATTRIBUTEVALUE:
currentValue += token;
break;
case WHITESPACE:
if (!Character.isWhitespace(tokenIndex) && previousState != null) {
quit("Expected a whitespace!!");
} else if (!Character.isWhitespace(tokenIndex) && previousState == null) {
tokenIndex--;
}
break;
case ATTRIBUTECLOSE:
assertToken(token, "]", "Expected attribute close", tokenIndex);
break;
case ATTRIBUTEEQUALS:
assertToken(token, "=", "Expected attribute equals", tokenIndex);
break;
case ATTRIBUTEOPEN:
assertToken(token, "[", "Expected attribute open '['", tokenIndex);
break;
case ENDRULE:
assertToken(token, ",", "Expected rule end ','", tokenIndex);
break;
case ENDSELECTOR:
assertToken(token, ";", "Expected selector end ';'", tokenIndex);
tokenIndex = this.selector.length(); // end the loop
break;
case ID:
assertToken(token, "#", "Expected id selector '#'", tokenIndex);
break;
}
var newState = transition(state, previousState, tokenIndex);
if (state != newState && (previousState == CSSState.ATTRIBUTENAME || previousState == CSSState.CLASSNAME || previousState == CSSState.IDNAME || previousState == CSSState.ATTRIBUTEVALUE)) { // mudou de State
var verifyEmpty = true;
if (state.equals(CSSState.ATTRIBUTEVALUE))
verifyEmpty = false;
addToRule(state, currentValue, verifyEmpty);
currentValue = "";
}
state = newState;
}
}
private CSSState transition(CSSState state, CSSState previouState, int tokenIndex) {
if (state == CSSState.ENDSELECTOR) { // if is finalizing
return null; // doesn't matter, i will end the loop
}
if (tokenIndex >= this.selector.length() - 1) {
quit("Syntax erro, EOF unexpected");
}
if (state == CSSState.CLASS) {
return CSSState.CLASSNAME;
}
if (state == CSSState.CLASSNAME) {
if (Character.isWhitespace(nextChar(tokenIndex))) {
return CSSState.WHITESPACE;
}
if (nextChar(tokenIndex) == '[') {
return CSSState.ATTRIBUTEOPEN;
}
if (nextChar(tokenIndex) == '#') {
return CSSState.ID;
}
if (nextChar(tokenIndex) == ',') {
return CSSState.ENDRULE;
}
if (nextChar(tokenIndex) == ';') {
return CSSState.ENDSELECTOR;
}
return CSSState.CLASSNAME;
}
if (state == CSSState.ATTRIBUTEOPEN) {
if (Character.isWhitespace(nextChar(tokenIndex))) {
return CSSState.WHITESPACE;
}
return CSSState.ATTRIBUTENAME;
}
if (state == CSSState.ID) {
return CSSState.IDNAME;
}
if (state == CSSState.IDNAME) {
if (Character.isWhitespace(nextChar(tokenIndex))) {
return CSSState.WHITESPACE;
}
if (nextChar(tokenIndex) == '[') {
return CSSState.ATTRIBUTEOPEN;
}
if (nextChar(tokenIndex) == ',') {
return CSSState.ENDRULE;
}
if (nextChar(tokenIndex) == ';') {
return CSSState.ENDSELECTOR;
}
return CSSState.IDNAME;
}
if (state == CSSState.ATTRIBUTEEQUALS) {
if (nextChar(tokenIndex) == ']') {
return CSSState.ATTRIBUTECLOSE;
}
return CSSState.ATTRIBUTEVALUE;
}
if (state == CSSState.ATTRIBUTEVALUE) {
if (nextChar(tokenIndex) == ']') {
return CSSState.ATTRIBUTECLOSE;
}
return CSSState.ATTRIBUTEVALUE;
}
if (state == CSSState.ATTRIBUTECLOSE) {
if (Character.isWhitespace(nextChar(tokenIndex))) {
return CSSState.WHITESPACE;
}
if (nextChar(tokenIndex) == ',') {
return CSSState.ENDRULE;
}
if (nextChar(tokenIndex) == ';') {
return CSSState.ENDSELECTOR;
}
return CSSState.ATTRIBUTEOPEN;
}
if (state == CSSState.WHITESPACE) {
if (Character.isWhitespace(nextChar(tokenIndex))) {
return CSSState.WHITESPACE;
}
if ((previouState == null || previouState == CSSState.ENDRULE) && nextChar(tokenIndex) != '.') {
quit("SyntaxError, expected begin of Rule.");
}
if ((previouState == null || previouState == CSSState.ENDRULE) && nextChar(tokenIndex) == '.') {
return CSSState.CLASS;
}
if (previouState == null && !Character.isWhitespace(this.selector.charAt(tokenIndex))) {
return CSSState.CLASS;
}
if (previouState == CSSState.IDNAME || previouState == CSSState.CLASSNAME || previouState == CSSState.ATTRIBUTECLOSE || previouState == CSSState.ENDRULE) {
return CSSState.CLASS;
}
if (previouState == CSSState.ATTRIBUTENAME) {
return CSSState.ATTRIBUTEEQUALS;
}
if (previouState == CSSState.ATTRIBUTEOPEN) {
return CSSState.ATTRIBUTENAME;
}
if (nextChar(tokenIndex) == ',') {
return CSSState.ENDRULE;
}
if (nextChar(tokenIndex) == ';') {
return CSSState.ENDSELECTOR;
}
}
if (state == CSSState.ATTRIBUTENAME) {
if (Character.isWhitespace(nextChar(tokenIndex))) {
return CSSState.WHITESPACE;
}
if (nextChar(tokenIndex) == '=') {
return CSSState.ATTRIBUTEEQUALS;
}
return CSSState.ATTRIBUTENAME;
}
if (state == CSSState.ENDRULE) {
if (Character.isWhitespace(nextChar(tokenIndex))) {
return CSSState.WHITESPACE;
}
return CSSState.ID;
}
quit("Unexpected behavior");
return null;
}
private char nextChar(int index) {
try {
return this.selector.charAt(index + 1);
} catch (IndexOutOfBoundsException indexOutOfBoundsException) {
quit("Unexpected EOF");
throw new IndexOutOfBoundsException(index);
}
}
private void assertPrintable(char token) {
if (!Character.isAlphabetic(token) && !Character.isDigit(token)) {
quit("Blank class, attribute or id names");
}
}
private void addToRule(CSSState state, String currentValue, boolean verifyEmpty) {
if (currentValue.isEmpty() && verifyEmpty) {
quit("Syntax error!!!");
}
var newestRule = rules.getLast();
switch (state) {
case CLASSNAME: {
newestRule.addMember(currentValue);
break;
}
case ATTRIBUTEVALUE: {
var newestMember = newestRule.getNewestMember();
var newestCondition = newestMember.getNewestCondition();
newestCondition.setValue(currentValue);
break;
}
case ATTRIBUTECLOSE:
case ATTRIBUTENAME: {
var newestMember = newestRule.getNewestMember();
newestMember.addCondition(currentValue);
break;
} case IDNAME: {
var newestMember = newestRule.getNewestMember();
newestMember.setIdName(currentValue);
break;
}
default:
quit("Syntax error, could not identify the correct state to store the value");
}
}
public List<CSSRule> getRules() {
return rules.stream().collect(Collectors.toList());
}
public String getSelector() {
return selector;
}
private void assertToken(char token, String expected, String message, int index) {
if (expected.charAt(0) != token) {
quit(message.trim() + String.format(" at column %d", index));
}
}
private void quit(String message) {
System.out.println(message);
System.exit(1);
}
}
public static void main(String[] args) {
var teste = new Teste();
teste.test();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment