package ee.tkasekamp.vickywaranalyzer.parser;
import ee.tkasekamp.vickywaranalyzer.core.*;
import ee.tkasekamp.vickywaranalyzer.core.Battle.Result;
import ee.tkasekamp.vickywaranalyzer.service.ModelService;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
/**
* Readers in order of activation in a typical save game:
* saveGameReader
* referenceReader
* saveGameReader
* ...
* warReader
* warGoalReader
* battleReader
* ...
* battleReader
* warReader
* originalWarGoalReader
* warGoalReader
* warReader
* saveGameReader
* warReader
* ...
*/
public class Parser {
/*Everything to read in wars */
private ArrayList<War> warList; // Stores all wars in the save game
private boolean warProcessing = false; // True when previous or active war has been found
private int WAR_COUNTER = 0; // Used to count the list in allWars
/* War details */
private String dateBuffer = ""; // Stores the last date for JoinedCountry or Battle dates
private ArrayList<JoinedCountry> countryList = new ArrayList<>(); // Stores temporarily to give to war
private ArrayList<Battle> battleList = new ArrayList<>(); // Stores temporarily to give to war
private ArrayList<WarGoal> warGoalList = new ArrayList<>();
/* Battle details */
private ArrayList<Unit> unitList = new ArrayList<>(); // Stores temporarily to give to battle. Used by both attacker and defender.
private boolean battleProcessing; // True so all new lines will be read into battleReader
private int BATTLE_COUNTER = 0; // Used to change the current battle in battleList
private boolean attackerDefender; // True is attacker, false is defender. Changed while reading a battle as the first units are attackers, the rest defenders
/* WarGoal details */
private boolean warGoalProcessing; // True so all new lines will be read into warGoalReader
private int WARGOAL_COUNTER = 0;
private boolean originalWarGoalProcessing; // New for Hod. Has the same function as warGoalProcessing
/* Various */
private int bracketCounter = 0; // bracketCounter is uses to check if all data from the war has been read in
// static public Reference saveGameData = new Reference(); // public so it can be used by all methods
private ModelService modelService;
public Parser(ModelService modelService) {
this.modelService = modelService;
}
/**
* This is the main reader. It gets a path and reads line by line from it.
* If it find a line with a specific keyword, it passes it on to the other readers in this class.
* When reading a war, battle or wargoal, the Processing is set true and all lines are passed to that specific reader until
* the Processing is set false. War reading ends when the bracketCounter is 0 again.
*
* @param saveGamePath
* @return ArrayList<War>
* @throws IOException
*/
public ArrayList<War> read(String saveGamePath) throws IOException {
/* Resetting values for when user loads multiple files during a session
* Might not be necessary but doing it just in case. */
// saveGameData = new Reference();
warList = new ArrayList<>();
WAR_COUNTER = 0;
BATTLE_COUNTER = 0;
WARGOAL_COUNTER = 0;
bracketCounter = 0;
InputStreamReader reader = new InputStreamReader(new FileInputStream(saveGamePath), "ISO8859_1"); // This encoding seems to work for รถ
BufferedReader scanner = new BufferedReader(reader);
String line;
while ((line = scanner.readLine()) != null) {
line = line.replaceAll("\t", "");
/* Data about the game: date, player and start_date
* The reading is done in referenceReader*/
if (line.startsWith("date=") || line.startsWith("player=") || line.startsWith("start_date=")) {
referenceReader(line);
}
if (line.startsWith("previous_war=") || line.equals("active_war=")) {
warProcessing = true;
/* Further check if war is active */
if (line.equals("previous_war=")) {
warList.add(new War(false));
} else {
warList.add(new War(true));
}
}
if (warProcessing) {
bracketCounterChange(line);
/* Checking if the line needs to be passed on to other readers */
if (line.startsWith("battle") || battleProcessing) {
battleProcessing = true;
battleReader(line);
} else if (line.startsWith("war_goal") || warGoalProcessing) {
warGoalProcessing = true;
warGoalReader(line);
} else if (line.startsWith("original_wargoal") || originalWarGoalProcessing) {
originalWarGoalProcessing = true;
originalWGoalReader(line);
} else {
warReader(line);
}
}
/*
* bracketCounter will only reach 0 when the whole war has been processed. Name check is required because
* on the first cycle bracketcounter will still be 0.
*/
if (bracketCounter == 0 && warProcessing && !(warList.get(WAR_COUNTER).getName().equals(""))) {
/* General housekeeping
* country and battle lists to proper types
* */
JoinedCountry[] countryTempList = new JoinedCountry[countryList.size()];
JoinedCountry[] countryTempList2 = countryList.toArray(countryTempList);
warList.get(WAR_COUNTER).setCountryList(countryTempList2);
Battle[] battleTempList = new Battle[battleList.size()];
Battle[] battleTempList2 = battleList.toArray(battleTempList);
warList.get(WAR_COUNTER).setBattleList(battleTempList2);
WarGoal[] warGoalTempList = new WarGoal[warGoalList.size()];
WarGoal[] warGoalTempList2 = warGoalList.toArray(warGoalTempList);
warList.get(WAR_COUNTER).setWarGoalList(warGoalTempList2);
warGoalList.clear();
warGoalProcessing = false;
WARGOAL_COUNTER = 0;
BATTLE_COUNTER = 0;
countryList.clear();
battleList.clear();
WAR_COUNTER++;
warProcessing = false;
}
}
scanner.close();
/* Setting the start date and casus belli */
warList.forEach(ee.tkasekamp.vickywaranalyzer.core.War::setCasusBelliAndStartDate);
return warList;
}
/**
* This processes the war. It creates a new war class and assigns values to it by reading new lines.
* War reading ends when the bracketCounter is 0 again.
*
* @param line
*/
public void warReader(String line) {
if (line.startsWith("name") && (warList.get(WAR_COUNTER).getName().equals(""))) { // Name check required so it is not overwritten
line = nameExtractor(line, 6, true);
warList.get(WAR_COUNTER).setName(line);
} else if (line.startsWith("1")) {
line = line.replaceAll("=", "");
setDateBuffer(line);
} else if (line.startsWith("add_attacker=")) {
line = nameExtractor(line, 14, true);
JoinedCountry country = new JoinedCountry(line, true, dateBuffer);
countryList.add(country);
} else if (line.startsWith("add_defender=")) {
line = nameExtractor(line, 14, true);
JoinedCountry country = new JoinedCountry(line, false, dateBuffer);
countryList.add(country);
} else if (line.startsWith("rem_attacker=")) {
line = nameExtractor(line, 14, true);
for (JoinedCountry item : countryList) {
if (item.getTag().equals(line)) {
item.setEndDate(dateBuffer);
}
}
} else if (line.startsWith("rem_defender=")) {
line = nameExtractor(line, 14, true);
for (JoinedCountry item : countryList) {
if (item.getTag().equals(line)) {
item.setEndDate(dateBuffer);
if (!warList.get(WAR_COUNTER).isActive()) { // If the war is not active, the date the last defender is removed will also be the war's enddate
warList.get(WAR_COUNTER).setEndDate(dateBuffer);
}
}
}
} else if (line.startsWith("attacker")) {
/* Checking if it's empty so only the first one is the attacker */
if (warList.get(WAR_COUNTER).getAttacker().equals("")) {
line = nameExtractor(line, 10, true);
warList.get(WAR_COUNTER).setAttacker(line);
}
} else if (line.startsWith("defender")) {
/* Checking if it's empty so only the first one is the defender */
if (warList.get(WAR_COUNTER).getDefender().equals("")) {
line = nameExtractor(line, 10, true);
warList.get(WAR_COUNTER).setDefender(line);
}
} else if (line.startsWith("original_attacker")) {
line = nameExtractor(line, 19, true);
/* Checking required for some older wars */
if (line.equals("---")) {
for (JoinedCountry country : countryList) {
if (country.isJoinType()) {
warList.get(WAR_COUNTER).setOriginalAttacker(country.getTag());
break;
}
}
} else {
warList.get(WAR_COUNTER).setOriginalAttacker(line);
}
} else if (line.startsWith("original_defender")) {
line = nameExtractor(line, 19, true);
/* Checking required for some older wars */
if (line.equals("---")) {
for (JoinedCountry country : countryList) {
if (!country.isJoinType()) {
warList.get(WAR_COUNTER).setOriginalDefender(country.getTag());
break;
}
}
} else {
warList.get(WAR_COUNTER).setOriginalDefender(line);
}
} else if (line.startsWith("action")) {
line = nameExtractor(line, 8, true);
warList.get(WAR_COUNTER).setAction(line);
}
}
/**
* All data about the battle is sent here
* Since the attacker is always first, it is only required to check if the attacker already has the data
*/
public void battleReader(String line) {
if (line.startsWith("name")) {
line = nameExtractor(line, 6, true);
Battle b = new Battle(dateBuffer, line);
battleList.add(b);
attackerDefender = true; // From now on all unit data will be about the attacker
} else if (line.startsWith("location")) {
line = nameExtractor(line, 9, false);
int location = Integer.parseInt(line);
battleList.get(BATTLE_COUNTER).setLocation(location);
} else if (line.startsWith("result")) {
line = nameExtractor(line, 7, false);
if (line.equals("yes")) {
battleList.get(BATTLE_COUNTER).setRes(Result.YES);
} else {
battleList.get(BATTLE_COUNTER).setRes(Result.NO);
}
} else if (line.startsWith("country")) {
line = nameExtractor(line, 9, true);
if (attackerDefender) {
battleList.get(BATTLE_COUNTER).setAttacker(line);
} else {
battleList.get(BATTLE_COUNTER).setDefender(line);
}
} else if (line.startsWith("leader")) {
line = nameExtractor(line, 8, true);
if (attackerDefender) {
battleList.get(BATTLE_COUNTER).setLeaderAttacker(line);
} else {
battleList.get(BATTLE_COUNTER).setLeaderDefender(line);
}
} else if (line.startsWith("losses")) {
line = nameExtractor(line, 7, false);
int losses = Integer.parseInt(line);
if (attackerDefender) {
/* If this point is reached all data about the attackers units will have been processed.
* Unit list added to battle and cleared
* Now the defenders units will be processed
*/
attackerDefender = false;
battleList.get(BATTLE_COUNTER).setAttackerLosses(losses);
/* Housekeeping */
Unit[] unitTempList = new Unit[unitList.size()];
Unit[] unitTempList2 = unitList.toArray(unitTempList);
battleList.get(BATTLE_COUNTER).setAttackerUnits(unitTempList2);
unitList.clear();
/* Battle type */
battleList.get(BATTLE_COUNTER).determineType();
} else {
/* If this point is reached all data about the defender units will have been processed.
* Unit list added to battle and cleared
* Total losses calculated
*/
battleList.get(BATTLE_COUNTER).setDefenderLosses(losses);
battleList.get(BATTLE_COUNTER).setTotalLosses(losses + battleList.get(BATTLE_COUNTER).getAttackerLosses());
Unit[] unitTempList = new Unit[unitList.size()];
Unit[] unitTempList2 = unitList.toArray(unitTempList);
battleList.get(BATTLE_COUNTER).setDefenderUnits(unitTempList2);
unitList.clear();
battleProcessing = false;
BATTLE_COUNTER++;
}
} else if (!(line.equals("attacker=")) && !(line.startsWith("defender=")) && !(line.equals("{"))
&& !(line.equals("}")) && !(line.equals("battle="))) {
/* All units such as "infantry=9000" will come here
*
*/
try {
String[] pieces = line.split("=");
int losses = Integer.parseInt(pieces[1]);
unitList.add(new Unit(pieces[0], losses));
} catch (NumberFormatException e) {
// Mainly debug if the lines which come here aren't integers
// controller.getErrorLabel().setText(controller.getErrorLabel().getText() + "Problem with reading: " + line);
}
}
}
/**
* Very similar to battleReader in use
* If war_goal is read in, all data is sent here until the line receiver comes
*/
public void warGoalReader(String line) {
/* Check required because the first line in war goal is not always the same */
if (line.startsWith("war_goal")) {
WarGoal w = new WarGoal();
warGoalList.add(w);
} else if (line.startsWith("state")) { // state_province_id
line = nameExtractor(line, 18, false);
int state = Integer.parseInt(line);
warGoalList.get(WARGOAL_COUNTER).setState_province_id(state);
} else if (line.startsWith("casus")) {
line = nameExtractor(line, 13, true);
warGoalList.get(WARGOAL_COUNTER).setCasus_belli(line);
} else if (line.startsWith("country")) {
line = nameExtractor(line, 9, true);
warGoalList.get(WARGOAL_COUNTER).setCountry(line);
} else if (line.startsWith("actor")) {
line = nameExtractor(line, 7, true);
warGoalList.get(WARGOAL_COUNTER).setActor(line);
} else if (line.startsWith("receiver")) {
line = nameExtractor(line, 10, true);
warGoalList.get(WARGOAL_COUNTER).setReceiver(line);
// WARGOAL_COUNTER++;
// warGoalProcessing = false;
} else if (line.startsWith("score")) {
line = nameExtractor(line, 6, false);
double score = Double.parseDouble(line);
warGoalList.get(WARGOAL_COUNTER).setScore(score);
} else if (line.startsWith("change")) {
line = nameExtractor(line, 7, false);
double c = Double.parseDouble(line);
warGoalList.get(WARGOAL_COUNTER).setChange(c);
} else if (line.startsWith("date")) {
line = nameExtractor(line, 6, true);
warGoalList.get(WARGOAL_COUNTER).setDate(line);
} else if (line.startsWith("is_fulfilled")) {
line = nameExtractor(line, 13, false);
if (line.equals("yes")) {
warGoalList.get(WARGOAL_COUNTER).setFulfilled(Result.YES);
}
} else if (line.startsWith("}")) {
/* This is always the last line in a war goal
* Clearing the wargoal list and passing it on to war like in battleReader*/
// line = nameExtractor(line, 10, true);
// warGoalList.get(WARGOAL_COUNTER).setReceiver(line);
WARGOAL_COUNTER++;
warGoalProcessing = false;
}
}
/**
* Similar to warGoalReader. Only used in HoD
*/
public void originalWGoalReader(String line) {
if (line.startsWith("state")) { // state_province_id
line = nameExtractor(line, 18, false);
int state = Integer.parseInt(line);
warList.get(WAR_COUNTER).getOriginalWarGoal().setState_province_id(state);
} else if (line.startsWith("casus")) {
line = nameExtractor(line, 13, true);
warList.get(WAR_COUNTER).getOriginalWarGoal().setCasus_belli(line);
} else if (line.startsWith("country")) {
line = nameExtractor(line, 9, true);
warList.get(WAR_COUNTER).getOriginalWarGoal().setCountry(line);
} else if (line.startsWith("actor")) {
line = nameExtractor(line, 7, true);
warList.get(WAR_COUNTER).getOriginalWarGoal().setActor(line);
} else if (line.startsWith("receiver")) {
/* This is always the last line in a war goal
* Clearing the wargoal list and passing it on to war like in battleReader*/
line = nameExtractor(line, 10, true);
warList.get(WAR_COUNTER).getOriginalWarGoal().setReceiver(line);
} else if (line.startsWith("score")) {
line = nameExtractor(line, 6, false);
double score = Double.parseDouble(line);
warList.get(WAR_COUNTER).getOriginalWarGoal().setScore(score);
} else if (line.startsWith("change")) {
line = nameExtractor(line, 7, false);
double c = Double.parseDouble(line);
warList.get(WAR_COUNTER).getOriginalWarGoal().setChange(c);
} else if (line.startsWith("date")) {
line = nameExtractor(line, 6, true);
warList.get(WAR_COUNTER).getOriginalWarGoal().setDate(line);
} else if (line.startsWith("is_fulfilled")) {
line = nameExtractor(line, 13, false);
if (line.equals("yes")) {
warList.get(WAR_COUNTER).getOriginalWarGoal().setFulfilled(Result.YES);
}
} else if (line.startsWith("}")) {
/* This means there is no more data to be added */
originalWarGoalProcessing = false;
}
}
public void referenceReader(String line) {
/* Checking the line and if there is no date
* Same with start_date*/
if (line.startsWith("date=") && modelService.getDate().equals("")) {
line = nameExtractor(line, 6, true);
modelService.setDate(line);
}
/* Checking if it's empty is not needed as there is only one line with player= */
else if (line.startsWith("player=")) {
line = nameExtractor(line, 8, true);
modelService.setPlayer(line);
} else if (line.startsWith("start_date=") && modelService.getStartDate().equals("")) {
line = nameExtractor(line, 12, true);
modelService.setStartDate(line);
}
}
public void bracketCounterChange(String line) {
// Increases or decreases the bracketCounter
if (line.equals("{")) {
bracketCounter++;
} else if (line.equals("}")) {
bracketCounter--;
}
}
/**
* Extracts the valuable data from a line by deleting the unimportant characters.
*
* @param line
* @param index Last character to be removed
* @param removeLast If true, removes the last character (used for ")
* @return The correct line
*/
public String nameExtractor(String line, int index, boolean removeLast) {
StringBuilder sb = new StringBuilder(line);
sb.delete(0, index);
if (removeLast) {
sb.setLength(sb.length() - 1); // Removes last "
}
return sb.toString();
}
public String getDateBuffer() {
return dateBuffer;
}
public void setDateBuffer(String dateBuffer) {
this.dateBuffer = dateBuffer;
}
}