Update TurnEvents.xml based on country-level conflict data.

Tested with the Great War and it's working well.
2 files changed, 194 insertions(+), 0 deletions(-)

A => src/main/java/DBObjects/ConflictChange.java
M src/main/java/com/ajtjp/gearCity/Main.java
A => src/main/java/DBObjects/ConflictChange.java +99 -0
@@ 0,0 1,99 @@ 
+
+package DBObjects;
+
+import static DBObjects.CityDB.logger;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Represents a change in a conflict (usually a war) for a country.
+ * This has a goal of allowing the XML files to be auto-updated even as cities are
+ * added, with countries controlling the granularity of events.
+ * @author Andrew
+ */
+public class ConflictChange {
+    
+    private final String countryName;
+    private final String conflictName;
+    private final int statusType;   //-2 = total war, -1 = war, 0 = mid-level conflict, 1 = back to normal
+    private final int year;
+    private final int month;
+    
+    private static final Map<String, List<ConflictChange>> conflictChangesByMonth = new HashMap<>();
+
+    private ConflictChange(String countryName, String conflictName, int statusType, int year, int month) {
+        this.countryName = countryName;
+        this.conflictName = conflictName;
+        this.statusType = statusType;
+        this.year = year;
+        this.month = month;
+        String key = year + "-" + month;
+        if (!conflictChangesByMonth.containsKey(key)) {
+            conflictChangesByMonth.put(key, new ArrayList<>());
+        }
+        conflictChangesByMonth.get(key).add(this);
+    }
+    
+    public static void read(Connection conn) {
+        try {
+            PreparedStatement stmt = conn.prepareStatement(
+                "SELECT Country, Conflict, StartYear, StartMonth, EndYear, EndMonth, Type from CountryConflicts"
+            );
+            ResultSet rs = stmt.executeQuery();
+            while (rs.next()) {
+                String country = rs.getString(1);
+                String conflict = rs.getString(2);
+                int startYear = rs.getInt(3);
+                int startMonth = rs.getInt(4);
+                int endYear = rs.getInt(5);
+                int endMonth = rs.getInt(6);
+                int type = rs.getInt(7);
+                ConflictChange start = new ConflictChange(country, conflict, type, startYear, startMonth);
+                ConflictChange end = new ConflictChange(country, conflict, 1, endYear, endMonth);
+            }
+        }
+        catch(SQLException ex) {
+            logger.error("Unexpected SQL exception", ex);
+        }
+    }
+    
+    public static List<ConflictChange> getConflictChangesByMonth(int year, int month) {
+        String key = year + "-" + month;
+        if (!conflictChangesByMonth.containsKey(key)) {
+            return new ArrayList<>();
+        }
+        else {
+            return conflictChangesByMonth.get(key);
+        }
+    }
+
+    public String getCountryName() {
+        return countryName;
+    }
+
+    public String getConflictName() {
+        return conflictName;
+    }
+
+    public int getStatusType() {
+        return statusType;
+    }
+
+    public int getYear() {
+        return year;
+    }
+
+    public int getMonth() {
+        return month;
+    }
+
+    public Map<String, List<ConflictChange>> getConflictChangesByMonth() {
+        return conflictChangesByMonth;
+    }
+}

          
M src/main/java/com/ajtjp/gearCity/Main.java +95 -0
@@ 3,6 3,7 @@ package com.ajtjp.gearCity;
 
 import DBObjects.CalculatedObjects.CurrencyFigure;
 import DBObjects.CityDB;
+import DBObjects.ConflictChange;
 import DBObjects.CountryDB;
 import DBObjects.ExchangeRates;
 import DBObjects.RegionDB;

          
@@ 23,14 24,19 @@ import com.ajtjp.gearCity.CityInfoFile.X
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStreamWriter;
 import java.nio.charset.Charset;
 import java.text.DecimalFormat;
 import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
 import java.util.Set;
 import java.util.stream.Collectors;
 import javax.xml.parsers.DocumentBuilder;

          
@@ 81,6 87,7 @@ public class Main {
         List<RegionalDemographicsDB> regionalDemographics = dbReader.getRegionalDemographics();
         List<UrbanPopulationDB> urbanPopulation = dbReader.getUrbanPopulationDB();
         ExchangeRates.readFromDatabase(dbReader.getConn());
+        ConflictChange.read(dbReader.getConn());
         
         //Test our currency conversion
         CurrencyFigure oneThousandNorwegianKroner = new CurrencyFigure(1900, 1000, "NOK");

          
@@ 153,6 160,9 @@ cityDBLoop: for (CityDB city : cities) {
             ExportToCityFile("D:\\Gear City Mods\\Europe Expanded II\\Maps\\Europe Expanded II\\scripts\\City2020.xml", transformer, source);
         }
         
+        //Update TurnEvents.xml
+        updateTurnEvents(cities);
+        
         //Copy things over
         Process proc = Runtime.getRuntime().exec("D:\\Gear City Mods\\Europe Expanded II\\desktopCopy.bat", null, new File("D:/Gear City Mods/Europe Expanded II/"));
         int result = proc.waitFor();

          
@@ 173,6 183,91 @@ cityDBLoop: for (CityDB city : cities) {
             }
         }
     }
