() {
@Override
public Boolean fromString(String string) {
return Boolean.parseBoolean(string);
}
};
rendererSupplier = () -> new SimpleBooleanControl();
userInput.set(stringConverter.toString(value.getValue()));
}
/**
* {@inheritDoc}
*/
@Override
protected boolean validateRequired(String newValue) {
return !isRequired() || (isRequired() && newValue.equals("true"));
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/DataField.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.event.FieldEvent;
import com.dlsc.formsfx.model.util.BindingMode;
import com.dlsc.formsfx.model.util.TranslationService;
import com.dlsc.formsfx.model.util.ValueTransformer;
import com.dlsc.formsfx.model.validators.ValidationResult;
import com.dlsc.formsfx.model.validators.Validator;
import javafx.beans.InvalidationListener;
import javafx.beans.binding.Bindings;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.util.StringConverter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* {@code DataField} holds a single value. This value can be represented and
* manipulated as a {@code String}. It is stored as a concrete type.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public abstract class DataField> extends Field {
/**
* Every field tracks its value in multiple ways.
*
* - The user input is bound to a specific control's input value and is a
* 1-to-1 representation of what the user enters.
* - The value is the last valid value entered by the user. This means that
* the value passes the type transformation of the concrete field and all
* user-defined validations.
* - The persistent value is the value that was last saved on the field. It
* is the responsibility of the form creator to persist the field values
* at the correct time.
*/
protected final P value;
protected final P persistentValue;
protected final StringProperty userInput = new SimpleStringProperty("");
/**
* Every field contains a list of validators. The validators are limited to
* the ones that correspond to the field's type.
*/
protected final List> validators = new ArrayList<>();
/**
* The value transformer is responsible for transforming the user input
* string to the specific type of the field's value.
* @deprecated Use DataField#stringConverter instead.
*/
@Deprecated
ValueTransformer valueTransformer;
protected StringConverter stringConverter = new AbstractStringConverter() {
@Override
public V fromString(String string) {
return null;
}
};
/**
* The format error is displayed when the value transformation fails.
*
* This property is translatable if a {@link TranslationService} is set on
* the containing form.
*/
protected final StringProperty formatErrorKey = new SimpleStringProperty("");
protected final StringProperty formatError = new SimpleStringProperty("");
/**
* This listener updates the field when the external binding changes.
*/
private final InvalidationListener externalBindingListener = (observable) -> userInput.setValue(stringConverter.toString((V) ((P) observable).getValue()));
/**
* Internal constructor for the {@code DataField} class. To create new
* elements, see the static factory methods in {@code Field}.
*
* @see Field::ofStringType
* @see Field::ofIntegerType
* @see Field::ofDoubleType
* @see Field::ofBooleanType
*
* @param valueProperty
* The property that is used to store the current valid value
* of the field.
* @param persistentValueProperty
* The property that is used to store the latest persisted
* value of the field.
*/
protected DataField(P valueProperty, P persistentValueProperty) {
value = valueProperty;
persistentValue = persistentValueProperty;
// The changed property is a binding that compares the persistent value
// with the current value. This means that a field is marked as changed
// until Field::persist or Field::reset are called or the value is back
// to the persistent value.
changed.bind(Bindings.createBooleanBinding(() -> !stringConverter.toString((V) persistentValue.getValue()).equals(userInput.getValue()), userInput, persistentValue));
// Whenever one of the translatable elements' keys change, update the
// displayed value based on the new translation.
formatErrorKey.addListener((observable, oldValue, newValue) -> formatError.setValue(translationService.translate(newValue)));
// Changes to the user input are reflected in the value only if the new
// user input is valid.
userInput.addListener((observable, oldValue, newValue) -> {
if (validate()) {
value.setValue(stringConverter.fromString(newValue));
}
});
}
protected static abstract class AbstractStringConverter extends StringConverter {
@Override
public String toString(V object) {
return String.valueOf(object);
}
}
private static class StringConverterAdapter extends AbstractStringConverter {
private final ValueTransformer valueTransformer;
public StringConverterAdapter(ValueTransformer valueTransformer) {
this.valueTransformer = valueTransformer;
}
@Override
public V fromString(String string) {
return valueTransformer.transform(string);
}
}
/**
* Sets the string converter for the current field.
*
* @param newValue
* The string converter that transforms the user input string to
* the field's underlying value and back.
*
* @return Returns the current field to allow for chaining.
*/
public F format(StringConverter newValue) {
stringConverter = newValue;
validate();
return (F) this;
}
/**
* Applies a new string converter that converts the entered string input
* to a concrete value.
*
* @param newValue
* The string converter that transforms the user input string to
* the field's underlying value and back.
* @param errorMessage
* The error message to display if the transformation was
* unsuccessful.
*
* @return Returns the current field to allow for chaining.
*/
public F format(StringConverter newValue, String errorMessage) {
stringConverter = newValue;
if (isI18N()) {
formatErrorKey.set(errorMessage);
} else {
formatError.set(errorMessage);
}
validate();
return (F) this;
}
/**
* Sets the value transformer for the current field.
*
* @param newValue
* The value transformer that parses the user input string to
* the field's underlying value.
*
* @return Returns the current field to allow for chaining.
* @deprecated Use format(StringConverter) instead
*/
@Deprecated
public F format(ValueTransformer newValue) {
stringConverter = new StringConverterAdapter<>(newValue);
validate();
return (F) this;
}
/**
* Applies a new value transformer that converts the entered string input
* to a concrete value.
*
* @param newValue
* The new value transformer. Takes a string as an input and
* returns the concrete type.
* @param errorMessage
* The error message to display if the transformation was
* unsuccessful.
*
* @return Returns the current field to allow for chaining.
* @deprecated Use format(StringConverter, errorMessage) instead
*/
@Deprecated
public F format(ValueTransformer newValue, String errorMessage) {
stringConverter = new StringConverterAdapter<>(newValue);
if (isI18N()) {
formatErrorKey.set(errorMessage);
} else {
formatError.set(errorMessage);
}
validate();
return (F) this;
}
/**
* Adds an error message to handle formatting errors with the default
* value transformers.
*
* @param errorMessage
* The error message to display if the transformation was
* unsuccessful.
*
* @return Returns the current field to allow for chaining.
*/
public F format(String errorMessage) {
if (isI18N()) {
formatErrorKey.set(errorMessage);
} else {
formatError.set(errorMessage);
}
validate();
return (F) this;
}
/**
* Sets the list of validators for the current field. This overrides all
* validators that have previously been added.
*
* @param newValue
* The validators that are to be used for validating this
* field. Limited to validators that are able to handle the
* field's underlying type.
*
* @return Returns the current field to allow for chaining.
*/
@SafeVarargs
public final F validate(Validator... newValue) {
validators.clear();
Collections.addAll(validators, newValue);
validate();
return (F) this;
}
/**
* Binds the given property with the field.
*
* @param binding
* The property to be bound with.
*
* @return Returns the current field to allow for chaining.
*/
public F bind(P binding) {
persistentValue.bindBidirectional(binding);
binding.addListener(externalBindingListener);
return (F) this;
}
/**
* Unbinds the given property with the field.
*
* @param binding
* The property to be unbound with.
*
* @return Returns the current field to allow for chaining.
*/
public F unbind(P binding) {
persistentValue.unbindBidirectional(binding);
binding.removeListener(externalBindingListener);
return (F) this;
}
/**
* {@inheritDoc}
*/
public void setBindingMode(BindingMode newValue) {
if (BindingMode.CONTINUOUS.equals(newValue)) {
value.addListener(bindingModeListener);
} else {
value.removeListener(bindingModeListener);
}
}
/**
* Stores the field's current value in its persistent value. This stores
* the user's changes in the model.
*/
public void persist() {
if (!isValid()) {
return;
}
persistentValue.setValue(value.getValue());
fireEvent(FieldEvent.fieldPersistedEvent(this));
}
/**
* Sets the field's current value to its persistent value, thus resetting
* any changes made by the user.
*/
public void reset() {
if (!hasChanged()) {
return;
}
userInput.setValue(stringConverter.toString((V) persistentValue.getValue()));
}
/**
* Validates that the new field input matches the required condition.
*
* @param newValue
* The new value to check for the required state.
*
* @return Returns whether the input matches the required condition.
*/
protected boolean validateRequired(String newValue) {
return !isRequired() || (isRequired() && !newValue.isEmpty());
}
/**
* Validates a user input based on the field's value transformer and its
* validation rules. Also considers the {@code required} flag. This method
* directly updates the {@code valid} property.
*
* @return Returns whether the user input is a valid value or not.
*/
public boolean validate() {
String newValue = userInput.getValue();
if (!validateRequired(newValue)) {
if (isI18N() && !requiredErrorKey.get().isEmpty()) {
errorMessageKeys.setAll(requiredErrorKey.get());
} else if (!requiredError.get().isEmpty()) {
errorMessages.setAll(requiredError.get());
}
valid.set(false);
return false;
}
V transformedValue;
// Attempt a transformation from String to the field's underlying type.
try {
transformedValue = stringConverter.fromString(newValue);
} catch (Exception e) {
if (isI18N() && !formatErrorKey.get().isEmpty()) {
errorMessageKeys.setAll(formatErrorKey.get());
} else if (!formatError.get().isEmpty()) {
errorMessages.setAll(formatError.get());
}
valid.set(false);
return false;
}
// Check all validation rules and collect any error messages.
List errorMessages = validators.stream()
.map(v -> v.validate(transformedValue))
.filter(r -> !r.getResult())
.map(ValidationResult::getErrorMessage)
.collect(Collectors.toList());
// Update the validation results with the current results. Listeners
// will handle the translation aspect.
if (isI18N()) {
errorMessageKeys.setAll(errorMessages);
} else {
this.errorMessages.setAll(errorMessages);
}
if (errorMessages.size() > 0) {
valid.set(false);
return false;
}
// If all above conditions have succeeded, the user input is
// considered valid.
valid.set(true);
return true;
}
/**
* {@inheritDoc}
*/
public void translate(TranslationService service) {
super.translate(service);
updateElement(formatError, formatErrorKey);
validate();
}
public String getUserInput() {
return userInput.get();
}
public StringProperty userInputProperty() {
return userInput;
}
public V getValue() {
return (V) value.getValue();
}
public P valueProperty() {
return value;
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/DateField.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 - 2018 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.view.controls.SimpleDateControl;
import javafx.beans.property.ObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.util.converter.LocalDateStringConverter;
import java.time.LocalDate;
import java.time.chrono.Chronology;
import java.time.format.FormatStyle;
import java.util.Locale;
/**
* This class provides an implementation of a {@link Field} containing a {@code LocalDate} value.
*
* @author Tomasz Krzemiński
*/
public class DateField extends DataField, LocalDate, DateField> {
/**
* Internal constructor for the {@code DataField} class. To create new
* elements, see the static factory methods in {@code Field}.
*
* @param valueProperty The property that is used to store the current valid value
* of the field.
* @param persistentValueProperty The property that is used to store the latest persisted
* @see Field ::ofStringType
* @see Field ::ofIntegerType
* @see Field ::ofDoubleType
* @see Field ::ofBooleanType
*/
public DateField(ObjectProperty valueProperty, ObjectProperty persistentValueProperty) {
super(valueProperty, persistentValueProperty);
Chronology chronology = Chronology.ofLocale(Locale.getDefault(Locale.Category.FORMAT));
stringConverter = new LocalDateStringConverter(FormatStyle.SHORT, null, chronology);
rendererSupplier = () -> new SimpleDateControl();
userInput.setValue(null);
userInput.setValue(stringConverter.toString((LocalDate) persistentValue.getValue()));
}
@Override
public DateField bind(ObjectProperty binding) {
return super.bind(binding);
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/DoubleField.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.view.controls.SimpleDoubleControl;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
/**
* This class provides an implementation of a {@link Field} containing a
* {@code double} value.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class DoubleField extends DataField {
/**
* The constructor of {@code DoubleField}.
*
* @param valueProperty
* The property that is used to store the current valid value
* of the field.
* @param persistentValueProperty
* The property that is used to store the latest persisted
* value of the field.
*/
protected DoubleField(SimpleDoubleProperty valueProperty, SimpleDoubleProperty persistentValueProperty) {
super(valueProperty, persistentValueProperty);
stringConverter = new AbstractStringConverter() {
@Override
public Double fromString(String string) {
return Double.parseDouble(string);
}
};
rendererSupplier = () -> new SimpleDoubleControl();
userInput.set(stringConverter.toString(value.getValue()));
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/Element.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 - 2018 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.view.util.ColSpan;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.util.UUID;
/**
* @author Andres Almiray
*/
public abstract class Element> {
/**
* Fields can be styled using CSS through ID or class hooks.
*/
protected final StringProperty id = new SimpleStringProperty(UUID.randomUUID().toString());
protected final ListProperty styleClass = new SimpleListProperty<>(FXCollections.observableArrayList());
protected final IntegerProperty span = new SimpleIntegerProperty(12);
/**
* Sets the id property of the current field.
*
* @param newValue
* The new value for the id property.
*
* @return Returns the current field to allow for chaining.
*/
public E id(String newValue) {
id.set(newValue);
return (E) this;
}
/**
* Sets the style classes for the current field.
*
* @param newValue
* The new style classes.
*
* @return Returns the current field to allow for chaining.
*/
public E styleClass(String... newValue) {
styleClass.setAll(newValue);
return (E) this;
}
/**
* Sets the amount of columns the field takes up inside the section grid.
*
* @param newValue
* The new number of columns.
*
* @return Returns the current field to allow for chaining.
*/
public E span(int newValue) {
span.setValue(newValue);
return (E) this;
}
/**
* Sets the amount of columns the field takes up inside the section grid.
*
* @param newValue
* The new span fraction.
*
* @return Returns the current field to allow for chaining.
*/
public E span(ColSpan newValue) {
span.setValue(newValue.valueOf());
return (E) this;
}
public int getSpan() {
return span.get();
}
public IntegerProperty spanProperty() {
return span;
}
public String getID() {
return id.get();
}
public StringProperty idProperty() {
return id;
}
public ObservableList getStyleClass() {
return styleClass.get();
}
public ListProperty styleClassProperty() {
return styleClass;
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/Field.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.event.FieldEvent;
import com.dlsc.formsfx.model.util.BindingMode;
import com.dlsc.formsfx.model.util.TranslationService;
import com.dlsc.formsfx.view.controls.SimpleControl;
import javafx.beans.InvalidationListener;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import javafx.collections.ObservableList;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.Labeled;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* This class provides the base implementation for a FormsFX field. It is not
* meant to be used directly, but instead acts as a base for concrete
* implementations.
*
* A field is the smallest unit of the form. It contains only the value and
* relevant information.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public abstract class Field> extends Element implements FormElement {
/**
* The label acts as a description for the field. It is always visible to
* the user and tells them what should be entered into the field.
*
* This property is translatable if a {@link TranslationService} is set on
* the containing form.
*/
protected final StringProperty label = new SimpleStringProperty("");
protected final StringProperty labelKey = new SimpleStringProperty("");
/**
* The tooltip is an extension of the label. It contains additional
* information about the contained data that is only contextually visible.
*
* This property is translatable if a {@link TranslationService} is set on
* the containing form.
*/
protected final StringProperty tooltip = new SimpleStringProperty("");
protected final StringProperty tooltipKey = new SimpleStringProperty("");
/**
* The placeholder is only visible in an empty field. It provides a hint to
* the user about what should be entered into the field.
*
* This property is translatable if a {@link TranslationService} is set on
* the containing form.
*/
protected final StringProperty placeholder = new SimpleStringProperty("");
protected final StringProperty placeholderKey = new SimpleStringProperty("");
/**
* Every field can be marked as {@code required} and {@code editable}. These
* properties can change the field's behaviour.
*/
protected final StringProperty requiredErrorKey = new SimpleStringProperty("");
protected final StringProperty requiredError = new SimpleStringProperty("");
protected final BooleanProperty required = new SimpleBooleanProperty(false);
protected final BooleanProperty editable = new SimpleBooleanProperty(true);
/**
* The field's current state is represented by the value properties, as
* well as by the {@code valid} and {@code changed} flags.
*/
protected final BooleanProperty valid = new SimpleBooleanProperty(true);
protected final BooleanProperty changed = new SimpleBooleanProperty(false);
/**
* Fields can be styled using CSS through ID or class hooks.
*/
protected final StringProperty id = new SimpleStringProperty(UUID.randomUUID().toString());
protected final ListProperty styleClass = new SimpleListProperty<>(FXCollections.observableArrayList());
protected final IntegerProperty span = new SimpleIntegerProperty(12);
/**
* The results of the field's validation is stored in this property. After
* every validation, the results are updated and reflected in this property.
*
* This property is translatable if a {@link TranslationService} is set on
* the containing form.
*/
protected final ListProperty errorMessages = new SimpleListProperty<>(FXCollections.observableArrayList());
protected final ListProperty errorMessageKeys = new SimpleListProperty<>(FXCollections.observableArrayList());
/**
* Additional descriptions for the field's label and value are stored in these properties.
*
* These properties are translatable if a {@link TranslationService} is set on
* the containing form.
*/
private Node labelDescription;
private Node valueDescription;
private final StringProperty labelDescriptionKey = new SimpleStringProperty("");
private final StringProperty valueDescriptionKey = new SimpleStringProperty("");
private static final String LABEL_DESCRIPTION_STYLE_CLASS = "field-label-description";
private static final String VALUE_DESCRIPTION_STYLE_CLASS = "field-value-description";
/**
* The translation service is passed down from the containing section. It
* is used to translate all translatable values of the field.
*/
protected TranslationService translationService;
protected SimpleControl renderer;
protected Supplier> rendererSupplier;
protected final Map,List>> eventHandlers = new ConcurrentHashMap<>();
/**
* With the continuous binding mode, values are always directly persisted
* upon any changes.
*/
protected final InvalidationListener bindingModeListener = (observable) -> {
if (validate()) {
persist();
}
};
/**
* Internal constructor for the {@code Field} class. To create new elements,
* see the static factory methods in this class.
*
* @see Field::ofStringType
* @see Field::ofIntegerType
* @see Field::ofDoubleType
* @see Field::ofBooleanType
* @see Field::ofMultiSelectionType
* @see Field::ofSingleSelectionType
*/
protected Field() {
// Whenever one of the translatable elements' keys change, update the
// displayed value based on the new translation.
labelKey.addListener((observable, oldValue, newValue) -> label.setValue(translationService.translate(newValue)));
tooltipKey.addListener((observable, oldValue, newValue) -> tooltip.setValue(translationService.translate(newValue)));
placeholderKey.addListener((observable, oldValue, newValue) -> placeholder.setValue(translationService.translate(newValue)));
labelDescriptionKey.addListener((observable, oldValue, newValue) -> labelDescription = asLabel(translationService.translate(newValue), LABEL_DESCRIPTION_STYLE_CLASS));
valueDescriptionKey.addListener((observable, oldValue, newValue) -> valueDescription = asLabel(translationService.translate(newValue), VALUE_DESCRIPTION_STYLE_CLASS));
requiredErrorKey.addListener((observable, oldValue, newValue) -> validate());
// Whenever the errorMessageKeys change, update the displayed
// label to the new translation. This maps the keys to their translated
// representation.
errorMessageKeys.addListener((observable, oldValue, newValue) -> errorMessages.setAll(errorMessageKeys.stream()
.map(s -> translationService.translate(s))
.collect(Collectors.toList())));
}
/**
* Creates a new {@link PasswordField} with the given default value.
*
* @param defaultValue
* The initial value and persistent value of the field.
*
* @return Returns a new {@link PasswordField}.
*/
public static PasswordField ofPasswordType(String defaultValue) {
return new PasswordField(new SimpleStringProperty(defaultValue), new SimpleStringProperty(defaultValue));
}
/**
* Creates a new {@link PasswordField} with the given property.
*
* @param binding
* The property from the model to be bound with.
*
* @return Returns a new {@link PasswordField}.
*/
public static PasswordField ofPasswordType(StringProperty binding) {
return new PasswordField(new SimpleStringProperty(binding.getValue()), new SimpleStringProperty(binding.getValue())).bind(binding);
}
/**
* Creates a new {@link StringField} with the given default value.
*
* @param defaultValue
* The initial value and persistent value of the field.
*
* @return Returns a new {@link StringField}.
*/
public static StringField ofStringType(String defaultValue) {
return new StringField(new SimpleStringProperty(defaultValue), new SimpleStringProperty(defaultValue));
}
/**
* Creates a new {@link StringField} with the given property.
*
* @param binding
* The property from the model to be bound with.
*
* @return Returns a new {@link StringField}.
*/
public static StringField ofStringType(StringProperty binding) {
return new StringField(new SimpleStringProperty(binding.getValue()), new SimpleStringProperty(binding.getValue())).bind(binding);
}
/**
* Creates a new {@link DoubleField} with the given default value.
*
* @param defaultValue
* The initial value and persistent value of the field.
*
* @return Returns a new {@link DoubleField}.
*/
public static DoubleField ofDoubleType(double defaultValue) {
return new DoubleField(new SimpleDoubleProperty(defaultValue), new SimpleDoubleProperty(defaultValue));
}
/**
* Creates a new {@link DoubleField} with the given property.
*
* @param binding
* The property from the model to be bound with.
*
* @return Returns a new {@link DoubleField}.
*/
public static DoubleField ofDoubleType(DoubleProperty binding) {
return new DoubleField(new SimpleDoubleProperty(binding.getValue()), new SimpleDoubleProperty(binding.getValue())).bind(binding);
}
/**
* Creates a new {@link IntegerField} with the given default value.
*
* @param defaultValue
* The initial value and persistent value of the field.
*
* @return Returns a new {@link IntegerField}.
*/
public static IntegerField ofIntegerType(int defaultValue) {
return new IntegerField(new SimpleIntegerProperty(defaultValue), new SimpleIntegerProperty(defaultValue));
}
/**
* Creates a new {@link IntegerField} with the given property.
*
* @param binding
* The property from the model to be bound with.
*
* @return Returns a new {@link IntegerField}.
*/
public static IntegerField ofIntegerType(IntegerProperty binding) {
return new IntegerField(new SimpleIntegerProperty(binding.getValue()), new SimpleIntegerProperty(binding.getValue())).bind(binding);
}
/**
* Creates a new {@link BooleanField} with the given default value.
*
* @param defaultValue
* The initial value and persistent value of the field.
*
* @return Returns a new {@link BooleanField}.
*/
public static BooleanField ofBooleanType(boolean defaultValue) {
return new BooleanField(new SimpleBooleanProperty(defaultValue), new SimpleBooleanProperty(defaultValue));
}
/**
* Creates a new {@link BooleanField} with the given property.
*
* @param binding
* The property from the model to be bound with.
*
* @return Returns a new {@link BooleanField}.
*/
public static BooleanField ofBooleanType(BooleanProperty binding) {
return new BooleanField(new SimpleBooleanProperty(binding.getValue()), new SimpleBooleanProperty(binding.getValue())).bind(binding);
}
/**
* Creates a new {@link MultiSelectionField} with the given items and a
* pre-defined selection.
*
* @param items
* The list of available items on the field.
* @param selection
* The pre-defined indices of the selected items.
*
* @return Returns a new {@link MultiSelectionField}.
*/
public static MultiSelectionField ofMultiSelectionType(List items, List selection) {
return new MultiSelectionField<>(new SimpleListProperty<>(FXCollections.observableArrayList(items)), selection);
}
/**
* Creates a new {@link MultiSelectionField} with the given items and no
* pre-defined selection.
*
* @param items
* The list of available items on the field.
*
* @return Returns a new {@link MultiSelectionField}.
*/
public static MultiSelectionField ofMultiSelectionType(List items) {
return new MultiSelectionField<>(new SimpleListProperty<>(FXCollections.observableArrayList(items)), new ArrayList<>());
}
/**
* Creates a new {@link MultiSelectionField} with the given properties for
* items and selection.
*
* @param itemsBinding
* The items property to be bound with.
*
* @param selectionBinding
* The selection property to be bound with.
*
* @return Returns a new {@link MultiSelectionField}.
*/
public static MultiSelectionField ofMultiSelectionType(ListProperty itemsBinding, ListProperty selectionBinding) {
return new MultiSelectionField<>(new SimpleListProperty<>(itemsBinding.getValue()), new ArrayList<>(selectionBinding.getValue().stream().map(t -> itemsBinding.getValue().indexOf(t)).collect(Collectors.toList()))).bind(itemsBinding, selectionBinding);
}
/**
* Creates a new {@link SingleSelectionField} with the given items and a
* pre-defined selection.
*
* @param items
* The list of available items on the field.
* @param selection
* The pre-defined index of the selected item.
*
* @return Returns a new {@link SingleSelectionField}.
*/
public static SingleSelectionField ofSingleSelectionType(List items, int selection) {
return new SingleSelectionField<>(new SimpleListProperty<>(FXCollections.observableArrayList(items)), selection);
}
/**
* Creates a new {@link SingleSelectionField} with the given items and no
* pre-defined selection.
*
* @param items
* The list of available items on the field.
*
* @return Returns a new {@link SingleSelectionField}.
*/
public static SingleSelectionField ofSingleSelectionType(List items) {
return new SingleSelectionField<>(new SimpleListProperty<>(FXCollections.observableArrayList(items)), -1);
}
/**
* Creates a new {@link SingleSelectionField} with the given properties for
* items and selection.
*
* @param itemsBinding
* The items property to be bound with.
*
* @param selectionBinding
* The selection property to be bound with.
*
* @return Returns a new {@link SingleSelectionField}.
*/
public static SingleSelectionField ofSingleSelectionType(ListProperty itemsBinding, ObjectProperty selectionBinding) {
return new SingleSelectionField<>(new SimpleListProperty<>(itemsBinding.getValue()), itemsBinding.indexOf(selectionBinding.getValue())).bind(itemsBinding, selectionBinding);
}
/**
* Creates a new {@link DateField} with given default value
*
* @param defaultValue The initial value and persistent value of the field.
* @return Returns a new {@link DateField}.
*/
public static DateField ofDate(LocalDate defaultValue) {
return new DateField(new SimpleObjectProperty<>(defaultValue), new SimpleObjectProperty<>(defaultValue));
}
/**
* Creates a new {@link DateField} with given property
*
* @param binding The property from the model to be bound with.
* @return Returns a new {@link DateField}.
*/
public static DateField ofDate(ObjectProperty binding) {
return new DateField(new SimpleObjectProperty<>(binding.getValue()), new SimpleObjectProperty<>(binding.getValue())).bind(binding);
}
/**
* Sets the required property to for the current field without providing an
* error message.
*
* @param newValue
* The new state of the required property.
*
* @return Returns the current field to allow for chaining.
*/
public F required(boolean newValue) {
required.set(newValue);
validate();
return (F) this;
}
/**
* Sets the required property to true for the current field.
*
* @param errorMessage
* The error message if the field is not filled in.
*
* @return Returns the current field to allow for chaining.
*/
public F required(String errorMessage) {
required.set(true);
if (isI18N()) {
requiredErrorKey.set(errorMessage);
} else {
requiredError.set(errorMessage);
}
validate();
return (F) this;
}
/**
* Sets the editable property of the current field.
*
* @param newValue
* The new value for the editable property.
*
* @return Returns the current field to allow for chaining.
*/
public F editable(boolean newValue) {
editable.set(newValue);
return (F) this;
}
/**
* Sets the label property of the current field.
*
* @param newValue
* The new value for the label property. This can be the label
* itself or a key that is then used for translation.
*
* @see TranslationService
*
* @return Returns the current field to allow for chaining.
*/
public F label(String newValue) {
if (isI18N()) {
labelKey.set(newValue);
} else {
label.set(newValue);
}
return (F) this;
}
/**
* Sets the label description property of the current field.
*
* @param newValue
* The new value for the label description property.
*
*
* @return Returns the current field to allow for chaining.
*/
public F labelDescription(Node newValue) {
labelDescription = newValue;
if (labelDescription != null) {
labelDescription.getStyleClass().add(LABEL_DESCRIPTION_STYLE_CLASS);
}
return (F) this;
}
/**
* Sets the label description property of the current field.
*
* @param newValue
* The new value for the label description property,
* wrapped with a {@code Text}.
*
*
* @return Returns the current field to allow for chaining.
*/
public F labelDescription(String newValue) {
if(isI18N()) {
labelDescriptionKey.set(newValue);
} else if (newValue != null) {
labelDescription = asLabel(newValue, LABEL_DESCRIPTION_STYLE_CLASS);
}
return (F) this;
}
/**
* Sets the value description property of the current field.
*
* @param newValue
* The new value for the field description property.
*
*
* @return Returns the current field to allow for chaining.
*/
public F valueDescription(Node newValue) {
valueDescription = newValue;
if (valueDescription != null) {
valueDescription.getStyleClass().add(VALUE_DESCRIPTION_STYLE_CLASS);
}
return (F) this;
}
/**
* Sets the value description property of the current field.
*
* @param newValue
* The new value for the field description property,
* wrapped with a {@code Text}.
*
*
* @return Returns the current field to allow for chaining.
*/
public F valueDescription(String newValue) {
if(isI18N()) {
valueDescriptionKey.set(newValue);
} else if (newValue != null) {
valueDescription = asLabel(newValue, VALUE_DESCRIPTION_STYLE_CLASS);
}
return (F) this;
}
private Label asLabel(String text, String styleClass) {
Label label = new Label(text);
label.setWrapText(true);
label.getStyleClass().add(styleClass);
return label;
}
/**
* Sets the tooltip property of the current field.
*
* @param newValue
* The new value for the tooltip property. This can be the
* label itself or a key that is then used for translation.
*
* @see TranslationService
*
* @return Returns the current field to allow for chaining.
*/
public F tooltip(String newValue) {
if (isI18N()) {
tooltipKey.set(newValue);
} else {
tooltip.set(newValue);
}
return (F) this;
}
/**
* Sets the placeholder property of the current field.
*
* @param newValue
* The new value for the placeholder property. This can be the
* label itself or a key that is then used for translation.
*
* @see TranslationService
*
* @return Returns the current field to allow for chaining.
*/
public F placeholder(String newValue) {
if (isI18N()) {
placeholderKey.set(newValue);
} else {
placeholder.set(newValue);
}
return (F) this;
}
/**
* Sets the control that renders this field.
*
* @param newValue
* The new control to render the field.
*
* @return Returns the current field to allow for chaining.
*/
public F render(SimpleControl newValue) {
renderer = newValue;
return (F) this;
}
/**
* Sets the control supplier that renders this field.
* The supplier is only called when required, i.e., when the GUI is created.
*
* @param newValue
* The new control supplier to render the field.
*
* @return Returns the current field to allow for chaining.
*/
public F render(Supplier> newValue) {
rendererSupplier = newValue;
return (F) this;
}
/**
* Activates or deactivates the {@code bindingModeListener} based on the
* given {@code BindingMode}.
*
* @param newValue
* The new binding mode for the current field.
*/
public abstract void setBindingMode(BindingMode newValue);
// abstract void persist();
// abstract void reset();
/**
* This internal method is called by the containing section when a new
* translation has been added to the form.
*
* @param newValue
* The new service to use for translating translatable values.
*/
public void translate(TranslationService newValue) {
translationService = newValue;
if (!isI18N()) {
return;
}
updateElement(label, labelKey);
updateElement(tooltip, tooltipKey);
updateElement(placeholder, placeholderKey);
updateElement(requiredError, requiredErrorKey);
updateElement(labelDescription, labelDescriptionKey);
updateElement(valueDescription, valueDescriptionKey);
// Validation results are handled separately as they use a somewhat
// more complex structure.
validate();
}
/**
* Updates a displayable field property to include translated text.
*
* @param displayProperty
* The property that is displayed to the user.
* @param keyProperty
* The internal property that holds the translation key.
*/
protected void updateElement(StringProperty displayProperty, StringProperty keyProperty) {
// If the key has not yet been set that means that the translation
// service was added for the first time. We can simply set the key
// to the value stored in the display property, the listener will
// then take care of the translation.
if ((keyProperty.get() == null || keyProperty.get().isEmpty()) && !displayProperty.get().isEmpty()) {
keyProperty.setValue(displayProperty.get());
} else if (!keyProperty.get().isEmpty()) {
displayProperty.setValue(translationService.translate(keyProperty.get()));
}
}
/**
* Updates a displayable field property to include translated text.
*
* @param node
* The property that is displayed to the user.
* @param keyProperty
* The internal property that holds the translation key.
*/
void updateElement(Node node, StringProperty keyProperty) {
// If the key has not yet been set that means that the translation
// service was added for the first time. We can simply set the key
// to the value stored in the display property, the listener will
// then take care of the translation.
if (!(node instanceof Labeled)) {
// no automatic update
return;
}
Labeled labeled = (Labeled) node;
if ((keyProperty.get() == null || keyProperty.get().isEmpty()) && !labeled.getText().isEmpty()) {
keyProperty.setValue(labeled.getText());
} else if (!keyProperty.get().isEmpty()) {
labeled.setText(translationService.translate(keyProperty.get()));
}
}
/**
* Validates a user input based on the field's value transformer and its
* validation rules. Also considers the {@code required} flag. This method
* directly updates the {@code valid} property.
*
* @return Returns whether the user input is a valid value or not.
*/
protected abstract boolean validate();
public String getPlaceholder() {
return placeholder.get();
}
public StringProperty placeholderProperty() {
return placeholder;
}
public String getLabel() {
return label.get();
}
public StringProperty labelProperty() {
return label;
}
public String getTooltip() {
return tooltip.get();
}
public StringProperty tooltipProperty() {
return tooltip;
}
public boolean isValid() {
return valid.get();
}
public BooleanProperty validProperty() {
return valid;
}
public boolean hasChanged() {
return changed.get();
}
public BooleanProperty changedProperty() {
return changed;
}
public boolean isRequired() {
return required.get();
}
public BooleanProperty requiredProperty() {
return required;
}
public boolean isEditable() {
return editable.get();
}
public BooleanProperty editableProperty() {
return editable;
}
public boolean isI18N() {
return translationService != null;
}
public SimpleControl getRenderer() {
if (renderer == null) {
renderer = rendererSupplier.get();
}
return renderer;
}
public List getErrorMessages() {
return errorMessages.get();
}
public ListProperty errorMessagesProperty() {
return errorMessages;
}
/**
* Registers an event handler to this field. The handler is called when the
* field receives an {@code Event} of the specified type during the bubbling
* phase of event delivery.
*
* @param eventType the type of the events to receive by the handler
* @param eventHandler the handler to register
*
* @throws NullPointerException if either event type or handler are {@code null}.
*/
public Field addEventHandler(EventType eventType, EventHandler super FieldEvent> eventHandler) {
if (eventType == null) {
throw new NullPointerException("Argument eventType must not be null");
}
if (eventHandler == null) {
throw new NullPointerException("Argument eventHandler must not be null");
}
this.eventHandlers.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>()).add(eventHandler);
return this;
}
/**
* Unregisters a previously registered event handler from this field. One
* handler might have been registered for different event types, so the
* caller needs to specify the particular event type from which to
* unregister the handler.
*
* @param eventType the event type from which to unregister
* @param eventHandler the handler to unregister
*
* @throws NullPointerException if either event type or handler are {@code null}.
*/
public Field removeEventHandler(EventType eventType, EventHandler super FieldEvent> eventHandler) {
if (eventType == null) {
throw new NullPointerException("Argument eventType must not be null");
}
if (eventHandler == null) {
throw new NullPointerException("Argument eventHandler must not be null");
}
List> list = this.eventHandlers.get(eventType);
if (list != null) {
list.remove(eventHandler);
}
return this;
}
protected void fireEvent(FieldEvent event) {
List> list = this.eventHandlers.get(event.getEventType());
if (list == null) {
return;
}
for (EventHandler super FieldEvent> eventHandler : list) {
if (!event.isConsumed()) {
eventHandler.handle(event);
}
}
}
public Node getLabelDescription() {
return labelDescription;
}
public Node getValueDescription() {
return valueDescription;
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/Form.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.event.FormEvent;
import com.dlsc.formsfx.model.util.BindingMode;
import com.dlsc.formsfx.model.util.TranslationService;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.event.EventHandler;
import javafx.event.EventType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;
/**
* A form is the containing unit for sections and elements and is used to bring
* structure to form data. It also acts as a proxy to some properties of the
* contained data, such as validity or changes.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class Form {
protected final List groups = new ArrayList<>();
/**
* The title acts as a description for the form.
*
* This property is translatable if a {@link TranslationService} is set.
*/
protected final StringProperty title = new SimpleStringProperty("");
protected final StringProperty titleKey = new SimpleStringProperty("");
/**
* The form acts as a proxy for its contained sections' {@code changed}
* and {@code valid} properties.
*/
protected final BooleanProperty valid = new SimpleBooleanProperty(true);
protected final BooleanProperty changed = new SimpleBooleanProperty(false);
protected final BooleanProperty persistable = new SimpleBooleanProperty(false);
/**
* A form can optionally have a translation service. This service is used to
* translate displayed values into multiple locales. The form registers
* itself as a listener on the translation service to handle locale changes.
*
* @see TranslationService
*/
protected final ObjectProperty translationService = new SimpleObjectProperty<>();
protected final Runnable localeChangeListener = this::translate;
private final Map,List>> eventHandlers = new ConcurrentHashMap<>();
/**
* Internal constructor for the {@code Form} class. To create new
* forms, see the static factory method in this class.
*
* @see Form::of
*
* @param groups
* A varargs list of groups that are contained in this
* form.
*/
private Form(Group... groups) {
Collections.addAll(this.groups, groups);
// If any of the groups are marked as changed, the section is updated
// accordingly.
this.groups.forEach(s -> s.changedProperty().addListener((observable, oldValue, newValue) -> setChangedProperty()));
// If any of the groups are marked as invalid, the section is updated
// accordingly.
this.groups.forEach(s -> s.validProperty().addListener((observable, oldValue, newValue) -> setValidProperty()));
setValidProperty();
setChangedProperty();
setPersistableProperty();
// Whenever the title's key changes, update the displayed value based
// on the new translation.
titleKey.addListener((observable, oldValue, newValue) -> title.setValue(translationService.get().translate(newValue)));
// Whenever the underlying translation service changes, update all
// translation on the form and its contained elements.
translationService.addListener((observable, oldValue, newValue) -> translate());
}
/**
* Creates a new form containing the given sections.
*
* @param sections
* The sections to be included in the form.
*
* @return Returns a new {@code Form}.
*/
public static Form of(Group... sections) {
return new Form(sections);
}
/**
* Sets the title property of the current form.
*
* @param newValue
* The new value for the title property. This can be the title
* itself or a key that is then used for translation.
*
* @see TranslationService
*
* @return Returns the current form to allow for chaining.
*/
public Form title(String newValue) {
if (isI18N()) {
titleKey.set(newValue);
} else {
title.set(newValue);
}
return this;
}
/**
* Sets the translation service property of the current form.
*
* @param newValue
* The new value for the translation service property.
*
* @return Returns the current form to allow for chaining.
*/
public Form i18n(TranslationService newValue) {
if (translationService.get() != null) {
translationService.get().removeListener(localeChangeListener);
}
translationService.setValue(newValue);
translationService.get().addListener(localeChangeListener);
return this;
}
/**
* Changes the way field values are bound to external properties.
*
* @see BindingMode
*
* @param newValue
* The new mode for handling external bindings.
*
* @return Returns the current form to allow for chaining.
*/
public Form binding(BindingMode newValue) {
getFields().forEach(f -> f.setBindingMode(newValue));
return this;
}
/**
* This internal method is used as a callback for when the translation
* service or its locale changes. Also applies the translation to all
* contained sections.
*
* @see Group::translate
*/
protected void translate() {
TranslationService tr = translationService.get();
if (!isI18N()) {
return;
}
if (titleKey.get() == null || titleKey.get().isEmpty()) {
titleKey.setValue(title.get());
} else {
title.setValue(tr.translate(titleKey.get()));
}
groups.forEach(s -> s.translate(tr));
}
/**
* Persists the values for all elements contained in this form's groups.
*
* @see Field::reset
*/
public void persist() {
if (!isPersistable()) {
return;
}
groups.forEach(Group::persist);
fireEvent(FormEvent.formPersistedEvent(this));
}
/**
* Resets the values for all elements contained in this form's groups.
*
* @see Field::reset
*/
public void reset() {
if (!hasChanged()) {
return;
}
groups.forEach(Group::reset);
fireEvent(FormEvent.formResetEvent(this));
}
/**
* Sets this form's {@code changed} property based on its contained
* groups' changed properties.
*/
protected void setChangedProperty() {
changed.setValue(groups.stream().anyMatch(Group::hasChanged));
setPersistableProperty();
}
/**
* Sets this form's {@code valid} property based on its contained groups'
* changed properties.
*/
protected void setValidProperty() {
valid.setValue(groups.stream().allMatch(Group::isValid));
setPersistableProperty();
}
/**
* Sets this form's {@code persistable} property based on its contained
* groups' persistable properties.
*/
protected void setPersistableProperty() {
persistable.setValue(groups.stream().anyMatch(Group::hasChanged) && groups.stream().allMatch(Group::isValid));
}
public List getGroups() {
return groups;
}
public List getElements() {
return groups.stream()
.map(Group::getElements)
.flatMap(List::stream)
.collect(Collectors.toList());
}
public List getFields() {
return groups.stream()
.map(Group::getElements)
.flatMap(List::stream)
.filter(e -> e instanceof Field)
.map(e -> (Field) e)
.collect(Collectors.toList());
}
public boolean hasChanged() {
return changed.get();
}
public BooleanProperty changedProperty() {
return changed;
}
public boolean isValid() {
return valid.get();
}
public BooleanProperty validProperty() {
return valid;
}
public boolean isPersistable() {
return persistable.get();
}
public BooleanProperty persistableProperty() {
return persistable;
}
public String getTitle() {
return title.get();
}
public StringProperty titleProperty() {
return title;
}
public boolean isI18N() {
return translationService.get() != null;
}
/**
* Registers an event handler to this form. The handler is called when the
* form receives an {@code Event} of the specified type during the bubbling
* phase of event delivery.
*
* @param eventType the type of the events to receive by the handler
* @param eventHandler the handler to register
*
* @throws NullPointerException if either event type or handler are {@code null}.
*/
public Form addEventHandler(EventType eventType, EventHandler super FormEvent> eventHandler) {
if (eventType == null) {
throw new NullPointerException("Argument eventType must not be null");
}
if (eventHandler == null) {
throw new NullPointerException("Argument eventHandler must not be null");
}
this.eventHandlers.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>()).add(eventHandler);
return this;
}
/**
* Unregisters a previously registered event handler from this form. One
* handler might have been registered for different event types, so the
* caller needs to specify the particular event type from which to
* unregister the handler.
*
* @param eventType the event type from which to unregister
* @param eventHandler the handler to unregister
*
* @throws NullPointerException if either event type or handler are {@code null}.
*/
public Form removeEventHandler(EventType eventType, EventHandler super FormEvent> eventHandler) {
if (eventType == null) {
throw new NullPointerException("Argument eventType must not be null");
}
if (eventHandler == null) {
throw new NullPointerException("Argument eventHandler must not be null");
}
List> list = this.eventHandlers.get(eventType);
if (list != null) {
list.remove(eventHandler);
}
return this;
}
protected void fireEvent(FormEvent event) {
List> list = this.eventHandlers.get(event.getEventType());
if (list == null) {
return;
}
for (EventHandler super FormEvent> eventHandler : list) {
if (!event.isConsumed()) {
eventHandler.handle(event);
}
}
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/FormElement.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 - 2018 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
/**
* @author Andres Almiray
*/
public interface FormElement {
void persist();
void reset();
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/Group.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.event.GroupEvent;
import com.dlsc.formsfx.model.util.TranslationService;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.event.EventHandler;
import javafx.event.EventType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* A group is the intermediate unit in a form. It is used to group form
* elements to a larger unit. It also acts as a proxy to some properties of
* the contained data, such as validity or changes.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class Group {
protected final List elements = new ArrayList<>();
/**
* The group acts as a proxy for its contained elements' {@code changed}
* and {@code valid} properties.
*/
protected final BooleanProperty valid = new SimpleBooleanProperty(true);
protected final BooleanProperty changed = new SimpleBooleanProperty(false);
/**
* The translation service is passed down from the containing form. It
* is used to translate all translatable values of the field.
*/
protected TranslationService translationService;
private final Map,List>> eventHandlers = new ConcurrentHashMap<>();
/**
* Internal constructor for the {@code Group} class. To create new
* groups, see the static factory method in this class.
*
* @see Group::of
*
* @param elements
* A varargs list of elements that are contained in this
* group.
*/
protected Group(Element... elements) {
Collections.addAll(this.elements, elements);
// If any of the elements are marked as changed, the group is updated
// accordingly.
this.elements.stream()
.filter(e -> e instanceof Field)
.map(e -> (Field) e)
.forEach(f -> f.changedProperty().addListener((observable, oldValue, newValue) -> setChangedProperty()));
// If any of the elements are marked as invalid, the group is updated
// accordingly.
this.elements.stream()
.filter(e -> e instanceof Field)
.map(e -> (Field) e)
.forEach(f -> f.validProperty().addListener((observable, oldValue, newValue) -> setValidProperty()));
setValidProperty();
setChangedProperty();
}
/**
* Creates a new group containing the given elements.
*
* @param elements
* The elements to be included in the group.
*
* @return Returns a new {@code Group}.
*/
public static Group of(Element... elements) {
return new Group(elements);
}
/**
* This internal method is called by the containing form when a new
* translation has been added to the form. Also applies the translation
* to all contained elements.
*
* @see Field::translate
*
* @param newValue
* The new service to use for translating translatable values.
*/
protected void translate(TranslationService newValue) {
translationService = newValue;
if (!isI18N()) {
return;
}
elements.stream()
.filter(e -> e instanceof Field)
.map(e -> (Field) e)
.forEach(f -> f.translate(translationService));
}
/**
* Persists the values for all contained elements.
* @see Field::persist
*/
public void persist() {
if (!isValid()) {
return;
}
elements.stream()
.filter(e -> e instanceof FormElement)
.map(e -> (FormElement) e)
.forEach(FormElement::persist);
fireEvent(GroupEvent.groupPersistedEvent(this));
}
/**
* Resets the values for all contained elements.
* @see Field::reset
*/
public void reset() {
if (!hasChanged()) {
return;
}
elements.stream()
.filter(e -> e instanceof FormElement)
.map(e -> (FormElement) e)
.forEach(FormElement::reset);
}
/**
* Sets this group's {@code changed} property based on its contained
* elements' changed properties.
*/
private void setChangedProperty() {
changed.setValue(elements.stream()
.filter(e -> e instanceof Field)
.map(e -> (Field) e)
.anyMatch(Field::hasChanged));
}
/**
* Sets this group's {@code valid} property based on its contained elements'
* changed properties.
*/
private void setValidProperty() {
valid.setValue(elements.stream()
.filter(e -> e instanceof Field)
.map(e -> (Field) e)
.allMatch(Field::isValid));
}
public List getElements() {
return elements;
}
public boolean hasChanged() {
return changed.get();
}
public BooleanProperty changedProperty() {
return changed;
}
public boolean isValid() {
return valid.get();
}
public BooleanProperty validProperty() {
return valid;
}
public boolean isI18N() {
return translationService != null;
}
/**
* Registers an event handler to this group. The handler is called when the
* group receives an {@code Event} of the specified type during the bubbling
* phase of event delivery.
*
* @param eventType the type of the events to receive by the handler
* @param eventHandler the handler to register
*
* @throws NullPointerException if either event type or handler are {@code null}.
*/
public Group addEventHandler(EventType eventType, EventHandler super GroupEvent> eventHandler) {
if (eventType == null) {
throw new NullPointerException("Argument eventType must not be null");
}
if (eventHandler == null) {
throw new NullPointerException("Argument eventHandler must not be null");
}
this.eventHandlers.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>()).add(eventHandler);
return this;
}
/**
* Unregisters a previously registered event handler from this group. One
* handler might have been registered for different event types, so the
* caller needs to specify the particular event type from which to
* unregister the handler.
*
* @param eventType the event type from which to unregister
* @param eventHandler the handler to unregister
*
* @throws NullPointerException if either event type or handler are {@code null}.
*/
public Group removeEventHandler(EventType eventType, EventHandler super GroupEvent> eventHandler) {
if (eventType == null) {
throw new NullPointerException("Argument eventType must not be null");
}
if (eventHandler == null) {
throw new NullPointerException("Argument eventHandler must not be null");
}
List> list = this.eventHandlers.get(eventType);
if (list != null) {
list.remove(eventHandler);
}
return this;
}
protected void fireEvent(GroupEvent event) {
List> list = this.eventHandlers.get(event.getEventType());
if (list == null) {
return;
}
for (EventHandler super GroupEvent> eventHandler : list) {
if (!event.isConsumed()) {
eventHandler.handle(event);
}
}
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/IntegerField.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.view.controls.SimpleIntegerControl;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
/**
* This class provides an implementation of a {@link Field} containing a
* {@code integer} value.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class IntegerField extends DataField {
/**
* The constructor of {@code IntegerField}.
*
* @param valueProperty
* The property that is used to store the current valid value
* of the field.
* @param persistentValueProperty
* The property that is used to store the latest persisted
* value of the field.
*/
protected IntegerField(SimpleIntegerProperty valueProperty, SimpleIntegerProperty persistentValueProperty) {
super(valueProperty, persistentValueProperty);
stringConverter = new AbstractStringConverter() {
@Override
public Integer fromString(String string) {
return Integer.parseInt(string);
}
};
rendererSupplier = () -> new SimpleIntegerControl();
userInput.set(stringConverter.toString(value.getValue()));
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/MultiSelectionField.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.event.FieldEvent;
import com.dlsc.formsfx.model.util.BindingMode;
import com.dlsc.formsfx.model.validators.ValidationResult;
import com.dlsc.formsfx.model.validators.Validator;
import com.dlsc.formsfx.view.controls.SimpleListViewControl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
/**
* This class provides an implementation of a {@link MultiSelectionField}
* allowing for multi-selection.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class MultiSelectionField extends SelectionField> {
/**
* A {@code MultiSelectionField} can have multiple items selected. These
* items are stored in a {@code ListProperty}.
*/
protected final ListProperty persistentSelection = new SimpleListProperty<>(FXCollections.observableArrayList());
protected final ListProperty selection = new SimpleListProperty<>(FXCollections.observableArrayList());
/**
* Every field contains a list of validators. The validators are limited to
* the ones that correspond to the field's type.
*/
protected final List>> validators = new ArrayList<>();
/**
* The constructor of {@code MultiSelectionField}.
*
* @param items
* The property that is used to store the items of the field.
* @param selection
* The list of indices of items that are to be selected.
*/
protected MultiSelectionField(ListProperty items, List selection) {
super(items);
// Add items to the selection, based on their indices. This also
// determines the persistent selection.
selection.forEach(i -> {
if (i < this.items.size() && i >= 0) {
this.selection.add(this.items.get(i));
}
});
persistentSelection.addAll(this.selection.getValue());
// The changed property is a binding that compares the persistent
// selection with the current selection. This means that a field is
// marked as changed until Field::persist or Field::reset are called
// or the selection is back to the persistent selection.
changed.bind(Bindings.createBooleanBinding(() -> !persistentSelection.equals(this.selection), this.selection, persistentSelection));
// Changes to the user input are reflected in the value only if the new
// user input is valid.
this.selection.addListener((observable, oldValue, newValue) -> validate());
// Clear the current selection and persistent selection whenever new
// items are added. The selection is built back up if it is passed along
// with the new list of items.
items.addListener((observable, oldValue, newValue) -> {
this.selection.clear();
persistentSelection.clear();
});
rendererSupplier = () -> new SimpleListViewControl<>();
}
/**
* Updates the list of available items to a new list, along with a
* pre-defined selection.
*
* @param newValue
* The new list of items.
* @param newSelection
* The new pre-defined selection.
*
* @return Returns the current field to allow for chaining.
*/
public MultiSelectionField items(List newValue, List newSelection) {
items.setAll(newValue);
newSelection.forEach(i -> selection.add(items.get(i)));
persistentSelection.setAll(selection.getValue());
return this;
}
/**
* Updates the list of available items to a new list, without a
* pre-defined selection.
*
* @param newValue
* The new list of items.
*
* @return Returns the current field to allow for chaining.
*/
public MultiSelectionField items(List newValue) {
return this.items(newValue, new ArrayList<>());
}
/**
* Sets the list of validators for the current field. This overrides all
* validators that have previously been added.
*
* @param newValue
* The validators that are to be used for validating this
* field.
*
* @return Returns the current field to allow for chaining.
*/
@SafeVarargs
public final MultiSelectionField validate(Validator>... newValue) {
validators.clear();
Collections.addAll(validators, newValue);
validate();
return this;
}
/**
* Adds the element at the given index to the current selection.
*
* @param index
* The index of the element to be selected.
*
* @return Returns the current field to allow for chaining.
*/
public MultiSelectionField select(int index) {
if (index < items.size() && index > -1 && !selection.contains(items.get(index))) {
selection.add(items.get(index));
}
return this;
}
/**
* Removes the element at the given index from the current selection.
*
* @param index
* The index of the element to be removed.
*
* @return Returns the current field to allow for chaining.
*/
public MultiSelectionField deselect(int index) {
if (index < items.size() && selection.contains(items.get(index))) {
selection.remove(items.get(index));
}
return this;
}
/**
* Binds the given items and selection property with the corresponding
* elements.
*
* @param itemsBinding
* The items property to be bound with.
*
* @param selectionBinding
* The selection property to be bound with.
*
* @return Returns the current field to allow for chaining.
*/
public MultiSelectionField bind(ListProperty itemsBinding, ListProperty selectionBinding) {
items.bindBidirectional(itemsBinding);
selection.bindBidirectional(selectionBinding);
return this;
}
/**
* Unbinds the given items and selection property with the corresponding
* elements.
*
* @param itemsBinding
* The items property to be unbound with.
*
* @param selectionBinding
* The selection property to be unbound with.
*
* @return Returns the current field to allow for chaining.
*/
public MultiSelectionField unbind(ListProperty itemsBinding, ListProperty selectionBinding) {
items.unbindBidirectional(itemsBinding);
selection.unbindBidirectional(selectionBinding);
return this;
}
/**
* {@inheritDoc}
*/
public void setBindingMode(BindingMode newValue) {
if (BindingMode.CONTINUOUS.equals(newValue)) {
selection.addListener(bindingModeListener);
} else {
selection.removeListener(bindingModeListener);
}
}
/**
* Stores the field's current selection in its persistent selection. This
* stores the user's changes in the model.
*/
public void persist() {
if (!isValid()) {
return;
}
persistentSelection.setAll(selection.getValue());
fireEvent(FieldEvent.fieldPersistedEvent(this));
}
/**
* Sets the field's current selection to its persistent selection, thus
* resetting any changes made by the user.
*/
public void reset() {
if (!hasChanged()) {
return;
}
selection.setAll(persistentSelection.getValue());
fireEvent(FieldEvent.fieldResetEvent(this));
}
/**
* {@inheritDoc}
*/
protected boolean validateRequired() {
return !isRequired() || (isRequired() && selection.size() > 0);
}
/**
* Validates a user input based on the field's selection and its validation
* rules. Also considers the {@code required} flag. This method directly
* updates the {@code valid} property.
*
* @return Returns whether the user selection is a valid value or not.
*/
public boolean validate() {
// Check all validation rules and collect any error messages.
List errorMessages = validators.stream()
.map(v -> v.validate(selection.getValue()))
.filter(r -> !r.getResult())
.map(ValidationResult::getErrorMessage)
.collect(Collectors.toList());
return super.validate(errorMessages);
}
public ObservableList getSelection() {
return selection.get();
}
public ListProperty selectionProperty() {
return selection;
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/NodeElement.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 - 2018 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import javafx.scene.Node;
/**
* @author Andres Almiray
*/
public class NodeElement extends Element {
protected N node;
public static NodeElement of(T node) {
return new NodeElement(node);
}
protected NodeElement(N node) {
if (node == null) {
throw new NullPointerException("Node argument must not be null");
}
this.node = node;
}
public N getNode() {
return node;
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/PasswordField.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.view.controls.SimplePasswordControl;
import com.dlsc.formsfx.view.controls.SimpleTextControl;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
* This class provides an implementation of a {@link Field} containing a
* {@code string} value intended for passwords.
*
* @author Andres Almiray
*/
public class PasswordField extends DataField {
/**
* The constructor of {@code PasswordField}.
*
* @param valueProperty
* The property that is used to store the current valid value
* of the field.
* @param persistentValueProperty
* The property that is used to store the latest persisted
* value of the field.
*/
protected PasswordField(SimpleStringProperty valueProperty, SimpleStringProperty persistentValueProperty) {
super(valueProperty, persistentValueProperty);
stringConverter = new AbstractStringConverter() {
@Override
public String fromString(String string) {
return string;
}
};
rendererSupplier = () -> new SimplePasswordControl();
userInput.set(stringConverter.toString(value.getValue()));
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/Section.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.util.TranslationService;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
* A section is a kind of group with more options. It can have a title and can
* be collapsed by the user. Sections represent a more semantically heavy
* grouping of elements, compared to groups.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class Section extends Group {
/**
* The title acts as a description for the group. It is always visible to
* the user and tells them how the contained elements are grouped.
*
* This property is translatable if a {@link TranslationService} is set on
* the containing form.
*/
protected final StringProperty titleKey = new SimpleStringProperty("");
protected final StringProperty title = new SimpleStringProperty("");
/**
* A group can optionally be collapsed.
*/
protected final BooleanProperty collapsed = new SimpleBooleanProperty(false);
/**
* Section is collapsible by default
*/
protected final BooleanProperty collapsible = new SimpleBooleanProperty(true);
/**
* {@inheritDoc}
*/
private Section(Element... elements) {
super(elements);
// Whenever the title's key changes, update the displayed value based
// on the new translation.
titleKey.addListener((observable, oldValue, newValue) -> title.setValue(translationService.translate(newValue)));
}
/**
* Creates a new section containing the given elements.
*
* @param elements
* The elements to be included in the section.
*
* @return Returns a new {@code Section}.
*/
public static Section of(Element... elements) {
return new Section(elements);
}
/**
* Sets the title property of the current group.
*
* @param newValue
* The new value for the title property. This can be the title
* itself or a key that is then used for translation.
*
* @see TranslationService
*
* @return Returns the current group to allow for chaining.
*/
public Section title(String newValue) {
if (isI18N()) {
titleKey.set(newValue);
} else {
title.set(newValue);
}
return this;
}
/**
* {@inheritDoc}
*/
protected void translate(TranslationService newValue) {
translationService = newValue;
if (!isI18N()) {
return;
}
if (titleKey.get() == null || titleKey.get().isEmpty()) {
titleKey.setValue(title.get());
} else {
title.setValue(translationService.translate(titleKey.get()));
}
elements.stream()
.filter(e -> e instanceof Field)
.map(e -> (Field) e)
.forEach(f -> f.translate(translationService));
}
/**
* Changes the collapsed state on a section.
*
* @param newValue
* The new value for the collapsed state.
*/
public Section collapse(boolean newValue) {
collapsed.setValue(newValue);
return this;
}
public BooleanProperty collapsedProperty() {
return collapsed;
}
public boolean isCollapsed() {
return collapsed.get();
}
public String getTitle() {
return title.get();
}
public StringProperty titleProperty() {
return title;
}
/**
* Changes the collapsible state on a section.
*
* @param newValue
* The new value for the collapsible state.
*/
public Section collapsible(boolean newValue) {
this.collapsible.set(newValue);
return this;
}
public boolean isCollapsible() {
return collapsible.get();
}
public BooleanProperty collapsibleProperty() {
return collapsible;
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/SelectionField.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import javafx.beans.property.ListProperty;
import javafx.collections.ObservableList;
import java.util.List;
/**
* {@code SelectionField} holds a list of values. Users can select one or more
* of these values, depending on the concrete type of the field.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public abstract class SelectionField> extends Field {
/**
* Stores a typed list of available items on this field.
*/
protected final ListProperty items;
/**
* Internal constructor for the {@code SelectionField} class. To create new
* elements, see the static factory methods in {@code Field}.
*
* @see Field::ofMultiSelectionType
* @see Field::ofSingleSelectionType
*
* @param items
* The list of available items on the field.
*/
protected SelectionField(ListProperty items) {
this.items = items;
}
/**
* Validates that the new field input matches the required condition.
*
* @return Returns whether the input matches the required condition.
*/
protected abstract boolean validateRequired();
/**
* Validates a user input based on the field's selection and its validation
* rules. Also considers the {@code required} flag. This method directly
* updates the {@code valid} property.
*
* This method should not be called directly but instead only be used in
* concrete subclasses.
*
* @param errorMessages
* A list of error messages based on the field's validators.
*
* @return Returns whether the user selection is a valid value or not.
*/
protected boolean validate(List errorMessages) {
if (!validateRequired()) {
if (isI18N() && requiredErrorKey.get() != null) {
this.errorMessageKeys.setAll(requiredErrorKey.get());
} else if (requiredError.get() != null) {
this.errorMessages.setAll(requiredError.get());
}
valid.set(false);
return false;
}
// Update the validation results with the current results. Listeners
// will handle the translation aspect.
if (isI18N()) {
errorMessageKeys.setAll(errorMessages);
} else {
this.errorMessages.setAll(errorMessages);
}
if (errorMessages.size() > 0) {
valid.set(false);
return false;
}
// If, and only if all above conditions have succeeded, the user input
// is considered valid.
valid.set(true);
return true;
}
public ObservableList getItems() {
return items.get();
}
public ListProperty itemsProperty() {
return items;
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/SingleSelectionField.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.event.FieldEvent;
import com.dlsc.formsfx.model.util.BindingMode;
import com.dlsc.formsfx.model.validators.ValidationResult;
import com.dlsc.formsfx.model.validators.Validator;
import com.dlsc.formsfx.view.controls.SimpleComboBoxControl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ListProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
/**
* This class provides an implementation of a {@link SelectionField} allowing
* only for single selection.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class SingleSelectionField extends SelectionField> {
/**
* A {@code SingleSelectionField} can only ever have one item selected.
* This item is stored in an {@code ObjectProperty}.
*/
protected final ObjectProperty persistentSelection = new SimpleObjectProperty<>();
protected final ObjectProperty selection = new SimpleObjectProperty<>();
/**
* Every field contains a list of validators. The validators are limited to
* the ones that correspond to the field's type.
*/
protected final List> validators = new ArrayList<>();
/**
* The constructor of {@code SingleSelectionField}.
*
* @param items
* The property that is used to store the items of the field.
* @param selection
* The index of the item that is to be selected.
*/
protected SingleSelectionField(ListProperty items, int selection) {
super(items);
// Sets the initial selection, based on an index. This also determines
// the persistent selection.
if (selection < items.size() && selection >= 0) {
this.selection.set(this.items.get(selection));
persistentSelection.setValue(this.selection.getValue());
}
// The changed property is a binding that compares the persistent
// selection with the current selection. This means that a field is
// marked as changed until Field::persist or Field::reset are called
// or the selection is back to the persistent selection.
changed.bind(Bindings.createBooleanBinding(() -> persistentSelection.get() == null ? this.selection.get() != null : !persistentSelection.get().equals(this.selection.get()), this.selection, persistentSelection));
// Changes to the user input are reflected in the value only if the new
// user input is valid.
this.selection.addListener((observable, oldValue, newValue) -> validate());
// Clear the current selection and persistent selection whenever new
// items are added. The selection is built back up if it is passed along
// with the new list of items.
items.addListener((observable, oldValue, newValue) -> {
this.selection.setValue(null);
persistentSelection.setValue(null);
});
rendererSupplier = () -> new SimpleComboBoxControl<>();
}
/**
* Updates the list of available items to a new list, along with a
* pre-defined selection.
*
* @param newValue
* The new list of items.
* @param newSelection
* The new pre-defined selection.
*
* @return Returns the current field to allow for chaining.
*/
public SingleSelectionField items(List newValue, int newSelection) {
items.setAll(newValue);
if (newSelection != -1) {
this.selection.setValue(items.get(newSelection));
this.persistentSelection.setValue(this.selection.getValue());
}
return this;
}
/**
* Updates the list of available items to a new list, without a
* pre-defined selection.
*
* @param newValue
* The new list of items.
*
* @return Returns the current field to allow for chaining.
*/
public SingleSelectionField items(List newValue) {
return this.items(newValue, -1);
}
/**
* Sets the list of validators for the current field. This overrides all
* validators that have previously been added.
*
* @param newValue
* The validators that are to be used for validating this
* field.
*
* @return Returns the current field to allow for chaining.
*/
@SafeVarargs
public final SingleSelectionField validate(Validator... newValue) {
validators.clear();
Collections.addAll(validators, newValue);
validate();
return this;
}
/**
* Sets the selection to the element at the given index.
*
* @param index
* The index of the element to be selected.
*
* @return Returns the current field to allow for chaining.
*/
public SingleSelectionField select(int index) {
if (index == -1) {
selection.setValue(null);
} else if (index < items.size() && index > -1 && (selection.get() == null || (selection.get() != null && !selection.get().equals(items.get(index))))) {
selection.setValue(items.get(index));
}
return this;
}
/**
* Removes the selection on the current field.
*
* @return Returns the current field to allow for chaining.
*/
public SingleSelectionField deselect() {
if (selection.get() != null) {
selection.setValue(null);
}
return this;
}
/**
* Binds the given items and selection property with the corresponding
* elements.
*
* @param itemsBinding
* The items property to be bound with.
*
* @param selectionBinding
* The selection property to be bound with.
*
* @return Returns the current field to allow for chaining.
*/
public SingleSelectionField bind(ListProperty itemsBinding, ObjectProperty selectionBinding) {
items.bindBidirectional(itemsBinding);
selection.bindBidirectional(selectionBinding);
return this;
}
/**
* Unbinds the given items and selection property with the corresponding
* elements.
*
* @param itemsBinding
* The items property to be unbound with.
*
* @param selectionBinding
* The selection property to be unbound with.
*
* @return Returns the current field to allow for chaining.
*/
public SingleSelectionField unbind(ListProperty itemsBinding, ObjectProperty selectionBinding) {
items.unbindBidirectional(itemsBinding);
selection.unbindBidirectional(selectionBinding);
return this;
}
/**
* {@inheritDoc}
*/
public void setBindingMode(BindingMode newValue) {
if (BindingMode.CONTINUOUS.equals(newValue)) {
selection.addListener(bindingModeListener);
} else {
selection.removeListener(bindingModeListener);
}
}
/**
* Stores the field's current value in its persistent value. This stores
* the user's changes in the model.
*/
public void persist() {
if (!isValid()) {
return;
}
persistentSelection.setValue(selection.getValue());
fireEvent(FieldEvent.fieldPersistedEvent(this));
}
/**
* Sets the field's current value to its persistent value, thus resetting
* any changes made by the user.
*/
public void reset() {
if (!hasChanged()) {
return;
}
selection.setValue(persistentSelection.getValue());
fireEvent(FieldEvent.fieldResetEvent(this));
}
/**
* {@inheritDoc}
*/
protected boolean validateRequired() {
return !isRequired() || (isRequired() && selection.get() != null);
}
/**
* Validates a user input based on the field's selection and its validation
* rules. Also considers the {@code required} flag. This method directly
* updates the {@code valid} property.
*
* @return Returns whether the user selection is a valid value or not.
*/
public boolean validate() {
// Check all validation rules and collect any error messages.
List errorMessages = validators.stream()
.map(v -> v.validate(selection.getValue()))
.filter(r -> !r.getResult())
.map(ValidationResult::getErrorMessage)
.collect(Collectors.toList());
return super.validate(errorMessages);
}
public V getSelection() {
return selection.get();
}
public ObjectProperty selectionProperty() {
return selection;
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/structure/StringField.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.view.controls.SimpleTextControl;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
* This class provides an implementation of a {@link Field} containing a
* {@code string} value.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class StringField extends DataField {
protected final BooleanProperty multiline = new SimpleBooleanProperty(false);
/**
* The constructor of {@code StringField}.
*
* @param valueProperty
* The property that is used to store the current valid value
* of the field.
* @param persistentValueProperty
* The property that is used to store the latest persisted
* value of the field.
*/
protected StringField(SimpleStringProperty valueProperty, SimpleStringProperty persistentValueProperty) {
super(valueProperty, persistentValueProperty);
stringConverter = new AbstractStringConverter() {
@Override
public String fromString(String string) {
return string;
}
};
rendererSupplier = () -> new SimpleTextControl();
userInput.set(stringConverter.toString(value.getValue()));
}
/**
* Sets whether the field is considered to be multiline or not.
*
* @param newValue
* The new value for the multiline property.
*
* @return Returns the current field to allow for chaining.
*/
public StringField multiline(boolean newValue) {
multiline.setValue(newValue);
return this;
}
public boolean isMultiline() {
return multiline.get();
}
public BooleanProperty multilineProperty() {
return multiline;
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/util/BindingMode.java
================================================
package com.dlsc.formsfx.model.util;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
/**
* Contains constants for the different ways to handle value bindings.
* {@code CONTINUOUS} persists field values upon any change, while
* {@code PERSISTENT} only persists values when explicitly requested.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public enum BindingMode {
CONTINUOUS,
PERSISTENT
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/util/ResourceBundleService.java
================================================
package com.dlsc.formsfx.model.util;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import java.util.ResourceBundle;
/**
* The ResourceBundleService is a concrete implementation of a
* {@link TranslationService} and uses ResourceBundles to perform translations.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class ResourceBundleService extends TranslationService {
private ResourceBundle rb;
public ResourceBundleService(ResourceBundle rb) {
this.rb = rb;
}
/**
* Change the resource bundle to use for this service. Notifies all
* listeners of the locale change.
*
* @param newValue
* The new resource bundle to use for translations.
*/
public void changeLocale(ResourceBundle newValue) {
if (newValue.equals(rb)) {
return;
}
rb = newValue;
notifyListeners();
}
/**
* {@inheritDoc}
*/
@Override
public String translate(String key) {
return rb.getString(key);
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/util/TranslationService.java
================================================
package com.dlsc.formsfx.model.util;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import java.util.ArrayList;
import java.util.List;
/**
* A general purpose translation service that is used to translate values into
* multiple locales based on keys. A concrete sample implementation is provided
* in the {@link ResourceBundleService}.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public abstract class TranslationService {
private List listeners = new ArrayList<>();
/**
* Looks up a key in the translation service and returns the translate string.
*
* @param key
* The key to use for the lookup.
*
* @return The translated string.
*/
public abstract String translate(String key);
public void addListener(Runnable listener) {
listeners.add(listener);
}
public void removeListener(Runnable listener) {
listeners.remove(listener);
}
/**
* Notifies all listeners of a locale change. Concrete implementations must
* call this method after every locale change.
*/
protected void notifyListeners() {
listeners.forEach(Runnable::run);
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/util/ValueTransformer.java
================================================
package com.dlsc.formsfx.model.util;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
/**
* A value transformer takes a string as an input and returns a parsed type.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
* @deprecated Use {@code StringConverter} instead.
* @see javafx.util.StringConverter
*/
@Deprecated
public interface ValueTransformer {
T transform(String input);
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/CustomValidator.java
================================================
package com.dlsc.formsfx.model.validators;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import java.util.function.Predicate;
/**
* A custom validator implementation of the root validator. This validator
* takes a generic object as an input parameter and allows users to perform
* any kind of validation.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class CustomValidator extends RootValidator {
private Predicate callback;
protected CustomValidator(Predicate callback, String errorMessage) {
super(errorMessage);
this.callback = callback;
}
public static CustomValidator forPredicate(Predicate callback, String errorMessage) {
return new CustomValidator<>(callback, errorMessage);
}
/**
* {@inheritDoc}
*/
@Override
public ValidationResult validate(T input) {
return createResult(callback.test(input));
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/DoubleRangeValidator.java
================================================
package com.dlsc.formsfx.model.validators;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
/**
* A DoubleRangeValidator checks if a double value is between a minimum and a
* maximum value.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class DoubleRangeValidator extends CustomValidator {
private DoubleRangeValidator(double min, double max, String errorMessage) {
super(input -> input >= min && input <= max, errorMessage);
}
/**
* Creates a DoubleRangeValidator with given lower and upper bounds.
*
* @param min
* The lower bound for the validation.
* @param max
* The upper bound for the validation.
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @throws IllegalArgumentException
* Thrown if the maximum is not larger than or equal to the
* minimum.
*
* @return Returns a new DoubleRangeValidator.
*/
public static DoubleRangeValidator between(double min, double max, String errorMessage) {
if (min > max) {
throw new IllegalArgumentException("Minimum must not be larger than maximum.");
}
return new DoubleRangeValidator(min, max, errorMessage);
}
/**
* Creates a DoubleRangeValidator with a given lower bound.
*
* @param min
* The lower bound for the validation.
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @return Returns a new DoubleRangeValidator.
*/
public static DoubleRangeValidator atLeast(double min, String errorMessage) {
return new DoubleRangeValidator(min, Double.MAX_VALUE, errorMessage);
}
/**
* Creates a DoubleRangeValidator with a given upper bound.
*
* @param max
* The upper bound for the validation.
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @return Returns a new DoubleRangeValidator.
*/
public static DoubleRangeValidator upTo(double max, String errorMessage) {
return new DoubleRangeValidator(Double.MIN_VALUE, max, errorMessage);
}
/**
* Creates a DoubleRangeValidator with a given lower and upper bound, which
* are equal.
*
* @param value
* The lower and upper bound for the validation.
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @return Returns a new DoubleRangeValidator.
*/
public static DoubleRangeValidator exactly(double value, String errorMessage) {
return new DoubleRangeValidator(value, value, errorMessage);
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/IntegerRangeValidator.java
================================================
package com.dlsc.formsfx.model.validators;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
/**
* A IntegerRangeValidator checks if an integer value is between a minimum and a
* maximum value.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class IntegerRangeValidator extends CustomValidator {
private IntegerRangeValidator(int min, int max, String errorMessage) {
super(input -> input >= min && input <= max, errorMessage);
}
/**
* Creates an IntegerRangeValidator with given lower and upper bounds.
*
* @param min
* The lower bound for the validation.
* @param max
* The upper bound for the validation.
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @throws IllegalArgumentException
* Thrown if the maximum is not larger than or equal to the
* minimum.
*
* @return Returns a new IntegerRangeValidator.
*/
public static IntegerRangeValidator between(int min, int max, String errorMessage) {
if (min > max) {
throw new IllegalArgumentException("Minimum must not be larger than maximum.");
}
return new IntegerRangeValidator(min, max, errorMessage);
}
/**
* Creates an IntegerRangeValidator with a given lower bound.
*
* @param min
* The lower bound for the validation.
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @return Returns a new IntegerRangeValidator.
*/
public static IntegerRangeValidator atLeast(int min, String errorMessage) {
return new IntegerRangeValidator(min, Integer.MAX_VALUE, errorMessage);
}
/**
* Creates an IntegerRangeValidator with a given upper bound.
*
* @param max
* The upper bound for the validation.
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @return Returns a new IntegerRangeValidator.
*/
public static IntegerRangeValidator upTo(int max, String errorMessage) {
return new IntegerRangeValidator(Integer.MIN_VALUE, max, errorMessage);
}
/**
* Creates a IntegerRangeValidator with a given lower and upper bound,
* which are equal.
*
* @param value
* The lower and upper bound for the validation.
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @return Returns a new IntegerRangeValidator.
*/
public static IntegerRangeValidator exactly(int value, String errorMessage) {
return new IntegerRangeValidator(value, value, errorMessage);
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/RegexValidator.java
================================================
package com.dlsc.formsfx.model.validators;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* A RegexValidator checks if a given input matches a regular expression.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class RegexValidator extends RootValidator {
private Pattern pattern;
private RegexValidator(String pattern, String errorMessage) throws PatternSyntaxException {
super(errorMessage);
this.pattern = Pattern.compile(pattern);
}
/**
* Creates a RegexValidator with a custom pattern.
*
* @param pattern
* The pattern to use for the validation. Must be a valid
* RegEx.
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @throws PatternSyntaxException
* Thrown if the given pattern is not a valid RegEx.
*
* @return Returns a new RegexValidator.
*/
public static RegexValidator forPattern(String pattern, String errorMessage) {
return new RegexValidator(pattern, errorMessage);
}
/**
* Creates a RegexValidator for email addresses.
*
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @throws PatternSyntaxException
* Thrown if the given pattern is not a valid RegEx.
*
* @return Returns a new RegexValidator.
*/
public static RegexValidator forEmail(String errorMessage) {
return new RegexValidator("^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])$", errorMessage);
}
/**
* Creates a RegexValidator for URLs.
*
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @throws PatternSyntaxException
* Thrown if the given pattern is not a valid RegEx.
*
* @return Returns a new RegexValidator.
*/
public static RegexValidator forURL(String errorMessage) {
return new RegexValidator("(https?:\\/\\/(?:www\\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|www\\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\\.[^\\s]{2,}|https?:\\/\\/(?:www\\.|(?!www))[a-zA-Z0-9]\\.[^\\s]{2,}|www\\.[a-zA-Z0-9]\\.[^\\s]{2,})", errorMessage);
}
/**
* Creates a RegexValidator for alphanumeric inputs.
*
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @throws PatternSyntaxException
* Thrown if the given pattern is not a valid RegEx.
*
* @return Returns a new RegexValidator.
*/
public static RegexValidator forAlphaNumeric(String errorMessage) {
return new RegexValidator("^[a-zA-Z0-9]*$", errorMessage);
}
/**
* {@inheritDoc}
*/
@Override
public ValidationResult validate(String input) {
return createResult(pattern.matcher(input).matches());
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/RootValidator.java
================================================
package com.dlsc.formsfx.model.validators;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
/**
* The RootValidator contains helper methods for concrete validator implementations.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
abstract class RootValidator implements Validator {
private String errorMessage;
protected RootValidator(String errorMessage) {
this.errorMessage = errorMessage;
}
/**
* Creates a {@link ValidationResult} based on the validation result.
*
* @param result
* The result of the validation.
*
* @return Returns a new ValidationResult containing result and message.
*/
protected ValidationResult createResult(boolean result) {
return new ValidationResult(result, errorMessage);
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/SelectionLengthValidator.java
================================================
package com.dlsc.formsfx.model.validators;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import javafx.collections.ObservableList;
/**
* A SelectionLengthValidator checks if a selection list's length is between a
* minimum and a maximum value.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class SelectionLengthValidator extends CustomValidator> {
private SelectionLengthValidator(int min, int max, String errorMessage) {
super(input -> input.size() >= min && input.size() <= max, errorMessage);
}
/**
* Creates an SelectionLengthValidator with given lower and upper bounds.
*
* @param min
* The lower bound for the validation.
* @param max
* The upper bound for the validation.
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @throws IllegalArgumentException
* Thrown if the minimum is a negative number.
*
* @return Returns a new SelectionLengthValidator.
*/
public static SelectionLengthValidator between(int min, int max, String errorMessage) {
if (min < 0) {
throw new IllegalArgumentException("Minimum string length cannot be negative.");
} else if (min > max) {
throw new IllegalArgumentException("Minimum must not be larger than maximum.");
}
return new SelectionLengthValidator<>(min, max, errorMessage);
}
/**
* Creates an SelectionLengthValidator with a given lower bound.
*
* @param min
* The lower bound for the validation.
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @throws IllegalArgumentException
* Thrown if the minimum is a negative number.
*
* @return Returns a new SelectionLengthValidator.
*/
public static SelectionLengthValidator atLeast(int min, String errorMessage) {
if (min < 0) {
throw new IllegalArgumentException("Minimum string length cannot be negative.");
}
return new SelectionLengthValidator<>(min, Integer.MAX_VALUE, errorMessage);
}
/**
* Creates an SelectionLengthValidator with a given upper bound.
*
* @param max
* The upper bound for the validation.
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @return Returns a new SelectionLengthValidator.
*/
public static SelectionLengthValidator upTo(int max, String errorMessage) {
return new SelectionLengthValidator<>(0, max, errorMessage);
}
/**
* Creates a SelectionLengthValidator with a given lower and upper bound,
* which are equal.
*
* @param value
* The lower and upper bound for the validation.
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @return Returns a new SelectionLengthValidator.
*/
public static SelectionLengthValidator exactly(int value, String errorMessage) {
return new SelectionLengthValidator<>(value, value, errorMessage);
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/StringLengthValidator.java
================================================
package com.dlsc.formsfx.model.validators;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
/**
* A StringLengthValidator checks if a string value's length is between a
* minimum and a maximum value.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class StringLengthValidator extends CustomValidator {
private StringLengthValidator(int min, int max, String errorMessage) {
super(input -> input.length() >= min && input.length() <= max, errorMessage);
}
/**
* Creates an StringLengthValidator with given lower and upper bounds.
*
* @param min
* The lower bound for the validation.
* @param max
* The upper bound for the validation.
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @throws IllegalArgumentException
* Thrown if the minimum is a negative number.
*
* @return Returns a new StringLengthValidator.
*/
public static StringLengthValidator between(int min, int max, String errorMessage) {
if (min < 0) {
throw new IllegalArgumentException("Minimum string length cannot be negative.");
} else if (min > max) {
throw new IllegalArgumentException("Minimum must not be larger than maximum.");
}
return new StringLengthValidator(min, max, errorMessage);
}
/**
* Creates an StringLengthValidator with a given lower bound.
*
* @param min
* The lower bound for the validation.
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @throws IllegalArgumentException
* Thrown if the minimum is a negative number.
*
* @return Returns a new StringLengthValidator.
*/
public static StringLengthValidator atLeast(int min, String errorMessage) {
if (min < 0) {
throw new IllegalArgumentException("Minimum string length cannot be negative.");
}
return new StringLengthValidator(min, Integer.MAX_VALUE, errorMessage);
}
/**
* Creates an StringLengthValidator with a given upper bound.
*
* @param max
* The upper bound for the validation.
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @return Returns a new StringLengthValidator.
*/
public static StringLengthValidator upTo(int max, String errorMessage) {
return new StringLengthValidator(0, max, errorMessage);
}
/**
* Creates a StringLengthValidator with a given lower and upper bound, which
* are equal.
*
* @param value
* The lower and upper bound for the validation.
* @param errorMessage
* The error message that is returned if the validation fails.
*
* @return Returns a new StringLengthValidator.
*/
public static StringLengthValidator exactly(int value, String errorMessage) {
return new StringLengthValidator(value, value, errorMessage);
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/ValidationResult.java
================================================
package com.dlsc.formsfx.model.validators;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
/**
* A ValidationResult is the description of the result of a validation. It
* contains the actual result, as well as possibly an error message.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class ValidationResult {
private boolean result;
private String errorMessage;
ValidationResult(boolean result, String errorMessage) {
this.result = result;
// The error message is only included in the result if the validation
// failed.
this.errorMessage = !result ? errorMessage : null;
}
public String getErrorMessage() {
return errorMessage;
}
public boolean getResult() {
return result;
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/model/validators/Validator.java
================================================
package com.dlsc.formsfx.model.validators;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
/**
* A validator is used to validate a generic input for a specific syntax or
* semantic.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public interface Validator {
ValidationResult validate(T input);
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleBooleanControl.java
================================================
package com.dlsc.formsfx.view.controls;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.BooleanField;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
/**
* This class provides the base implementation for a simple control to edit
* boolean values.
*
* @author Rinesch Murugathas
* @author Sacha Schmid
*/
public class SimpleBooleanControl extends SimpleControl {
/**
* - fieldLabel is the container that displays the label property of the
* field.
* - checkBox is the editable checkbox to set user input.
* - container holds the checkbox so that it can be styled properly.
*/
protected Label fieldLabel;
protected CheckBox checkBox;
protected VBox container;
/**
* {@inheritDoc}
*/
@Override
public void initializeParts() {
super.initializeParts();
getStyleClass().add("simple-boolean-control");
fieldLabel = new Label(field.labelProperty().getValue());
checkBox = new CheckBox();
container = new VBox();
checkBox.setSelected(field.getValue());
}
/**
* {@inheritDoc}
*/
@Override
public void layoutParts() {
super.layoutParts();
int columns = field.getSpan();
container.getChildren().add(checkBox);
Node labelDescription = field.getLabelDescription();
Node valueDescription = field.getValueDescription();
add(fieldLabel, 0, 0, 2, 1);
if (labelDescription != null) {
GridPane.setValignment(labelDescription, VPos.TOP);
add(labelDescription, 0, 1, 2, 1);
}
add(container, 2, 0, columns - 2, 1);
if (valueDescription != null) {
GridPane.setValignment(valueDescription, VPos.TOP);
add(valueDescription, 2, 1, columns - 2, 1);
}
}
/**
* {@inheritDoc}
*/
@Override
public void setupBindings() {
super.setupBindings();
checkBox.disableProperty().bind(field.editableProperty().not());
fieldLabel.textProperty().bind(field.labelProperty());
}
/**
* {@inheritDoc}
*/
@Override
public void setupValueChangedListeners() {
super.setupValueChangedListeners();
field.userInputProperty().addListener((observable, oldValue, newValue) -> checkBox.setSelected(Boolean.parseBoolean(field.getUserInput())));
field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(checkBox));
field.tooltipProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(checkBox));
checkBox.focusedProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(checkBox));
}
/**
* {@inheritDoc}
*/
@Override
public void setupEventHandlers() {
setOnMouseEntered(event -> toggleTooltip(checkBox));
setOnMouseExited(event -> toggleTooltip(checkBox));
checkBox.selectedProperty().addListener((observable, oldValue, newValue) -> field.userInputProperty().setValue(String.valueOf(newValue)));
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleCheckBoxControl.java
================================================
package com.dlsc.formsfx.view.controls;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.MultiSelectionField;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import java.util.ArrayList;
import java.util.List;
/**
* This class provides the base implementation for a simple control to edit
* checkbox values.
*
* @author Rinesch Murugathas
* @author Sacha Schmid
*/
public class SimpleCheckBoxControl extends SimpleControl> {
/**
* - The fieldLabel is the container that displays the label property of
* the field.
* - The checkboxes list contains all the checkboxes to display.
* - The box is a VBox holding all box.
*/
protected Label fieldLabel;
protected final List checkboxes = new ArrayList<>();
protected VBox box;
/**
* {@inheritDoc}
*/
@Override
public void initializeParts() {
super.initializeParts();
getStyleClass().add("simple-checkbox-control");
fieldLabel = new Label(field.labelProperty().getValue());
box = new VBox();
createCheckboxes();
}
/**
* {@inheritDoc}
*/
@Override
public void layoutParts() {
super.layoutParts();
int columns = field.getSpan();
box.setSpacing(5);
Node labelDescription = field.getLabelDescription();
Node valueDescription = field.getValueDescription();
add(fieldLabel, 0, 0, 2, 1);
if (labelDescription != null) {
GridPane.setValignment(labelDescription, VPos.TOP);
add(labelDescription, 0, 1, 2, 1);
}
add(box, 2, 0, columns - 2, 1);
if (valueDescription != null) {
GridPane.setValignment(valueDescription, VPos.TOP);
add(valueDescription, 2, 1, columns - 2, 1);
}
}
/**
* {@inheritDoc}
*/
@Override
public void setupBindings() {
super.setupBindings();
fieldLabel.textProperty().bind(field.labelProperty());
setupCheckboxBindings();
}
/**
* {@inheritDoc}
*/
@Override
public void setupValueChangedListeners() {
super.setupValueChangedListeners();
field.itemsProperty().addListener((observable, oldValue, newValue) -> {
createCheckboxes();
setupCheckboxBindings();
setupCheckboxEventHandlers();
});
field.selectionProperty().addListener((observable, oldValue, newValue) -> {
for (int i = 0; i < checkboxes.size(); i++) {
checkboxes.get(i).setSelected(field.getSelection().contains(field.getItems().get(i)));
}
});
field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(box, checkboxes.get(checkboxes.size() - 1)));
field.tooltipProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(box, checkboxes.get(checkboxes.size() - 1)));
for (int i = 0; i < checkboxes.size(); i++) {
checkboxes.get(i).focusedProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(box, checkboxes.get(checkboxes.size() - 1)));
}
}
/**
* {@inheritDoc}
*/
@Override
public void setupEventHandlers() {
box.setOnMouseEntered(event -> toggleTooltip(box, checkboxes.get(checkboxes.size() - 1)));
box.setOnMouseExited(event -> toggleTooltip(box, checkboxes.get(checkboxes.size() - 1)));
setupCheckboxEventHandlers();
}
/**
* This method creates box and adds them to checkboxes and is
* used when the itemsProperty on the field changes.
*/
protected void createCheckboxes() {
box.getChildren().clear();
checkboxes.clear();
for (int i = 0; i < field.getItems().size(); i++) {
CheckBox cb = new CheckBox();
cb.setText(field.getItems().get(i).toString());
cb.setSelected(field.getSelection().contains(field.getItems().get(i)));
checkboxes.add(cb);
}
box.getChildren().addAll(checkboxes);
}
/**
* Sets up bindings for all checkboxes.
*/
protected void setupCheckboxBindings() {
for (CheckBox checkbox : checkboxes) {
checkbox.disableProperty().bind(field.editableProperty().not());
}
}
/**
* Sets up event handlers for all checkboxes.
*/
protected void setupCheckboxEventHandlers() {
for (int i = 0; i < checkboxes.size(); i++) {
final int j = i;
checkboxes.get(i).setOnAction(event -> {
if (checkboxes.get(j).isSelected()) {
field.select(j);
} else {
field.deselect(j);
}
});
}
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleComboBoxControl.java
================================================
package com.dlsc.formsfx.view.controls;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.SingleSelectionField;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
/**
* This class provides the base implementation for a simple control to edit
* combobox values.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class SimpleComboBoxControl extends SimpleControl> {
/**
* - The fieldLabel is the container that displays the label property of
* the field.
* - The comboBox is the container that displays the values in the
* ComboBox.
* - The readOnlyLabel is used to show the current selection in read only.
* - The stack is a StackPane to hold the field and read only label.
*/
protected Label fieldLabel;
protected ComboBox comboBox;
protected Label readOnlyLabel;
protected StackPane stack;
/**
* {@inheritDoc}
*/
@Override
public void initializeParts() {
super.initializeParts();
getStyleClass().add("simple-select-control");
fieldLabel = new Label(field.labelProperty().getValue());
readOnlyLabel = new Label();
stack = new StackPane();
comboBox = new ComboBox<>(field.getItems());
comboBox.getSelectionModel().select(field.getItems().indexOf(field.getSelection()));
}
/**
* {@inheritDoc}
*/
@Override
public void layoutParts() {
super.layoutParts();
int columns = field.getSpan();
readOnlyLabel.getStyleClass().add("read-only-label");
comboBox.setMaxWidth(Double.MAX_VALUE);
comboBox.setVisibleRowCount(4);
stack.setAlignment(Pos.CENTER_LEFT);
stack.getChildren().addAll(comboBox, readOnlyLabel);
Node labelDescription = field.getLabelDescription();
Node valueDescription = field.getValueDescription();
add(fieldLabel, 0, 0, 2, 1);
if (labelDescription != null) {
GridPane.setValignment(labelDescription, VPos.TOP);
add(labelDescription, 0, 1, 2, 1);
}
add(stack, 2, 0, columns - 2, 1);
if (valueDescription != null) {
GridPane.setValignment(valueDescription, VPos.TOP);
add(valueDescription, 2, 1, columns - 2, 1);
}
}
/**
* {@inheritDoc}
*/
@Override
public void setupBindings() {
super.setupBindings();
fieldLabel.textProperty().bind(field.labelProperty());
comboBox.visibleProperty().bind(field.editableProperty());
readOnlyLabel.visibleProperty().bind(field.editableProperty().not());
readOnlyLabel.textProperty().bind(comboBox.valueProperty().asString());
}
/**
* {@inheritDoc}
*/
@Override
public void setupValueChangedListeners() {
super.setupValueChangedListeners();
field.itemsProperty().addListener((observable, oldValue, newValue) -> comboBox.setItems(field.getItems()));
field.selectionProperty().addListener((observable, oldValue, newValue) -> {
if (field.getSelection() != null) {
comboBox.getSelectionModel().select(field.getItems().indexOf(field.getSelection()));
} else {
comboBox.getSelectionModel().clearSelection();
}
});
field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(comboBox));
field.tooltipProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(comboBox));
comboBox.focusedProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(comboBox));
}
/**
* {@inheritDoc}
*/
@Override
public void setupEventHandlers() {
comboBox.setOnMouseEntered(event -> toggleTooltip(comboBox));
comboBox.setOnMouseExited(event -> toggleTooltip(comboBox));
comboBox.valueProperty().addListener((observable, oldValue, newValue) -> field.select(comboBox.getSelectionModel().getSelectedIndex()));
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleControl.java
================================================
package com.dlsc.formsfx.view.controls;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.Field;
import com.dlsc.formsfx.view.util.ViewMixin;
import javafx.collections.ListChangeListener;
import javafx.css.PseudoClass;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Control;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
/**
* This class provides a base for general purpose FormsFX controls.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public abstract class SimpleControl extends GridPane implements ViewMixin {
/**
* This is the Field that is used for binding and update styling changes.
*/
protected F field;
/**
* Tooltip to hold the error message.
*/
protected Tooltip tooltip;
/**
* Pseudo classes for state changes.
*/
protected static final PseudoClass REQUIRED_CLASS = PseudoClass.getPseudoClass("required");
protected static final PseudoClass INVALID_CLASS = PseudoClass.getPseudoClass("invalid");
protected static final PseudoClass CHANGED_CLASS = PseudoClass.getPseudoClass("changed");
protected static final PseudoClass DISABLED_CLASS = PseudoClass.getPseudoClass("disabled");
public void setField(F field) {
if (this.field != null) {
throw new IllegalStateException("Cannot change a control's field once set.");
}
this.field = field;
init();
}
/**
* {@inheritDoc}
*/
@Override
public void initializeParts() {
getStyleClass().add("simple-control");
tooltip = new Tooltip();
tooltip.getStyleClass().add("simple-tooltip");
getStyleClass().addAll(field.getStyleClass());
updateStyle(INVALID_CLASS, !field.isValid());
updateStyle(REQUIRED_CLASS, field.isRequired());
updateStyle(CHANGED_CLASS, field.hasChanged());
updateStyle(DISABLED_CLASS, !field.isEditable());
}
/**
* {@inheritDoc}
*/
@Override
public void layoutParts() {
setAlignment(Pos.CENTER_LEFT);
int columns = field.getSpan();
for (int i = 0; i < columns; i++) {
ColumnConstraints colConst = new ColumnConstraints();
colConst.setPercentWidth(100.0 / columns);
getColumnConstraints().add(colConst);
}
}
/**
* {@inheritDoc}
*/
public void setupBindings() {
idProperty().bind(field.idProperty());
}
/**
* {@inheritDoc}
*/
@Override
public void setupValueChangedListeners() {
field.validProperty().addListener((observable, oldValue, newValue) -> updateStyle(INVALID_CLASS, !newValue));
field.requiredProperty().addListener((observable, oldValue, newValue) -> updateStyle(REQUIRED_CLASS, newValue));
field.changedProperty().addListener((observable, oldValue, newValue) -> updateStyle(CHANGED_CLASS, newValue));
field.editableProperty().addListener((observable, oldValue, newValue) -> updateStyle(DISABLED_CLASS, !newValue));
field.getStyleClass().addListener((ListChangeListener) c -> {
while (c.next()) {
if (c.wasRemoved()) {
getStyleClass().removeAll(c.getRemoved());
}
if (c.wasAdded()) {
getStyleClass().addAll(c.getAddedSubList());
}
}
});
}
/**
* Sets the error message as tooltip for the matching control and shows
* them below the same control.
*
* @param reference
* The control which gets the tooltip.
*/
protected void toggleTooltip(Node reference) {
this.toggleTooltip(reference, (Control) reference);
}
/**
* Sets the error message as tooltip for the matching control.
*
* @param below
* The control needed for positioning the tooltip.
* @param reference
* The control which gets the tooltip.
*/
protected void toggleTooltip(Node reference, Control below) {
String fieldTooltip = field.getTooltip();
if ((reference.isFocused() || reference.isHover()) && (!fieldTooltip.equals("") || field.getErrorMessages().size() > 0)) {
tooltip.setText((!fieldTooltip.equals("") ? fieldTooltip + "\n" : "") + String.join("\n", field.getErrorMessages()));
if (tooltip.isShowing()) {
return;
}
Point2D p = below.localToScene(0.0, 0.0);
tooltip.show(
getScene().getWindow(),
p.getX() + getScene().getX() + getScene().getWindow().getX(),
p.getY() + getScene().getY() + getScene().getWindow().getY() + below.getHeight() + 5
);
} else {
tooltip.hide();
}
}
/**
* Sets the css style for the defined properties.
*
* @param pseudo
* The CSS pseudo class to toggle.
* @param newValue
* Determines whether the CSS class should be applied.
*/
protected void updateStyle(PseudoClass pseudo, boolean newValue) {
pseudoClassStateChanged(pseudo, newValue);
}
/**
* Adds a style class to the control.
* @param name of the style class to be added to the control
*/
public void addStyleClass(String name) {
getStyleClass().add(name);
}
/**
* Removes a style class from the control.
* @param name of the class to be removed from the control
*/
public void removeStyleClass(String name) {
getStyleClass().remove(name);
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleDateControl.java
================================================
package com.dlsc.formsfx.view.controls;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 - 2018 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.DateField;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
/**
* This class provides a specific implementation to edit date values.
*
* @author Tomasz Krzemiński
*/
public class SimpleDateControl extends SimpleControl {
protected Label fieldLabel;
protected DatePicker picker;
protected Label readOnlyLabel;
protected StackPane stack;
@Override
public void initializeParts() {
super.initializeParts();
stack = new StackPane();
fieldLabel = new Label();
readOnlyLabel = new Label();
picker = new DatePicker();
picker.setEditable(true);
}
@Override
public void layoutParts() {
super.layoutParts();
int columns = field.getSpan();
readOnlyLabel.getStyleClass().add("read-only-label");
picker.setMaxWidth(Double.MAX_VALUE);
stack.setAlignment(Pos.CENTER_LEFT);
stack.getChildren().addAll(picker, readOnlyLabel);
Node labelDescription = field.getLabelDescription();
Node valueDescription = field.getValueDescription();
add(fieldLabel, 0, 0, 2, 1);
if (labelDescription != null) {
GridPane.setValignment(labelDescription, VPos.TOP);
add(labelDescription, 0, 1, 2, 1);
}
add(stack, 2, 0, columns - 2, 1);
if (valueDescription != null) {
GridPane.setValignment(valueDescription, VPos.TOP);
add(valueDescription, 2, 1, columns - 2, 1);
}
}
@Override
public void setupBindings() {
super.setupBindings();
picker.disableProperty().bind(field.editableProperty().not());
readOnlyLabel.visibleProperty().bind(field.editableProperty().not());
picker.getEditor().textProperty().bindBidirectional(field.userInputProperty());
fieldLabel.textProperty().bind(field.labelProperty());
picker.promptTextProperty().bind(field.placeholderProperty());
picker.managedProperty().bind(picker.visibleProperty());
}
/**
* {@inheritDoc}
*/
@Override
public void setupEventHandlers() {
picker.getEditor().textProperty().addListener((observable, oldValue, newValue) -> field.userInputProperty().setValue(String.valueOf(newValue)));
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleDoubleControl.java
================================================
package com.dlsc.formsfx.view.controls;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.DoubleField;
import javafx.scene.control.SpinnerValueFactory;
/**
* This class provides a specific implementation to edit double values.
*
* @author Rinesch Murugathas
* @author Sacha Schmid
*/
public class SimpleDoubleControl extends SimpleNumberControl {
/**
* {@inheritDoc}
*/
@Override
public void initializeParts() {
super.initializeParts();
getStyleClass().add("simple-double-control");
editableSpinner.setValueFactory(new SpinnerValueFactory.DoubleSpinnerValueFactory(-Double.MAX_VALUE, Double.MAX_VALUE, field.getValue()));
}
/**
* {@inheritDoc}
*/
@Override
public void setupValueChangedListeners() {
super.setupValueChangedListeners();
field.tooltipProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableSpinner));
field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableSpinner));
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleIntegerControl.java
================================================
package com.dlsc.formsfx.view.controls;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.IntegerField;
import javafx.scene.control.SpinnerValueFactory;
/**
* This class provides a specific implementation to edit integer values.
*
* @author Rinesch Murugathas
* @author Sacha Schmid
*/
public class SimpleIntegerControl extends SimpleNumberControl {
/**
* {@inheritDoc}
*/
@Override
public void initializeParts() {
super.initializeParts();
getStyleClass().addAll("simple-integer-control");
editableSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(Integer.MIN_VALUE, Integer.MAX_VALUE, field.getValue()));
}
/**
* {@inheritDoc}
*/
@Override
public void setupValueChangedListeners() {
super.setupValueChangedListeners();
field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableSpinner));
field.tooltipProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableSpinner));
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleListViewControl.java
================================================
package com.dlsc.formsfx.view.controls;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.MultiSelectionField;
import javafx.collections.ListChangeListener;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.layout.GridPane;
/**
* This class provides the base implementation for a simple control to edit
* listview values.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class SimpleListViewControl extends SimpleControl> {
/**
* - The fieldLabel is the container that displays the label property of
* the field.
* - The listView is the container that displays list values.
*/
protected Label fieldLabel;
protected ListView listView = new ListView<>();
/**
* The flag used for setting the selection properly.
*/
protected boolean preventUpdate;
/**
* {@inheritDoc}
*/
@Override
public void initializeParts() {
super.initializeParts();
getStyleClass().add("simple-listview-control");
fieldLabel = new Label(field.labelProperty().getValue());
listView.setItems(field.getItems());
listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
for (int i = 0; i < field.getItems().size(); i++) {
if (field.getSelection().contains(field.getItems().get(i))) {
listView.getSelectionModel().select(i);
} else {
listView.getSelectionModel().clearSelection(i);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void layoutParts() {
super.layoutParts();
int columns = field.getSpan();
listView.setPrefHeight(200);
Node labelDescription = field.getLabelDescription();
Node valueDescription = field.getValueDescription();
add(fieldLabel, 0, 0, 2, 1);
if (labelDescription != null) {
GridPane.setValignment(labelDescription, VPos.TOP);
add(labelDescription, 0, 1, 2, 1);
}
add(listView, 2, 0, columns - 2, 1);
if (valueDescription != null) {
GridPane.setValignment(valueDescription, VPos.TOP);
add(valueDescription, 2, 1, columns - 2, 1);
}
}
/**
* {@inheritDoc}
*/
@Override
public void setupBindings() {
super.setupBindings();
fieldLabel.textProperty().bind(field.labelProperty());
listView.disableProperty().bind(field.editableProperty().not());
}
/**
* {@inheritDoc}
*/
@Override
public void setupValueChangedListeners() {
super.setupValueChangedListeners();
field.itemsProperty().addListener((observable, oldValue, newValue) -> listView.setItems(field.getItems()));
field.selectionProperty().addListener((observable, oldValue, newValue) -> {
if (preventUpdate) {
return;
}
preventUpdate = true;
for (int i = 0; i < field.getItems().size(); i++) {
if (field.getSelection().contains(field.getItems().get(i))) {
listView.getSelectionModel().select(i);
} else {
listView.getSelectionModel().clearSelection(i);
}
}
preventUpdate = false;
});
field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(listView));
field.tooltipProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(listView));
listView.focusedProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(listView));
}
/**
* {@inheritDoc}
*/
@Override
public void setupEventHandlers() {
listView.setOnMouseEntered(event -> toggleTooltip(listView));
listView.setOnMouseExited(event -> toggleTooltip(listView));
listView.getSelectionModel().getSelectedIndices().addListener((ListChangeListener) c -> {
if (preventUpdate) {
return;
}
preventUpdate = true;
for (int i = 0; i < listView.getItems().size(); i++) {
if (listView.getSelectionModel().getSelectedIndices().contains(i)) {
field.select(i);
} else {
field.deselect(i);
}
}
preventUpdate = false;
});
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleNumberControl.java
================================================
package com.dlsc.formsfx.view.controls;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.DataField;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.Spinner;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
/**
* This class provides the base implementation for a simple control to edit
* numerical elements.
*
* @author Rinesch Murugathas
* @author Sacha Schmid
*/
public abstract class SimpleNumberControl extends SimpleControl {
/**
* This StackPane is needed for achieving the readonly effect by putting
* the {@code readOnlyLabel} over the {@code editableSpinner} on the change
* of the {@code visibleProperty}.
*/
protected StackPane stack;
/**
* - The fieldLabel is the container that displays the label property of
* the field.
* - The editableSpinner is a Spinner for setting numerical values.
* - The readOnlyLabel is the label to put over editableSpinner.
*/
protected Label fieldLabel;
protected Spinner editableSpinner;
protected Label readOnlyLabel;
/**
* {@inheritDoc}
*/
@Override
public void initializeParts() {
super.initializeParts();
stack = new StackPane();
fieldLabel = new Label();
readOnlyLabel = new Label();
editableSpinner = new Spinner<>();
editableSpinner.setEditable(true);
}
/**
* {@inheritDoc}
*/
@Override
public void layoutParts() {
super.layoutParts();
readOnlyLabel.getStyleClass().add("read-only-label");
stack.getChildren().addAll(editableSpinner, readOnlyLabel);
stack.setAlignment(Pos.CENTER_LEFT);
editableSpinner.setMaxWidth(Double.MAX_VALUE);
Node labelDescription = field.getLabelDescription();
Node valueDescription = field.getValueDescription();
int columns = field.getSpan();
if (columns < 3) {
int rowIndex = 0;
add(fieldLabel, 0, rowIndex++, columns, 1);
if (labelDescription != null) {
GridPane.setValignment(labelDescription, VPos.TOP);
add(labelDescription, 0, rowIndex++, columns, 1);
}
add(stack, 0, rowIndex++, columns, 1);
if (valueDescription != null) {
GridPane.setValignment(valueDescription, VPos.TOP);
add(valueDescription, 0, rowIndex, columns, 1);
}
} else {
add(fieldLabel, 0, 0, 2, 1);
if (labelDescription != null) {
GridPane.setValignment(labelDescription, VPos.TOP);
add(labelDescription, 0, 1, 2, 1);
}
add(stack, 2, 0, columns - 2, 1);
if (valueDescription != null) {
GridPane.setValignment(valueDescription, VPos.TOP);
add(valueDescription, 2, 1, columns - 2, 1);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void setupBindings() {
super.setupBindings();
editableSpinner.visibleProperty().bind(field.editableProperty());
readOnlyLabel.visibleProperty().bind(field.editableProperty().not());
editableSpinner.getEditor().textProperty().bindBidirectional(field.userInputProperty());
readOnlyLabel.textProperty().bind(field.userInputProperty());
fieldLabel.textProperty().bind(field.labelProperty());
}
/**
* {@inheritDoc}
*/
@Override
public void setupEventHandlers() {
editableSpinner.getEditor().setOnKeyPressed(event -> {
switch (event.getCode()) {
case UP:
editableSpinner.increment(1);
break;
case DOWN:
editableSpinner.decrement(1);
break;
}
});
}
/**
* {@inheritDoc}
*/
@Override
public void setupValueChangedListeners() {
super.setupValueChangedListeners();
editableSpinner.focusedProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableSpinner));
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimplePasswordControl.java
================================================
package com.dlsc.formsfx.view.controls;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.PasswordField;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.StringBinding;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
/**
* This class provides the base implementation for a simple control to edit
* password values.
*
* @author Rinesch Murugathas
* @author Sacha Schmid
* @author Andres Almiray
*/
public class SimplePasswordControl extends SimpleControl {
/**
* This StackPane is needed for achieving the readonly effect by putting
* the readOnlyLabel over the editableField on the change of the
* visibleProperty.
*/
protected StackPane stack;
/**
* - The fieldLabel is the container that displays the label property of
* the field.
* - The editableField allows users to modify the field's value.
* - The readOnlyLabel displays the field's value if it is not editable.
*/
protected javafx.scene.control.PasswordField editableField;
protected Label readOnlyLabel;
protected Label fieldLabel;
/*
* Translates characters found in user input into '*'
*/
protected StringBinding obfuscatedUserInputBinding;
/**
* {@inheritDoc}
*/
@Override
public void initializeParts() {
super.initializeParts();
getStyleClass().add("simple-password-control");
stack = new StackPane();
editableField = new javafx.scene.control.PasswordField();
editableField.setText(field.getValue());
readOnlyLabel = new Label(obfuscate(field.getValue()));
fieldLabel = new Label(field.labelProperty().getValue());
editableField.setPromptText(field.placeholderProperty().getValue());
}
/**
* {@inheritDoc}
*/
@Override
public void layoutParts() {
super.layoutParts();
readOnlyLabel.getStyleClass().add("read-only-label");
readOnlyLabel.setPrefHeight(26);
stack.getChildren().addAll(editableField, readOnlyLabel);
stack.setAlignment(Pos.CENTER_LEFT);
Node labelDescription = field.getLabelDescription();
Node valueDescription = field.getValueDescription();
int columns = field.getSpan();
if (columns < 3) {
int rowIndex = 0;
add(fieldLabel, 0, rowIndex++, columns, 1);
if (labelDescription != null) {
GridPane.setValignment(labelDescription, VPos.TOP);
add(labelDescription, 0, rowIndex++, columns, 1);
}
add(stack, 0, rowIndex++, columns, 1);
if (valueDescription != null) {
GridPane.setValignment(valueDescription, VPos.TOP);
add(valueDescription, 0, rowIndex, columns, 1);
}
} else {
add(fieldLabel, 0, 0, 2, 1);
if (labelDescription != null) {
GridPane.setValignment(labelDescription, VPos.TOP);
add(labelDescription, 0, 1, 2, 1);
}
add(stack, 2, 0, columns - 2, 1);
if (valueDescription != null) {
GridPane.setValignment(valueDescription, VPos.TOP);
add(valueDescription, 2, 1, columns - 2, 1);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void setupBindings() {
super.setupBindings();
editableField.visibleProperty().bind(field.editableProperty());
readOnlyLabel.visibleProperty().bind(field.editableProperty().not());
editableField.textProperty().bindBidirectional(field.userInputProperty());
obfuscatedUserInputBinding = Bindings.createStringBinding(() -> obfuscate(field.getUserInput()), field.userInputProperty());
readOnlyLabel.textProperty().bind(obfuscatedUserInputBinding);
fieldLabel.textProperty().bind(field.labelProperty());
editableField.promptTextProperty().bind(field.placeholderProperty());
editableField.managedProperty().bind(editableField.visibleProperty());
}
/**
* {@inheritDoc}
*/
@Override
public void setupValueChangedListeners() {
super.setupValueChangedListeners();
field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableField));
editableField.focusedProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableField));
}
protected String obfuscate(String input) {
if (input == null) { return ""; }
int length = input.length();
StringBuilder b = new StringBuilder();
for (int i = 0; i < length; i++) {
b.append('*');
}
return b.toString();
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleRadioButtonControl.java
================================================
package com.dlsc.formsfx.view.controls;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.SingleSelectionField;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import java.util.ArrayList;
import java.util.List;
/**
* This class provides the base implementation for a simple control to edit
* radio button values.
*
* @author Rinesch Murugathas
* @author Sacha Schmid
*/
public class SimpleRadioButtonControl extends SimpleControl> {
/**
* - The fieldLabel is the container that displays the label property of
* the field.
* - The radioButtons is the list of radio buttons to display.
* - The toggleGroup defines the group for the radio buttons.
* - The box is a VBox holding all radio buttons.
*/
protected Label fieldLabel;
protected final List radioButtons = new ArrayList<>();
protected ToggleGroup toggleGroup;
protected VBox box;
/**
* {@inheritDoc}
*/
@Override
public void initializeParts() {
super.initializeParts();
getStyleClass().add("simple-radio-control");
fieldLabel = new Label(field.labelProperty().getValue());
toggleGroup = new ToggleGroup();
box = new VBox();
createRadioButtons();
}
/**
* {@inheritDoc}
*/
@Override
public void layoutParts() {
super.layoutParts();
int columns = field.getSpan();
box.setSpacing(5);
Node labelDescription = field.getLabelDescription();
Node valueDescription = field.getValueDescription();
add(fieldLabel, 0, 0, 2, 1);
if (labelDescription != null) {
GridPane.setValignment(labelDescription, VPos.TOP);
add(labelDescription, 0, 1, 2, 1);
}
add(box, 2, 0, columns - 2, 1);
if (valueDescription != null) {
GridPane.setValignment(valueDescription, VPos.TOP);
add(valueDescription, 2, 1, columns - 2, 1);
}
}
/**
* {@inheritDoc}
*/
@Override
public void setupBindings() {
super.setupBindings();
fieldLabel.textProperty().bind(field.labelProperty());
setupRadioButtonBindings();
}
/**
* {@inheritDoc}
*/
@Override
public void setupValueChangedListeners() {
super.setupValueChangedListeners();
field.itemsProperty().addListener((observable, oldValue, newValue) -> {
createRadioButtons();
setupRadioButtonBindings();
setupRadioButtonEventHandlers();
});
field.selectionProperty().addListener((observable, oldValue, newValue) -> {
if (field.getSelection() != null) {
radioButtons.get(field.getItems().indexOf(field.getSelection())).setSelected(true);
} else {
toggleGroup.getSelectedToggle().setSelected(false);
}
});
field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(box, radioButtons.get(radioButtons.size() - 1)));
field.tooltipProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(box, radioButtons.get(radioButtons.size() - 1)));
}
/**
* {@inheritDoc}
*/
@Override
public void setupEventHandlers() {
setOnMouseEntered(event -> toggleTooltip(box, radioButtons.get(radioButtons.size() - 1)));
setOnMouseExited(event -> toggleTooltip(box, radioButtons.get(radioButtons.size() - 1)));
setupRadioButtonEventHandlers();
}
/**
* This method creates radio buttons and adds them to radioButtons
* and is used when the itemsProperty on the field changes.
*/
protected void createRadioButtons() {
box.getChildren().clear();
radioButtons.clear();
for (int i = 0; i < field.getItems().size(); i++) {
RadioButton rb = new RadioButton();
rb.setText(field.getItems().get(i).toString());
rb.setToggleGroup(toggleGroup);
radioButtons.add(rb);
}
if (field.getSelection() != null) {
radioButtons.get(field.getItems().indexOf(field.getSelection())).setSelected(true);
}
box.getChildren().addAll(radioButtons);
}
/**
* Sets up bindings for all radio buttons.
*/
protected void setupRadioButtonBindings() {
for (RadioButton radio : radioButtons) {
radio.disableProperty().bind(field.editableProperty().not());
}
}
/**
* Sets up bindings for all radio buttons.
*/
protected void setupRadioButtonEventHandlers() {
for (int i = 0; i < radioButtons.size(); i++) {
final int j = i;
radioButtons.get(j).setOnAction(event -> field.select(j));
}
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/controls/SimpleTextControl.java
================================================
package com.dlsc.formsfx.view.controls;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.StringField;
import javafx.beans.binding.Bindings;
import javafx.geometry.Pos;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
/**
* This class provides the base implementation for a simple control to edit
* string values.
*
* @author Rinesch Murugathas
* @author Sacha Schmid
*/
public class SimpleTextControl extends SimpleControl {
/**
* This StackPane is needed for achieving the readonly effect by putting
* the readOnlyLabel over the editableField on the change of the
* visibleProperty.
*/
protected StackPane stack;
/**
* - The fieldLabel is the container that displays the label property of
* the field.
* - The editableField allows users to modify the field's value.
* - The readOnlyLabel displays the field's value if it is not editable.
*/
protected TextField editableField;
protected TextArea editableArea;
protected Label readOnlyLabel;
protected Label fieldLabel;
/**
* {@inheritDoc}
*/
@Override
public void initializeParts() {
super.initializeParts();
getStyleClass().add("simple-text-control");
stack = new StackPane();
editableField = new TextField(field.getValue());
editableArea = new TextArea(field.getValue());
readOnlyLabel = new Label(field.getValue());
fieldLabel = new Label(field.labelProperty().getValue());
editableField.setPromptText(field.placeholderProperty().getValue());
}
/**
* {@inheritDoc}
*/
@Override
public void layoutParts() {
super.layoutParts();
readOnlyLabel.getStyleClass().add("read-only-label");
readOnlyLabel.setPrefHeight(26);
editableArea.getStyleClass().add("simple-textarea");
editableArea.setPrefRowCount(5);
editableArea.setPrefHeight(80);
editableArea.setWrapText(true);
if (field.isMultiline()) {
stack.setPrefHeight(80);
readOnlyLabel.setPrefHeight(80);
}
stack.getChildren().addAll(editableField, editableArea, readOnlyLabel);
stack.setAlignment(Pos.CENTER_LEFT);
Node labelDescription = field.getLabelDescription();
Node valueDescription = field.getValueDescription();
int columns = field.getSpan();
if (columns < 3) {
int rowIndex = 0;
add(fieldLabel, 0, rowIndex++, columns, 1);
if (labelDescription != null) {
GridPane.setValignment(labelDescription, VPos.TOP);
add(labelDescription, 0, rowIndex++, columns, 1);
}
add(stack, 0, rowIndex++, columns, 1);
if (valueDescription != null) {
GridPane.setValignment(valueDescription, VPos.TOP);
add(valueDescription, 0, rowIndex, columns, 1);
}
} else {
add(fieldLabel, 0, 0, 2, 1);
if (labelDescription != null) {
GridPane.setValignment(labelDescription, VPos.TOP);
add(labelDescription, 0, 1, 2, 1);
}
add(stack, 2, 0, columns - 2, 1);
if (valueDescription != null) {
GridPane.setValignment(valueDescription, VPos.TOP);
add(valueDescription, 2, 1, columns - 2, 1);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void setupBindings() {
super.setupBindings();
editableArea.visibleProperty().bind(Bindings.and(field.editableProperty(),
field.multilineProperty()));
editableField.visibleProperty().bind(Bindings.and(field.editableProperty(),
field.multilineProperty().not()));
readOnlyLabel.visibleProperty().bind(field.editableProperty().not());
editableField.textProperty().bindBidirectional(field.userInputProperty());
editableArea.textProperty().bindBidirectional(field.userInputProperty());
readOnlyLabel.textProperty().bind(field.userInputProperty());
fieldLabel.textProperty().bind(field.labelProperty());
editableField.promptTextProperty().bind(field.placeholderProperty());
editableArea.promptTextProperty().bind(field.placeholderProperty());
editableArea.managedProperty().bind(editableArea.visibleProperty());
editableField.managedProperty().bind(editableField.visibleProperty());
}
/**
* {@inheritDoc}
*/
@Override
public void setupValueChangedListeners() {
super.setupValueChangedListeners();
field.multilineProperty().addListener((observable, oldValue, newValue) -> {
stack.setPrefHeight(newValue ? 80 : 0);
readOnlyLabel.setPrefHeight(newValue ? 80 : 26);
});
field.errorMessagesProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(field.isMultiline() ? editableArea : editableField));
editableField.focusedProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableField));
editableArea.focusedProperty().addListener((observable, oldValue, newValue) -> toggleTooltip(editableArea));
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/renderer/FormRenderer.java
================================================
package com.dlsc.formsfx.view.renderer;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.Form;
import com.dlsc.formsfx.model.structure.Section;
import com.dlsc.formsfx.view.util.ViewMixin;
import javafx.geometry.Insets;
import javafx.scene.layout.VBox;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* This class is used to render a form.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class FormRenderer extends VBox implements ViewMixin {
protected Form form;
protected List sections = new ArrayList<>();
/**
* This is the constructor to pass over data.
* @param form
* The form which gets rendered.
*/
public FormRenderer(Form form) {
this.form = form;
init();
}
@Override
public String getUserAgentStylesheet() {
return FormRenderer.class.getResource("style.css").toExternalForm();
}
/**
* {@inheritDoc}
*/
@Override
public void initializeParts() {
sections = form.getGroups().stream()
.map(s -> {
if (s instanceof Section) {
return new SectionRenderer((Section) s);
} else {
return new GroupRenderer(s);
}
})
.collect(Collectors.toList());
}
/**
* {@inheritDoc}
*/
@Override
public void layoutParts() {
getStyleClass().add("formsfx-form");
setPadding(new Insets(10));
getChildren().addAll(sections);
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/renderer/GroupRenderer.java
================================================
package com.dlsc.formsfx.view.renderer;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.Group;
import javafx.geometry.Insets;
/**
* This class renders a group for a form.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class GroupRenderer extends GroupRendererBase {
/**
* This is the constructor to pass over data.
*
* @param group
* The section which gets rendered.
*/
protected GroupRenderer(Group group) {
this.element = group;
init();
}
/**
* {@inheritDoc}
*/
@Override
public void initializeParts() {
super.initializeParts();
}
/**
* {@inheritDoc}
*/
@Override
public void layoutParts() {
super.layoutParts();
getStyleClass().add("formsfx-group");
setFocusTraversable(false);
setPadding(new Insets(SPACING * 2));
getChildren().add(grid);
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/renderer/GroupRendererBase.java
================================================
package com.dlsc.formsfx.view.renderer;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.Element;
import com.dlsc.formsfx.model.structure.Field;
import com.dlsc.formsfx.model.structure.Group;
import com.dlsc.formsfx.model.structure.NodeElement;
import com.dlsc.formsfx.view.controls.SimpleControl;
import com.dlsc.formsfx.view.util.ViewMixin;
import javafx.geometry.Insets;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
/**
* This class handles shared aspects of groups and sections during rendering.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public abstract class GroupRendererBase extends StackPane implements ViewMixin {
/**
* - SPACING is used to set the spacing of the section as well as the
* spacing for vertical/horizontal gaps between controls.
*/
protected final int SPACING = 10;
protected GridPane grid;
protected V element;
/**
*
*/
@Override
public void initializeParts() {
grid = new GridPane();
}
/**
* {@inheritDoc}
*/
@Override
public void layoutParts() {
int COLUMN_COUNT = 12;
for (int i = 0; i < COLUMN_COUNT; i++) {
ColumnConstraints colConst = new ColumnConstraints();
colConst.setPercentWidth(100.0 / COLUMN_COUNT);
grid.getColumnConstraints().add(colConst);
}
grid.setHgap(SPACING);
grid.setVgap(SPACING);
setPadding(new Insets(SPACING));
int currentRow = 0;
int currentColumnCount = 0;
// Add the controls in the GridPane in a 12-column layout. If a control
// takes up too much horizontal space, wrap it to the next row.
for (Element e : element.getElements()) {
int span = e.getSpan();
if (currentColumnCount + span > COLUMN_COUNT) {
currentRow += 1;
currentColumnCount = 0;
}
if (e instanceof Field) {
Field f = (Field) e;
SimpleControl c = f.getRenderer();
c.setField(f);
grid.add(c, currentColumnCount, currentRow, span, 1);
} else if (e instanceof NodeElement){
grid.add(((NodeElement)e).getNode(), currentColumnCount, currentRow, span, 1);
}
currentColumnCount += span;
}
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/renderer/SectionRenderer.java
================================================
package com.dlsc.formsfx.view.renderer;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.Section;
import javafx.scene.control.TitledPane;
/**
* This class renders a section for a form.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class SectionRenderer extends GroupRendererBase {
private TitledPane titledPane;
/**
* This is the constructor to pass over data.
*
* @param section
* The section which gets rendered.
*/
protected SectionRenderer(Section section) {
this.element = section;
init();
}
/**
* {@inheritDoc}
*/
@Override
public void initializeParts() {
super.initializeParts();
titledPane = new TitledPane();
}
/**
* {@inheritDoc}
*/
@Override
public void layoutParts() {
super.layoutParts();
getStyleClass().add("formsfx-section");
titledPane.setContent(grid);
getChildren().add(titledPane);
titledPane.setFocusTraversable(false);
titledPane.setExpanded(true);
}
/**
* {@inheritDoc}
*/
@Override
public void setupValueChangedListeners() {
titledPane.collapsibleProperty().bind(element.collapsibleProperty());
titledPane.expandedProperty().addListener((observable, oldValue, newValue) -> element.collapsedProperty().setValue(!newValue));
element.collapsedProperty().addListener((observable, oldValue, newValue) -> titledPane.expandedProperty().setValue(!newValue));
}
/**
* {@inheritDoc}
*/
@Override
public void setupBindings() {
titledPane.textProperty().bind(element.titleProperty());
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/util/ColSpan.java
================================================
package com.dlsc.formsfx.view.util;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
/**
* An enum to set the span of columns in the form more easily.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public enum ColSpan {
FIVE_SIXTH(10),
TWO_THIRD(8),
HALF(6),
THIRD(4),
QUARTER(3),
SIXTH(2),
TWELFTH(1);
private int span;
ColSpan(int span) {
this.span = span;
}
public int valueOf() {
return span;
}
}
================================================
FILE: formsfx-core/src/main/java/com/dlsc/formsfx/view/util/ViewMixin.java
================================================
package com.dlsc.formsfx.view.util;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import java.util.List;
/**
* This interface defines lifecycle of a FormsFX view.
*
* @author Dieter Holz
*/
public interface ViewMixin {
/**
* This method holds a list of stylesheets.
*
* @return List of stylesheets.
*/
List getStylesheets();
/**
* This method calls all the other methods, so that it can be initialized
* easier.
*/
default void init() {
initializeSelf();
initializeParts();
layoutParts();
setupEventHandlers();
setupValueChangedListeners();
setupBindings();
}
/**
* This method can be used to initialize the parts of the same class.
*/
default void initializeSelf() {}
/**
* This method is used to initializes all the properties of a class.
*/
void initializeParts();
/**
* This method is used to align the parts of a class.
*/
void layoutParts();
/**
* This method is used to set up event handlers.
*/
default void setupEventHandlers() {}
/**
* This method is used to set up value change listeners.
*/
default void setupValueChangedListeners() {}
/**
* This method is used to configure the bindings of the properties.
*/
default void setupBindings() {}
/**
* This method adds the stylesheet files to the getStylesheets method.
*
* @param stylesheetFile
* List of stylesheet files
*/
default void addStylesheetFiles(String... stylesheetFile) {
for (String file : stylesheetFile) {
String stylesheet = getClass().getResource(file).toExternalForm();
getStylesheets().add(stylesheet);
}
}
}
================================================
FILE: formsfx-core/src/main/java/module-info.java
================================================
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 - 2018 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
module com.dlsc.formsfx {
requires javafx.controls;
exports com.dlsc.formsfx.model.event;
exports com.dlsc.formsfx.model.structure;
exports com.dlsc.formsfx.model.util;
exports com.dlsc.formsfx.model.validators;
exports com.dlsc.formsfx.view.controls;
exports com.dlsc.formsfx.view.renderer;
exports com.dlsc.formsfx.view.util;
opens com.dlsc.formsfx.view.renderer;
}
================================================
FILE: formsfx-core/src/main/resources/com/dlsc/formsfx/view/renderer/style.css
================================================
/*******************
* GENERAL STYLING *
*******************/
.formsfx-group {
-fx-border-width: 1px;
-fx-border-color: rgb(200, 200, 200);
-fx-border-insets: 10px;
}
.simple-control .text-field,
.simple-control .password-field,
.simple-control .text-area {
-fx-padding: 5px;
}
.simple-control .read-only-label {
-fx-padding: 0 6px;
}
/*********************
* TEXT AREA STYLING *
*********************/
.simple-textarea {
-fx-text-fill: -fx-text-inner-color;
-fx-highlight-fill: derive(-fx-control-inner-background,-20%);
-fx-highlight-text-fill: -fx-text-inner-color;
-fx-prompt-text-fill: derive(-fx-control-inner-background,-30%);
/*noinspection CssInvalidFunction*/
-fx-background-color:
linear-gradient(to bottom, derive(-fx-text-box-border, -10%), -fx-text-box-border),
linear-gradient(from 0px 0px to 0px 5px, derive(-fx-control-inner-background, -9%), -fx-control-inner-background);
-fx-background-insets: 0, 1;
-fx-background-radius: 3, 2;
-fx-cursor: text;
-fx-padding: 0.333333em;
}
.simple-textarea:focused {
-fx-highlight-fill: -fx-accent;
-fx-highlight-text-fill: white;
/*noinspection CssInvalidFunction*/
-fx-background-color:
-fx-focus-color,
-fx-control-inner-background,
-fx-faint-focus-color,
linear-gradient(from 0px 0px to 0px 5px, derive(-fx-control-inner-background, -9%), -fx-control-inner-background);
-fx-background-insets: -0.2, 1, -1.4, 3;
-fx-background-radius: 3, 2, 4, 0;
-fx-prompt-text-fill: transparent;
}
.simple-textarea .content {
-fx-padding: 0;
-fx-cursor: text;
-fx-background-color: -fx-control-inner-background;
-fx-background-radius: 0;
}
/********************
* LISTVIEW STYLING *
********************/
.simple-listview-control .list-view {
-fx-padding: 0 0 0 5px;
-fx-border-color: transparent;
-fx-border-width: 0 4px 0 0;
-fx-border-insets: 5px 5px 5px 0;
}
/***********************
* RADIOBUTTON STYLING *
***********************/
.simple-radio-control > VBox {
-fx-border-width: 0 4px 0 0;
-fx-border-insets: 5px 5px 5px 0;
-fx-border-color: transparent;
}
/*********************
* COMBOBOX STYLING *
*********************/
.simple-select-control .read-only-label {
-fx-padding: 0 9px;
}
/*****************
* STATE STYLING *
*****************/
/*noinspection CssInvalidPseudoSelector*/
.simple-control:changed .text-field,
.simple-control:changed .password-field,
.simple-control:changed .text-area,
.simple-control:required .text-field,
.simple-control:required .password-field,
.simple-control:required .text-area,
.simple-control:invalid .text-field,
.simple-control:invalid .password-field,
.simple-control:invalid .text-area,
.simple-select-control:changed .arrow-button,
.simple-select-control:required .arrow-button,
.simple-select-control:invalid .arrow-button,
.simple-listview-control:changed .list-view,
.simple-listview-control:required .list-view,
.simple-listview-control:invalid .list-view {
-fx-padding: 0 0 0 5px;
-fx-border-width: 0 4px 0 0;
-fx-border-insets: 5px 5px 5px 0;
}
/*noinspection CssInvalidPseudoSelector*/
.simple-boolean-control:invalid > VBox,
.simple-boolean-control:required > VBox,
.simple-boolean-control:changed > VBox,
.simple-checkbox-control:changed > VBox,
.simple-checkbox-control:required > VBox,
.simple-checkbox-control:invalid > VBox,
.simple-radio-control:changed > VBox,
.simple-radio-control:required > VBox,
.simple-radio-control:invalid > VBox {
-fx-border-width: 0 4px 0 0;
-fx-border-insets: 0 5px 0 0;
}
/*noinspection CssInvalidPseudoSelector*/
.simple-control:required .text-field,
.simple-control:required .password-field,
.simple-control:required .text-area,
.simple-boolean-control:required > VBox,
.simple-select-control:required .arrow-button,
.simple-radio-control:required > VBox,
.simple-checkbox-control:required > VBox,
.simple-listview-control:required .list-view {
-fx-border-color: transparent #EBDE4C transparent transparent;
}
/*noinspection CssInvalidPseudoSelector*/
.simple-control:changed .text-field,
.simple-control:changed .password-field,
.simple-control:changed .text-area,
.simple-boolean-control:changed > VBox,
.simple-select-control:changed .arrow-button,
.simple-radio-control:changed > VBox,
.simple-checkbox-control:changed > VBox,
.simple-listview-control:changed .list-view {
-fx-border-color: transparent #4cadeb transparent transparent;
}
/*noinspection CssInvalidPseudoSelector*/
.simple-control:invalid .text-field,
.simple-control:invalid .password-field,
.simple-control:invalid .text-area,
.simple-boolean-control:invalid > VBox,
.simple-select-control:invalid .arrow-button,
.simple-radio-control:invalid > VBox,
.simple-checkbox-control:invalid > VBox,
.simple-listview-control:invalid .list-view {
-fx-control-inner-background: #ffe5e5;
-fx-border-color: transparent #EB4C4C transparent transparent;
}
/*noinspection CssInvalidPseudoSelector*/
.simple-select-control:invalid .combo-box-base {
-fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, #ffe5e5;
-fx-background-insets: 0 0 -1 0, 0, 1, 2;
-fx-background-radius: 3px, 3px, 2px, 1px;
}
.simple-checkbox-control:disabled > VBox,
.simple-radio-control:disabled > VBox,
.simple-boolean-control:disabled > VBox,
.simple-listview-control:disabled .list-view {
-fx-border-color: transparent;
}
/*noinspection CssInvalidPseudoSelector*/
.simple-select-control:changed .arrow,
.simple-select-control:required .arrow,
.simple-select-control:invalid .arrow {
-fx-pref-width: 15px;
-fx-border-insets: 0 5px 0 0;
-fx-background-insets: 0 5px 0 0;
}
/*noinspection CssInvalidPseudoSelector*/
.simple-radio-control:invalid .radio-button > .radio,
.simple-boolean-control:invalid .check-box > .box,
.simple-checkbox-control:invalid .check-box > .box {
-fx-background-color:
-fx-shadow-highlight-color,
-fx-outer-border,
-fx-inner-border,
#ffe5e5;
}
/*noinspection CssInvalidPseudoSelector*/
.simple-radio-control:invalid .radio-button:hover > .radio,
.simple-radio-control:invalid .radio-button:armed > .radio {
-fx-color: #ffcccc;
}
/*noinspection CssInvalidPseudoSelector*/
.simple-radio-control:invalid .radio-button:focused > .radio,
.simple-boolean-control:invalid .check-box:focused > .box,
.simple-checkbox-control:invalid .check-box:focused > .box {
-fx-background-color:
-fx-focus-color,
-fx-inner-border,
-fx-body-color,
-fx-faint-focus-color,
#ffe5e5;
}
/*noinspection CssInvalidPseudoSelector*/
.simple-boolean-control:invalid .check-box:hover > .box,
.simple-boolean-control:invalid .check-box:armed > .box,
.simple-checkbox-control:invalid .check-box:hover > .box,
.simple-checkbox-control:invalid .check-box:armed > .box {
-fx-color: #ffcccc;
}
================================================
FILE: formsfx-core/src/test/java/com/dlsc/formsfx/model/structure/FieldTest.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.validators.StringLengthValidator;
import com.dlsc.formsfx.view.util.ColSpan;
import javafx.application.Platform;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ListProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import java.util.Arrays;
/**
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
@Ignore
public class FieldTest {
@BeforeClass
public static void beforeClass() {
try {
Platform.startup(() -> {});
} catch (IllegalStateException ex) {
// JavaFX may only be initialized once.
}
}
@Test
public void validTest() {
StringField s = Field.ofStringType("test");
final int[] changes = {0};
s.validProperty().addListener((observable, oldValue, newValue) -> changes[0] += 1);
s.required("This field is required.").validate(StringLengthValidator.atLeast(6, "test"));
Assert.assertFalse(s.isValid());
s.validate(StringLengthValidator.upTo(6, "test"));
Assert.assertEquals(2, changes[0]);
Assert.assertTrue(s.isValid());
}
@Test
public void singleSelectionTest() {
SingleSelectionField s = Field.ofSingleSelectionType(Arrays.asList("Test", "Test 1", "Test 2"), 0);
Assert.assertEquals("Test", s.getSelection());
s.select(2);
Assert.assertEquals("Test 2", s.getSelection());
s.select(4);
Assert.assertEquals("Test 2", s.getSelection());
s.deselect();
Assert.assertEquals(null, s.getSelection());
s.select(2);
Assert.assertEquals("Test 2", s.getSelection());
s.select(-1);
Assert.assertEquals(null, s.getSelection());
}
@Test
public void multiSelectionTest() {
MultiSelectionField s = Field.ofMultiSelectionType(Arrays.asList("Test", "Test 1", "Test 2"), Arrays.asList(0, 3));
Assert.assertEquals(1, s.getSelection().size());
s.select(2);
Assert.assertEquals(2, s.getSelection().size());
s.select(4);
Assert.assertEquals(2, s.getSelection().size());
s.deselect(1);
Assert.assertEquals(2, s.getSelection().size());
s.deselect(0);
Assert.assertEquals(1, s.getSelection().size());
}
@Test
public void multilineTest() {
StringField s = Field.ofStringType("test").multiline(true);
Assert.assertTrue(s.isMultiline());
s.multiline(false);
Assert.assertFalse(s.multilineProperty().getValue());
}
@Test
public void dataBindingTest() {
StringProperty s = new SimpleStringProperty("test");
DoubleProperty d = new SimpleDoubleProperty(1.0);
IntegerProperty i = new SimpleIntegerProperty(3);
BooleanProperty b = new SimpleBooleanProperty(false);
StringField sf = Field.ofStringType(s);
DoubleField df = Field.ofDoubleType(d);
IntegerField inf = Field.ofIntegerType(i);
BooleanField bf = Field.ofBooleanType(b);
sf.userInputProperty().setValue("test 2");
Assert.assertEquals("test", s.getValue());
sf.persist();
Assert.assertEquals("test 2", s.getValue());
df.userInputProperty().setValue("2");
Assert.assertEquals(1.0, d.get(), 0.0001);
df.persist();
Assert.assertEquals(2.0, d.get(), 0.0001);
inf.userInputProperty().setValue("5");
Assert.assertEquals(3, i.get());
inf.persist();
Assert.assertEquals(5, i.get());
bf.userInputProperty().setValue("true");
Assert.assertEquals(false, b.get());
bf.persist();
Assert.assertEquals(true, b.get());
s.setValue("test 3");
Assert.assertEquals("test 3", sf.getValue());
Assert.assertEquals("test 3", sf.getValue());
}
@Ignore
public void selectionBindingTest() {
ObjectProperty o1 = new SimpleObjectProperty<>("hello");
ListProperty l1 = new SimpleListProperty<>(FXCollections.observableArrayList("hello", "world"));
ListProperty l2 = new SimpleListProperty<>(FXCollections.observableArrayList("hi", "there"));
ListProperty l3 = new SimpleListProperty<>(FXCollections.observableArrayList("hi"));
SingleSelectionField sif = Field.ofSingleSelectionType(l1, o1);
MultiSelectionField mf = Field.ofMultiSelectionType(l2, l3);
Assert.assertEquals("hello", sif.getSelection());
Assert.assertEquals(1, mf.getSelection().size());
l1.setValue(FXCollections.observableArrayList("oh", "wonder"));
Assert.assertEquals(null, sif.getSelection());
mf.select(1);
Assert.assertEquals(2, mf.getSelection().size());
Assert.assertEquals(1, l3.size());
mf.persist();
Assert.assertEquals(2, l3.size());
}
@Ignore
public void propertiesTest() {
StringProperty sp = new SimpleStringProperty("test 3");
StringField s = Field.ofStringType("test")
.multiline(true)
.editable(true)
.format(String::toString)
.styleClass("class", "thing")
.id("element")
.placeholder("temp")
.label("Field")
.required("error")
.span(6)
.span(ColSpan.HALF);
Assert.assertEquals("test", s.getValue());
Assert.assertEquals(6, s.getSpan());
Assert.assertEquals(2, s.getStyleClass().size());
Assert.assertEquals("Field", s.getLabel());
Assert.assertEquals("temp", s.getPlaceholder());
s.bind(sp);
Assert.assertEquals("test 3", s.getValue());
}
}
================================================
FILE: formsfx-core/src/test/java/com/dlsc/formsfx/model/structure/FormTest.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.util.ResourceBundleService;
import com.dlsc.formsfx.model.validators.RegexValidator;
import com.dlsc.formsfx.model.validators.StringLengthValidator;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.util.Locale;
import java.util.ResourceBundle;
/**
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class FormTest {
private ResourceBundleService service;
private ResourceBundle rbDE = ResourceBundle.getBundle("testbundle", new Locale("de", "CH"));
private ResourceBundle rbEN = ResourceBundle.getBundle("testbundle", new Locale("en", "UK"));
@Before
public void beforeSuite() {
service = new ResourceBundleService(rbEN);
}
@Test
public void generateFormTest() {
Form f = Form.of(
Group.of(
Field.ofDoubleType(2.0)
),
Section.of(
Field.ofStringType("test"),
Field.ofBooleanType(false)
),
Section.of(
Field.ofIntegerType(1),
Field.ofDoubleType(1.0)
),
Section.of(
Field.ofPasswordType("")
)
);
Assert.assertEquals(6, f.getElements().size());
Assert.assertEquals(4, f.getGroups().size());
}
@Test
public void translationTest() {
Form f = Form.of(
Section.of(
Field.ofStringType("test")
.label("string_label")
).title("section_1_title")
).title("form_title")
.i18n(service);
Assert.assertEquals("Test Form", f.getTitle());
service.changeLocale(rbDE);
Assert.assertEquals("Testformular", f.getTitle());
Assert.assertEquals("Erste Gruppe", ((Section) f.getGroups().get(0)).getTitle());
}
@Test
public void changeTest() {
StringField s = Field.ofStringType("test");
DoubleField d = Field.ofDoubleType(2.0);
Section sec = Section.of(s, d);
Form f = Form.of(sec);
s.userInputProperty().setValue("testttt");
d.userInputProperty().setValue("3.0");
Assert.assertEquals(true, s.hasChanged());
Assert.assertEquals(true, sec.hasChanged());
Assert.assertEquals(true, f.hasChanged());
s.reset();
Assert.assertEquals(false, s.hasChanged());
Assert.assertEquals(true, sec.hasChanged());
Assert.assertEquals(true, f.hasChanged());
d.persist();
Assert.assertEquals(false, s.hasChanged());
Assert.assertEquals(false, sec.hasChanged());
Assert.assertEquals(false, f.hasChanged());
}
@Test
public void validationTest() {
StringField s = Field.ofStringType("Testing")
.validate(
StringLengthValidator.atLeast(5, "String not long enough."),
RegexValidator.forPattern("[a-z ]*", "String is not all lowercase.")
);
Section sec = Section.of(s);
Form f = Form.of(sec);
Assert.assertEquals(false, s.isValid());
Assert.assertEquals(false, sec.isValid());
Assert.assertEquals(false, f.isValid());
Assert.assertEquals(1, s.getErrorMessages().size());
s.userInputProperty().setValue("Test");
Assert.assertEquals(false, s.isValid());
Assert.assertEquals(false, sec.isValid());
Assert.assertEquals(false, f.isValid());
Assert.assertEquals(2, s.getErrorMessages().size());
s.userInputProperty().setValue("Testing this");
Assert.assertEquals(false, s.isValid());
Assert.assertEquals(false, sec.isValid());
Assert.assertEquals(false, f.isValid());
Assert.assertEquals(1, s.getErrorMessages().size());
s.userInputProperty().setValue("testing this properly");
Assert.assertEquals(true, s.isValid());
Assert.assertEquals(true, sec.isValid());
Assert.assertEquals(true, f.isValid());
Assert.assertTrue(s.getErrorMessages().isEmpty());
}
@Test
public void persistableTest() {
StringField s = Field.ofStringType("test");
DoubleField d = Field.ofDoubleType(2.0);
IntegerField i = Field.ofIntegerType(10);
Form f = Form.of(Group.of(i), Section.of(s), Section.of(d));
s.userInputProperty().setValue("testttt");
Assert.assertEquals(true, s.hasChanged());
Assert.assertEquals(true, f.hasChanged());
Assert.assertEquals(true, f.isPersistable());
s.reset();
Assert.assertEquals(false, s.hasChanged());
Assert.assertEquals(false, f.hasChanged());
Assert.assertEquals(false, f.isPersistable());
i.userInputProperty().setValue("testttt");
Assert.assertEquals(true, i.hasChanged());
Assert.assertEquals(true, f.hasChanged());
Assert.assertEquals(false, f.isPersistable());
f.persist();
Assert.assertEquals(true, i.hasChanged());
Assert.assertEquals(true, f.hasChanged());
Assert.assertEquals(false, f.isPersistable());
i.userInputProperty().setValue("50");
Assert.assertEquals(true, f.isPersistable());
f.reset();
Assert.assertEquals(false, i.hasChanged());
Assert.assertEquals(false, f.hasChanged());
Assert.assertEquals(false, f.isPersistable());
i.userInputProperty().setValue("50");
f.persist();
Assert.assertEquals(false, i.hasChanged());
Assert.assertEquals(false, f.hasChanged());
Assert.assertEquals(false, f.isPersistable());
}
}
================================================
FILE: formsfx-core/src/test/java/com/dlsc/formsfx/model/structure/SectionTest.java
================================================
package com.dlsc.formsfx.model.structure;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import org.junit.Assert;
import org.junit.Test;
/**
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class SectionTest {
@Test
public void collapseTest() {
Section s = Section.of();
final int[] changes = { 0 };
s.collapsedProperty().addListener((observable, oldValue, newValue) -> changes[0] += 1);
s.collapse(true);
s.collapse(false);
Assert.assertEquals(2, changes[0]);
Assert.assertFalse(s.isCollapsed());
}
}
================================================
FILE: formsfx-core/src/test/java/com/dlsc/formsfx/model/util/ResourceBundleServiceTest.java
================================================
package com.dlsc.formsfx.model.util;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import org.junit.Assert;
import org.junit.Test;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import static junit.framework.TestCase.fail;
/**
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class ResourceBundleServiceTest {
private final ResourceBundle rbEN = ResourceBundle.getBundle("testbundle", new Locale("en", "UK"));
private final ResourceBundle rbDE = ResourceBundle.getBundle("testbundle", new Locale("de", "CH"));
@Test
public void translateTest() {
ResourceBundleService rbs = new ResourceBundleService(rbEN);
Assert.assertEquals("Test Form", rbs.translate("form_title"));
try {
rbs.translate("non_existing");
fail();
} catch (MissingResourceException ignored) {}
}
@Test
public void changeLocaleTest() {
ResourceBundleService rbs = new ResourceBundleService(rbEN);
final int[] calls = new int[] { 0 };
Runnable r = () -> calls[0] += 1;
rbs.addListener(r);
rbs.changeLocale(rbDE);
Assert.assertEquals(1, calls[0]);
rbs.changeLocale(rbDE);
Assert.assertEquals(1, calls[0]);
rbs.removeListener(r);
}
}
================================================
FILE: formsfx-core/src/test/java/com/dlsc/formsfx/model/validators/CustomValidatorTest.java
================================================
package com.dlsc.formsfx.model.validators;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import org.junit.Assert;
import org.junit.Test;
/**
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class CustomValidatorTest {
@Test
public void customTest() {
CustomValidator c = CustomValidator.forPredicate(s -> s.length() % 2 == 0, "test");
Assert.assertTrue(c.validate("abcd").getResult());
Assert.assertFalse(c.validate("abc").getResult());
Assert.assertTrue(c.validate("").getResult());
}
}
================================================
FILE: formsfx-core/src/test/java/com/dlsc/formsfx/model/validators/DoubleRangeValidatorTest.java
================================================
package com.dlsc.formsfx.model.validators;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import org.junit.Assert;
import org.junit.Test;
import static junit.framework.TestCase.fail;
/**
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class DoubleRangeValidatorTest {
@Test
public void betweenTest() {
DoubleRangeValidator i = DoubleRangeValidator.between(3.5, 12.1351, "test");
Assert.assertTrue(i.validate(11.5).getResult());
Assert.assertTrue(i.validate(3.50000001).getResult());
Assert.assertFalse(i.validate(12.13511).getResult());
Assert.assertFalse(i.validate(3.4999999).getResult());
try {
DoubleRangeValidator i2 = DoubleRangeValidator.between(10.0, 5.3, "test");
fail();
} catch (IllegalArgumentException ignored) {}
try {
DoubleRangeValidator i2 = DoubleRangeValidator.between(5.5, 5.5, "test");
} catch (IllegalArgumentException ignored) {
fail();
}
}
@Test
public void atLeastTest() {
DoubleRangeValidator i = DoubleRangeValidator.atLeast(5.12351, "test");
Assert.assertTrue(i.validate(6234.1).getResult());
Assert.assertFalse(i.validate(1.31).getResult());
Assert.assertTrue(i.validate(5.12351).getResult());
Assert.assertFalse(i.validate(5.1235).getResult());
Assert.assertTrue(i.validate(Double.MAX_VALUE).getResult());
}
@Test
public void upToTest() {
DoubleRangeValidator i = DoubleRangeValidator.upTo(3.14, "test");
Assert.assertFalse(i.validate(-1.14).getResult());
Assert.assertFalse(i.validate(5.13).getResult());
Assert.assertTrue(i.validate(3.14).getResult());
Assert.assertFalse(i.validate(3.141).getResult());
Assert.assertTrue(i.validate(Double.MIN_VALUE).getResult());
}
@Test
public void exactlyTest() {
DoubleRangeValidator i = DoubleRangeValidator.exactly(3.14, "test");
Assert.assertFalse(i.validate(-3.4).getResult());
Assert.assertFalse(i.validate(3.145).getResult());
Assert.assertTrue(i.validate(3.14).getResult());
Assert.assertFalse(i.validate(3.0).getResult());
Assert.assertFalse(i.validate(Double.MIN_VALUE).getResult());
}
}
================================================
FILE: formsfx-core/src/test/java/com/dlsc/formsfx/model/validators/IntegerRangeValidatorTest.java
================================================
package com.dlsc.formsfx.model.validators;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import org.junit.Assert;
import org.junit.Test;
import static junit.framework.TestCase.fail;
/**
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class IntegerRangeValidatorTest {
@Test
public void betweenTest() {
IntegerRangeValidator i = IntegerRangeValidator.between(10, 20, "test");
Assert.assertTrue(i.validate(14).getResult());
Assert.assertFalse(i.validate(21).getResult());
Assert.assertTrue(i.validate(20).getResult());
Assert.assertTrue(i.validate(10).getResult());
try {
IntegerRangeValidator i2 = IntegerRangeValidator.between(20, 10, "test");
fail();
} catch (IllegalArgumentException ignored) {}
try {
IntegerRangeValidator i2 = IntegerRangeValidator.between(10, 10, "test");
} catch (IllegalArgumentException ignored) {
fail();
}
}
@Test
public void atLeastTest() {
IntegerRangeValidator i = IntegerRangeValidator.atLeast(10, "test");
Assert.assertTrue(i.validate(14).getResult());
Assert.assertFalse(i.validate(-139).getResult());
Assert.assertTrue(i.validate(10).getResult());
Assert.assertFalse(i.validate(9).getResult());
Assert.assertTrue(i.validate(Integer.MAX_VALUE).getResult());
}
@Test
public void upToTest() {
IntegerRangeValidator i = IntegerRangeValidator.upTo(10, "test");
Assert.assertFalse(i.validate(14).getResult());
Assert.assertFalse(i.validate(21).getResult());
Assert.assertTrue(i.validate(10).getResult());
Assert.assertFalse(i.validate(11).getResult());
Assert.assertTrue(i.validate(Integer.MIN_VALUE).getResult());
}
@Test
public void exactlyTest() {
IntegerRangeValidator i = IntegerRangeValidator.exactly(10, "test");
Assert.assertFalse(i.validate(11).getResult());
Assert.assertFalse(i.validate(9).getResult());
Assert.assertTrue(i.validate(10).getResult());
Assert.assertFalse(i.validate(Integer.MIN_VALUE).getResult());
}
}
================================================
FILE: formsfx-core/src/test/java/com/dlsc/formsfx/model/validators/RegexValidatorTest.java
================================================
package com.dlsc.formsfx.model.validators;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import org.junit.Assert;
import org.junit.Test;
import java.util.regex.PatternSyntaxException;
import static junit.framework.TestCase.fail;
/**
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class RegexValidatorTest {
@Test
public void regexTest() {
RegexValidator r = RegexValidator.forPattern("[a-z]*", "test");
Assert.assertTrue(r.validate("abc").getResult());
Assert.assertTrue(r.validate("aeihafpiaheypfhapfhpa").getResult());
Assert.assertFalse(r.validate("Ajj").getResult());
Assert.assertTrue(r.validate("").getResult());
Assert.assertFalse(r.validate("hlhlhL").getResult());
try {
RegexValidator r1 = RegexValidator.forPattern("a[aa[", "test");
fail();
} catch (PatternSyntaxException ignored) {}
}
@Test
public void emailTest() {
RegexValidator r = RegexValidator.forEmail("test");
Assert.assertTrue(r.validate("test@test.com").getResult());
Assert.assertFalse(r.validate("test@test").getResult());
Assert.assertFalse(r.validate("test.com@test@").getResult());
Assert.assertTrue(r.validate("test+ea@test.com").getResult());
}
@Test
public void urlTest() {
RegexValidator r = RegexValidator.forURL("test");
Assert.assertTrue(r.validate("http://test.com").getResult());
Assert.assertFalse(r.validate("http:/test.com").getResult());
Assert.assertTrue(r.validate("www.test.com").getResult());
Assert.assertTrue(r.validate("https://www.test.com").getResult());
}
@Test
public void alphaNumericTest() {
RegexValidator r= RegexValidator.forAlphaNumeric("test");
Assert.assertTrue(r.validate("afaehofh3r1ohr1o3hro1h3A13OIHho").getResult());
Assert.assertFalse(r.validate("aefih 391ur fj ").getResult());
Assert.assertFalse(r.validate("¢æ±#").getResult());
}
}
================================================
FILE: formsfx-core/src/test/java/com/dlsc/formsfx/model/validators/SelectionLengthValidatorTest.java
================================================
package com.dlsc.formsfx.model.validators;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import javafx.collections.FXCollections;
import org.junit.Assert;
import org.junit.Test;
import static junit.framework.TestCase.fail;
/**
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class SelectionLengthValidatorTest {
@Test
public void betweenTest() {
SelectionLengthValidator s = SelectionLengthValidator.between(1, 3, "test");
Assert.assertTrue(s.validate(FXCollections.observableArrayList(1, 2, 3)).getResult());
Assert.assertFalse(s.validate(FXCollections.observableArrayList()).getResult());
Assert.assertFalse(s.validate(FXCollections.observableArrayList(1, 2, 3, 4, 5)).getResult());
try {
SelectionLengthValidator s2 = SelectionLengthValidator.between(-10, 2, "test");
fail();
} catch (IllegalArgumentException ignored) {}
try {
SelectionLengthValidator s3 = SelectionLengthValidator.between(0, 0, "test");
} catch (IllegalArgumentException e) {
fail();
}
try {
SelectionLengthValidator s4 = SelectionLengthValidator.between(10, 1, "test");
fail();
} catch (IllegalArgumentException ignored) {}
}
@Test
public void atLeastTest() {
SelectionLengthValidator s = SelectionLengthValidator.atLeast(2, "test");
Assert.assertTrue(s.validate(FXCollections.observableArrayList(1, 4)).getResult());
Assert.assertFalse(s.validate(FXCollections.observableArrayList(1)).getResult());
Assert.assertFalse(s.validate(FXCollections.observableArrayList()).getResult());
Assert.assertTrue(s.validate(FXCollections.observableArrayList(1, 2, 3)).getResult());
try {
SelectionLengthValidator s2 = SelectionLengthValidator.atLeast(-10, "test");
fail();
} catch (IllegalArgumentException ignored) {}
try {
SelectionLengthValidator s3 = SelectionLengthValidator.atLeast(0, "test");
} catch (IllegalArgumentException e) {
fail();
}
}
@Test
public void upToTest() {
SelectionLengthValidator s = SelectionLengthValidator.upTo(2, "test");
Assert.assertFalse(s.validate(FXCollections.observableArrayList(3, 5, 1)).getResult());
Assert.assertTrue(s.validate(FXCollections.observableArrayList(1, 2)).getResult());
Assert.assertTrue(s.validate(FXCollections.observableArrayList()).getResult());
Assert.assertFalse(s.validate(FXCollections.observableArrayList(1, 2, 3, 5, 14)).getResult());
}
@Test
public void exactlyTest() {
SelectionLengthValidator s = SelectionLengthValidator.exactly(2, "test");
Assert.assertFalse(s.validate(FXCollections.observableArrayList(1, 2, 3)).getResult());
Assert.assertTrue(s.validate(FXCollections.observableArrayList(1, 2)).getResult());
Assert.assertFalse(s.validate(FXCollections.observableArrayList()).getResult());
Assert.assertFalse(s.validate(FXCollections.observableArrayList(1)).getResult());
}
}
================================================
FILE: formsfx-core/src/test/java/com/dlsc/formsfx/model/validators/StringLengthValidatorTest.java
================================================
package com.dlsc.formsfx.model.validators;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import org.junit.Assert;
import org.junit.Test;
import static junit.framework.TestCase.fail;
/**
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class StringLengthValidatorTest {
@Test
public void betweenTest() {
StringLengthValidator s = StringLengthValidator.between(10, 20, "test");
Assert.assertTrue(s.validate("abcdefghijklmno").getResult());
Assert.assertFalse(s.validate("abcde").getResult());
Assert.assertTrue(s.validate(" ").getResult());
Assert.assertTrue(s.validate("梢äöä1ö3䱿#¢æ±“{").getResult());
try {
StringLengthValidator s2 = StringLengthValidator.between(-10, 2, "test");
fail();
} catch (IllegalArgumentException ignored) {}
try {
StringLengthValidator s3 = StringLengthValidator.between(0, 0, "test");
} catch (IllegalArgumentException e) {
fail();
}
try {
StringLengthValidator s4 = StringLengthValidator.between(10, 1, "test");
fail();
} catch (IllegalArgumentException ignored) {}
}
@Test
public void atLeastTest() {
StringLengthValidator s = StringLengthValidator.atLeast(5, "test");
Assert.assertTrue(s.validate("gosjrgohgsr").getResult());
Assert.assertFalse(s.validate(" ").getResult());
Assert.assertFalse(s.validate("ae").getResult());
Assert.assertTrue(s.validate("¶æ¢¶ππ§±#").getResult());
try {
StringLengthValidator s2 = StringLengthValidator.atLeast(-10, "test");
fail();
} catch (IllegalArgumentException ignored) {}
try {
StringLengthValidator s3 = StringLengthValidator.atLeast(0, "test");
} catch (IllegalArgumentException e) {
fail();
}
}
@Test
public void upToTest() {
StringLengthValidator s = StringLengthValidator.upTo(5, "test");
Assert.assertFalse(s.validate("gosjrgohgsr").getResult());
Assert.assertTrue(s.validate(" ").getResult());
Assert.assertTrue(s.validate("ae").getResult());
Assert.assertFalse(s.validate("¶æ¢¶ππ§±#").getResult());
}
@Test
public void exactlyTest() {
StringLengthValidator s = StringLengthValidator.exactly(3, "test");
Assert.assertFalse(s.validate("gfyf").getResult());
Assert.assertTrue(s.validate(" ").getResult());
Assert.assertTrue(s.validate("aee").getResult());
Assert.assertFalse(s.validate("ee").getResult());
}
}
================================================
FILE: formsfx-core/src/test/java/com/dlsc/formsfx/view/controls/SimpleControlTest.java
================================================
package com.dlsc.formsfx.view.controls;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.Field;
import com.dlsc.formsfx.model.structure.MultiSelectionField;
import com.dlsc.formsfx.model.structure.SingleSelectionField;
import com.dlsc.formsfx.model.structure.StringField;
import javafx.application.Platform;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListView;
import javafx.scene.control.RadioButton;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import java.util.Arrays;
/**
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
@Ignore
public class SimpleControlTest {
@BeforeClass
public static void before() {
try {
Platform.startup(() -> {});
} catch (IllegalStateException ex) {
// JavaFX may only be initialized once.
}
}
@Test
public void itemsTest() {
MultiSelectionField mf = Field.ofMultiSelectionType(Arrays.asList(1, 2, 3), Arrays.asList(1, 2));
SingleSelectionField sf = Field.ofSingleSelectionType(Arrays.asList(1, 2, 3), 1);
SimpleCheckBoxControl cb = new SimpleCheckBoxControl<>();
cb.setField(mf);
SimpleListViewControl lv = new SimpleListViewControl<>();
lv.setField(mf);
SimpleRadioButtonControl rb = new SimpleRadioButtonControl<>();
rb.setField(sf);
SimpleComboBoxControl cmb = new SimpleComboBoxControl<>();
cmb.setField(sf);
Assert.assertEquals(3, ((VBox) cb.getChildren().get(1)).getChildren().size());
Assert.assertTrue(((CheckBox) ((VBox) cb.getChildren().get(1)).getChildren().get(1)).isSelected());
Assert.assertEquals(3, ((ListView) lv.getChildren().get(1)).getItems().size());
Assert.assertTrue(((ListView) lv.getChildren().get(1)).getSelectionModel().isSelected(1));
Assert.assertEquals(3, ((VBox) rb.getChildren().get(1)).getChildren().size());
Assert.assertTrue(((RadioButton) ((VBox) rb.getChildren().get(1)).getChildren().get(1)).isSelected());
Assert.assertEquals(3, ((ComboBox) ((StackPane) cmb.getChildren().get(1)).getChildren().get(0)).getItems().size());
Assert.assertTrue(((ComboBox) ((StackPane) cmb.getChildren().get(1)).getChildren().get(0)).getSelectionModel().isSelected(1));
mf.items(Arrays.asList(1, 2, 3, 4, 5), Arrays.asList(0, 3));
sf.items(Arrays.asList(1, 2, 3, 4, 5), 3);
Assert.assertEquals(5, ((VBox) cb.getChildren().get(1)).getChildren().size());
Assert.assertTrue(((CheckBox) ((VBox) cb.getChildren().get(1)).getChildren().get(0)).isSelected());
Assert.assertEquals(5, ((ListView) lv.getChildren().get(1)).getItems().size());
Assert.assertTrue(((ListView) lv.getChildren().get(1)).getSelectionModel().isSelected(0));
Assert.assertEquals(5, ((VBox) rb.getChildren().get(1)).getChildren().size());
Assert.assertTrue(((RadioButton) ((VBox) rb.getChildren().get(1)).getChildren().get(3)).isSelected());
Assert.assertEquals(5, ((ComboBox) ((StackPane) cmb.getChildren().get(1)).getChildren().get(0)).getItems().size());
Assert.assertTrue(((ComboBox) ((StackPane) cmb.getChildren().get(1)).getChildren().get(0)).getSelectionModel().isSelected(3));
}
@Test
public void styleTest() {
StringField s = Field.ofStringType("test").styleClass("test");
SimpleTextControl t = new SimpleTextControl();
t.setField(s);
Assert.assertEquals(3, t.getStyleClass().size());
s.styleClass("hello", "world");
Assert.assertEquals(4, t.getStyleClass().size());
s.styleClass("hi", "world");
Assert.assertEquals(4, t.getStyleClass().size());
Assert.assertEquals("world", t.getStyleClass().get(3));
}
}
================================================
FILE: formsfx-core/src/test/java/com/dlsc/formsfx/view/renderer/RendererTest.java
================================================
package com.dlsc.formsfx.view.renderer;
/*-
* ========================LICENSE_START=================================
* FormsFX
* %%
* Copyright (C) 2017 DLSC Software & Consulting
* %%
* 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.
* =========================LICENSE_END==================================
*/
import com.dlsc.formsfx.model.structure.Field;
import com.dlsc.formsfx.model.structure.Form;
import com.dlsc.formsfx.model.structure.Group;
import com.dlsc.formsfx.model.structure.Section;
import com.dlsc.formsfx.view.controls.SimpleCheckBoxControl;
import com.dlsc.formsfx.view.controls.SimpleRadioButtonControl;
import com.dlsc.formsfx.view.controls.SimpleTextControl;
import javafx.application.Platform;
import javafx.scene.control.TitledPane;
import javafx.scene.layout.GridPane;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
/**
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
@Ignore
public class RendererTest {
@BeforeClass
public static void before() throws IllegalStateException {
try {
Platform.startup(() -> {});
} catch (IllegalStateException ex) {
// JavaFX may only be initialized once.
}
}
@Test
public void formTest() {
Form f = Form.of(Group.of(), Section.of(), Group.of(), Group.of(), Section.of());
FormRenderer r = new FormRenderer(f);
Assert.assertTrue(r.getChildren().get(0) instanceof GroupRenderer);
Assert.assertEquals(5, r.getChildren().size());
Assert.assertFalse(r.getChildren().get(0) instanceof SectionRenderer);
Assert.assertTrue(r.getChildren().get(1) instanceof SectionRenderer);
}
@Test
public void groupTest() {
Group g = Group.of(
Field.ofStringType("").span(7),
Field.ofBooleanType(false).span(8),
Field.ofMultiSelectionType(Arrays.asList(1, 2, 3), Collections.singletonList(1)),
Field.ofSingleSelectionType(Arrays.asList(1, 2, 3), 1).render(new SimpleRadioButtonControl<>())
);
GroupRenderer r = new GroupRenderer(g);
Assert.assertTrue(r.getChildren().get(0) instanceof GridPane);
Assert.assertTrue(((GridPane) r.getChildren().get(0)).getChildren().get(0) instanceof SimpleTextControl);
}
@Test
public void sectionTest() {
Section s = Section.of(
Field.ofDoubleType(2.0),
Field.ofIntegerType(1),
Field.ofMultiSelectionType(Arrays.asList(1, 2, 3), Collections.singletonList(1)).render(new SimpleCheckBoxControl<>()),
Field.ofSingleSelectionType(Arrays.asList(1, 2, 3), 1)
);
SectionRenderer r = new SectionRenderer(s);
Assert.assertTrue(r.getChildren().get(0) instanceof TitledPane);
Assert.assertTrue(((TitledPane) r.getChildren().get(0)).getContent() instanceof GridPane);
Assert.assertTrue(((TitledPane) r.getChildren().get(0)).isExpanded());
s.collapse(true);
Assert.assertFalse(((TitledPane) r.getChildren().get(0)).isExpanded());
}
}
================================================
FILE: formsfx-core/src/test/resources/testbundle_de_CH.properties
================================================
form_title = Testformular
section_1_title = Erste Gruppe
string_label = Text
================================================
FILE: formsfx-core/src/test/resources/testbundle_en_UK.properties
================================================
form_title = Test Form
section_1_title = First Section
string_label = String
================================================
FILE: formsfx-demo/.gitignore
================================================
/target
*.iml
================================================
FILE: formsfx-demo/pom.xml
================================================
com.dlsc.formsfx
formsfx
11.6.0
FormsFX Demo
jar
4.0.0
formsfx-demo
com.dlsc.formsfx
formsfx-core
11.6.0
================================================
FILE: formsfx-demo/src/main/java/com/dlsc/formsfx/demo/Demo.java
================================================
package com.dlsc.formsfx.demo;
import com.dlsc.formsfx.demo.model.DemoModel;
import com.dlsc.formsfx.demo.view.RootPane;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
* This is the main class to start the application.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class Demo extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
DemoModel model = new DemoModel();
RootPane rootPanel = new RootPane(model);
Scene scene = new Scene(rootPanel);
primaryStage.titleProperty().bind(model.getFormInstance().titleProperty());
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
================================================
FILE: formsfx-demo/src/main/java/com/dlsc/formsfx/demo/model/Country.java
================================================
package com.dlsc.formsfx.demo.model;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.time.LocalDate;
import java.time.Month;
/**
* This class serves as the model for the demo application.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class Country {
private StringProperty name = new SimpleStringProperty("Switzerland");
private StringProperty iso = new SimpleStringProperty("CH");
private BooleanProperty independence = new SimpleBooleanProperty(true);
private ObjectProperty independenceDay = new SimpleObjectProperty<>(LocalDate.of(1648, Month.OCTOBER, 24));
private StringProperty currencyShort = new SimpleStringProperty("CHF");
private StringProperty currencyLong = new SimpleStringProperty("Swiss Franc");
private IntegerProperty population = new SimpleIntegerProperty(8401120);
private DoubleProperty area = new SimpleDoubleProperty(41285);
private StringProperty tld = new SimpleStringProperty(".ch");
private StringProperty dateFormat = new SimpleStringProperty("dd.mm.yyyy");
private ObjectProperty driverSide = new SimpleObjectProperty<>("Right");
private StringProperty timeZone = new SimpleStringProperty("CET");
private StringProperty summerTimeZone = new SimpleStringProperty("CEST");
private ListProperty allSides = new SimpleListProperty<>(FXCollections.observableArrayList("Right", "Left"));
private ListProperty allCities = new SimpleListProperty<>(FXCollections.observableArrayList("Zurich (ZH)", "Geneva (GE)", "Basel (BS)", "Lausanne (VD)", "Bern (BE)", "Winterthur (ZH)", "Lucerne (LU)", "St. Gallen (SG)", "Lugano (TI)", "Biel (BE)"));
private ListProperty allCapitals = new SimpleListProperty<>(FXCollections.observableArrayList("Zurich (ZH)", "Geneva (GE)", "Basel (BS)", "Lausanne (VD)", "Bern (BE)", "Winterthur (ZH)", "Lucerne (LU)", "St. Gallen (SG)", "Lugano (TI)", "Biel (BE)"));
private ListProperty allContinents = new SimpleListProperty<>(FXCollections.observableArrayList("Africa", "Asia", "Europe", "North America", "South America", "Australia"));
private ListProperty continents = new SimpleListProperty<>(FXCollections.observableArrayList("Europe"));
private ObjectProperty capital = new SimpleObjectProperty<>("Bern (BE)");
private ListProperty germanCities = new SimpleListProperty<>(FXCollections.observableArrayList("Zurich (ZH)", "Basel (BS)", "Bern (BE)", "Winterthur (ZH)", "Lucerne (LU)", "St. Gallen (SG)", "Biel (BE)"));
public String getName() {
return name.get();
}
public StringProperty nameProperty() {
return name;
}
public String getIso() {
return iso.get();
}
public StringProperty isoProperty() {
return iso;
}
public boolean isIndependence() {
return independence.get();
}
public BooleanProperty independenceProperty() {
return independence;
}
public LocalDate getIndependenceDay() {
return independenceDay.get();
}
public ObjectProperty independenceDayProperty() {
return independenceDay;
}
public String getCurrencyShort() {
return currencyShort.get();
}
public StringProperty currencyShortProperty() {
return currencyShort;
}
public String getCurrencyLong() {
return currencyLong.get();
}
public StringProperty currencyLongProperty() {
return currencyLong;
}
public int getPopulation() {
return population.get();
}
public IntegerProperty populationProperty() {
return population;
}
public double getArea() {
return area.get();
}
public DoubleProperty areaProperty() {
return area;
}
public String getTld() {
return tld.get();
}
public StringProperty tldProperty() {
return tld;
}
public String getDateFormat() {
return dateFormat.get();
}
public StringProperty dateFormatProperty() {
return dateFormat;
}
public String getDriverSide() {
return driverSide.get();
}
public ObjectProperty driverSideProperty() {
return driverSide;
}
public String getTimeZone() {
return timeZone.get();
}
public StringProperty timeZoneProperty() {
return timeZone;
}
public String getSummerTimeZone() {
return summerTimeZone.get();
}
public StringProperty summerTimeZoneProperty() {
return summerTimeZone;
}
public ObservableList getAllSides() {
return allSides.get();
}
public ListProperty allSidesProperty() {
return allSides;
}
public ObservableList getAllCities() {
return allCities.get();
}
public ListProperty allCitiesProperty() {
return allCities;
}
public ObservableList getAllCapitals() {
return allCapitals.get();
}
public ListProperty allCapitalsProperty() {
return allCapitals;
}
public ObservableList getAllContinents() {
return allContinents.get();
}
public ListProperty allContinentsProperty() {
return allContinents;
}
public ObservableList getContinents() {
return continents.get();
}
public ListProperty continentsProperty() {
return continents;
}
public String getCapital() {
return capital.get();
}
public ObjectProperty capitalProperty() {
return capital;
}
public ObservableList getGermanCities() {
return germanCities.get();
}
public ListProperty germanCitiesProperty() {
return germanCities;
}
}
================================================
FILE: formsfx-demo/src/main/java/com/dlsc/formsfx/demo/model/DemoModel.java
================================================
package com.dlsc.formsfx.demo.model;
import com.dlsc.formsfx.model.structure.Field;
import com.dlsc.formsfx.model.structure.Form;
import com.dlsc.formsfx.model.structure.Group;
import com.dlsc.formsfx.model.structure.Section;
import com.dlsc.formsfx.model.util.ResourceBundleService;
import com.dlsc.formsfx.model.validators.DoubleRangeValidator;
import com.dlsc.formsfx.model.validators.IntegerRangeValidator;
import com.dlsc.formsfx.model.validators.RegexValidator;
import com.dlsc.formsfx.model.validators.StringLengthValidator;
import com.dlsc.formsfx.view.controls.SimpleCheckBoxControl;
import com.dlsc.formsfx.view.controls.SimpleRadioButtonControl;
import com.dlsc.formsfx.view.util.ColSpan;
import java.util.Locale;
import java.util.ResourceBundle;
/**
* This class is used to create the form and holds all the necessary data. This
* class acts as a singleton where the current instance is available using
* {@code getInstance}.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public final class DemoModel {
private Country country = new Country();
/**
* These are the resource bundles for german and english.
*/
private ResourceBundle rbDE = ResourceBundle.getBundle("demo-locale", new Locale("de", "CH"));
private ResourceBundle rbEN = ResourceBundle.getBundle("demo-locale", new Locale("en", "UK"));
/**
* The default locale is English, thus the {@code ResourceBundleService} is
* initialised with it.
*/
private ResourceBundleService rbs = new ResourceBundleService(rbEN);
private Form formInstance;
/**
* Creates or simply returns to form singleton instance.
*
* @return Returns the form instance.
*/
public Form getFormInstance() {
if (formInstance == null) {
createForm();
}
return formInstance;
}
/**
* Creates a new form instance with the required information.
*/
private void createForm() {
formInstance = Form.of(
Group.of(
Field.ofStringType(country.nameProperty())
.label("country_label")
.placeholder("country_placeholder")
.required("required_error_message")
.validate(StringLengthValidator.atLeast(2, "country_error_message")),
Field.ofStringType(country.isoProperty())
.label("ISO_3166_label")
.placeholder("ISO_3166_placeholder")
.required("required_error_message")
.validate(StringLengthValidator.exactly(2, "ISO_3166_error_message")),
Field.ofBooleanType(country.independenceProperty())
.label("independent_label")
.required("required_error_message"),
Field.ofDate(country.getIndependenceDay())
.label("independent_since_label")
.required("required_error_message")
.placeholder("independent_since_placeholder")
),
Section.of(
Field.ofStringType(country.currencyShortProperty())
.label("currency_label")
.placeholder("currency_placeholder")
.validate(StringLengthValidator.exactly(3, "currency_error_message"))
.span(ColSpan.HALF),
Field.ofStringType(country.currencyLongProperty())
.label("currency_long_label")
.placeholder("currency_long_placeholder")
.span(ColSpan.HALF),
Field.ofDoubleType(country.areaProperty())
.label("area_label")
.format("format_error_message")
.placeholder("area_placeholder")
.validate(DoubleRangeValidator.atLeast(1, "area_error_message"))
.span(ColSpan.HALF),
Field.ofStringType(country.tldProperty())
.label("internet_TLD_label")
.placeholder("internet_TLD_placeholder")
.span(ColSpan.HALF)
.validate(
StringLengthValidator.exactly(3, "internet_TLD_error_message"),
RegexValidator.forPattern("^.[a-z]{2}$", "internet_TLD_format_error_message")
),
Field.ofStringType(country.dateFormatProperty())
.label("date_format_label")
.placeholder("date_format_placeholder")
.multiline(true)
.span(ColSpan.HALF)
.validate(StringLengthValidator.atLeast(8, "date_format_error_message")),
Field.ofSingleSelectionType(country.allSidesProperty(), country.driverSideProperty())
.required("required_error_message")
.label("driving_label")
.span(ColSpan.HALF)
.render(() -> new SimpleRadioButtonControl<>()),
Field.ofStringType(country.timeZoneProperty())
.label("time_zone_label")
.placeholder("time_zone_placeholder")
.span(ColSpan.HALF)
.validate(StringLengthValidator.exactly(3, "time_zone_error_message")),
Field.ofStringType(country.summerTimeZoneProperty())
.label("summer_time_zone_label")
.placeholder("summer_time_zone_placeholder")
.span(ColSpan.HALF)
.validate(StringLengthValidator.atLeast(3, "summer_time_zone_error_message"))
).title("other_information_label"),
Section.of(
Field.ofSingleSelectionType(country.allCapitalsProperty(), country.capitalProperty())
.label("capital_label")
.required("required_error_message")
.tooltip("capital_tooltip")
.span(ColSpan.HALF),
Field.ofIntegerType(country.populationProperty())
.label("population_label")
.format("format_error_message")
.placeholder("population_placeholder")
.required("required_error_message")
.span(ColSpan.HALF)
.validate(IntegerRangeValidator.atLeast(1, "population_error_message")),
Field.ofMultiSelectionType(country.allContinentsProperty(), country.continentsProperty())
.label("continent_label")
.required("required_error_message")
.span(ColSpan.HALF)
.render(() -> new SimpleCheckBoxControl<>()),
Field.ofMultiSelectionType(country.allCitiesProperty(), country.germanCitiesProperty())
.label("german_cities_label")
.span(ColSpan.HALF),
Field.ofPasswordType("secret")
.label("secret_label")
.required("required_error_message")
.span(ColSpan.HALF)
.validate(StringLengthValidator.between(1, 10, "secret_error_message"))
).title("cities_and_population_label")
).title("form_label")
.i18n(rbs);
}
/**
* Sets the locale of the form.
*
* @param language The language identifier for the new locale. Either DE or EN.
*/
public void translate(String language) {
switch (language) {
case "EN":
rbs.changeLocale(rbEN);
break;
case "DE":
rbs.changeLocale(rbDE);
break;
default:
throw new IllegalArgumentException("Not a valid locale");
}
}
public Country getCountry() {
return country;
}
}
================================================
FILE: formsfx-demo/src/main/java/com/dlsc/formsfx/demo/view/RootPane.java
================================================
package com.dlsc.formsfx.demo.view;
import com.dlsc.formsfx.demo.model.DemoModel;
import com.dlsc.formsfx.model.structure.Section;
import com.dlsc.formsfx.view.renderer.FormRenderer;
import com.dlsc.formsfx.view.util.ViewMixin;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
/**
* This class contains all the nodes and regions needed for the demo
* application.
*
* @author Sacha Schmid
* @author Rinesch Murugathas
*/
public class RootPane extends BorderPane implements ViewMixin {
/**
* - controls holds all the nodes which are rendered on the right side.
* - scrollContent holds the form.
* - languageButtons holds both buttons to change language.
* - statusContent holds all the state change labels.
* - formButtons holds the save and reset button.
* - toggleContent holds the buttons to toggle editable and sections.
* - bindingInfo holds the info of property changes.
*/
private GridPane controls;
private ScrollPane scrollContent;
private HBox languageButtons;
private VBox statusContent;
private HBox formButtons;
private VBox toggleContent;
private VBox bindingInfo;
private Button save;
private Button reset;
private Button languageDE;
private Button languageEN;
private Label validLabel;
private Label changedLabel;
private Label persistableLabel;
private Label countryLabel;
private Label currencyLabel;
private Label populationLabel;
private Button editableToggle;
private Button sectionToggle;
private DemoModel model;
private FormRenderer displayForm;
/**
* The constructor to create the nodes and regions.
*
* @param model
* The model that holds the data.
*/
public RootPane(DemoModel model) {
this.model = model;
init();
}
/**
* This method is used to set up the stylesheets.
*/
@Override
public void initializeSelf() {
getStylesheets().add(getClass().getResource("/style.css").toExternalForm());
getStyleClass().add("root-pane");
}
/**
* This method initializes all nodes and regions.
*/
@Override
public void initializeParts() {
save = new Button("Save");
reset = new Button("Reset");
save.getStyleClass().add("save-button");
reset.getStyleClass().add("reset-button");
// The language buttons get a picture of the country from the flaticon
// font in the css.
languageDE = new Button("\ue001");
languageEN = new Button("\ue000");
validLabel = new Label("The form is valid.");
persistableLabel = new Label("The form is not persistable.");
changedLabel = new Label("The form has not changed.");
countryLabel = new Label("Country: " + model.getCountry().getName());
currencyLabel = new Label("Currency: " + model.getCountry().getCurrencyShort());
populationLabel = new Label("Population: " + model.getCountry().getPopulation());
editableToggle = new Button("Toggle Editable");
sectionToggle = new Button("Toggle Sections");
editableToggle.getStyleClass().add("toggle-button");
sectionToggle.getStyleClass().add("toggle-button");
languageButtons = new HBox();
statusContent = new VBox();
formButtons = new HBox();
toggleContent = new VBox();
bindingInfo = new VBox();
controls = new GridPane();
scrollContent = new ScrollPane();
displayForm = new FormRenderer(model.getFormInstance());
}
/**
* This method sets up the necessary bindings for the logic of the
* application.
*/
@Override
public void setupBindings() {
save.disableProperty().bind(model.getFormInstance().persistableProperty().not());
reset.disableProperty().bind(model.getFormInstance().changedProperty().not());
displayForm.prefWidthProperty().bind(scrollContent.prefWidthProperty());
}
/**
* This method sets up listeners and sets the text of the state change
* labels.
*/
@Override
public void setupValueChangedListeners() {
model.getFormInstance().changedProperty().addListener((observable, oldValue, newValue) -> changedLabel.setText("The form has " + (newValue ? "" : "not ") + "changed."));
model.getFormInstance().validProperty().addListener((observable, oldValue, newValue) -> validLabel.setText("The form is " + (newValue ? "" : "not ") + "valid."));
model.getFormInstance().persistableProperty().addListener((observable, oldValue, newValue) -> persistableLabel.setText("The form is " + (newValue ? "" : "not ") + "persistable."));
model.getCountry().nameProperty().addListener((observable, oldValue, newValue) -> countryLabel.setText("Country: " + newValue));
model.getCountry().currencyShortProperty().addListener((observable, oldValue, newValue) -> currencyLabel.setText("Currency: " + newValue));
model.getCountry().populationProperty().addListener((observable, oldValue, newValue) -> populationLabel.setText("Population: " + newValue));
}
/**
* This method sets up the handling for all the button clicks.
*/
@Override
public void setupEventHandlers() {
reset.setOnAction(event -> model.getFormInstance().reset());
save.setOnAction(event -> model.getFormInstance().persist());
languageDE.setOnAction(event -> {
model.translate("DE");
languageDE.setDisable(true);
languageEN.setDisable(false);
});
languageEN.setOnAction(event -> {
model.translate("EN");
languageEN.setDisable(true);
languageDE.setDisable(false);
});
sectionToggle.setOnAction(event -> model.getFormInstance().getGroups().stream().filter(s -> s instanceof Section).forEach(s -> {
Section sec = (Section) s;
sec.collapse(!sec.isCollapsed());
}));
editableToggle.setOnAction(event -> model.getFormInstance().getFields().forEach(s -> s.editable(!s.isEditable())));
}
/**
* This method is used to layout the nodes and regions properly.
*/
@Override
public void layoutParts() {
scrollContent.setContent(displayForm);
scrollContent.setFitToWidth(true);
scrollContent.getStyleClass().add("scroll-pane");
languageDE.getStyleClass().addAll("flaticon", "lang-button", "lang-button--left");
languageEN.getStyleClass().addAll("flaticon", "lang-button", "lang-button--right");
languageEN.setDisable(true);
languageDE.setMaxWidth(Double.MAX_VALUE);
languageEN.setMaxWidth(Double.MAX_VALUE);
HBox.setHgrow(languageDE, Priority.ALWAYS);
HBox.setHgrow(languageEN, Priority.ALWAYS);
languageButtons.getChildren().addAll(languageDE, languageEN);
languageButtons.setPadding(new Insets(10));
controls.add(languageButtons, 0, 0);
statusContent.setPadding(new Insets(10));
statusContent.getChildren().addAll(validLabel, changedLabel, persistableLabel);
statusContent.setSpacing(10);
statusContent.setPrefWidth(200);
statusContent.getStyleClass().add("bordered");
controls.add(statusContent, 0, 1);
save.setMaxWidth(Double.MAX_VALUE);
reset.setMaxWidth(Double.MAX_VALUE);
HBox.setHgrow(save, Priority.ALWAYS);
HBox.setHgrow(reset, Priority.ALWAYS);
formButtons.getChildren().addAll(reset, save);
formButtons.setPadding(new Insets(10));
formButtons.setSpacing(10);
formButtons.setPrefWidth(200);
formButtons.getStyleClass().add("bordered");
controls.add(formButtons, 0, 2);
bindingInfo.setPadding(new Insets(10));
bindingInfo.getChildren().addAll(countryLabel, currencyLabel, populationLabel);
bindingInfo.setSpacing(10);
bindingInfo.setPrefWidth(200);
bindingInfo.getStyleClass().add("bordered");
controls.add(bindingInfo, 0, 3);
editableToggle.setMaxWidth(Double.MAX_VALUE);
sectionToggle.setMaxWidth(Double.MAX_VALUE);
HBox.setHgrow(editableToggle, Priority.ALWAYS);
HBox.setHgrow(sectionToggle, Priority.ALWAYS);
toggleContent.setPadding(new Insets(10));
toggleContent.getChildren().addAll(editableToggle, sectionToggle);
toggleContent.setSpacing(10);
toggleContent.setPrefWidth(200);
toggleContent.getStyleClass().add("bordered");
controls.add(toggleContent, 0, 4);
controls.setPrefWidth(200);
controls.getStyleClass().add("controls");
setCenter(scrollContent);
setRight(controls);
}
}
================================================
FILE: formsfx-demo/src/main/java/module-info.java
================================================
module com.dlsc.formsfx.demo {
requires javafx.controls;
requires com.dlsc.formsfx;
exports com.dlsc.formsfx.demo;
}
================================================
FILE: formsfx-demo/src/main/resources/demo-locale_de_CH.properties
================================================
capital_label = Hauptstadt
german_cities_label = Deutschsprachige St\u00e4dte
continent_label = Kontinent
population_label = Einwohner
secret_label = Geheimnis
cities_and_population_label = St\u00e4dte und Einwohner
currency_label = W\u00e4hrung
currency_long_label = W\u00e4hrung lang
area_label = Fl\u00e4che
internet_TLD_label = Internet Endung
date_format_label = Datumsformat
time_zone_label = Zeitzone
summer_time_zone_label = Sommer-Zeitzone
other_information_label = Weitere Informationen
country_label = Land
independent_since_label = Independence Day
independent_since_placeholder = Independent Since
ISO_3166_label = ISO 3166
independent_label = Unabh\u00e4ngig
form_label = L\u00e4nder Formular
driving_label = Fahrerseite
population_placeholder = Einwohner des Landes
currency_placeholder = W\u00e4hrungsk\u00fcrzel des Landes
currency_long_placeholder = W\u00e4hrung des Landes ausgeschrieben
area_placeholder = Fl\u00e4che des Landes
internet_TLD_placeholder = Internet Endung des Landes
date_format_placeholder = Datumsformat des Landes
time_zone_placeholder = Zeitzone des Landes
summer_time_zone_placeholder = Sommer-Zeitzone des Landes
country_placeholder = Landesname
ISO_3166_placeholder = ISO 3166 code des Landes
format_error_message = Keine g\u00fcltiges Format f\u00fcr dieses Feld
required_error_message = Feld darf nicht leer sein
population_error_message = Einwohnerzahl muss gr\u00f6sser als 1 sein
currency_error_message = W\u00e4hrungsk\u00fcrzel muss genau 3 Zeichen lang sein
area_error_message = Fl\u00e4che muss gr\u00f6sser als 1 sein
internet_TLD_error_message = Internet Endung muss genau 3 Zeichen lang sein
internet_TLD_format_error_message = Internet Endung muss dem Format .xy folgen
date_format_error_message = Datumsformat muss l\u00e4nger als 8 Zeichen sein
time_zone_error_message = Zeitzone muss genau 3 Zeichen lang sein
summer_time_zone_error_message = Sommer-Zeitzone muss l\u00e4nger als 3 Zeichen sein
country_error_message = Landesname muss l\u00e4nger als 2 Zeichen sein
ISO_3166_error_message = ISO 3166 code muss genau 2 Zeichen lang sein
secret_error_message = Passwortl\u00e4nge muss zwischen 1 und 10 Zeichen sein
capital_tooltip = Die Hauptstadt des Landes ist oftmals nicht die gr\u00f6sste Stadt.
================================================
FILE: formsfx-demo/src/main/resources/demo-locale_en_UK.properties
================================================
capital_label = Capital
german_cities_label = German-speaking Cities
continent_label = Continent
population_label = Population
secret_label = Secret
cities_and_population_label = Cities and Population
currency_label = Currency
currency_long_label = Currency long
area_label = Area
internet_TLD_label = Internet TLD
date_format_label = Date Format
time_zone_label = Time Zone
summer_time_zone_label = Summer Time Zone
other_information_label = Other Information
country_label = Country
ISO_3166_label = ISO 3166
independent_label = Independent
independent_since_label = Independence Day
independent_since_placeholder = Independent Since
form_label = Country Form
driving_label = Driving on the
population_placeholder = Population of country
currency_placeholder = Currency code of country
currency_long_placeholder = Currency in written format
area_placeholder = Area of country
internet_TLD_placeholder = Internet domain of country
date_format_placeholder = Date format of country
time_zone_placeholder = Time zone of country
summer_time_zone_placeholder = Summer time zone of country
country_placeholder = Name of country
ISO_3166_placeholder = ISO 3166 code of country
format_error_message = Not a valid input for this field
required_error_message = Field must not be empty
population_error_message = Population has to be larger than 1
currency_error_message = Currency code has to be exactly 3 characters long
area_error_message = Area has to be larger than 1
internet_TLD_error_message = Domain length has to be exactly 3 characters long
internet_TLD_format_error_message = Domain must follow the .xy format
date_format_error_message = Date format has to be longer than 8 characters
time_zone_error_message = Time zone has to be exactly 3 characters long
summer_time_zone_error_message = Summer time zone has to be at least 3 characters long
country_error_message = Name of country has to be longer than 2 characters
ISO_3166_error_message = ISO 3166 code has to be exactly 2 characters long
secret_error_message = Password length must be between 1 and 10 characters
capital_tooltip = The capital city is often not the largest city.
================================================
FILE: formsfx-demo/src/main/resources/style.css
================================================
@font-face {
font-family: 'Flaticon';
src: url(/flaticon.ttf);
}
.field-label-description,
.field-value-description {
-fx-text-fill: red;
}
.root-pane {
-fx-padding: 0;
-fx-font-family: 'Arial';
}
.flaticon .text {
-fx-font-family: 'Flaticon';
}
.lang-button {
-fx-border-color: dimgrey;
-fx-background-color: white;
-fx-background-insets: 0 0 0 0, 0, 1, 2;
-fx-border-width: 1px;
-fx-text-fill: dimgrey;
-fx-font-size: 24px;
-fx-cursor: hand;
}
.lang-button:disabled {
-fx-background-color: dimgrey;
-fx-opacity: 1;
-fx-text-fill: white;
}
.lang-button--left {
-fx-background-radius: 5px 0 0 5px;
-fx-border-radius: 5px 0 0 5px;
}
.lang-button--right {
-fx-background-radius: 0 5px 5px 0;
-fx-border-radius: 0 5px 5px 0;
}
.scroll-pane {
-fx-padding: 0;
}
.content {
-fx-padding: 10;
}
.controls {
-fx-background-color: #e0e0e0;
-fx-border-color: #b1b1b1;
-fx-border-width: 0 0 0 1px;
}
.save-button,
.reset-button,
.toggle-button {
-fx-font-size: 14px;
-fx-font-weight: normal;
-fx-text-alignment: center;
-fx-cursor: hand;
-fx-background-radius: 4px;
-fx-background-insets: 0 0 0 0, 0, 1, 2;
-fx-border-width: 1px;
-fx-border-radius: 4px;
}
.save-button {
-fx-text-fill: #fff;
-fx-background-color: #337ab7;
-fx-border-color: darkblue;
}
.save-button:hover {
-fx-background-color: #2d6da4;
}
.save-button:pressed {
-fx-background-color: #286192;
}
.reset-button,
.toggle-button {
-fx-text-fill: #333;
-fx-background-color: #fff;
-fx-border-color: #ababab;
}
.reset-button:hover,
.toggle-button:hover {
-fx-background-color: #f0f0f0;
}
.reset-button:pressed {
-fx-text-fill: #fff;
-fx-background-color: red;
-fx-border-color: darkred;
}
.bordered {
-fx-border-color: #b1b1b1 transparent transparent transparent;
-fx-border-width: 1px;
}
================================================
FILE: jreleaser.yml
================================================
#
# SPDX-License-Identifier: Apache-2.0
#
# Copyright 2021 Dirk Lemmermann Software & Consulting (dlsc.com)
#
# 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
#
# https://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.
#
project:
name: FormsFX
description: A framework for easily creating forms for a JavaFX UI.
website: https://github.com/dlsc-software-consulting-gmbh/GemsFX/
authors:
- Dirk Lemmermann
license: Apache-2.0
extraProperties:
inceptionYear: 2022
release:
github:
branch: master-11
overwrite: true
milestone:
name: '{{projectVersion}}'
changelog:
sort: DESC
formatted: ALWAYS
format: '- {{commitShortHash}} {{commitTitle}}'
contributors:
format: '- {{contributorName}}{{#contributorUsernameAsLink}} ({{.}}){{/contributorUsernameAsLink}}'
hide:
contributors:
- 'GitHub'
================================================
FILE: mvnw
================================================
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /usr/local/etc/mavenrc ] ; then
. /usr/local/etc/mavenrc
fi
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`\\unset -f command; \\command -v java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
if [ -n "$MVNW_REPOURL" ]; then
jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
fi
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if $cygwin; then
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
fi
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl -o "$wrapperJarPath" "$jarUrl" -f
else
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaClass=`cygpath --path --windows "$javaClass"`
fi
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
================================================
FILE: mvnw.cmd
================================================
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% ^
%JVM_CONFIG_MAVEN_PROPS% ^
%MAVEN_OPTS% ^
%MAVEN_DEBUG_OPTS% ^
-classpath %WRAPPER_JAR% ^
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%"=="on" pause
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
cmd /C exit /B %ERROR_CODE%
================================================
FILE: pom.xml
================================================