//
// @(#)AdjustmentParser.java 1.00 6/2002
//
// Copyright 2002 Zachary DelProposto. All rights reserved.
// Use is subject to license terms.
//
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
// Or from http://www.gnu.org/
//
package dip.judge.parser;
import dip.world.Phase;
import dip.world.Power;
import dip.world.Map;
import java.io.*;
import java.util.regex.*;
import java.util.*;
/**
* Parses the Adjustment information block.
* <br>
* This includes both supply center ownership, and build/removes.
*
*
*
*/
public class AdjustmentParser
{
// CONSTANTS
// empty string
private static final String[] EMPTY = new String[0];
/** Header text to look for */
public static final String HEADER_REGEX = "(?i)ownership of supply centers:";
/** Adjustment regex
* Capture groups: 1:power 2:# supply centers 3:#units 4:# to build or remove
* This is always fed a trimmed string, and assumed that it is always on one line. But we search a whole block.
* case-insensitive. Power names: alphanumeric + "-" and "_" supported.
* Parsed on a per-line basis.
*/
public static final String ADJUST_REGEX = "(?i)([\\p{Alnum}\\-_]*):\\D*(\\d+)\\D+(\\d+)\\D+((\\d+)).*\\.";
// INSTANCE VARIABLES
private dip.world.Map map = null;
private List ownerList = null;
private List adjustList = null;
private Pattern regexAdjust = null;
private OwnerInfo[] ownerInfo = null;
private AdjustInfo[] adjustInfo = null;
/*
// TEST
public static void main(String args[])
throws IOException
{
// NOTE: 2 stp. because one is split, as it's been seen on floc.net and
// thus is a good test.
String in =
"The deadline for orders will be Wed Apr 10 2002 17:54:23 -0500.\n"+
"asdfjafsdkjfadslk: sdljfjldksfkjfdlsls \n"+
"fadslkfkjdlsafslkj\n"+
"\n"+
"Ownership of supply centers:\n"+
"\n"+
"Austria: Greece, Serbia.\n"+
"England: Edinburgh, Liverpool, London.\n"+
"France: Belgium, Brest, Marseilles, Paris, \n"+
" Portugal, Spain.\n"+
"Germany: Berlin, Denmark, Holland, Kiel, Munich, Norway.\n"+
"Italy: Naples, Rome, Trieste, Tunis, Venice, St.\n"+
" Petersburg.\n"+
"Russia: Moscow, Norway, Rumania, Sevastopol,\n"+
" St. Petersburg, Vienna, Warsaw.\n"+
"Turkey: Ankara, Bulgaria, Constantinople, Smyrna.\n"+
"\n"+
"Austria: 2 Supply centers, 3 Units: Removes 1 unit.\n"+
"England: 3 Supply centers, 4 Units: Removes 1 unit.\n"+
"France: 6 Supply centers, 4 Units: Builds 2 units.\n"+
"Germany: 6 Supply centers, 5 Units: Builds 1 unit.\n"+
"Italy: 5 Supply centers, 5 Units: Builds 0 units.\n"+
"Russia: 8 Supply centers, 7 Units: Builds 1 unit.\n"+
"Turkey: 4 Supply centers, 4 Units: Builds 0 units.\n"+
"\n"+
"The next phase of 'delphi' will be Adjustments for Winter of 1902.\n";
AdjustmentParser ap = new AdjustmentParser(in);
OwnerInfo[] oi = ap.getOwnership();
AdjustInfo[] ai = ap.getAdjustments();
System.out.println("oi = "+oi);
System.out.println("oi.length = "+oi.length);
for(int i=0; i<oi.length; i++)
{
System.out.println(oi[i]);
System.out.println("");
}
System.out.println("ai = "+ai);
System.out.println("ai.length = "+ai.length);
for(int i=0; i<ai.length; i++)
{
System.out.println(ai[i]);
System.out.println("");
}
}// main()
*/
/** Creates a AdjustmentParser object, which parses the given input for an Ownership and Adjustment info blocks */
public AdjustmentParser(Map map, String input)
throws IOException
{
if(map == null || input == null)
{
throw new IllegalArgumentException();
}
this.map = map;
parseInput(input);
}// AdjustmentParser()
/** Returns an array of OwnerInfo objects; this never returns null. */
public OwnerInfo[] getOwnership()
{
return ownerInfo;
}// getOwnership()
/** Returns an array of AdjustInfo objects; this never returns null. */
public AdjustInfo[] getAdjustments()
{
return adjustInfo;
}// getAdjustments()
/**
* An OwnerInfo object is created for each power.
* <p>
*
*/
public static class OwnerInfo
{
private final String power;
private final String[] locations;
/** Create a OwnerInfo object */
public OwnerInfo(String power, String[] locations)
{
this.power = power;
this.locations = (locations == null) ? EMPTY : locations;
}// OwnerInfo()
/** Name of the Power */
public String getPowerName() { return power; }
/** Names of provinces with owned supply centers */
public String[] getProvinces() { return locations; }
/** String output for debugging; may change between versions. */
public String toString()
{
StringBuffer sb = new StringBuffer();
sb.append("OwnerInfo[power=");
sb.append(power);
sb.append(", locations=");
for(int i=0; i<locations.length; i++)
{
sb.append(locations[i]);
sb.append(',');
}
sb.append(']');
return sb.toString();
}// toString()
}// nested class OwnerInfo
/**
* An AdjustInfo object is created for each power, and contains adjustment information
* <p>
*
*/
public static class AdjustInfo
{
private final String power;
private final int numSC;
private final int numUnits;
private final int toBuildOrRemove;
/** Create an AdjustInfo object */
public AdjustInfo(String power, int numSC, int numUnits, int toBuildOrRemove)
{
if(numSC < 0 || numUnits < 0 || toBuildOrRemove < 0 || power == null)
{
throw new IllegalArgumentException("bad arguments");
}
this.power = power;
this.numSC = numSC;
this.numUnits = numUnits;
this.toBuildOrRemove = toBuildOrRemove;
}// AdjustInfo()
/** Name of the Power */
public String getPowerName() { return power; }
/** Current number of supply centers */
public int getNumSupplyCenters() { return numSC; }
/** Current number of units */
public int getNumUnits() { return numUnits; }
/** Number of units to build or remove */
public int getNumBuildOrRemove() { return toBuildOrRemove; }
/** String output for debugging; may change between versions. */
public String toString()
{
StringBuffer sb = new StringBuffer();
sb.append("AdjustInfo[power=");
sb.append(power);
sb.append(", SC=");
sb.append(numSC);
sb.append(", units=");
sb.append(numUnits);
sb.append(", change=");
sb.append(toBuildOrRemove);
sb.append(']');
return sb.toString();
}// toString()
}// nested class AdjustInfo
/** Parse the input, and create the appropriate Info objects (or a zero-length arrays) */
private void parseInput(String input)
throws IOException
{
// create lists
ownerList = new LinkedList();
adjustList = new LinkedList();
// create patterns
regexAdjust = Pattern.compile(ADJUST_REGEX);
// Create HEADER_REGEX pattern
Pattern header = Pattern.compile(HEADER_REGEX);
// search for HEADER_REGEX
// create a block of text
BufferedReader br = new BufferedReader(new StringReader(input));
String line = br.readLine();
while(line != null)
{
Matcher m = header.matcher(line);
if(m.lookingAt())
{
parseOwnerBlock( ParserUtils.parseBlock(br) );
parseAdjustmentBlock( ParserUtils.parseBlock(br) );
break;
}
line = br.readLine();
}
// create the output array
ownerInfo = (OwnerInfo[]) ownerList.toArray(new OwnerInfo[ownerList.size()]);
adjustInfo = (AdjustInfo[]) adjustList.toArray(new AdjustInfo[adjustList.size()]);
// cleanup
br.close();
ownerList.clear();
adjustList.clear();
ownerList = null;
adjustList = null;
regexAdjust = null;
}// parseInput()
/**
* Given a trimmed block, determines ownership
*/
private void parseOwnerBlock(String text)
throws IOException
{
// map of Powers to StringBuffers
HashMap pmap = new HashMap();
// parse and re-formulate
// into a new string
//
Power currentPower = null;
StringTokenizer st = new StringTokenizer(text, " \f\t\n\r");
while(st.hasMoreTokens())
{
String tok = st.nextToken();
if(tok.equalsIgnoreCase("unowned:"))
{
// we don't process unowned SC yet. I'm not sure that
// all judges support this??
currentPower = null;
}
else if(tok.endsWith(":"))
{
// should be a Power
//
Power p = map.getPower(tok.substring(0, tok.length() - 1));
if(p == null)
{
throw new IOException("Adjustment Block: Power "+tok+" not recognized.");
}
// toss into the map
currentPower = p;
pmap.put(p, new StringBuffer());
}
else
{
if(currentPower != null)
{
StringBuffer sb = (StringBuffer) pmap.get(currentPower);
sb.append(tok);
sb.append(" ");
}
}
}
// now, iterate through the powers
// parse the province
// there may be a "." on the end of some
// which should be eliminated.
//
final Power[] allPowers = map.getPowers();
for(int i=0; i<allPowers.length; i++)
{
StringBuffer sb = (StringBuffer) pmap.get(allPowers[i]);
if(sb != null)
{
final String[] provs = sb.toString().split("[\\,]");
// clean up province tokens
for(int pi=0; pi<provs.length; pi++)
{
provs[pi] = provs[pi].trim();
if(provs[pi].endsWith("."))
{
provs[pi] = provs[pi].substring(0, provs[pi].length()-1);
}
}
// create OwnerInfo
ownerList.add( new OwnerInfo(allPowers[i].getName(), provs) );
}
}
}// parseOwnerBlock()
/**
* Given a trimmed block, determines adjustment
*/
private void parseAdjustmentBlock(String text)
{
String[] lines = text.split("\\n");
for(int i=0; i<lines.length; i++)
{
Matcher m = regexAdjust.matcher(lines[i]);
if(m.find())
{
adjustList.add(new AdjustInfo(
m.group(1),
Integer.parseInt(m.group(2)),
Integer.parseInt(m.group(3)),
Integer.parseInt(m.group(4)) ));
}
else
{
break;
}
}
}// parseAdjustmentBlock()
}// class AdjustmentParser