+    
+    /**
+     * Updates the turn events file.
+     * This is not a proper XML parser.  XML is not as convenient to build for as JSON, so rather than go to the work of parsing everything we need,
+     * I've just built a custom reader for the things we care about.
+     */
+    private static void updateTurnEvents(List<CityDB> cities) {
+        int year = 1899;
+        int month = 1900;
+        List<ConflictChange> changes = new ArrayList<>();
+        List<Integer> cityIDsExpected = new ArrayList<>();
+        Map<Integer, Integer> cityToStatusMap = new HashMap<>();
+        List<Integer> cityIDsFound = new ArrayList<>();
+        File tempOutput = new File("D:/Gear City Mods/Europe Expanded II/Maps/Europe Expanded II/Scripts/TurnEventsUpdated.xml");
+        try {
+            FileWriter fw = new FileWriter("D:/Gear City Mods/Europe Expanded II/Maps/Europe Expanded II/scripts/TurnEventsUpdated.xml");
+            File source = new File("D:/Gear City Mods/Europe Expanded II/Maps/Europe Expanded II/scripts/TurnEvents.xml");
+            if (!source.exists()) {
+                System.out.println(source + " does not exist");
+            }
+            Scanner scan = new Scanner(new File("D:/Gear City Mods/Europe Expanded II/Maps/Europe Expanded II/scripts/TurnEventsOrig.xml"));
+            while (scan.hasNextLine()) {
+                String nextLine = scan.nextLine();
+                
+                int yearIndex = nextLine.indexOf("<year y");
+                if (yearIndex > -1) {
+                    year = Integer.parseInt(nextLine.substring(yearIndex + 9, yearIndex + 13));
+                    System.out.println("Updated turn events year to " + year);
+                    if (year == 2019) {
+                        System.out.println("debug");
+                    }
+                }
+                int monthIndex = nextLine.indexOf("<turn t");
+                if (monthIndex > -1) {
+                    boolean twoDigits = nextLine.length() == 21;
+                    month = Integer.parseInt(nextLine.substring(monthIndex + 9, monthIndex + 9 + (twoDigits ? 2 : 1)));
+                    System.out.println("Updated turn events month to " + month);
+                    changes = ConflictChange.getConflictChangesByMonth(year, month);
+                    if (changes.size() > 0) {
+                        for (ConflictChange change : changes) {
+                            for (CityDB city : cities) {
+                                if (city.getCountry().getName().equals(change.getCountryName())) {
+                                    cityIDsExpected.add(city.getXmlID());
+                                    cityToStatusMap.put(city.getXmlID(), change.getStatusType());
+                                }
+                            }
+                        }
+                    }
+                }
+                
+                if (cityIDsExpected.size() > 0 && nextLine.contains("<cityChange id")) {
+                    int idIndex = nextLine.indexOf("id");
+                    int endQuoteIndex = nextLine.indexOf("\"", idIndex + 4);
+                    int foundId = Integer.parseInt(nextLine.substring(idIndex + 4, endQuoteIndex));
+                    System.out.println("Found id " + foundId + " in " + year + "-" + month);
+                    cityIDsFound.add(foundId);
+                }
+                
+                if (nextLine.contains("</WorldEvts>") && cityIDsExpected.size() > 0) {
+                    //Need to add lines for any city IDs not found
+                    for (Integer idExpected : cityIDsExpected) {
+                        if (!cityIDsFound.contains(idExpected)) {
+                            //TODO: This isn't quite right yet.  The government should not always be -1.  We have to store that somewhere.
+                            System.out.println("Adding city " + idExpected);
+                            String lineToAdd = "                <cityChange id=\"" + idExpected + "\" gov=\"" + cityToStatusMap.get(idExpected) + "\"/>\r\n";
+                            fw.write(lineToAdd);
+                        }
+                    }
+                    
+                    //Reset
+                    cityIDsExpected.clear();
+                    cityIDsFound.clear();
+                }
+                
+                //If we are checking for events...
+                
+                fw.write(nextLine + "\r\n");
+            }
+            fw.flush();
+            fw.close();
+        }
+        catch(IOException ex) {
+            System.err.println(ex.getMessage());
+        }
+    }
 
     static void printCityGrowthStats(List<CityDB> cities, int year) {
         List<CityDB> fastestGrowingCities = (List<CityDB>)cities.stream().sorted(new Comparator() {