/*
* Copyright 2010 Chad Retz
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.statmantis.mport.retro.event;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.statmantis.model.AtBatEvent;
import org.statmantis.model.GamePlayer;
import org.statmantis.model.GamePosition;
import org.statmantis.model.HandPreference;
import org.statmantis.model.Play;
import org.statmantis.model.PlayRunnerAdvancement;
import org.statmantis.model.Position;
import org.statmantis.mport.retro.RetrosheetRuntimeException;
/**
* Utilities for parsing game lines
*
* @author Chad Retz
*/
final class EventFileUtils {
private static final Logger logger = LoggerFactory.getLogger(EventFileUtils.class);
/**
* Parse a line of an event file
*
* @param line
* @param context
* @see <a href="http://www.retrosheet.org/eventfile.htm">Retrosheet Reference</a>
*/
public static void parseLine(String[] line, GameContext context) {
String discriminator = line[0];
if ("id".equals(discriminator)) {
context.newGame(line[1]);
} else if ("version".equals(discriminator)) {
//who cares...
} else if ("info".equals(discriminator)) {
parseInformation(line, context);
} else if ("start".equals(discriminator) || "sub".equals(discriminator)) {
parseStartSub(line, context);
} else if ("com".equals(discriminator)) {
parseComment(line, context);
} else if ("play".equals(discriminator)) {
parsePlay(line, context);
} else if ("padj".equals(discriminator) || "badj".equals(discriminator)) {
parseBatterPitcherAdjustment(line, context, "badj".equals(discriminator));
} else if ("ladj".equals(discriminator)) {
parseBattedOutOfTurn(line, context);
} else {
throw new RetrosheetRuntimeException("Unrecognized discriminator: " +
discriminator);
}
}
/**
* Parse an information line in an event file
*
* @param line
* @param context
* @see <a href="http://www.retrosheet.org/eventfile.htm#1">Retrosheet Reference</a>
*/
public static void parseInformation(String[] line, GameContext context) {
InformationRecord record = InformationRecord.RECORDS.get(line[1]);
if (record == null) {
throw new RetrosheetRuntimeException("Unknown info record " + line[1]);
}
record.populate(line[2], context);
}
/**
* Parse a start/sub line in an event file
*
* @param line
* @param context
* @see <a href="http://www.retrosheet.org/eventfile.htm">Retrosheet Reference</a>
*/
public static void parseStartSub(String[] line, GameContext context) {
GamePlayer player = context.getCurrentPlayers().get(line[1]);
if (player == null) {
//haven't seen this guy before...
player = new GamePlayer();
player.setPlayer(context.getRetroContext().getPersons().get(line[1]));
if (player.getPlayer() == null) {
throw new RetrosheetRuntimeException("Unknown player " + line[1]);
}
if ("0".equals(line[3])) {
context.getCurrentGame().getVisitorInfo().getGamePlayers().add(player);
} else {
context.getCurrentGame().getHomeInfo().getGamePlayers().add(player);
}
context.getCurrentPlayers().put(player.getPlayer().getRetroId(), player);
}
//build up his position
GamePosition position = new GamePosition();
position.setBeginningInning(context.getCurrentInning());
if (!"0".equals(line[4])) {
position.setLineupPosition(Integer.valueOf(line[4]));
}
position.setPosition(Position.fromRetroId(
Integer.valueOf(line[5])));
if (player.getPositions() == null) {
player.setPositions(new ArrayList<GamePosition>(
Collections.singleton(position)));
} else {
player.getPositions().add(position);
}
if ("sub".equals(line[0])) {
//a substitution is also a play
Play play = new Play();
play.setHomeTeam(!"0".equals(line[3]));
play.setInning(context.getCurrentInning());
play.setPlayer(player);
play.setSubstitution(true);
context.getCurrentGame().getPlays().add(play);
}
}
/**
* Parse a comment line in an event file
*
* @param line
* @param context
* @see <a href="http://www.retrosheet.org/eventfile.htm">Retrosheet Reference</a>
*/
public static void parseComment(String[] line, GameContext context) {
//a comment to be added to the previous play
if (!context.getCurrentGame().getPlays().isEmpty()) {
Play lastPlay = context.getCurrentGame().getPlays().get(
context.getCurrentGame().getPlays().size() - 1);
List<String> comments = lastPlay.getComments();
if (comments == null) {
comments = new ArrayList<String>();
lastPlay.setComments(comments);
}
comments.add(line[1]);
} else if (logger.isWarnEnabled()) {
logger.warn("Comment '" + line[1] + "' can't be added to game " +
context.getCurrentGame() + " because it has no plays");
}
}
/**
* Parse a play line in an event file
*
* @param line
* @param context
* @see <a href="http://www.retrosheet.org/eventfile.htm#5">Retrosheet Reference</a>
*/
public static void parsePlay(String[] line, GameContext context) {
//the holy grail...a play record
Play play = new Play();
//simple stuff
play.setInning(Integer.valueOf(line[1]));
play.setHomeTeam(!"0".equals(line[2]));
play.setPlayer(context.getCurrentPlayers().get(line[3]));
if (play.getPlayer() == null) {
throw new RetrosheetRuntimeException("Couldn't find player " + line[3]);
}
if (!"??".equals(line[4])) {
//the count if present
play.setBalls(Integer.valueOf(line[4].charAt(0)));
play.setStrikes(Integer.valueOf(line[4].charAt(1)));
}
if (!line[5].isEmpty()) {
//at bat events
play.setAtBatEvents(new ArrayList<AtBatEvent>(line[5].length()));
for (int j = 0; j < line[5].length(); j++) {
AtBatEvent event = AtBatEvent.EVENTS.get(String.valueOf(
line[5].charAt(j)));
if (event == null) {
throw new RetrosheetRuntimeException(
"Unrecognized AB event " + line[5].charAt(j));
}
play.getAtBatEvents().add(event);
}
}
if (!line[6].isEmpty()) {
//split it in to pieces
int periodIndex = line[6].indexOf('.');
String event;
if (periodIndex != -1) {
play.setAdvancements(new ArrayList<PlayRunnerAdvancement>());
//separated by semicolons
for (String adv : line[6].substring(periodIndex + 1).split(";")) {
play.getAdvancements().add(PlayUtils.buildPlayRunnerAdvancement(adv, context));
}
event = line[6].substring(0, periodIndex);
} else {
event = line[6];
}
play.setEvent(PlayUtils.buildPlayEvent(event, context));
//TODO: update the damn context w/ base runners and such
} else {
throw new RetrosheetRuntimeException("Play is empty");
}
context.getCurrentGame().getPlays().add(play);
}
public static void parseBatterPitcherAdjustment(String[] line,
GameContext context, boolean batter) {
Play play = new Play();
play.setPlayer(context.getCurrentPlayers().get(line[1]));
if (batter) {
play.setBatterHandAdjustment("R".equals(line[2]) ?
HandPreference.RIGHT : HandPreference.LEFT);
} else {
play.setPitcherHandAdjustment("R".equals(line[2]) ?
HandPreference.RIGHT : HandPreference.LEFT);
}
context.getCurrentGame().getPlays().add(play);
}
public static void parseBattedOutOfTurn(String[] line, GameContext context) {
Play play = new Play();
play.setBattedOutOfTurn(true);
boolean home = "0".equals(line[1]);
int properPosition = Integer.parseInt(line[2]);
//find that player!
for (GamePlayer player : (home ?
context.getCurrentGame().getHomeInfo().getGamePlayers() :
context.getCurrentGame().getVisitorInfo().getGamePlayers())) {
//check last position
Integer lineupPosition = player.getPositions().get(
player.getPositions().size() - 1).getLineupPosition();
if (lineupPosition != null && properPosition == lineupPosition) {
play.setPlayer(player);
context.getCurrentGame().getPlays().add(play);
return;
}
}
//AHH!
throw new RetrosheetRuntimeException(
"Unable to find player at position for batting out of turn");
}
private EventFileUtils() {
}
}