/*
* 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.util.List;
import javax.validation.Validation;
import net.formio.binding.AnnotationArgumentNameResolver;
import net.formio.binding.ArgumentNameResolver;
import net.formio.binding.BeanExtractor;
import net.formio.binding.Binder;
import net.formio.binding.ConstructorInstantiator;
import net.formio.binding.DefaultBeanExtractor;
import net.formio.binding.DefaultBinder;
import net.formio.binding.Instantiator;
import net.formio.binding.PropertyMethodRegex;
import net.formio.binding.collection.BasicCollectionBuilders;
import net.formio.binding.collection.CollectionBuilders;
import net.formio.binding.collection.CollectionSpec;
import net.formio.binding.collection.ItemsOrder;
import net.formio.format.Location;
import net.formio.format.BasicFormatters;
import net.formio.format.Formatters;
import net.formio.security.HashTokenAuthorizer;
import net.formio.security.TokenAuthorizer;
import net.formio.validation.BeanValidator;
import net.formio.validation.DefaultBeanValidator;
/**
* Configuration of form. Immutable.
* @author Radek Beran
*/
public class Config {
/**
* Separator of parts in the path (used in fully qualified field name).
*/
public static final String DEFAULT_PATH_SEP = "-";
private final Location location;
private final String messageBundleName;
private final Formatters formatters;
private final CollectionBuilders collectionBuilders;
private final ArgumentNameResolver argumentNameResolver;
private final BeanExtractor beanExtractor;
private final Binder binder;
private final BeanValidator beanValidator;
private final TokenAuthorizer tokenAuthorizer;
private final boolean inputTrimmed;
private final PropertyMethodRegex accessorRegex;
private final PropertyMethodRegex setterRegex;
private final String urlBase;
private final int colFormWidth;
private final int colLabelWidth;
private final int colInputWidth;
private final Instantiator defaultInstantiator;
private final CollectionSpec<?> listMappingCollection;
private final String pathSeparator;
Config(Builder builder) {
this.location = builder.location;
this.messageBundleName = builder.messageBundleName;
this.formatters = builder.formatters;
this.collectionBuilders = builder.collectionBuilders;
this.argumentNameResolver = builder.argumentNameResolver;
this.beanExtractor = builder.beanExtractor;
this.binder = builder.binder;
this.beanValidator = builder.beanValidator;
this.tokenAuthorizer = builder.tokenAuthorizer;
this.inputTrimmed = builder.inputTrimmed;
this.accessorRegex = builder.accessorRegex;
this.setterRegex = builder.setterRegex;
this.urlBase = builder.urlBase;
this.colFormWidth = builder.colFormWidth;
this.colLabelWidth = builder.colLabelWidth;
this.colInputWidth = builder.colInputWidth;
this.defaultInstantiator = builder.defaultInstantiator;
this.listMappingCollection = builder.listMappingCollection;
this.pathSeparator = builder.pathSeparator;
}
public static class Builder {
// Optional parameters - initialized to default values (these are only here in a single location)
Location location;
String messageBundleName;
Formatters formatters;
CollectionBuilders collectionBuilders;
ArgumentNameResolver argumentNameResolver;
PropertyMethodRegex accessorRegex;
PropertyMethodRegex setterRegex;
BeanExtractor beanExtractor;
Binder binder;
BeanValidator beanValidator;
TokenAuthorizer tokenAuthorizer;
boolean extractorSpecified;
boolean binderSpecified;
boolean validatorSpecified;
boolean inputTrimmed = true;
int colFormWidth = 12;
int colLabelWidth = 2;
int colInputWidth = 4;
String urlBase;
Instantiator defaultInstantiator = new ConstructorInstantiator();
CollectionSpec<?> listMappingCollection = CollectionSpec.getInstance(List.class, ItemsOrder.LINEAR);
String pathSeparator = DEFAULT_PATH_SEP;
Builder() {
// package-default access so only Forms (and classes in current package) can create the builder
}
public Builder location(Location location) {
this.location = location;
return this;
}
public Builder messageBundleName(String msgBundleName) {
if (this.validatorSpecified) throw new IllegalStateException("messageBundleName must be specified before the validator.");
this.messageBundleName = msgBundleName;
return this;
}
public Builder formatters(Formatters formatters) {
this.formatters = formatters;
return this;
}
public Builder collectionBuilders(CollectionBuilders collectionBuilders) {
this.collectionBuilders = collectionBuilders;
return this;
}
public Builder accessorRegex(PropertyMethodRegex accessorRegex) {
if (this.extractorSpecified) throw new IllegalStateException("accessorRegex must be specified before the extractor.");
this.accessorRegex = accessorRegex;
return this;
}
public Builder setterRegex(PropertyMethodRegex setterRegex) {
if (this.binderSpecified) throw new IllegalStateException("setterRegex must be specified before the binder.");
this.setterRegex = setterRegex;
return this;
}
public Builder beanExtractor(BeanExtractor beanExtractor) {
this.beanExtractor = beanExtractor;
this.extractorSpecified = true;
return this;
}
public Builder binder(Binder binder) {
this.binder = binder;
this.binderSpecified = true;
return this;
}
public Builder argumentNameResolver(ArgumentNameResolver argumentNameResolver) {
this.argumentNameResolver = argumentNameResolver;
return this;
}
public Builder beanValidator(BeanValidator beanValidator) {
this.beanValidator = beanValidator;
this.validatorSpecified = true;
return this;
}
public Builder tokenAuthorizer(TokenAuthorizer tokenAuthorizer) {
this.tokenAuthorizer = tokenAuthorizer;
return this;
}
public Builder inputTrimmed(boolean inputTrimmed) {
this.inputTrimmed = inputTrimmed;
return this;
}
/**
* Base URL for handling AJAX requests.
* @param urlBase
* @return
*/
public Builder urlBase(String urlBase) {
this.urlBase = urlBase;
return this;
}
/**
* Maximum width of form in number of columns.
* @param max
* @return
*/
public Builder colFormWidth(int max) {
this.colFormWidth = max;
return this;
}
/**
* Default width of label in number of columns.
* @param width
* @return
*/
public Builder colLabelWidth(int width) {
this.colLabelWidth = width;
return this;
}
/**
* Default width of input in number of columns.
* @param width
* @return
*/
public Builder colInputWidth(int width) {
this.colInputWidth = width;
return this;
}
/**
* Default instantiator of classes.
* @param inst
* @return
*/
public Builder defaultInstantiator(Instantiator inst) {
this.defaultInstantiator = inst;
return this;
}
/**
* Specification of collection which should be constructed for nested collections
* of complex types used in {@link MappingType#LIST} mappings. By providing this configuration,
* default {@link List} collection can be changed to some different type of collection for list mappings.
* Given specification should be supported by used {@link CollectionBuilders}.
* @param collSpec
* @return collection specification
*/
public Builder listMappingCollection(CollectionSpec<?> collSpec) {
this.listMappingCollection = collSpec;
return this;
}
/**
* Separator of parts in the path (used in fully qualified field name).
* @param pathSeparator path separator
* @return this builder for chaining calls
*/
public Builder pathSeparator(String pathSeparator) {
this.pathSeparator = pathSeparator;
return this;
}
public Config build() {
if (this.location == null) this.location = DEFAULT_LOCATION;
if (this.messageBundleName == null) this.messageBundleName = DEFAULT_MESSAGE_BUNDLE_NAME;
if (this.formatters == null) this.formatters = DEFAULT_FORMATTERS;
if (this.collectionBuilders == null) this.collectionBuilders = DEFAULT_COLLECTION_BUILDERS;
if (this.argumentNameResolver == null) this.argumentNameResolver = DEFAULT_ARGUMENT_NAME_RESOLVER;
if (this.accessorRegex == null) this.accessorRegex = DefaultBeanExtractor.DEFAULT_ACCESSOR_REGEX;
if (this.setterRegex == null) this.setterRegex = DefaultBinder.DEFAULT_SETTER_REGEX;
if (this.beanExtractor == null) this.beanExtractor = defaultBeanExtractor(this.accessorRegex);
if (this.binder == null) this.binder = new DefaultBinder(this.formatters, this.collectionBuilders, this.argumentNameResolver, this.setterRegex);
if (this.beanValidator == null) this.beanValidator = new DefaultBeanValidator(Validation.buildDefaultValidatorFactory(), this.beanExtractor, this.messageBundleName);
if (this.tokenAuthorizer == null) this.tokenAuthorizer = DEFAULT_TOKEN_AUTHORIZER;
if (this.pathSeparator == null) this.pathSeparator = DEFAULT_PATH_SEP;
Config cfg = new Config(this);
if (cfg.getPathSeparator() == null) throw new IllegalStateException("path separator cannot be null");
if (cfg.getLocation() == null) throw new IllegalStateException("location cannot be null");
if (cfg.getMessageBundleName() == null) throw new IllegalStateException("message bundle name cannot be null");
if (cfg.getFormatters() == null) throw new IllegalStateException("formatters cannot be null");
if (cfg.getCollectionBuilders() == null) throw new IllegalStateException("collectionBuilders cannot be null");
if (cfg.getArgumentNameResolver() == null) throw new IllegalStateException("argumentNameResolver cannot be null");
if (cfg.getBeanExtractor() == null) throw new IllegalStateException("beanExtractor cannot be null");
if (cfg.getBinder() == null) throw new IllegalStateException("binder cannot be null");
if (cfg.getBeanValidator() == null) throw new IllegalStateException("beanValidator cannot be null");
if (cfg.getTokenAuthorizer() == null) throw new IllegalStateException("tokenAuthorizer cannot be null");
if (cfg.getAccessorRegex() == null) throw new IllegalStateException("accessorRegex cannot be null");
if (cfg.getColLabelWidth() > cfg.getColFormWidth()) {
throw new IllegalStateException("width of label cannot be bigger than width of form");
}
if (cfg.getColInputWidth() > cfg.getColFormWidth()) {
throw new IllegalStateException("width of input cannot be bigger than width of form");
}
if (cfg.getDefaultInstantiator() == null) throw new IllegalStateException("Default instantiator cannot be null");
if (cfg.getListMappingCollection() == null) throw new IllegalStateException("List Mapping collection specification cannot be null");
if (!cfg.getCollectionBuilders().canHandle(cfg.getListMappingCollection())) {
throw new IllegalStateException("List Mapping collection specification should be supported by used collection builders.");
}
return cfg;
}
private static final Formatters DEFAULT_FORMATTERS = new BasicFormatters();
private static final Location DEFAULT_LOCATION = Location.getDefault();
private static final String DEFAULT_MESSAGE_BUNDLE_NAME = "ValidationMessages";
private static final CollectionBuilders DEFAULT_COLLECTION_BUILDERS = new BasicCollectionBuilders();
private static final ArgumentNameResolver DEFAULT_ARGUMENT_NAME_RESOLVER = new AnnotationArgumentNameResolver();
private static final TokenAuthorizer DEFAULT_TOKEN_AUTHORIZER = new HashTokenAuthorizer();
private static BeanExtractor defaultBeanExtractor(PropertyMethodRegex accessorRegex) {
return new DefaultBeanExtractor(accessorRegex);
}
}
public Location getLocation() {
return location;
}
public String getMessageBundleName() {
return messageBundleName;
}
public Formatters getFormatters() {
return formatters;
}
public CollectionBuilders getCollectionBuilders() {
return collectionBuilders;
}
public ArgumentNameResolver getArgumentNameResolver() {
return argumentNameResolver;
}
public BeanExtractor getBeanExtractor() {
return beanExtractor;
}
public Binder getBinder() {
return binder;
}
public BeanValidator getBeanValidator() {
return beanValidator;
}
public TokenAuthorizer getTokenAuthorizer() {
return tokenAuthorizer;
}
/**
* Returns true if text from the form inputs should be trimmed before binding to form data.
* @return
*/
public boolean isInputTrimmed() {
return inputTrimmed;
}
public PropertyMethodRegex getAccessorRegex() {
return accessorRegex;
}
public PropertyMethodRegex getSetterRegex() {
return setterRegex;
}
/**
* Base URL for handling AJAX requests.
* @return
*/
public String getUrlBase() {
return urlBase;
}
/**
* Maximum width of form in number of columns.
* @return
*/
public int getColFormWidth() {
return colFormWidth;
}
/**
* Default width of label in number of columns.
* @return
*/
public int getColLabelWidth() {
return colLabelWidth;
}
/**
* Default width of input in number of columns.
* @return
*/
public int getColInputWidth() {
return colInputWidth;
}
/**
* Default instantiator of classes.
* @return
*/
public Instantiator getDefaultInstantiator() {
return defaultInstantiator;
}
/**
* Specification of collection which should be constructed for nested collections
* of complex types used in {@link MappingType#LIST} mappings.
* @return
*/
public CollectionSpec<?> getListMappingCollection() {
return listMappingCollection;
}
/**
* Separator of parts in the path (used in fully qualified field name).
* @return
*/
public String getPathSeparator() {
return pathSeparator;
}
}