package printer;
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
import speclistener.SpecBaseListener;
import symboltable.ASTNode;
import symboltable.Constructor;
import symboltable.ContainsProperties;
import symboltable.Entity;
import symboltable.EntityProperty;
import symboltable.EnumProperty;
import symboltable.ExistentialProperty;
import symboltable.IntProperty;
import symboltable.NodeProperty;
import symboltable.Property;
import symboltable.StringProperty;
import static util.PrintUtil.*;
/**
*
* @author kostasferles
*/
/*
* REVIEW: The way that the properties for every are being collected is a bit messy. See if there is an efficient way
* to collect all the properties for constructors and not have to traverse multiple times a node (just for coordinating the
* variables for the file predicates and IDB rules).
*/
public class PrintLogicQLImportCode extends SpecBaseListener {
Path csvOutputDir;
private static class Var{
final String varNameStart;
int index = 0;
String varName;
private void setVarName(){
varName = varNameStart + index;
}
public Var(String varNameStart){
this.varNameStart = varNameStart;
setVarName();
}
public Var(String varNameStart, int index){
this.varNameStart = varNameStart;
this.index = index;
setVarName();
}
public void newVar(){
index++;
setVarName();
}
public int getCurrIndex(){
return this.index;
}
@Override
public String toString(){
return varName;
}
}
private BufferedWriter output;
private Var enumVar;
private Var nodeVar;
private Var filePredVar;
private CodeForCons currConsCode;
private Constructor currCons;
private String currNodeVar;
private Stack<CodeForCons> consSt = new Stack<>();
private boolean visitingCons = false;
private LogicQLPrinter currPrinter;
private LogicQLPrinter tmpPrinterForNonFuncProp;
private final String delim;
private void handleProperty(Property prop, LogicQLCode codeForProp){
Property.Type pType = prop.getType();
if(visitingCons){
currConsCode.addCodeForProperty(codeForProp);
if(consSt.isEmpty())
currPrinter.addProperty(codeForProp);
}
else{
switch(pType){
case ManyToOne:
case OneToOne:
if(!prop.isOptional()){
currPrinter.addProperty(codeForProp);
break;
}
case OneToMany:
case ManyToMany:
LogicQLPrinter pr = new LogicQLPrinter(tmpPrinterForNonFuncProp, getFullPropertyName(prop));
pr.addProperty(codeForProp);
writeToFile(output, pr.print(false));
break;
case Existential:
LogicQLPrinter ePr = new LogicQLPrinter(tmpPrinterForNonFuncProp, getFullPropertyName(prop));
ePr.addProperty(codeForProp);
writeToFile(output, ePr.print(false));
break;
default :
assert false;
}
}
}
private void enterContainsProps(ContainsProperties conProp){
nodeVar = new Var("n");
enumVar = new Var("e");
filePredVar = new Var("v");
Constructor cons = conProp.getConstructor();
currCons = cons;
currNodeVar = nodeVar.toString();
currConsCode = new CodeForCons(cons, conProp.getName(), currNodeVar);
nodeVar.newVar();
currPrinter = new LogicQLPrinter(this.csvOutputDir, conProp.getName(), currConsCode, delim);
Iterator<Property> it = cons.getProperties();
visitingCons = true;
while(it.hasNext()){
this.enterProperty(it.next());
}
visitingCons = false;
tmpPrinterForNonFuncProp = new LogicQLPrinter(currPrinter, "");
}
private CodeForCons getConsCodeForContainsPropObjs(ContainsProperties rangeConProps){
Constructor rangeCons = rangeConProps.getConstructor();
String rangeNodeVar = nodeVar.toString();
CodeForCons rangeConsCode = new CodeForCons(rangeCons, rangeConProps.getName(), rangeNodeVar);
boolean prevVisitingCons = visitingCons;
currConsCode = rangeConsCode;
nodeVar.newVar();
Iterator<Property> it = rangeCons.getProperties();
visitingCons = true;
while(it.hasNext()){
this.enterProperty(it.next());
}
visitingCons = prevVisitingCons;
currConsCode = consSt.pop();
return rangeConsCode;
}
public PrintLogicQLImportCode(BufferedWriter output, Path csvOutDir, String delim){
this.delim = delim;
this.output = output;
this.csvOutputDir = csvOutDir;
}
@Override
public void enterAstNode(ASTNode node){
enterContainsProps(node);
}
@Override
public void enterEntity(Entity en){
enterContainsProps(en);
}
@Override
public void enterProperty(Property p){
if(!this.visitingCons && this.currCons.containsProperty(p)) return;
super.enterProperty(p);
}
@Override
public void enterStringProperty(StringProperty prop){
CodeForStringProperty codeForProp = new CodeForStringProperty(currNodeVar, prop, filePredVar.toString());
filePredVar.newVar();
handleProperty(prop, codeForProp);
}
@Override
public void enterIntProperty(IntProperty prop){
CodeForIntProperty codeForProp = new CodeForIntProperty(currNodeVar, prop, filePredVar.toString());
filePredVar.newVar();
handleProperty(prop, codeForProp);
}
@Override
public void enterEnumProperty(EnumProperty enumProp){
CodeForEnumProperty codeForEnum = new CodeForEnumProperty(currNodeVar, enumProp, filePredVar.toString(), enumVar.toString());
filePredVar.newVar();
enumVar.newVar();
handleProperty(enumProp, codeForEnum);
}
@Override
public void enterNodeProperty(NodeProperty nodeProp){
consSt.push(currConsCode);
ASTNode rangeNode = nodeProp.getRangeNode();
CodeForCons rangeConsCode = getConsCodeForContainsPropObjs(rangeNode);
CodeForContainsPropsObj codeForProp = new CodeForContainsPropsObj(currNodeVar, currConsCode, nodeProp, rangeConsCode);
handleProperty(nodeProp, codeForProp);
}
@Override
public void enterEntityProperty(EntityProperty enProp){
consSt.push(currConsCode);
Entity rangeEntity = enProp.getRangeEntity();
CodeForCons rangeConsCode = getConsCodeForContainsPropObjs(rangeEntity);
CodeForContainsPropsObj codeFroProp = new CodeForContainsPropsObj(currNodeVar, currConsCode, enProp, rangeConsCode);
handleProperty(enProp, codeFroProp);
}
@Override
public void enterExistentialProperty(ExistentialProperty ep){
CodeForExistentialProperty code = new CodeForExistentialProperty(currNodeVar, ep);
handleProperty(ep, code);
}
@Override
public void exitAstNode(ASTNode node){
assert consSt.isEmpty();
writeToFile(output, currPrinter.print(true));
}
@Override
public void exitEntity(Entity en){
assert consSt.isEmpty();
writeToFile(output, currPrinter.print(true));
}
}
interface Constants{
final String identForFilePred = " <- ";
final String identation = " ";
final String filePredTail = ":fPred";
final String ordIndexVar = "i";
}
class AuxPrintUtil implements Constants{
public static String getPropertyToString(Property p, String var1, String var2){
StringBuilder rv = new StringBuilder("+");
String propName = getFullPropertyName(p);
rv.append(propName);
switch(p.getType()){
case OneToOne:
case ManyToOne:
rv.append("(").
append(var1).
append(",").
append(var2).append(")");
break;
case OneToMany:
if(!p.isOrdered()){
rv.append("(").
append(var2).
append(",").
append(var1).
append(")");
}
else{
rv.append("(").
append(var2).
append(",").
append(ordIndexVar).
append(",").
append(var1).append(")");
}
break;
case ManyToMany:
if(!p.isOrdered()){
rv.append("(").
append(var1).
append(",").
append(var2).
append(")");
}
else{
rv.append("(").
append(var1).
append(",").
append(ordIndexVar).
append(",").
append(var2).append(")");
}
break;
}
return rv.toString();
}
private static FileSystem fSystem = FileSystems.getDefault();
public static String getLangDirectives(Path csvOutDir, String propName, String delim){
StringBuilder rv = new StringBuilder();
String filePredName = getFilePredName(propName);
String fileName = csvOutDir.toString() + "/" + propName + ".csv";
Path p = fSystem.getPath(fileName);
if(!Files.exists(p)){
try{
Files.createFile(p);
}catch(IOException er){
System.err.println("failed to create csv file " + fileName);
System.err.println("reason: " + er.getMessage());
}
}
rv.append("lang:physical:filePath[`").
append(filePredName).
append("] = ").
append(quote(fileName)).
append(".\n").
append("lang:physical:delimiter[`").
append(filePredName).
append("] = ").
append(quote(delim)).
append(".\nlang:physical:hasColumnNames[`").
append(filePredName).
append("] = false.\n");
return rv.toString();
}
public static String getOrderedTail(Property prop){
return (prop.isOrdered() ? ",int(" + ordIndexVar + ")" : "");
}
public static String getOrderedHead(Property prop){
return (prop.isOrdered() ? "," + ordIndexVar : "");
}
public static String getFilePredName(String propName){
return "_" + propName + filePredTail;
}
}
class LogicQLPrinter implements Constants{
private String cachedFilePredHead;
private String cachedFilePredBody;
private final String delim;
private String relName;
private CodeForCons cons;
private List<LogicQLCode> functionalProps = new ArrayList<>();
private Path csvOutDir;
public LogicQLPrinter(Path csvDir, String relName, CodeForCons cons, String delim){
this.cons = cons;
this.relName = relName;
this.delim = delim;
this.csvOutDir = csvDir;
}
public LogicQLPrinter(LogicQLPrinter n, String withName){
this.cons = n.cons;
this.relName = withName;
this.delim = n.delim;
this.csvOutDir = n.csvOutDir;
this.functionalProps = new ArrayList<>(n.functionalProps);
}
public void addProperty(LogicQLCode codeForFuncProp){
functionalProps.add(codeForFuncProp);
}
private String printFilePredHead(){
if(cachedFilePredHead == null){
int funcPredSiz = functionalProps.size();
assert funcPredSiz > 0;
StringBuilder rv = new StringBuilder();
rv.append(AuxPrintUtil.getFilePredName(relName)).
append("(");
rv.append(functionalProps.get(0).getFilePredVars());
for(int i = 1 ; i < funcPredSiz ; i++){
String code = functionalProps.get(i).getFilePredVars();
rv.append(code.equals("") ? "" : ",").append(code);
}
rv.append(")");
cachedFilePredHead = rv.toString();
}
return cachedFilePredHead;
}
private String printFilePredBody(){
if(cachedFilePredBody == null){
int funcPredSiz = functionalProps.size();
assert funcPredSiz > 0;
StringBuilder rv = new StringBuilder(" -> ");
rv.append(functionalProps.get(0).getFilePredBody());
for(int i = 1 ; i < funcPredSiz ; i++){
String code = functionalProps.get(i).getFilePredBody();
rv.append(code.equals("") ? "" : ",").append(code);
}
cachedFilePredBody = rv.toString();
}
return cachedFilePredBody;
}
public String print(boolean printConsPorps){
StringBuilder rv = new StringBuilder();
StringBuilder tail = new StringBuilder();
String filePredHead = printFilePredHead();
String filePredBody = printFilePredBody();
rv.append(filePredHead).
append(filePredBody).
append(".\n\n");
rv.append(AuxPrintUtil.getLangDirectives(this.csvOutDir, relName, delim)).
append("\n");
rv.append(cons.getCodeForHead(true));
if(printConsPorps){
for(LogicQLCode code : functionalProps){
boolean printCons = true;
if(this.cons.containsProperty(code))
printCons = false;
rv.append(",\n").append(code.getCodeForHead(printCons));
String codeForBody = code.getCodeForBody();
if(!codeForBody.equals(""))
tail.append(",\n").append(code.getCodeForBody());
}
}
else{
for(LogicQLCode code : functionalProps){
if(!this.cons.containsProperty(code)){
rv.append(",\n").append(code.getCodeForHead(true));
String codeForBody = code.getCodeForBody();
if(!codeForBody.equals(""))
tail.append(",\n").append(code.getCodeForBody());
}
}
}
rv.append("\n").
append(identForFilePred).
append(filePredHead).
append(tail).
append(".\n\n");
return rv.toString();
}
}
abstract class LogicQLCode implements Constants{
protected String nodeVar;
public abstract String getFilePredVars();
public abstract String getVarsForHead();
public abstract String getFilePredBody();
public abstract String getName();
public abstract String getCodeForHead(boolean printCons);
public abstract String getCodeForBody();
public String getCodeForHeadCompl(){
return "";
}
public LogicQLCode(String nodeVar){
this.nodeVar = nodeVar;
}
}
abstract class CodeForProperty extends LogicQLCode{
protected Property prop;
public CodeForProperty(String nodeVar, Property prop){
super(nodeVar);
this.prop = prop;
}
@Override
public String getName(){
return getFullPropertyName(prop);
}
@Override
public String getCodeForBody(){
return "";
}
}
/*
* REVIEW: May it is possible to group up the primitive types class (string, int and maybe enum)
*/
class CodeForStringProperty extends CodeForProperty{
private String filePredVarForString;
public CodeForStringProperty(String nodeVar, StringProperty prop, String filePredVarForString) {
super(nodeVar, prop);
this.filePredVarForString = filePredVarForString;
}
@Override
public String getFilePredVars() {
return filePredVarForString;
}
@Override
public String getVarsForHead() {
return filePredVarForString + AuxPrintUtil.getOrderedHead(prop);
}
@Override
public String getFilePredBody() {
return "string(" + filePredVarForString + ")" + AuxPrintUtil.getOrderedTail(prop);
}
@Override
public String getCodeForHead(boolean _) {
return AuxPrintUtil.getPropertyToString(prop, nodeVar, filePredVarForString);
}
}
class CodeForExistentialProperty extends CodeForProperty{
public CodeForExistentialProperty(String nodeVar, ExistentialProperty ep){
super(nodeVar, ep);
}
@Override
public String getFilePredVars() {
return "";
}
@Override
public String getVarsForHead() {
return "";
}
@Override
public String getFilePredBody() {
return "";
}
@Override
public String getCodeForHead(boolean _) {
StringBuilder rv = new StringBuilder("+");
rv.append(getFullPropertyName(prop)).
append("(").append(nodeVar).append(")");
return rv.toString();
}
}
class CodeForIntProperty extends CodeForProperty{
private String filePredVarForInt;
public CodeForIntProperty(String nodeVar, IntProperty prop, String filePredVarForString) {
super(nodeVar, prop);
this.filePredVarForInt = filePredVarForString;
}
@Override
public String getFilePredVars() {
return filePredVarForInt + AuxPrintUtil.getOrderedHead(prop);
}
@Override
public String getVarsForHead() {
return filePredVarForInt + AuxPrintUtil.getOrderedHead(prop);
}
@Override
public String getFilePredBody() {
return "int(" + filePredVarForInt + ")" + AuxPrintUtil.getOrderedTail(prop);
}
@Override
public String getCodeForHead(boolean _) {
return AuxPrintUtil.getPropertyToString(prop, nodeVar, filePredVarForInt);
}
}
class CodeForEnumProperty extends CodeForProperty{
private String filePredVarForEnumInt;
private String enumVar;
public CodeForEnumProperty(String nodeVar, EnumProperty prop, String filePredVarForEnumInt, String enumVar){
super(nodeVar, prop);
this.filePredVarForEnumInt = filePredVarForEnumInt;
this.enumVar = enumVar;
}
@Override
public String getFilePredVars() {
return filePredVarForEnumInt + AuxPrintUtil.getOrderedHead(prop);
}
@Override
public String getVarsForHead() {
return enumVar + AuxPrintUtil.getOrderedHead(prop);
}
@Override
public String getFilePredBody() {
return "int(" + filePredVarForEnumInt + ")" + AuxPrintUtil.getOrderedTail(prop);
}
@Override
public String getCodeForHead(boolean _) {
return AuxPrintUtil.getPropertyToString(prop, nodeVar, enumVar);
}
@Override
public String getCodeForBody() {
StringBuilder rv = new StringBuilder();
rv.append(CodeForEnumProperty.identation).
append(((EnumProperty)prop).getEnumeration().getName()).
append(":fromIndex[").
append(filePredVarForEnumInt).
append("] = ").
append(enumVar);
return rv.toString();
}
}
class CodeForContainsPropsObj extends CodeForProperty{
private CodeForCons thisCons;
private CodeForCons rangeNodeCons;
public CodeForContainsPropsObj(String varNode, CodeForCons thisCons, NodeProperty prop, CodeForCons rangeNodeCons){
super(varNode, prop);
this.rangeNodeCons = rangeNodeCons;
this.thisCons = thisCons;
}
public CodeForContainsPropsObj(String varNode, CodeForCons thisCons, EntityProperty ep, CodeForCons rangeNodeCons){
super(varNode, ep);
this.rangeNodeCons = rangeNodeCons;
this.thisCons = thisCons;
}
@Override
public String getFilePredVars() {
return rangeNodeCons.getFilePredVars() + AuxPrintUtil.getOrderedHead(prop);
}
@Override
public String getVarsForHead() {
return rangeNodeCons.nodeVar + AuxPrintUtil.getOrderedHead(prop);
}
@Override
public String getFilePredBody() {
return rangeNodeCons.getFilePredBody() + AuxPrintUtil.getOrderedTail(prop);
}
@Override
public String getCodeForHead(boolean printCons) {
String rangeConsStr = printCons ? rangeNodeCons.getCodeForHead(true) + ",\n" : "";
return rangeConsStr + AuxPrintUtil.getPropertyToString(prop, nodeVar, rangeNodeCons.nodeVar);
}
@Override
public String getCodeForHeadCompl(){
return rangeNodeCons.getCodeForHeadCompl();
}
}
class CodeForCons extends LogicQLCode{
private String filePredVarsCached;
private Constructor cons;
private List<LogicQLCode> listOfPropCode = new ArrayList<>();
private String typeToConstruct;
public CodeForCons(Constructor cons, String typeToConstruct, String nodeVar){
super(nodeVar);
this.cons = cons;
this.typeToConstruct = typeToConstruct;
}
public void addCodeForProperty(LogicQLCode code){
listOfPropCode.add(code);
}
@Override
public String getFilePredVars() {
if(filePredVarsCached == null){
int propSize = listOfPropCode.size();
assert propSize > 0;
StringBuilder rv = new StringBuilder();
rv.append(listOfPropCode.get(0).getFilePredVars());
for(int i = 1 ; i < propSize ; i++){
String code = listOfPropCode.get(i).getFilePredVars();
rv.append(code.equals("") ? "" : ",").append(code);
}
filePredVarsCached = rv.toString();
}
return filePredVarsCached;
}
@Override
public String getVarsForHead(){
return nodeVar;
}
@Override
public String getFilePredBody() {
int propSize = listOfPropCode.size();
assert propSize > 0;
StringBuilder rv = new StringBuilder();
rv.append(listOfPropCode.get(0).getFilePredBody());
for(int i = 1 ; i < propSize ; i++)
rv.append(",").append(listOfPropCode.get(i).getFilePredBody());
return rv.toString();
}
@Override
public String getCodeForHead(boolean _){
int propSz = this.listOfPropCode.size();
assert propSz > 0;
StringBuilder rv = new StringBuilder("+");
rv.append(getName()).
append("[").
append(listOfPropCode.get(0).getVarsForHead());
for(int i = 1 ; i < propSz ; ++i)
rv.append(",").append(listOfPropCode.get(i).getVarsForHead());
rv.append("] = ").
append(nodeVar).
append(",\n").
append("+").
append(typeToConstruct).
append("(").
append(nodeVar).
append(")");
for(LogicQLCode code : listOfPropCode){
String complHead = code.getCodeForHeadCompl();
if(!complHead.equals(""))
rv.append(",\n").append(complHead);
}
return rv.toString();
}
@Override
public String getCodeForHeadCompl(){
return getCodeForHead(true);
}
@Override
public String getCodeForBody(){
StringBuilder rv = new StringBuilder();
for(LogicQLCode code : listOfPropCode){
String codeForBody = code.getCodeForBody();
if(!codeForBody.equals(""))
rv.append(",\n").append(identation).append(code.getCodeForBody());
}
return rv.toString();
}
@Override
public String getName() {
return cons.getParent().getName() + ":cons";
}
public boolean containsProperty(LogicQLCode code){
return this.listOfPropCode.contains(code);
}
}