uk.co.q3c.v7.i18n.AnnotationI18NTranslator.java Source code

Java tutorial

Introduction

Here is the source code for uk.co.q3c.v7.i18n.AnnotationI18NTranslator.java

Source

/*
 * Copyright (C) 2013 David Sowerby
 * 
 * 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 uk.co.q3c.v7.i18n;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;

import javax.inject.Inject;
import javax.inject.Provider;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.vaadin.data.Property;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.Label;
import com.vaadin.ui.Table;

/**
 * Utility class to manipulate Vaadin component settings to reflect locale changes. This implementation uses field
 * annotations to specify the keys to use, and this {@link I18NTranslator} implementation then looks up the key values
 * and sets caption, description and value properties of the component.
 * <p>
 * There are two sources of annotations. They may be applied to the UI Components directly, or on an entity which is
 * being used as a model for form creation.
 * <p>
 * All the annotation parameters are optional. If caption or description keys are not specified, then the caption or
 * description of the component is set to null. If the value key is not specified, the value of the component remains
 * unchanged.
 * <p>
 * The value parameter is only relevant to components which implement the {@link com.vaadin.data.Property} interface
 * (for example {@link Label}), and if a value key is specified for any other component, it is ignored
 * <p>
 * The annotations used are those registered using {@link CurrentLocale#registerAnnotation(Class, Provider)}. Note that
 * {@link I18N} is registered by default.
 * <p>
 * The locale of all components with a registered annotation is always updated to {@link CurrentLocale#getLocale()}
 * <p>
 * The call is cascaded to any contained properties which implement the {@link I18NListener} interface. Any compound
 * components you wish to include within the scope of I18N should therefore implement the {@link I18NListener}
 * interface.
 * 
 * @author David Sowerby 8 Feb 2013
 * 
 */
public class AnnotationI18NTranslator implements I18NTranslator {
    private static Logger log = LoggerFactory.getLogger(AnnotationI18NTranslator.class);
    private final CurrentLocale currentLocale;
    private final Provider<I18NTranslator> translatorPro;
    private final Map<Class<? extends Annotation>, Provider<? extends I18NAnnotationReader>> readers;

    @Inject
    protected AnnotationI18NTranslator(CurrentLocale currentLocale, Provider<I18NTranslator> translatorPro) {
        super();
        this.currentLocale = currentLocale;
        this.translatorPro = translatorPro;
        this.readers = currentLocale.getI18NReaders();

    }

    /**
     * @see uk.co.q3c.v7.i18n.I18NTranslator#translate(uk.co.q3c.v7.i18n.I18NListener)
     */
    @Override
    public void translate(I18NListener listener) {
        Class<?> clazz = listener.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {

            // process any subitems which implement I18NListener
            if (I18NListener.class.isAssignableFrom(field.getType())) {
                processSubI18NListener(listener, field);
            }

            if (AbstractComponent.class.isAssignableFrom(field.getType())) {
                processComponent(listener, field);
            }

        }
    }

    private void processSubI18NListener(I18NListener listener, Field field) {
        field.setAccessible(true);
        try {
            I18NListener sub = (I18NListener) field.get(listener);
            sub.localeChange(translatorPro.get());
        } catch (IllegalArgumentException | IllegalAccessException e) {
            log.error("Unable to process I18N sub-listener " + field.getName(), e);
        }
    }

    private void processComponent(I18NListener listener, Field field) {

        for (Entry<Class<? extends Annotation>, Provider<? extends I18NAnnotationReader>> readerEntry : readers
                .entrySet()) {
            if (field.isAnnotationPresent(readerEntry.getKey())) {
                decodeAnnotation(listener, field, field.getAnnotation(readerEntry.getKey()),
                        readerEntry.getValue());
            }
        }
        return;

    }

    private void decodeAnnotation(I18NListener listener, Field field, Annotation annotation,
            Provider<? extends I18NAnnotationReader> provider) {

        // get a reader
        I18NAnnotationReader reader = provider.get();

        // get the keys from the reader
        I18NKey<?> captionKey = reader.caption(annotation);
        I18NKey<?> descriptionKey = reader.description(annotation);
        I18NKey<?> valueKey = reader.value(annotation);

        // check for nulls. Nulls are used for caption and description so that content can be cleared.
        // for value, this is not the case, as it may be a bad idea
        String captionValue = captionKey.isNullKey() ? null : captionKey.getValue(currentLocale.getLocale());
        String descriptionValue = descriptionKey.isNullKey() ? null
                : descriptionKey.getValue(currentLocale.getLocale());

        // set caption and description
        field.setAccessible(true);
        try {
            AbstractComponent c = (AbstractComponent) field.get(listener);
            if (captionValue != null) {
                c.setCaption(captionValue);
            }
            if (descriptionValue != null) {
                c.setDescription(descriptionValue);
            }
            c.setLocale(currentLocale.getLocale());
        } catch (IllegalArgumentException | IllegalAccessException e) {
            log.error("Unable to set I18N caption or description for " + field.getName(), e);
        }

        // These components have a value. Usually I18N would only be used for Label values. If no key is provided
        // the component value is left unchanged
        if (valueKey != null) {
            if (Property.class.isAssignableFrom(field.getType())) {
                try {
                    @SuppressWarnings("unchecked")
                    Property<String> c = (Property<String>) field.get(listener);
                    String valueValue = valueKey.isNullKey() ? null : valueKey.getValue(currentLocale.getLocale());
                    if (valueValue != null) {
                        c.setValue(valueValue);
                    }
                } catch (Exception e) {
                    log.error("Unable to set I18N value for " + field.getName(), e);

                }
            }
        }

        // Table columns need special treatment
        if (Table.class.isAssignableFrom(field.getType())) {
            try {
                Table table = (Table) field.get(listener);
                Object[] columns = table.getVisibleColumns();
                List<String> headers = new ArrayList<>();
                for (Object column : columns) {
                    if (column instanceof LabelKey) {
                        LabelKey columnid = (LabelKey) column;
                        String header = columnid.getValue(currentLocale.getLocale());
                        headers.add(header);
                    } else {
                        headers.add(column.toString());
                    }
                }
                String headerArray[] = headers.toArray(new String[] {});
                table.setColumnHeaders(headerArray);

            } catch (Exception e) {
                log.error("Unable to set I18N table columns headers for " + field.getName(), e);
            }

        }
    }

    @Override
    public Locale getLocale() {
        return currentLocale.getLocale();
    }

    public void apply(AbstractComponent component, Field fieldWithAnnotations) {

    }

}