123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- /*
- * Copyright 2000-2014 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.ui.declarative;
-
- import java.beans.IntrospectionException;
- import java.io.Serializable;
- import java.lang.reflect.Field;
- import java.lang.reflect.InvocationTargetException;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Locale;
- import java.util.Map;
- import java.util.logging.Logger;
-
- import com.vaadin.ui.Component;
- import com.vaadin.util.ReflectTools;
-
- /**
- * Binder utility that binds member fields of a design class instance to given
- * component instances. Only fields of type {@link Component} are bound
- *
- * @since 7.4
- * @author Vaadin Ltd
- */
- public class FieldBinder implements Serializable {
-
- // the instance containing the bound fields
- private Object bindTarget;
- // mapping between field names and Fields
- private Map<String, Field> fieldMap = new HashMap<String, Field>();
-
- /**
- * Creates a new instance of LayoutFieldBinder
- *
- * @param design
- * the design class instance containing the fields to bind
- * @throws IntrospectionException
- * if the given design class can not be introspected
- */
- public FieldBinder(Object design) throws IntrospectionException {
- this(design, design.getClass());
- }
-
- /**
- * Creates a new instance of LayoutFieldBinder
- *
- * @param design
- * the instance containing the fields
- * @param classWithFields
- * the class which defines the fields to bind
- * @throws IntrospectionException
- * if the given design class can not be introspected
- */
- public FieldBinder(Object design, Class<?> classWithFields)
- throws IntrospectionException {
- if (design == null) {
- throw new IllegalArgumentException("The design must not be null");
- }
- bindTarget = design;
- resolveFields(classWithFields);
- }
-
- /**
- * Returns a collection of field names that are not bound
- *
- * @return a collection of fields assignable to Component that are not bound
- */
- public Collection<String> getUnboundFields() throws FieldBindingException {
- List<String> unboundFields = new ArrayList<String>();
- for (Field f : fieldMap.values()) {
- try {
- Object value = ReflectTools.getJavaFieldValue(bindTarget, f);
- if (value == null) {
- unboundFields.add(f.getName());
- }
- } catch (IllegalArgumentException e) {
- throw new FieldBindingException("Could not get field value", e);
- } catch (IllegalAccessException e) {
- throw new FieldBindingException("Could not get field value", e);
- } catch (InvocationTargetException e) {
- throw new FieldBindingException("Could not get field value", e);
- }
- }
- if (unboundFields.size() > 0) {
- getLogger().severe(
- "Found unbound fields in component root :" + unboundFields);
- }
- return unboundFields;
- }
-
- /**
- * Resolves the fields of the design class instance
- */
- private void resolveFields(Class<?> classWithFields) {
- for (Field memberField : getFieldsInDeclareOrder(classWithFields)) {
- if (Component.class.isAssignableFrom(memberField.getType())) {
- fieldMap.put(memberField.getName().toLowerCase(Locale.ENGLISH),
- memberField);
- }
- }
- }
-
- /**
- * Tries to bind the given {@link Component} instance to a member field of
- * the bind target. The name of the bound field is constructed based on the
- * id or caption of the instance, depending on which one is defined. If a
- * field is already bound (not null), {@link FieldBindingException} is
- * thrown.
- *
- * @param instance
- * the instance to be bound to a field
- * @return true on success, otherwise false
- * @throws FieldBindingException
- * if error occurs when trying to bind the instance to a field
- */
- public boolean bindField(Component instance) {
- return bindField(instance, null);
- }
-
- /**
- * Tries to bind the given {@link Component} instance to a member field of
- * the bind target. The fields are matched based on localId, id and caption.
- * If a field is already bound (not null), {@link FieldBindingException} is
- * thrown.
- *
- * @param instance
- * the instance to be bound to a field
- * @param localId
- * the localId used for mapping the field to an instance field
- * @return true on success
- * @throws FieldBindingException
- * if error occurs when trying to bind the instance to a field
- */
- public boolean bindField(Component instance, String localId) {
- // check that the field exists, is correct type and is null
- boolean success = bindFieldByIdentifier(localId, instance);
- if (!success) {
- success = bindFieldByIdentifier(instance.getId(), instance);
- }
- if (!success) {
- success = bindFieldByIdentifier(instance.getCaption(), instance);
- }
- if (!success) {
- String idInfo = "localId: " + localId + " id: " + instance.getId()
- + " caption: " + instance.getCaption();
- getLogger().finest(
- "Could not bind component to a field "
- + instance.getClass().getName() + " " + idInfo);
- }
- return success;
- }
-
- /**
- * Tries to bind the given {@link Component} instance to a member field of
- * the bind target. The field is matched based on the given identifier. If a
- * field is already bound (not null), {@link FieldBindingException} is
- * thrown.
- *
- * @param identifier
- * the identifier for the field.
- * @param instance
- * the instance to be bound to a field
- * @return true on success
- * @throws FieldBindingException
- * if error occurs when trying to bind the instance to a field
- */
- private boolean bindFieldByIdentifier(String identifier, Component instance) {
- try {
- // create and validate field name
- String fieldName = asFieldName(identifier);
- if (fieldName.length() == 0) {
- return false;
- }
- // validate that the field can be found
- Field field = fieldMap.get(fieldName.toLowerCase(Locale.ENGLISH));
- if (field == null) {
- getLogger().fine(
- "No field was found by identifier " + identifier);
- return false;
- }
- // validate that the field is not set
- Object fieldValue = ReflectTools.getJavaFieldValue(bindTarget,
- field);
- if (fieldValue != null) {
- getLogger().severe(
- "The field with identifier \"" + identifier
- + "\" already mapped");
- throw new FieldBindingException(
- "Duplicate identifier found for a field: " + fieldName);
- }
- // set the field value
- ReflectTools.setJavaFieldValue(bindTarget, field, instance);
- return true;
- } catch (IllegalAccessException e) {
- throw new FieldBindingException("Field binding failed", e);
- } catch (IllegalArgumentException e) {
- throw new FieldBindingException("Field binding failed", e);
- } catch (InvocationTargetException e) {
- throw new FieldBindingException("Field binding failed", e);
- }
- }
-
- /**
- * Converts the given identifier to a valid field name by stripping away
- * illegal character and setting the first letter of the name to lowercase
- *
- * @param identifier
- * the identifier to be converted to field name
- * @return the field name corresponding the identifier
- */
- private static String asFieldName(String identifier) {
- if (identifier == null) {
- return "";
- }
- StringBuilder result = new StringBuilder();
- for (int i = 0; i < identifier.length(); i++) {
- char character = identifier.charAt(i);
- if (Character.isJavaIdentifierPart(character)) {
- result.append(character);
- }
- }
- // lowercase first letter
- if (result.length() > 0 && Character.isLetter(result.charAt(0))) {
- result.setCharAt(0, Character.toLowerCase(result.charAt(0)));
- }
- return result.toString();
- }
-
- /**
- * Returns an array containing Field objects reflecting all the fields of
- * the class or interface represented by this Class object. The elements in
- * the array returned are sorted in declare order. The fields in
- * superclasses are excluded.
- *
- * @param searchClass
- * the class to be scanned for fields
- * @return the list of fields in this class
- */
- protected static List<java.lang.reflect.Field> getFieldsInDeclareOrder(
- Class<?> searchClass) {
- ArrayList<java.lang.reflect.Field> memberFieldsInOrder = new ArrayList<java.lang.reflect.Field>();
-
- for (java.lang.reflect.Field memberField : searchClass
- .getDeclaredFields()) {
- memberFieldsInOrder.add(memberField);
- }
- return memberFieldsInOrder;
- }
-
- private static Logger getLogger() {
- return Logger.getLogger(FieldBinder.class.getName());
- }
-
- }
|