package games.strategy.triplea.settings; import java.util.Arrays; import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.text.JTextComponent; import games.strategy.ui.SwingComponents; public final class SettingInputComponentFactory { private static final String EMPTY_VALUE_RANGE_DESCRIPTION = ""; private SettingInputComponentFactory() { } public static <Z extends HasDefaults> SettingInputComponent<Z> buildIntegerText( final IntegerValueRange valueRange, final String label, final String description, final JTextComponent component, final BiConsumer<Z, String> writer, final Function<Z, String> reader) { final String valueRangeDescription = String.format("%d - %d\ndefault: %d", valueRange.lowerValue, valueRange.upperValue, valueRange.defaultValue); final JPanel panel = new JPanel(); panel.add(component); return build( panel, new LabelDescription(label, description), valueRangeDescription, new SwingComponentReaderWriter(component::getText, component::setText), new SettingsModelReaderWriter<>(reader, writer), InputValidator.inRange(valueRange.lowerValue, valueRange.upperValue)); } /** * Factory method to create instances of this interface, backed by TextField component types. */ public static <Z extends HasDefaults> SettingInputComponent<Z> buildTextComponent( final String label, final String description, final JTextComponent component, final Function<Z, String> settingsModelReader, final BiConsumer<Z, String> settingsModelWriter, final InputValidator... validators) { JPanel panel = new JPanel(); panel.add(component); return build( panel, new LabelDescription(label, description), EMPTY_VALUE_RANGE_DESCRIPTION, new SwingComponentReaderWriter(component::getText, component::setText), new SettingsModelReaderWriter<>(settingsModelReader, settingsModelWriter), validators); } public static <Z extends HasDefaults> SettingInputComponent<Z> buildYesOrNoRadioButtons( final String label, final String description, final boolean initialValue, final BiConsumer<Z, String> settingsObjectWriter, final Function<Z, String> settingsObjectReader) { final JRadioButton radioButtonYes = new JRadioButton("Yes"); final JRadioButton radioButtonNo = new JRadioButton("No"); SwingComponents.createButtonGroup(radioButtonYes, radioButtonNo); Supplier<String> reader = () -> String.valueOf(radioButtonYes.isSelected()); Consumer<String> writer = (input) -> { if (Boolean.valueOf(input)) { radioButtonYes.setSelected(true); } else { radioButtonNo.setSelected(true); } }; return build( createRadioButtonPanel(radioButtonYes, radioButtonNo, initialValue), new LabelDescription(label, description), EMPTY_VALUE_RANGE_DESCRIPTION, new SwingComponentReaderWriter(reader, writer), new SettingsModelReaderWriter<>(settingsObjectReader, settingsObjectWriter)); } private static JPanel createRadioButtonPanel( final JRadioButton buttonYes, final JRadioButton buttonNo, final boolean yesOptionIsSelected) { if (yesOptionIsSelected) { buttonYes.setSelected(true); } else { buttonNo.setSelected(true); } final JPanel panel = new JPanel(); panel.add(buttonYes); panel.add(buttonNo); return panel; } private static class LabelDescription { private String label; private String description; LabelDescription(String label, String description) { this.label = label; this.description = description; } } private static class SwingComponentReaderWriter { private Supplier<String> reader; private Consumer<String> writer; SwingComponentReaderWriter(Supplier<String> reader, Consumer<String> writer) { this.reader = reader; this.writer = writer; } } private static class SettingsModelReaderWriter<Type extends HasDefaults> { private final Function<Type, String> settingsReader; private final BiConsumer<Type, String> settingsWriter; SettingsModelReaderWriter(Function<Type, String> settingsReader, BiConsumer<Type, String> settingsWriter) { this.settingsWriter = settingsWriter; this.settingsReader = settingsReader; } } private static <T extends HasDefaults> SettingInputComponent<T> build( JPanel componentPanel, LabelDescription labelDescription, final String valueRangeDescription, SwingComponentReaderWriter swingReaderWriter, SettingsModelReaderWriter<T> modelReaderWriter, final InputValidator... validators) { return new SettingInputComponent<T>() { @Override public String getLabel() { return labelDescription.label; } @Override public String getDescription() { return labelDescription.description; } @Override public String getValueRangeDescription() { return valueRangeDescription; } @Override public SettingsInput getInputElement() { return new SettingsInput() { @Override public JComponent getSwingComponent() { return componentPanel; } @Override public String getText() { return swingReaderWriter.reader.get(); } @Override public void setText(String valueToSet) { swingReaderWriter.writer.accept(valueToSet); } }; } @Override public boolean updateSettings(T toUpdate) { final String input = getInputElement().getText(); for (final InputValidator validator : Arrays.asList(validators)) { final boolean isValid = validator.apply(input); if (!isValid) { return false; } } modelReaderWriter.settingsWriter.accept(toUpdate, input); return true; } @Override public String getValue(T settingsType) { return modelReaderWriter.settingsReader.apply(settingsType); } @Override public void setValue(final String valueToSet) { getInputElement().setText(valueToSet); } @Override public String getErrorMessage() { final String input = getInputElement().getText(); final Optional<InputValidator> failedValidator = Arrays.stream(validators).filter(validator -> !validator.apply(input)).findFirst(); if (!failedValidator.isPresent()) { return ""; } return input + ", " + failedValidator.get().getErrorMessage(); } }; } }