aboutsummaryrefslogtreecommitdiffstats
path: root/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/BeanFieldGroup.java
diff options
context:
space:
mode:
Diffstat (limited to 'compatibility-server/src/main/java/com/vaadin/data/fieldgroup/BeanFieldGroup.java')
-rw-r--r--compatibility-server/src/main/java/com/vaadin/data/fieldgroup/BeanFieldGroup.java272
1 files changed, 272 insertions, 0 deletions
diff --git a/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/BeanFieldGroup.java b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/BeanFieldGroup.java
new file mode 100644
index 0000000000..96e4621761
--- /dev/null
+++ b/compatibility-server/src/main/java/com/vaadin/data/fieldgroup/BeanFieldGroup.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2000-2016 Vaadin Ltd.
+ *
+ * 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.
+ */
+package com.vaadin.data.fieldgroup;
+
+import java.beans.IntrospectionException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.vaadin.data.Item;
+import com.vaadin.data.util.BeanItem;
+import com.vaadin.data.util.BeanUtil;
+import com.vaadin.v7.data.validator.LegacyBeanValidator;
+import com.vaadin.v7.ui.LegacyField;
+
+public class BeanFieldGroup<T> extends FieldGroup {
+
+ private final Class<T> beanType;
+
+ private static Boolean beanValidationImplementationAvailable = null;
+ private final Map<LegacyField<?>, LegacyBeanValidator> defaultValidators;
+
+ public BeanFieldGroup(Class<T> beanType) {
+ this.beanType = beanType;
+ this.defaultValidators = new HashMap<LegacyField<?>, LegacyBeanValidator>();
+ }
+
+ @Override
+ protected Class<?> getPropertyType(Object propertyId) {
+ if (getItemDataSource() != null) {
+ return super.getPropertyType(propertyId);
+ } else {
+ // Data source not set so we need to figure out the type manually
+ /*
+ * toString should never really be needed as propertyId should be of
+ * form "fieldName" or "fieldName.subField[.subField2]" but the
+ * method declaration comes from parent.
+ */
+ try {
+ Class<?> type = BeanUtil.getPropertyType(beanType,
+ propertyId.toString());
+ if (type == null) {
+ throw new BindException(
+ "Cannot determine type of propertyId '" + propertyId
+ + "'. The propertyId was not found in "
+ + beanType.getName());
+ }
+ return type;
+ } catch (IntrospectionException e) {
+ throw new BindException("Cannot determine type of propertyId '"
+ + propertyId + "'. Unable to introspect " + beanType,
+ e);
+ }
+ }
+ }
+
+ @Override
+ protected Object findPropertyId(java.lang.reflect.Field memberField) {
+ String fieldName = memberField.getName();
+ Item dataSource = getItemDataSource();
+ if (dataSource != null
+ && dataSource.getItemProperty(fieldName) != null) {
+ return fieldName;
+ } else {
+ String minifiedFieldName = minifyFieldName(fieldName);
+ try {
+ return getFieldName(beanType, minifiedFieldName);
+ } catch (SecurityException e) {
+ } catch (NoSuchFieldException e) {
+ }
+ }
+ return null;
+ }
+
+ private static String getFieldName(Class<?> cls, String propertyId)
+ throws SecurityException, NoSuchFieldException {
+ for (java.lang.reflect.Field field1 : cls.getDeclaredFields()) {
+ if (propertyId.equals(minifyFieldName(field1.getName()))) {
+ return field1.getName();
+ }
+ }
+ // Try super classes until we reach Object
+ Class<?> superClass = cls.getSuperclass();
+ if (superClass != null && superClass != Object.class) {
+ return getFieldName(superClass, propertyId);
+ } else {
+ throw new NoSuchFieldException();
+ }
+ }
+
+ /**
+ * Helper method for setting the data source directly using a bean. This
+ * method wraps the bean in a {@link BeanItem} and calls
+ * {@link #setItemDataSource(Item)}.
+ * <p>
+ * For null values, a null item is passed to
+ * {@link #setItemDataSource(Item)} to be properly clear fields.
+ *
+ * @param bean
+ * The bean to use as data source.
+ */
+ public void setItemDataSource(T bean) {
+ if (bean == null) {
+ setItemDataSource((Item) null);
+ } else {
+ setItemDataSource(new BeanItem<T>(bean, beanType));
+ }
+ }
+
+ @Override
+ public void setItemDataSource(Item item) {
+ if (item == null || (item instanceof BeanItem)) {
+ super.setItemDataSource(item);
+ } else {
+ throw new RuntimeException(getClass().getSimpleName()
+ + " only supports BeanItems as item data source");
+ }
+ }
+
+ @Override
+ public BeanItem<T> getItemDataSource() {
+ return (BeanItem<T>) super.getItemDataSource();
+ }
+
+ private void ensureNestedPropertyAdded(Object propertyId) {
+ if (getItemDataSource() != null) {
+ // The data source is set so the property must be found in the item.
+ // If it is not we try to add it.
+ try {
+ getItemProperty(propertyId);
+ } catch (BindException e) {
+ // Not found, try to add a nested property;
+ // BeanItem property ids are always strings so this is safe
+ getItemDataSource().addNestedProperty((String) propertyId);
+ }
+ }
+ }
+
+ @Override
+ public void bind(LegacyField field, Object propertyId) {
+ ensureNestedPropertyAdded(propertyId);
+ super.bind(field, propertyId);
+ }
+
+ @Override
+ public <T extends LegacyField> T buildAndBind(String caption,
+ Object propertyId, Class<T> fieldType) throws BindException {
+ ensureNestedPropertyAdded(propertyId);
+ return super.buildAndBind(caption, propertyId, fieldType);
+ }
+
+ @Override
+ public void unbind(LegacyField<?> field) throws BindException {
+ super.unbind(field);
+
+ LegacyBeanValidator removed = defaultValidators.remove(field);
+ if (removed != null) {
+ field.removeValidator(removed);
+ }
+ }
+
+ @Override
+ protected void configureField(LegacyField<?> field) {
+ super.configureField(field);
+ // Add Bean validators if there are annotations
+ if (isBeanValidationImplementationAvailable()
+ && !defaultValidators.containsKey(field)) {
+ LegacyBeanValidator validator = new LegacyBeanValidator(beanType,
+ getPropertyId(field).toString());
+ field.addValidator(validator);
+ if (field.getLocale() != null) {
+ validator.setLocale(field.getLocale());
+ }
+ defaultValidators.put(field, validator);
+ }
+ }
+
+ /**
+ * Checks whether a bean validation implementation (e.g. Hibernate Validator
+ * or Apache Bean Validation) is available.
+ *
+ * TODO move this method to some more generic location
+ *
+ * @return true if a JSR-303 bean validation implementation is available
+ */
+ protected static boolean isBeanValidationImplementationAvailable() {
+ if (beanValidationImplementationAvailable != null) {
+ return beanValidationImplementationAvailable;
+ }
+ try {
+ Class<?> validationClass = Class
+ .forName("javax.validation.Validation");
+ Method buildFactoryMethod = validationClass
+ .getMethod("buildDefaultValidatorFactory");
+ Object factory = buildFactoryMethod.invoke(null);
+ beanValidationImplementationAvailable = (factory != null);
+ } catch (Exception e) {
+ // no bean validation implementation available
+ beanValidationImplementationAvailable = false;
+ }
+ return beanValidationImplementationAvailable;
+ }
+
+ /**
+ * Convenience method to bind Fields from a given "field container" to a
+ * given bean with buffering disabled.
+ * <p>
+ * The returned {@link BeanFieldGroup} can be used for further
+ * configuration.
+ *
+ * @see #bindFieldsBuffered(Object, Object)
+ * @see #bindMemberFields(Object)
+ * @since 7.2
+ * @param bean
+ * the bean to be bound
+ * @param objectWithMemberFields
+ * the class that contains {@link LegacyField}s for bean
+ * properties
+ * @return the bean field group used to make binding
+ */
+ public static <T> BeanFieldGroup<T> bindFieldsUnbuffered(T bean,
+ Object objectWithMemberFields) {
+ return createAndBindFields(bean, objectWithMemberFields, false);
+ }
+
+ /**
+ * Convenience method to bind Fields from a given "field container" to a
+ * given bean with buffering enabled.
+ * <p>
+ * The returned {@link BeanFieldGroup} can be used for further
+ * configuration.
+ *
+ * @see #bindFieldsUnbuffered(Object, Object)
+ * @see #bindMemberFields(Object)
+ * @since 7.2
+ * @param bean
+ * the bean to be bound
+ * @param objectWithMemberFields
+ * the class that contains {@link LegacyField}s for bean
+ * properties
+ * @return the bean field group used to make binding
+ */
+ public static <T> BeanFieldGroup<T> bindFieldsBuffered(T bean,
+ Object objectWithMemberFields) {
+ return createAndBindFields(bean, objectWithMemberFields, true);
+ }
+
+ private static <T> BeanFieldGroup<T> createAndBindFields(T bean,
+ Object objectWithMemberFields, boolean buffered) {
+ @SuppressWarnings("unchecked")
+ BeanFieldGroup<T> beanFieldGroup = new BeanFieldGroup<T>(
+ (Class<T>) bean.getClass());
+ beanFieldGroup.setItemDataSource(bean);
+ beanFieldGroup.setBuffered(buffered);
+ beanFieldGroup.bindMemberFields(objectWithMemberFields);
+ return beanFieldGroup;
+ }
+
+}