package games.strategy.engine.framework.startup.ui;
import java.awt.Color;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import games.strategy.debug.ClientLogger;
import games.strategy.engine.ClientFileSystemHelper;
import games.strategy.engine.data.GameData;
import games.strategy.engine.data.PlayerID;
import games.strategy.engine.framework.message.PlayerListing;
import games.strategy.engine.framework.startup.launcher.ILauncher;
import games.strategy.engine.framework.startup.launcher.LocalLauncher;
import games.strategy.engine.framework.startup.mc.GameSelectorModel;
import games.strategy.engine.framework.startup.ui.editors.IBean;
import games.strategy.engine.framework.startup.ui.editors.SelectAndViewEditor;
import games.strategy.engine.pbem.GenericEmailSender;
import games.strategy.engine.pbem.GmailEmailSender;
import games.strategy.engine.pbem.HotmailEmailSender;
import games.strategy.engine.pbem.IEmailSender;
import games.strategy.engine.pbem.IForumPoster;
import games.strategy.engine.pbem.IWebPoster;
import games.strategy.engine.pbem.NullEmailSender;
import games.strategy.engine.pbem.NullForumPoster;
import games.strategy.engine.pbem.NullWebPoster;
import games.strategy.engine.pbem.PBEMMessagePoster;
import games.strategy.engine.pbem.TripleAForumPoster;
import games.strategy.engine.pbem.TripleAWarClubForumPoster;
import games.strategy.engine.pbem.TripleAWebPoster;
import games.strategy.engine.random.IRemoteDiceServer;
import games.strategy.engine.random.InternalDiceServer;
import games.strategy.engine.random.PBEMDiceRoller;
import games.strategy.engine.random.PropertiesDiceRoller;
import games.strategy.triplea.pbem.AxisAndAlliesForumPoster;
/**
* A panel for setting up Play by Email/Forum.
* This panel listens to the GameSelectionModel so it can refresh when a new game is selected or save game loaded
* The MainPanel also listens to this panel, and we notify it through the notifyObservers()
*/
public class PBEMSetupPanel extends SetupPanel implements Observer {
private static final long serialVersionUID = 9006941131918034674L;
private static final String DICE_ROLLER = "games.strategy.engine.random.IRemoteDiceServer";
private final GameSelectorModel m_gameSelectorModel;
private final SelectAndViewEditor m_diceServerEditor;
private final SelectAndViewEditor m_forumPosterEditor;
private final SelectAndViewEditor m_emailSenderEditor;
private final SelectAndViewEditor m_webPosterEditor;
private final List<PBEMLocalPlayerComboBoxSelector> m_playerTypes = new ArrayList<>();
private final JPanel m_localPlayerPanel = new JPanel();
private final JButton m_localPlayerSelection = new JButton("Select Local Players and AI's");
/**
* Creates a new instance.
*
* @param model
* the GameSelectionModel, though which changes are obtained when new games are chosen, or save games loaded
*/
public PBEMSetupPanel(final GameSelectorModel model) {
m_gameSelectorModel = model;
m_diceServerEditor = new SelectAndViewEditor("Dice Server", "");
m_forumPosterEditor = new SelectAndViewEditor("Post to Forum", "forumPosters.html");
m_emailSenderEditor = new SelectAndViewEditor("Provider", "emailSenders.html");
m_webPosterEditor = new SelectAndViewEditor("Send to Website", "websiteSenders.html");
createComponents();
layoutComponents();
setupListeners();
if (m_gameSelectorModel.getGameData() != null) {
loadAll();
}
setWidgetActivation();
}
private void createComponents() {
m_localPlayerSelection.addActionListener(
e -> JOptionPane.showMessageDialog(PBEMSetupPanel.this, m_localPlayerPanel, "Select Local Players and AI's",
JOptionPane.PLAIN_MESSAGE));
}
private void layoutComponents() {
removeAll();
setLayout(new GridBagLayout());
// Empty border works as margin
setBorder(new EmptyBorder(10, 10, 10, 10));
int row = 0;
add(m_diceServerEditor, new GridBagConstraints(0, row++, 1, 1, 1.0d, 0d, GridBagConstraints.NORTHWEST,
GridBagConstraints.HORIZONTAL, new Insets(10, 0, 20, 0), 0, 0));
// the play by Forum settings
m_forumPosterEditor.setBorder(new TitledBorder("Play By Forum"));
add(m_forumPosterEditor, new GridBagConstraints(0, row++, 1, 1, 1.0d, 0d, GridBagConstraints.NORTHWEST,
GridBagConstraints.HORIZONTAL, new Insets(0, 0, 20, 0), 0, 0));
final JPanel emailPanel = new JPanel(new GridBagLayout());
emailPanel.setBorder(new TitledBorder("Play By Email"));
add(emailPanel, new GridBagConstraints(0, row++, 1, 1, 1.0d, 0d, GridBagConstraints.NORTHWEST,
GridBagConstraints.HORIZONTAL, new Insets(0, 0, 20, 0), 0, 0));
int panelRow = 0;
emailPanel.add(m_emailSenderEditor, new GridBagConstraints(0, panelRow++, 1, 1, 1.0d, 0d,
GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 2, 0), 0, 0));
// add selection of local players
add(m_localPlayerSelection, new GridBagConstraints(0, row++, 1, 1, 1.0d, 0d, GridBagConstraints.NORTHEAST,
GridBagConstraints.NONE, new Insets(10, 0, 10, 0), 0, 0));
layoutPlayerPanel(this);
setWidgetActivation();
}
@Override
public boolean isMetaSetupPanelInstance() {
return false;
}
@Override
public void setWidgetActivation() {}
private void setupListeners() {
// register, so we get notified when the game model (GameData) changes (e.g if the user load a save game or selects
// another game)
m_gameSelectorModel.addObserver(this);
// subscribe to editor changes, so we cannotify the MainPanel
m_diceServerEditor.addPropertyChangeListener(new NotifyingPropertyChangeListener());
m_forumPosterEditor.addPropertyChangeListener(new NotifyingPropertyChangeListener());
m_emailSenderEditor.addPropertyChangeListener(new NotifyingPropertyChangeListener());
m_webPosterEditor.addPropertyChangeListener(new NotifyingPropertyChangeListener());
}
private void loadAll() {
loadDiceServer(m_gameSelectorModel.getGameData());
loadForumPosters(m_gameSelectorModel.getGameData());
loadEmailSender(m_gameSelectorModel.getGameData());
loadWebPosters(m_gameSelectorModel.getGameData());
}
/**
* Load the dice rollers from cache, if the game was a save game, the dice roller store is selected.
*
* @param data
* the game data
*/
private void loadDiceServer(final GameData data) {
final List<IRemoteDiceServer> diceRollers = new ArrayList<>(PropertiesDiceRoller.loadFromFile());
diceRollers.add(new InternalDiceServer());
for (final IRemoteDiceServer diceRoller : diceRollers) {
final IRemoteDiceServer cached =
(IRemoteDiceServer) LocalBeanCache.INSTANCE.getSerializable(diceRoller.getDisplayName());
if (cached != null) {
diceRoller.setCcAddress(cached.getCcAddress());
diceRoller.setToAddress(cached.getToAddress());
diceRoller.setGameId(cached.getGameId());
}
}
m_diceServerEditor.setBeans(diceRollers);
if (m_gameSelectorModel.isSavedGame()) {
// get the dice roller from the save game, if any
final IRemoteDiceServer roller = (IRemoteDiceServer) data.getProperties().get(DICE_ROLLER);
if (roller != null) {
m_diceServerEditor.setSelectedBean(roller);
}
}
}
/**
* Load the Forum poster that are stored in the GameData, and select it in the list.
* Sensitive information such as passwords are not stored in save games, so the are loaded from the LocalBeanCache
*
* @param data
* the game data
*/
private void loadForumPosters(final GameData data) {
// get the forum posters,
final List<IForumPoster> forumPosters = new ArrayList<>();
forumPosters.add((IForumPoster) findCachedOrCreateNew(NullForumPoster.class));
forumPosters.add((IForumPoster) findCachedOrCreateNew(AxisAndAlliesForumPoster.class));
forumPosters.add((IForumPoster) findCachedOrCreateNew(TripleAWarClubForumPoster.class));
forumPosters.add((IForumPoster) findCachedOrCreateNew(TripleAForumPoster.class));
m_forumPosterEditor.setBeans(forumPosters);
// now get the poster stored in the save game
final IForumPoster forumPoster = (IForumPoster) data.getProperties().get(PBEMMessagePoster.FORUM_POSTER_PROP_NAME);
if (forumPoster != null) {
// if we have a cached version, use the credentials from this, as each player has different forum login
final IForumPoster cached =
(IForumPoster) LocalBeanCache.INSTANCE.getSerializable(forumPoster.getClass().getCanonicalName());
if (cached != null) {
forumPoster.setUsername(cached.getUsername());
forumPoster.setPassword(cached.getPassword());
}
m_forumPosterEditor.setSelectedBean(forumPoster);
}
}
private void loadWebPosters(final GameData data) {
final List<IWebPoster> webPosters = new ArrayList<>();
webPosters.add((IWebPoster) findCachedOrCreateNew(NullWebPoster.class));
final TripleAWebPoster poster = (TripleAWebPoster) findCachedOrCreateNew(TripleAWebPoster.class);
poster.setParties(data.getPlayerList().getNames());
webPosters.add(poster);
m_webPosterEditor.setBeans(webPosters);
// now get the poster stored in the save game
final IWebPoster webPoster = (IWebPoster) data.getProperties().get(PBEMMessagePoster.WEB_POSTER_PROP_NAME);
if (webPoster != null) {
poster.addToAllHosts(webPoster.getHost());
webPoster.setAllHosts(poster.getAllHosts());
m_webPosterEditor.setSelectedBean(webPoster);
}
}
/**
* Configures the list of Email senders. If the game was saved we use this email sender.
* Since passwords are not stored in save games, the LocalBeanCache is checked
*
* @param data
* the game data
*/
private void loadEmailSender(final GameData data) {
// The list of email, either loaded from cache or created
final List<IEmailSender> emailSenders = new ArrayList<>();
emailSenders.add((IEmailSender) findCachedOrCreateNew(NullEmailSender.class));
emailSenders.add((IEmailSender) findCachedOrCreateNew(GmailEmailSender.class));
emailSenders.add((IEmailSender) findCachedOrCreateNew(HotmailEmailSender.class));
emailSenders.add((IEmailSender) findCachedOrCreateNew(GenericEmailSender.class));
m_emailSenderEditor.setBeans(emailSenders);
// now get the sender from the save game, update it with credentials from the cache, and set it
final IEmailSender sender = (IEmailSender) data.getProperties().get(PBEMMessagePoster.EMAIL_SENDER_PROP_NAME);
if (sender != null) {
final IEmailSender cached =
(IEmailSender) LocalBeanCache.INSTANCE.getSerializable(sender.getClass().getCanonicalName());
if (cached != null) {
sender.setUserName(cached.getUserName());
sender.setPassword(cached.getPassword());
}
m_emailSenderEditor.setSelectedBean(sender);
}
}
/**
* finds a cached instance of the give type. If a cached version is not available a new one is created
*
* @param theClassType
* the type of class
* @return a IBean either loaded from the cache or created
*/
private static IBean findCachedOrCreateNew(final Class<? extends IBean> theClassType) {
IBean cached = LocalBeanCache.INSTANCE.getSerializable(theClassType.getCanonicalName());
if (cached == null) {
try {
cached = theClassType.newInstance();
} catch (final Exception e) {
throw new RuntimeException(
"Bean of type " + theClassType + " doesn't have public default constructor, error: " + e.getMessage());
}
}
return cached;
}
@Override
public void shutDown() {
m_gameSelectorModel.deleteObserver(this);
}
/**
* Called when the current game changes.
*/
@Override
public void cancel() {
m_gameSelectorModel.deleteObserver(this);
}
/**
* Called when the observers detect change, to see if the game is in a startable state.
*/
@Override
public boolean canGameStart() {
if (m_gameSelectorModel.getGameData() == null) {
return false;
}
final boolean diceServerValid = m_diceServerEditor.isBeanValid();
final boolean summaryValid = m_forumPosterEditor.isBeanValid();
final boolean webSiteValid = m_webPosterEditor.isBeanValid();
final boolean emailValid = m_emailSenderEditor.isBeanValid();
final boolean pbemReady =
diceServerValid && summaryValid && emailValid && webSiteValid && m_gameSelectorModel.getGameData() != null;
if (!pbemReady) {
return false;
}
// make sure at least 1 player is enabled
for (final PBEMLocalPlayerComboBoxSelector player : m_playerTypes) {
if (player.isPlayerEnabled()) {
return true;
}
}
return false;
}
@Override
public void postStartGame() {
// // store the dice server
final GameData data = m_gameSelectorModel.getGameData();
data.getProperties().set(DICE_ROLLER, m_diceServerEditor.getBean());
// store the Turn Summary Poster
final IForumPoster poster = (IForumPoster) m_forumPosterEditor.getBean();
if (poster != null) {
IForumPoster summaryPoster = poster;
// clone the poster, the remove sensitive info, and put the clone into the game data
// this was the sensitive info is not stored in the save game, but the user cache still has the password
summaryPoster = summaryPoster.doClone();
summaryPoster.clearSensitiveInfo();
data.getProperties().set(PBEMMessagePoster.FORUM_POSTER_PROP_NAME, summaryPoster);
}
// store the email poster
IEmailSender sender = (IEmailSender) m_emailSenderEditor.getBean();
if (sender != null) {
// create a clone, delete the sensitive information in the clone, and use it in the game
// the locally cached version still has the password so the user doesn't have to enter it every time
sender = sender.doClone();
sender.clearSensitiveInfo();
data.getProperties().set(PBEMMessagePoster.EMAIL_SENDER_PROP_NAME, sender);
}
// store the web site poster
IWebPoster webPoster = (IWebPoster) m_webPosterEditor.getBean();
if (webPoster != null) {
webPoster = webPoster.doClone();
webPoster.clearSensitiveInfo();
data.getProperties().set(PBEMMessagePoster.WEB_POSTER_PROP_NAME, webPoster);
}
// store whether we are a pbem game or not, whether we are capable of posting a game save
if (poster != null || sender != null || webPoster != null) {
data.getProperties().set(PBEMMessagePoster.PBEM_GAME_PROP_NAME, true);
}
}
/**
* Is called in response to the GameSelectionModel being updated. It means the we have to reload the form
*
* @param o
* always null
* @param arg
* always null
*/
@Override
public void update(final Observable o, final Object arg) {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(() -> {
loadAll();
layoutComponents();
});
return;
} else {
loadAll();
layoutComponents();
}
}
/**
* Called when the user hits play.
*/
@Override
public ILauncher getLauncher() {
// update local cache and write to disk before game starts
final IForumPoster poster = (IForumPoster) m_forumPosterEditor.getBean();
if (poster != null) {
LocalBeanCache.INSTANCE.storeSerializable(poster.getClass().getCanonicalName(), poster);
}
final IEmailSender sender = (IEmailSender) m_emailSenderEditor.getBean();
if (sender != null) {
LocalBeanCache.INSTANCE.storeSerializable(sender.getClass().getCanonicalName(), sender);
}
final IWebPoster web = (IWebPoster) m_webPosterEditor.getBean();
if (web != null) {
LocalBeanCache.INSTANCE.storeSerializable(web.getClass().getCanonicalName(), web);
}
final IRemoteDiceServer server = (IRemoteDiceServer) m_diceServerEditor.getBean();
LocalBeanCache.INSTANCE.storeSerializable(server.getDisplayName(), server);
LocalBeanCache.INSTANCE.writeToDisk();
// create local launcher
final String gameUUID = (String) m_gameSelectorModel.getGameData().getProperties().get(GameData.GAME_UUID);
final PBEMDiceRoller randomSource = new PBEMDiceRoller((IRemoteDiceServer) m_diceServerEditor.getBean(), gameUUID);
final Map<String, String> playerTypes = new HashMap<>();
final Map<String, Boolean> playersEnabled = new HashMap<>();
for (final PBEMLocalPlayerComboBoxSelector player : m_playerTypes) {
playerTypes.put(player.getPlayerName(), player.getPlayerType());
playersEnabled.put(player.getPlayerName(), player.isPlayerEnabled());
}
// we don't need the playerToNode list, the
// disable-able players, or the alliances
// list, for a local game
final PlayerListing pl =
new PlayerListing(null, playersEnabled, playerTypes, m_gameSelectorModel.getGameData().getGameVersion(),
m_gameSelectorModel.getGameName(), m_gameSelectorModel.getGameRound(), null, null);
return new LocalLauncher(m_gameSelectorModel, randomSource, pl);
}
/**
* A property change listener that notify our observers.
*/
private class NotifyingPropertyChangeListener implements PropertyChangeListener {
@Override
public void propertyChange(final PropertyChangeEvent evt) {
notifyObservers();
}
}
public String getPlayerType(final String playerName) {
for (final PBEMLocalPlayerComboBoxSelector item : m_playerTypes) {
if (item.getPlayerName().equals(playerName)) {
return item.getPlayerType();
}
}
throw new IllegalStateException("No player found:" + playerName);
}
private void layoutPlayerPanel(final SetupPanel parent) {
final GameData data = m_gameSelectorModel.getGameData();
m_localPlayerPanel.removeAll();
m_playerTypes.clear();
m_localPlayerPanel.setLayout(new GridBagLayout());
if (data == null) {
m_localPlayerPanel.add(new JLabel("No game selected!"));
return;
}
final Collection<String> disableable = data.getPlayerList().getPlayersThatMayBeDisabled();
final HashMap<String, Boolean> playersEnablementListing = data.getPlayerList().getPlayersEnabledListing();
final Map<String, String> reloadSelections = PlayerID.currentPlayers(data);
final String[] playerTypes = data.getGameLoader().getServerPlayerTypes();
final String[] playerNames = data.getPlayerList().getNames();
// if the xml was created correctly, this list will be in turn order. we want to keep it that way.
int gridx = 0;
int gridy = 0;
if (!disableable.isEmpty() || playersEnablementListing.containsValue(Boolean.FALSE)) {
final JLabel enableLabel = new JLabel("Use");
enableLabel.setForeground(Color.black);
m_localPlayerPanel.add(enableLabel, new GridBagConstraints(gridx++, gridy, 1, 1, 0, 0, GridBagConstraints.WEST,
GridBagConstraints.NONE, new Insets(0, 5, 5, 0), 0, 0));
}
final JLabel nameLabel = new JLabel("Name");
nameLabel.setForeground(Color.black);
m_localPlayerPanel.add(nameLabel, new GridBagConstraints(gridx++, gridy, 1, 1, 0, 0, GridBagConstraints.WEST,
GridBagConstraints.NONE, new Insets(0, 5, 5, 0), 0, 0));
final JLabel typeLabel = new JLabel("Type");
typeLabel.setForeground(Color.black);
m_localPlayerPanel.add(typeLabel, new GridBagConstraints(gridx++, gridy, 1, 1, 0, 0, GridBagConstraints.WEST,
GridBagConstraints.NONE, new Insets(0, 5, 5, 0), 0, 0));
final JLabel allianceLabel = new JLabel("Alliance");
allianceLabel.setForeground(Color.black);
m_localPlayerPanel.add(allianceLabel, new GridBagConstraints(gridx++, gridy, 1, 1, 0, 0, GridBagConstraints.WEST,
GridBagConstraints.NONE, new Insets(0, 7, 5, 5), 0, 0));
for (final String playerName : playerNames) {
final PBEMLocalPlayerComboBoxSelector selector =
new PBEMLocalPlayerComboBoxSelector(playerName, reloadSelections, disableable, playersEnablementListing,
data.getAllianceTracker().getAlliancesPlayerIsIn(data.getPlayerList().getPlayerID(playerName)),
playerTypes, parent);
m_playerTypes.add(selector);
selector.layout(++gridy, m_localPlayerPanel);
}
m_localPlayerPanel.validate();
m_localPlayerPanel.invalidate();
}
}
class PBEMLocalPlayerComboBoxSelector {
private final JCheckBox m_enabledCheckBox;
private final String m_playerName;
private final JComboBox<String> m_playerTypes;
private boolean m_enabled = true;
private final JLabel m_name;
private final JLabel m_alliances;
private final Collection<String> m_disableable;
private final String[] m_types;
private final SetupPanel m_parent;
PBEMLocalPlayerComboBoxSelector(final String playerName, final Map<String, String> reloadSelections,
final Collection<String> disableable, final HashMap<String, Boolean> playersEnablementListing,
final Collection<String> playerAlliances, final String[] types, final SetupPanel parent) {
m_playerName = playerName;
m_name = new JLabel(m_playerName + ":");
m_enabledCheckBox = new JCheckBox();
final ActionListener m_disablePlayerActionListener = new ActionListener() {
@Override
public void actionPerformed(final ActionEvent e) {
if (m_enabledCheckBox.isSelected()) {
m_enabled = true;
// the 1st in the list should be human
m_playerTypes.setSelectedItem(m_types[0]);
} else {
m_enabled = false;
// the 2nd in the list should be Weak AI
m_playerTypes.setSelectedItem(m_types[Math.max(0, Math.min(m_types.length - 1, 1))]);
}
setWidgetActivation();
}
};
m_enabledCheckBox.addActionListener(m_disablePlayerActionListener);
m_enabledCheckBox.setSelected(playersEnablementListing.get(playerName));
m_enabledCheckBox.setEnabled(disableable.contains(playerName));
m_disableable = disableable;
m_parent = parent;
m_types = types;
m_playerTypes = new JComboBox<>(types);
String previousSelection = reloadSelections.get(playerName);
if (previousSelection.equalsIgnoreCase("Client")) {
previousSelection = types[0];
}
if (!(previousSelection.equals("no_one")) && Arrays.asList(types).contains(previousSelection)) {
m_playerTypes.setSelectedItem(previousSelection);
} else if (m_playerName.startsWith("Neutral") || playerName.startsWith("AI")) {
// the 4th in the list should be Pro AI (Hard AI)
m_playerTypes.setSelectedItem(types[Math.max(0, Math.min(types.length - 1, 3))]);
}
// we do not set the default for the combobox because the default is the top item, which in this case is human
String m_playerAlliances;
if (playerAlliances.contains(playerName)) {
m_playerAlliances = "";
} else {
m_playerAlliances = playerAlliances.toString();
}
m_alliances = new JLabel(m_playerAlliances);
setWidgetActivation();
}
public void layout(final int row, final Container container) {
int gridx = 0;
if (!m_disableable.isEmpty()) {
container.add(m_enabledCheckBox, new GridBagConstraints(gridx++, row, 1, 1, 0, 0, GridBagConstraints.WEST,
GridBagConstraints.NONE, new Insets(0, 5, 5, 0), 0, 0));
}
container.add(m_name, new GridBagConstraints(gridx++, row, 1, 1, 0, 0, GridBagConstraints.WEST,
GridBagConstraints.NONE, new Insets(0, 5, 5, 0), 0, 0));
container.add(m_playerTypes, new GridBagConstraints(gridx++, row, 1, 1, 0, 0, GridBagConstraints.WEST,
GridBagConstraints.NONE, new Insets(0, 5, 5, 0), 0, 0));
container.add(m_alliances, new GridBagConstraints(gridx++, row, 1, 1, 0, 0, GridBagConstraints.WEST,
GridBagConstraints.NONE, new Insets(0, 7, 5, 5), 0, 0));
}
public String getPlayerName() {
return m_playerName;
}
public String getPlayerType() {
return (String) m_playerTypes.getSelectedItem();
}
public boolean isPlayerEnabled() {
return m_enabledCheckBox.isSelected();
}
private void setWidgetActivation() {
m_name.setEnabled(m_enabled);
m_alliances.setEnabled(m_enabled);
m_enabledCheckBox.setEnabled(m_disableable.contains(m_playerName));
m_parent.notifyObservers();
}
/**
* A cache for serialized beans that should be stored locally.
* This is used to store settings which are not game related, and should therefore not go into the options cache
* This is often used by editors to remember previous values
*/
}
/** A bean cache used by PBEMSetupPanel. */
enum LocalBeanCache {
INSTANCE;
private final File m_file;
private final Object m_mutex = new Object();
Map<String, IBean> m_map = new HashMap<>();
private LocalBeanCache() {
m_file = new File(ClientFileSystemHelper.getUserRootFolder(), "local.cache");
m_map = loadMap();
// add a shutdown, just in case someone forgets to call writeToDisk
final Thread shutdown = new Thread(() -> writeToDisk());
Runtime.getRuntime().addShutdownHook(shutdown);
}
@SuppressWarnings("unchecked")
private Map<String, IBean> loadMap() {
if (m_file.exists()) {
try (FileInputStream fin = new FileInputStream(m_file);
ObjectInput oin = new ObjectInputStream(fin);) {
final Object o = oin.readObject();
if (o instanceof Map) {
final Map<?, ?> m = (Map<?, ?>) o;
for (final Object o1 : m.keySet()) {
if (!(o1 instanceof String)) {
throw new Exception("Map is corrupt");
}
}
} else {
throw new Exception("File is corrupt");
}
// we know that the map has proper type key/value
return (HashMap<String, IBean>) o;
} catch (final Exception e) {
// on error we delete the cache file, if we can
m_file.delete();
System.err.println("Serialization cache invalid: " + e.getMessage());
ClientLogger.logQuietly(e);
}
}
return new HashMap<>();
}
/**
* adds a new Serializable to the cache
*
* @param key
* the key the serializable should be stored under. Take care not to override a serializable stored by other
* code
* it is generally a good ide to use fully qualified class names, getClass().getCanonicalName() as key
* @param bean
* the bean
*/
public void storeSerializable(final String key, final IBean bean) {
m_map.put(key, bean);
}
/**
* Call to have the cache written to disk.
*/
public void writeToDisk() {
synchronized (m_mutex) {
try (FileOutputStream fout = new FileOutputStream(m_file, false);
ObjectOutputStream out = new ObjectOutputStream(fout);) {
out.writeObject(m_map);
} catch (final IOException e) {
// ignore
}
}
}
/**
* Get a serializable from the cache.
*
* @param key
* the key ot was stored under
* @return the serializable or null if one doesn't exists under the given key
*/
public IBean getSerializable(final String key) {
return m_map.get(key);
}
}