package parser;
import javax.swing.JOptionPane;
import com.mysql.jdbc.PacketTooBigException;
import ctex.Main;
import gui.Frame;
import database.Db;
/**
* Parser-functions for CrossTex-Files
* @param input - String to analyse
*
* @author lischkls
* @version 0.1
*/
public class Parser {
private Frame frame = null;
private enum states {START, INCDEFORENTRY, ENTRYTYPE, KEY, CONDORATTTYPE,
ATTTYPE, INFOTYPE, INFOQUATE, INFOPARAGRAPH1, INFOPARAGRAPH2,
INFOWITHOUT, COND, CONDTYPE, CONDVAL, CONDATTTYPE, CONDATTVAL, INCLUDE,
DEFAULTtyp, DEFAULTval, DEFAULTvaltype, DEFAULTobj, DEFAULTobjval, COMMENT};
private states state;
private String Text;
public Parser(String input){
Text = input;
state = states.START;
}
final boolean debug = false;
final boolean debug2 = false;
protected void print (String value){
if (debug){
System.out.println(value);
}
}
protected void print2 (String value){
if (debug2){
System.out.println(value);
}
}
/**
* Delete doube spaces, new lines or tabs
* @param String information: String which have to be without double spaces, new lines or tabs.
* @return String without multible spaces, new lines or tab
*
* @author lischkls
* @version 0.1
*/
protected String textOnly(String information){
String text = new String ();
//trim to eliminate spaces at the begining and the end
information = information.trim();
//space is false if last sign is not a sign space, new line or tab
Boolean space = false;
for (int l = 0; l < information.length(); l ++ ) {
if (information.charAt(l) == ' ' || information.charAt(l) == '\n' || information.charAt(l) == '\t') {
// replace new line and tab by space and set space to true - one space
if (space == false){
text = text.concat(" ");
space = true;
}
// reset last sign is not a space
}else{
space = false;
text = text.concat(Character.toString(information.charAt(l)));
}
}
return text;
}
/**
* proofs if the next sign without the outs charcters is the should charcater
* @param String text: String to proof
* @param char should: The character which should be the next without looking at outs
* @param char outs: Characters which should not looked at it
* @return return the position of the sign if it is true, if not -1
*
* @author lischkls
* @version 0.1
*/
protected int isNextSignWithout(String text,char should, char ... outs){
int i = 0;
//look to the hole string
while (i < text.length()){
// if the characters match, return position
if (text.charAt(i) == should){
return i;
}
//otherwise check outs
else{
int n = 0;
boolean out=false;
//goto all outs, until chracter is one of the outs
while ((n < outs.length) && out == false){
if (text.charAt(i) == outs[n]){
out = true;
}else{
n++;
}
}
// if it is a out, go next, else return -1
if (out == true){
i++;
}else{
return -1;
}
}
}
// if in the hole string only outs, return -1
return -1;
}
/**
* proofs if the string is only numeric with or without dots
* @param String text: String to proof
* @param boolean dotted: true if it could be a floatingpoint number
* @return true if it is only a number
*
* @author lischkls
* @version 0.1
*/
protected boolean isStringNumeric(String text, boolean dotted){
boolean dot=false;
//look to the hole string
for (int i = 0; i< text.length(); i++){
//if the character at position i is not a number:
if (!Character.isDigit(text.charAt(i))){
//if it have to be an integer, the floating dot is already found or it is not a dot, it can not be a number
if (!dotted || dot || text.charAt(i) !='.'){
return false;
//if it can be a floatingpoint number, the floatingpoint is not found until jet, an it is a dot, set dot to true
}else if (dotted && !dot && text.charAt(i) =='.'){
dot=true;
}
}
}
return true;
}
//Edits information, defVec, theDb
/**
* Write attribute with to database.
* if the string is numeric it is not a link. otherwise it is.
* @param Db theDb: Database to load data in it
* @param String information: Information
* @param DefaultHandler defVec: default handling
* @param int counter: Position at global text string
* @param int entryId: ID of the entry to which add the attribute
* @param int typeId: ID of the attribute type
* @param int comma: End position of given attribute, if it ends with a comma, otherwise -1
* @param int bracket: End position of given attribute, if it ends with a bracket, otherwise -1
* @return the position of the next information
* attention: Manipulates state, defVec and theDb!
*
* @author lischkls
* @version 0.1
*/
protected int setAttributeValueToDbGoNext (Db theDb, boolean checkOnly, String information, DefaultHandler defVec, int counter, int entryTypeId,int entryId, int typeId, int comma, int bracket){
int attributeId, ObjectLinkId;
information = textOnly(information);
if (checkOnly == false){
if (theDb.convTypeIdToTypeName(typeId).equals(Main.fileField)){
try {
theDb.addFile(information, entryId);
} catch (PacketTooBigException e) {
JOptionPane.showMessageDialog(null,
Main.myLang.getString(Main.myLang.getString("textFildTab.FileSizeToSmall.message")),
Main.myLang.getString(Main.myLang.getString("textFildTab.FileSizeToSmall.title")),
JOptionPane.ERROR_MESSAGE);
}
}else{
// Add Information (e.g. the name of the author) to database
print2 ("addAttribute " + information);
attributeId = theDb.addAttribute(typeId, information, false);
//Link Attribute to Entry
print2 ("ObjectLinkId: " + " ObejktId " + entryId +" attributeId "+ attributeId);
ObjectLinkId=theDb.addObjectLink(entryId, attributeId);
print2 ("OID:" + ObjectLinkId);
}
}
//if the entry is at the end, add defaults, set state at START and counter to the next position
if (bracket != -1) {
int nextComma=isNextSignWithout(Text.substring(counter+bracket+1), ',', ' ', '\n');
if (nextComma==-1){
if (checkOnly == false){
defVec.addDefaulsToDb(theDb,entryTypeId, entryId);
defVec.resetLink();
}
state = states.START;
return counter + bracket+1;
}
else{
state=states.CONDORATTTYPE;
return counter + nextComma + bracket+1;
}
}
//selct next state, q5 if the entry is not finish and more attributes will follow
// set counter to the right position
else{
state = states.CONDORATTTYPE;
return counter + comma+1;
}
}
/**
* Write attribute without paragraphs to database.
* if the string is numeric it is not a link. otherwise it is.
* @param Db theDb: Database to load data in it
* @param String information: Information
* @param DefaultHandler defVec: default handling
* @param int entryId: ID of the entry to which add the attribute
* @param int typeId: ID of the attribute type
* @return true
*
* @author lischkls
* @version 0.1
*/
protected void setAttribueValueWithoutLimits(Db theDb, boolean checkOnly, String information, DefaultHandler defVec, int entryId, int typeId){
if (checkOnly == false){
int attributeId;
information = textOnly(information);
print2("addAttribute " + information);
if (isStringNumeric(information, true)){
attributeId = theDb.addAttribute(typeId, information, false);
}else{
attributeId = theDb.addAttribute(typeId, information, true);
}
theDb.addObjectLink(entryId, attributeId);
}
}
/**
* Proof of xtx-syntax
* @return true if the given string is a correct xtx-database
* @param Db theDb: Database to load data in it
* @author lischkls
* @version 0.1
*/
public boolean proof(Db theDb, boolean checkOnly) throws ParserException{
DefaultHandler defVec = new DefaultHandler();
String information = new String();
int counter = 0;
int entryTypeId = -1, entryId = -1, typeId = -1, attributeId =-1,
condAttributeId =-1, defType=-1, defAttribute=-1, defObj=-1;
int bracketopencounter=0;
int sign = 0;
int line = 1;
if (frame != null){
frame.setBarText(3, "0.0");
}
int proCounter = 0, proMax = Text.length(), proSave = 0;
float proTest;
while (counter != Text.length()){
//counting lines and signs for errors
if (Text.charAt(counter) == '\n'){
line++;
sign = 1;
}else sign++;
print(counter + " : " + Text.charAt(counter)+ " : " + state);
//Ignore comments, only they are not in strings
if (Text.charAt(counter) =='%' &&
(state != states.INFOPARAGRAPH1 &&
state != states.INFOPARAGRAPH2 &&
state != states.INFOPARAGRAPH2)){
if(counter>0){
if (Text.charAt(counter-1)!='\\'){
counter = Text.indexOf(Character.toString('\n'), counter);
}
}else counter = Text.indexOf(Character.toString('\n'), counter);
}
if (state == states.START){
entryTypeId = entryId = typeId = -1;
information = new String ();
if (Text.charAt(counter) == '@') state = states.INCDEFORENTRY;
else if (Text.charAt(counter) != ' ' &&
Text.charAt(counter) != '\n'){
throw new ParserException("error at file line/ sign: " + line + "/ " + sign + Text.substring(counter-4, counter-4) + " \n");
}
}
else if (state == states.INCDEFORENTRY){
//@include
if (Text.substring(counter, (counter+7)).toLowerCase().compareTo("include") == 0){
//counter increase about 7 steps, because "include" have 7 characters
counter += 7;
sign += 7;
state= states.INCLUDE;
}
//@default
else if (Text.substring(counter, (counter+7)).toLowerCase().compareTo("default") == 0){
//counter increase about 7 steps, because "default" have 7 characters
counter += 7;
sign += 7;
state= states.DEFAULTtyp;
}
//@comment
else if (Text.substring(counter, (counter+7)).toLowerCase().compareTo("comment") == 0){
counter += 7;
sign += 7;
int bracket = isNextSignWithout (Text.substring(counter), '{', ' ', '\n');
if (bracket != -1){
bracketopencounter=1;
counter += bracket + 1;
state = states.COMMENT;
}
}
//Error if illegal character {
else if (Text.charAt(counter) == '{') throw new ParserException("error at file line/ sign: " + line + "/ " + sign + " } is not allowed jet.");
//Normal case
else if (Text.charAt(counter) != ' ' &&
Text.charAt(counter) != '\n'){
information = new String(Character.toString(Text.charAt(counter)));
state = states.ENTRYTYPE;
}
}
else if (state == states.ENTRYTYPE){
//End of entrytype:
if (Text.charAt(counter) == '{'){
// Add Entrytype (like book) to database
if(checkOnly == false){
entryTypeId = theDb.addType(textOnly(information).toLowerCase(), false, true, true);
print2 ("addType " + textOnly(information) + " => " + entryTypeId);
}
information= new String();
state = states.KEY;
}
//Error - @
else if (Text.charAt(counter) == '@') throw new ParserException("error at file line/ sign: " + line + "/ " + sign + " @ is not allowed jet.");
//Character of the entrytype name
else {
information = information.concat(Character.toString(Text.charAt(counter)));
}
}
//COMMENT
else if (state == states.COMMENT){
if (Text.charAt(counter) == '{') bracketopencounter++;
else if(Text.charAt(counter) == '}') bracketopencounter--;
if (bracketopencounter == 0){
state = states.START;
}
}
//Entrykey
else if (state == states.KEY){
if (counter + 1 < Text.length()){
int comma = isNextSignWithout(Text.substring(counter), ',', ' ', '\n');
if (comma != -1){
counter += comma+1;
// Add Entrykey to database:
if (checkOnly == false){
entryId = theDb.addObject(entryTypeId, textOnly(information));
print2("addObject " + information);
}
state = states.CONDORATTTYPE;
}else information = information.concat(Character.toString(Text.charAt(counter)));
}else throw new ParserException("error at file line/ sign: " + line + "/ " + sign);
}
else if (state == states.CONDORATTTYPE){
//if it is a condition go to q14
if (Text.charAt(counter) == '[') state = states.COND;
//Error
else if (Text.charAt(counter) == '=') throw new ParserException("error at file line/ sign: " + line + "/ " + sign + " = not allowd jet");
else if (Text.charAt(counter) != ' ' &&
Text.charAt(counter) != '\n')
{
state = states.ATTTYPE;
information = new String(Character.toString(Text.charAt(counter)));
}
}
else if (state == states.ATTTYPE){
if (Text.charAt(counter) == '='){
if (checkOnly == false){
//Add new information type (e.g. AUTHOR) to database
typeId = theDb.addType(textOnly(information), false, false, true);
defVec.setNotToLink(typeId);
//Add link between Entrytype and Type to catch all optionals
theDb.addTypeLink(entryTypeId, typeId, false);
print2 ("addType " + textOnly(information) + " => " + typeId);
}
state = states.INFOTYPE;
}
else {
information = information.concat(Character.toString(Text.charAt(counter)));
}
}
else if (state == states.INFOTYPE){
information = new String ();
if (Text.charAt(counter) == '\"'){
state = states.INFOQUATE;
}
else if (Text.charAt(counter) == '{'){
bracketopencounter=1;
state = states.INFOPARAGRAPH1;
}
else if (Text.charAt(counter) == '('){
bracketopencounter=1;
state = states.INFOPARAGRAPH2;
}
else if (Text.charAt(counter) != ' ' &&
Text.charAt(counter) != '\n'){
state = states.INFOWITHOUT;
information = new String (Character.toString(Text.charAt(counter)));
}
}
else if (state == states.INFOQUATE){
if (counter + 1 < Text.length()){
if (Text.charAt(counter) == '\"'){
int comma = isNextSignWithout(Text.substring(counter+1), ',', ' ', '\n');
int bracket = isNextSignWithout(Text.substring(counter+1), '}', ' ', ',' ,'\n');
if (comma != -1 || bracket !=-1){
//the information is only complite if the last charcter is an quoat
counter =
setAttributeValueToDbGoNext (theDb, checkOnly, information, defVec, counter, entryTypeId, entryId, typeId, comma, bracket);
}else information = information.concat(Character.toString(Text.charAt(counter)));
}else information = information.concat(Character.toString(Text.charAt(counter)));
}else throw new ParserException("error at file line/ sign: " + line + "/ " + sign + Text.substring(counter-4, counter-4));
}
else if (state == states.INFOPARAGRAPH1){
if (counter + 1 < Text.length()){
if (Text.charAt(counter) == '}') {
bracketopencounter--;
if (bracketopencounter == 0){
int comma = isNextSignWithout(Text.substring(counter+1), ',', ' ', '\n');
int bracket = isNextSignWithout(Text.substring(counter+1), '}', ' ', ',' ,'\n');
if (comma != -1 || bracket !=-1){
counter = setAttributeValueToDbGoNext (theDb, checkOnly, information, defVec, counter, entryTypeId, entryId, typeId, comma, bracket);
}else throw new ParserException("error at file line/ sign: " + line + "/ " + sign);
}else information = information.concat(Character.toString(Text.charAt(counter)));
}else{
information = information.concat(Character.toString(Text.charAt(counter)));
if (Text.charAt(counter) == '{'){
bracketopencounter++;
}
}
}else throw new ParserException("error at file line/ sign: " + line + "/ " + sign);
}
else if (state == states.INFOPARAGRAPH2){
if (counter + 1 < Text.length()){
if (Text.charAt(counter) == ')'){
bracketopencounter--;
if (bracketopencounter == 0){
int comma = isNextSignWithout(Text.substring(counter+1), ',', ' ', '\n');
int bracket = isNextSignWithout(Text.substring(counter+1), '}', ' ', ',' ,'\n');
if (comma != -1 || bracket !=-1){
counter = setAttributeValueToDbGoNext (theDb, checkOnly, information, defVec, counter, entryTypeId, entryId, typeId, comma, bracket);
}else throw new ParserException("error at file line/ sign: " + line + "/ " + sign);
}else information = information.concat(Character.toString(Text.charAt(counter)));
}else{
information = information.concat(Character.toString(Text.charAt(counter)));
if (Text.charAt(counter) == '('){
bracketopencounter++;
}
}
}else throw new ParserException("error at file line/ sign: " + line + "/ " + sign);
}
else if (state == states.INFOWITHOUT){
//It is the end of the entry
int bracket = isNextSignWithout(Text.substring(counter), '}', ' ', ',' , '\n');
if (bracket != -1 && bracketopencounter == 0){
counter += bracket;
setAttribueValueWithoutLimits(theDb, checkOnly, information, defVec, entryId, typeId);
if (checkOnly == false){
defVec.addDefaulsToDb(theDb,entryTypeId, entryId);
defVec.resetLink();
}
state = states.START;
}
else if (counter + 1 < Text.length()){
int comma = isNextSignWithout(Text.substring(counter), ',', ' ', '\n');
if (comma != -1){
counter += comma;
setAttribueValueWithoutLimits(theDb, checkOnly, information, defVec, entryId, typeId);
bracketopencounter = 0;
state = states.CONDORATTTYPE;
} else{
if (Text.charAt(counter) == '{') bracketopencounter++;
else if (Text.charAt(counter) == '}') bracketopencounter--;
information = information.concat(Character.toString(Text.charAt(counter)));
}
} else{
if (Text.charAt(counter) == '{') bracketopencounter++;
else if (Text.charAt(counter) == '}') bracketopencounter--;
information = information.concat(Character.toString(Text.charAt(counter)));
}
}
//Conditions
else if (state == states.COND){
print2("q14");
if (Text.charAt(counter) == '=') throw new ParserException("error at file line/ sign: " + line + "/ " + sign + Text.substring(counter-4, counter-4) + " q14 \n");
else{
state = states.CONDTYPE;
information = new String(Character.toString(Text.charAt(counter)));
}
}
else if (state == states.CONDTYPE){
print2("CONDSECEND");
if (Text.charAt(counter) == '='){
if (checkOnly == false){
//Add new information type (e.g. AUTHOR) to database
typeId = theDb.addType(textOnly(information), false, false, true);
//Add link between Entrytype and Type to catch all optionals
theDb.addTypeLink(entryTypeId, typeId, false);
print2("inforation: "+ information + " typeId " + typeId + " entryTypeId: "+ entryTypeId);
}
information= new String();
state = states.CONDVAL;
}
else {
information = information.concat(Character.toString(Text.charAt(counter)));
}
}
//Condition Value
else if (state == states.CONDVAL){
if (Text.charAt(counter) == ']'){
if (checkOnly == false){
attributeId = theDb.addAttribute(typeId, textOnly(information), false);
theDb.addObjectLink(entryId, attributeId);
print2("information: " + information + " typeID: " + typeId+ " attributeId: " + attributeId);
}
state = states.CONDATTTYPE;
information=new String();
}
else
{
information = information.concat(Character.toString(Text.charAt(counter)));
}
}
//Type
else if (state == states.CONDATTTYPE){
if (Text.charAt(counter) == '='){
if (checkOnly == false){
typeId = theDb.addType(textOnly(information), false, false, true);
//Add link between Entrytype and Type to catch all optionals
theDb.addTypeLink(entryTypeId, typeId, false);
print2("information: " + information + " typeId " +typeId + " entyTypeId " + entryTypeId);
}
information = new String();
state = states.CONDATTVAL;
}
else{
information= information.concat(Character.toString(Text.charAt(counter)));
}
}
//Value
else if (state == states.CONDATTVAL){
int comma = isNextSignWithout (Text.substring(counter), ',', ' ');
if (comma != -1){
counter = counter + comma + 1;
int bracked = isNextSignWithout (Text.substring(counter), '}', ' ', '\n');
if (information.charAt(0)=='\"' && information.charAt(information.length()-1)=='\"'){
information=information.substring(1, information.length()-1);
}
if (checkOnly == false){
condAttributeId = theDb.addAttribute(typeId, textOnly(information), true);
theDb.addAttributeLink(attributeId, condAttributeId);
print2("information " + information + " condAttributeId " + condAttributeId);
}
information = new String ();
int findnewline = isNextSignWithout(Text.substring(counter), '\n', ' ');
//End of Entry
if (bracked != -1){
state = states.START;
counter = counter+bracked;
}
//End of condition
else if (findnewline != -1){
counter = counter+findnewline;
state = states.CONDORATTTYPE;
print2("goto CONDORATTTYPE");
//End of End of fact
}else{
state = states.CONDATTTYPE;
print2("goto CONDATTTYPE");
}
}else {
information = information.concat(Character.toString(Text.charAt(counter)));
}
}
else if(state == states.INCLUDE){
if (Text.charAt(counter)=='\n' || Text.length() == counter){
if (checkOnly == false) theDb.addInclude(textOnly(information));
state = states.START;
}
else if(Text.charAt(counter) == '@') throw new
ParserException("error at file line/ sign: " + line + "/ " + sign);
else if (Text.length()-1 == counter){
information = information.concat(Character.toString(Text.charAt(counter)));
if (checkOnly == false) theDb.addInclude(textOnly(information));
state = states.START;
}else information = information.concat(Character.toString(Text.charAt(counter)));
}
else if (state==states.DEFAULTtyp){
if (Text.charAt(counter) == '='){
//Save into the database
if (checkOnly == false) defType=theDb.addType(textOnly(information), false, false, true);
state=states.DEFAULTvaltype;
information = new String();
}else{
information = information.concat(Character.toString(Text.charAt(counter)));
}
}
// decide if author= "Max Muster" or author={mm, "Max Muster"}
else if (state == states.DEFAULTvaltype){
if (Text.charAt(counter) == '\n' || Text.charAt(counter) == ' '){
state = states.DEFAULTvaltype;
}
else if (Text.charAt(counter) == '{'){
state = states.DEFAULTobj;
}
else {
if(Text.charAt(counter) == '\"'){
state=states.DEFAULTval;
}
else{
information = new String(String.valueOf(Text.charAt(counter)));
state=states.DEFAULTval;
}
}
}
//author={mm, "Max Muster"}
else if (state == states.DEFAULTobj){
if (Text.charAt(counter) == ','){
if (checkOnly == false) {
//create an object from type deftype, with shortname information
information = textOnly(information);
defObj = theDb.addObject(defType, information);
//add attribute name=link
defAttribute = theDb.addAttribute(defType, information, true);
defVec.addDefault(defType, defAttribute);
}
state = states.DEFAULTobjval;
}else{
information = information.concat(Character.toString(Text.charAt(counter)));
}
}
//Default Object value
else if (state == states.DEFAULTobjval){
if (Text.charAt(counter) == '}'){
//Add attribute default-value to default-type, with attribute-type "name"
if (checkOnly == false)
theDb.addObjectLink
(defObj, theDb.addAttribute
(theDb.addType
("name", false, false, false), textOnly(information), true));
state = states.START;
}else{
information = information.concat(Character.toString(Text.charAt(counter)));
}
}
// author= "Max Muster"
else if (state == states.DEFAULTval){
if (Text.charAt(counter) == '\n'){
information = textOnly(information);
if (information.charAt(information.length()-1) == '\"'){
information = information.substring(0, information.length()-1);
}
if (checkOnly == false){
defAttribute = theDb.addAttribute(defType, information, false);
defVec.addDefault(defType, defAttribute);
}
state=states.START;
}
else{
information = information.concat(Character.toString(Text.charAt(counter)));
}
}
counter++;
//Prozess Counter
proCounter++;
proTest = (proCounter*100)/proMax;
if (frame != null && proTest != proSave){
proSave = (int)proTest;
frame.setBarText(3, Main.myLang.getString("parser.progress") + proSave + "%");
}
}
//DEBUG
print(state.name());
print2("Parser END");
if (frame != null){
frame.setBarText(3, "");
}
return state == states.START;
}
public void setFrame(Frame frame){
this.frame = frame;
}
public void finalize(){
try {
super.finalize();
} catch (Throwable e) {
e.printStackTrace();
}
}
}