/* * 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. */ package net.formio; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Locale; import net.formio.ajax.JsEvent; import net.formio.ajax.action.AjaxAction; import net.formio.ajax.action.AjaxHandler; import net.formio.choice.ChoiceProvider; import net.formio.choice.ChoiceRenderer; import net.formio.choice.DefaultChoiceProvider; import net.formio.choice.DefaultChoiceRenderer; import net.formio.format.Formatter; import net.formio.format.Formatters; import net.formio.format.Location; import net.formio.props.FormElementProperty; import net.formio.props.FormFieldProperties; import net.formio.props.FormFieldPropertiesImpl; import net.formio.props.types.InlinePosition; import net.formio.validation.Validator; import net.formio.validation.validators.RequiredValidator; /** * Specification of properties used to construct a {@link FormField}. * Builder of {@link FormField}. * * @author Radek Beran */ public class FieldProps<T> implements Serializable { private static final long serialVersionUID = 2328756250255932689L; private FormMapping<?> parent; private String propertyName; private String type; private String inputType; private String pattern; private Formatter<T> formatter; private ChoiceProvider<T> choiceProvider; private ChoiceRenderer<T> choiceRenderer; private FormFieldProperties formProperties = new FormFieldPropertiesImpl(FormElementProperty.createDefaultProperties()); List<T> filledObjects = new ArrayList<T>(); String strValue; String labelKey; int order; List<Validator<T>> validators; FieldProps(String propertyName) { this(propertyName, Field.TEXT.getType()); } FieldProps(String propertyName, String type) { // when the inputType is null, it will be taken from the found Field enum constant, // see the implementation of getInputType() this(propertyName, type, null); } FieldProps(String propertyName, String type, String inputType) { // package-default access so only Forms (and classes in current package) can create the builder if (propertyName == null || propertyName.isEmpty()) throw new IllegalArgumentException("propertyName must be specified"); this.propertyName = propertyName; this.type = type; if (inputType == null && type != null) { Field fld = Field.findByType(type); if (fld != null) { inputType = fld.getInputType(); } } this.inputType = inputType; this.validators = new ArrayList<Validator<T>>(); } FieldProps(FormField<T> field) { initFromField(field); } FieldProps(FormField<T> field, List<T> values, Location loc, Formatters formatters, String preferedStringValue) { String strValue = null; if (preferedStringValue != null) { strValue = preferedStringValue; } else if (values.size() > 0) { strValue = valueAsString(values.get(0), field.getPattern(), field.getFormatter(), loc, formatters); } // "this" cannot be used before this initialization of fields: initFromField(field).value(strValue).filledObjects(values); if (field.getChoiceRenderer() instanceof DefaultChoiceRenderer) { choiceRenderer(new DefaultChoiceRenderer<T>(loc.getLocale())); } } public FieldProps<T> type(String type) { this.type = type; return this; } public FieldProps<T> inputType(String inputType) { this.inputType = inputType; return this; } public FieldProps<T> pattern(String pattern) { this.pattern = pattern; return this; } public FieldProps<T> formatter(Formatter<T> formatter) { this.formatter = formatter; return this; } public FieldProps<T> choices(ChoiceProvider<T> choiceProvider) { this.choiceProvider = choiceProvider; return this; } public FieldProps<T> choices(List<? extends T> choices) { return choices(new DefaultChoiceProvider<T>(choices)); } public FieldProps<T> choiceRenderer(ChoiceRenderer<T> choiceRenderer) { this.choiceRenderer = choiceRenderer; return this; } public FieldProps<T> validator(Validator<T> validator) { if (validator != null) { this.validators.add(validator); } return this; } public <U> FieldProps<T> property(FormElementProperty<U> fieldProperty, U value) { this.formProperties = new FormFieldPropertiesImpl(this.formProperties, fieldProperty, value); return this; } public FieldProps<T> visible(boolean visible) { return property(FormElementProperty.VISIBLE, Boolean.valueOf(visible)); } public FieldProps<T> enabled(boolean enabled) { return property(FormElementProperty.ENABLED, Boolean.valueOf(enabled)); } public FieldProps<T> readonly(boolean readonly) { return property(FormElementProperty.READ_ONLY, Boolean.valueOf(readonly)); } public FieldProps<T> required(boolean required) { if (required) { Validator<T> validator = RequiredValidator.getInstance(); if (!validators.contains(validator)) { validators.add(validator); } } return this; } public FieldProps<T> help(String help) { return property(FormElementProperty.HELP, help); } public FieldProps<T> chooseOptionDisplayed(boolean displayed) { return property(FormElementProperty.CHOOSE_OPTION_DISPLAYED, Boolean.valueOf(displayed)); } public FieldProps<T> chooseOptionTitle(String title) { return property(FormElementProperty.CHOOSE_OPTION_TITLE, title); } public <U> FieldProps<T> ajaxHandler(AjaxAction<U> action) { return ajaxHandler(action, (JsEvent)null); } public <U> FieldProps<T> ajaxHandler(AjaxAction<U> action, JsEvent event) { return ajaxHandlers(Arrays.asList(new AjaxHandler<U>(action, event))); } public <U> FieldProps<T> ajaxHandler(AjaxAction<U> action, String requestParam) { return ajaxHandlers(Arrays.asList(new AjaxHandler<U>(action, requestParam))); } public FieldProps<T> ajaxHandlers(List<? extends AjaxHandler<?>> handlers) { return property(FormElementProperty.AJAX_HANDLERS, handlers.toArray(new AjaxHandler<?>[0])); } public FieldProps<T> ajaxRelatedElement(String element) { return property(FormElementProperty.AJAX_RELATED_ELEMENT, element); } public FieldProps<T> ajaxSourceAncestorElement(String element) { return property(FormElementProperty.AJAX_SOURCE_ANCESTOR_ELEMENT, element); } public FieldProps<T> placeholder(String placeholderText) { return property(FormElementProperty.PLACEHOLDER, placeholderText); } public FieldProps<T> labelVisible(boolean visible) { return property(FormElementProperty.LABEL_VISIBLE, Boolean.valueOf(visible)); } /** * True if this form field is not attached to underlying property of form data object * (it is not filled nor bound). * @param detached * @return */ public FieldProps<T> detached(boolean detached) { return property(FormElementProperty.DETACHED, Boolean.valueOf(detached)); } /** * Sets the inline position to non-{@code null} value if this is the inline field. * @param inlinePos * @return */ public FieldProps<T> inline(InlinePosition inlinePos) { return property(FormElementProperty.INLINE, inlinePos); } /** * Sets width of input in count of columns. * @param width * @return */ public FieldProps<T> colInputWidth(Integer width) { return property(FormElementProperty.COL_INPUT_WIDTH, width); } /** * Sets width of input in count of columns. * @param width * @return */ public FieldProps<T> colInputWidth(int width) { return property(FormElementProperty.COL_INPUT_WIDTH, Integer.valueOf(width)); } /** * Sets width of label in count of columns. * @param width * @return */ public FieldProps<T> colLabelWidth(int width) { return property(FormElementProperty.COL_LABEL_WIDTH, Integer.valueOf(width)); } /** * Sets the confirmation message. * @param msg * @return */ public FieldProps<T> confirmMessage(String msg) { return property(FormElementProperty.CONFIRM_MESSAGE, msg); } /** * Key for translation of the label. * @param labelKey * @return */ public FieldProps<T> labelKey(String labelKey) { this.labelKey = labelKey; return this; } // only for internal usage FieldProps<T> parent(FormMapping<?> parent) { this.parent = parent; return this; } // only for internal usage FieldProps<T> propertyName(String propertyName) { this.propertyName = propertyName; return this; } // only for internal usage FieldProps<T> order(int order) { this.order = order; return this; } // only for internal usage FieldProps<T> value(String value) { this.strValue = value; return this; } // only for internal usage FieldProps<T> filledObjects(List<T> filledObjects) { if (filledObjects == null) { filledObjects = new ArrayList<T>(); } this.filledObjects = filledObjects; return this; } // for internal usage <U> FieldProps<T> properties(FormFieldProperties properties) { this.formProperties = properties; return this; } public FormMapping<?> getParent() { return this.parent; } /** * Name of mapped propertyName of edited object. * @return */ public String getPropertyName() { return this.propertyName; } /** * Type of form field, for e.g.: text, checkbox, textarea, select-multiple, date-picker ..., * or {@code null} if not specified. * @return */ public String getType() { return type; } /** * Type of HTML input(s) that is used to render this form field. * @return */ public String getInputType() { return inputType; } /** * Pattern for formatting the value. * @return */ public String getPattern() { return this.pattern; } /** * Formatter that formats object to String and vice versa. * * @return */ public Formatter<T> getFormatter() { return this.formatter; } /** * Returns provider of all possible codebook items used when rendering this form field; * or {@code null}. * @return */ public ChoiceProvider<T> getChoices() { return this.choiceProvider; } /** * Returns renderer that prepares ids and titles of items when this * form field represents a choice from the codebook item(s); or {@code null} if this * form field does not represent such a choice. * @return */ public ChoiceRenderer<T> getChoiceRenderer() { return this.choiceRenderer; } /** * Field form properties (flags like required, ... - see {@link FormElementProperty}). * @return */ public FormFieldProperties getFormProperties() { return this.formProperties; } /** * Returns ordinal index of this form element. * @return */ public int getOrder() { return this.order; } public List<Validator<T>> getValidators() { return this.validators; } /** * Constructs new immutable form field. * @return */ public FormField<T> build() { return build(this.order); } /** * Constructs new immutable form field. * @return */ FormField<T> build(int order) { if (this.choiceRenderer == null && this.type != null && !this.type.isEmpty()) { Field formComponent = Field.findByType(this.type); if (formComponent != null && formComponent.isChoice()) { // This default locale will be replaced by the desired one when the form field is filled this.choiceRenderer = new DefaultChoiceRenderer<T>(Locale.getDefault()); } } return new FormFieldImpl<T>(this, order); } private static <T> String valueAsString(T value, String pattern, Formatter<T> formatter, Location loc, Formatters formatters) { if (value == null) return null; String str = null; if (formatter != null) { // formatter is specified explicitly by user str = formatter.makeString(value, pattern, loc); } else { // choose a suitable formatter from available formatters str = formatters.makeString(value, pattern, loc); } return str; } private FieldProps<T> initFromField(FormField<T> field) { this.propertyName = field.getPropertyName(); this.parent = field.getParent(); this.type = field.getType(); this.inputType = field.getInputType(); this.pattern = field.getPattern(); this.formatter = field.getFormatter(); this.choiceProvider = field.getChoices(); this.choiceRenderer = field.getChoiceRenderer(); this.formProperties = field.getProperties(); this.filledObjects = field.getFilledObjects(); this.strValue = field.getValue(); this.order = field.getOrder(); this.validators = new ArrayList<Validator<T>>(field.getValidators()); return this; } }