package games.strategy.engine.framework.ui; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.Comparator; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import games.strategy.debug.ClientLogger; import games.strategy.engine.data.EngineVersionException; import games.strategy.engine.data.GameData; import games.strategy.engine.data.GameParseException; import games.strategy.engine.data.GameParser; import games.strategy.engine.framework.GameRunner; import games.strategy.triplea.Constants; import games.strategy.util.UrlStreams; public class NewGameChooserEntry { private final URI url; private GameData gameData; private boolean gameDataFullyLoaded = false; private final String gameNameAndMapNameProperty; public static Comparator<NewGameChooserEntry> getComparator() { return new Comparator<NewGameChooserEntry>() { @Override public int compare(final NewGameChooserEntry o1, final NewGameChooserEntry o2) { return getLowerCaseComparable(o1).compareTo(getLowerCaseComparable(o2)); } private String getLowerCaseComparable(final NewGameChooserEntry newGameChooserEntry) { return newGameChooserEntry.getGameData().getGameName().toLowerCase(); } }; } public NewGameChooserEntry(final URI uri) throws IOException, GameParseException, SAXException, EngineVersionException { url = uri; final AtomicReference<String> gameName = new AtomicReference<>(); final Optional<InputStream> inputStream = UrlStreams.openStream(uri); if (!inputStream.isPresent()) { gameNameAndMapNameProperty = ""; // this means the map was deleted out from under us. return; } try (InputStream input = inputStream.get()) { final boolean delayParsing = GameRunner.getDelayedParsing(); gameData = new GameParser(uri.toString()).parse(input, gameName, delayParsing); gameDataFullyLoaded = !delayParsing; gameNameAndMapNameProperty = getGameName() + ":" + getMapNameProperty(); } } public void fullyParseGameData() throws GameParseException { // TODO: We should be setting this in the the constructor. At this point, you have to call methods in the // correct order for things to work, and that is bads. gameData = null; final AtomicReference<String> gameName = new AtomicReference<>(); final Optional<InputStream> inputStream = UrlStreams.openStream(url); if (!inputStream.isPresent()) { return; } try (InputStream input = inputStream.get()) { gameData = new GameParser(url.toString()).parse(input, gameName, false); gameDataFullyLoaded = true; } catch (final EngineVersionException e) { ClientLogger.logQuietly(e); throw new GameParseException(e.getMessage()); } catch (final SAXParseException e) { final String msg = "Could not parse:" + url + " error at line:" + e.getLineNumber() + " column:" + e.getColumnNumber(); ClientLogger.logError(msg, e); throw new GameParseException(e.getMessage()); } catch (final Exception e) { final String msg = "Could not parse:" + url; ClientLogger.logError(msg, e); throw new GameParseException(e.getMessage()); } } /** * Do not use this if possible. Instead try to remove the bad map from the GameChooserModel. * If that fails, then do a short parse so the user doesn't get a null pointer error. */ public void delayParseGameData() { gameData = null; final AtomicReference<String> gameName = new AtomicReference<>(); final Optional<InputStream> inputStream = UrlStreams.openStream(url); if (!inputStream.isPresent()) { return; } try (InputStream input = inputStream.get()) { gameData = new GameParser(url.toString()).parse(input, gameName, true); gameDataFullyLoaded = false; } catch (final EngineVersionException e) { System.out.println(e.getMessage()); } catch (final SAXParseException e) { System.err.println( "Could not parse:" + url + " error at line:" + e.getLineNumber() + " column:" + e.getColumnNumber()); ClientLogger.logQuietly(e); } catch (final Exception e) { System.err.println("Could not parse:" + url); ClientLogger.logQuietly(e); } } public boolean isGameDataLoaded() { return gameDataFullyLoaded; } public String getGameName() { return gameData.getGameName(); } // the user may have selected a map skin instead of this map folder, so don't use this for anything except our // equals/hashcode below private String getMapNameProperty() { final String mapName = (String) gameData.getProperties().get(Constants.MAP_NAME); if (mapName == null || mapName.trim().length() == 0) { throw new IllegalStateException("Map name property not set on game"); } return mapName; } @Override public String toString() { return getGameName(); } public GameData getGameData() { return gameData; } public URI getURI() { return url; } /** * Returns the location of the game file. * * <p> * The "location" is actually a URI in string form. * </p> * * @return The location of the game file; never {@code null}. */ public String getLocation() { return url.toString(); } @Override public int hashCode() { return gameNameAndMapNameProperty.hashCode(); } @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final NewGameChooserEntry other = (NewGameChooserEntry) obj; if (gameData == null && other.gameData != null) { return false; } else { if (other.gameData == null) { return false; } } return this.gameNameAndMapNameProperty.equals(other.gameNameAndMapNameProperty); } }