eu.esdihumboldt.hale.common.align.model.impl.AbstractCellExplanation.java Source code

Java tutorial

Introduction

Here is the source code for eu.esdihumboldt.hale.common.align.model.impl.AbstractCellExplanation.java

Source

/*
 * Copyright (c) 2012 Data Harmonisation Panel
 * 
 * All rights reserved. This program and the accompanying materials are made
 * available under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution. If not, see <http://www.gnu.org/licenses/>.
 * 
 * Contributors:
 *     HUMBOLDT EU Integrated Project #030962
 *     Data Harmonisation Panel <http://www.dhpanel.eu>
 */

package eu.esdihumboldt.hale.common.align.model.impl;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.stream.Collectors;

import javax.annotation.Nullable;

import org.apache.commons.lang.StringEscapeUtils;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import com.google.common.base.Joiner;

import de.fhg.igd.slf4jplus.ALogger;
import de.fhg.igd.slf4jplus.ALoggerFactory;
import eu.esdihumboldt.hale.common.align.model.AlignmentUtil;
import eu.esdihumboldt.hale.common.align.model.Cell;
import eu.esdihumboldt.hale.common.align.model.CellExplanation;
import eu.esdihumboldt.hale.common.align.model.ChildContext;
import eu.esdihumboldt.hale.common.align.model.Entity;
import eu.esdihumboldt.hale.common.align.model.EntityDefinition;
import eu.esdihumboldt.hale.common.core.service.ServiceProvider;

/**
 * Abstract cell explanation implementation.
 * 
 * @author Simon Templer
 */
public abstract class AbstractCellExplanation implements CellExplanation {

    private static final ALogger log = ALoggerFactory.getLogger(AbstractCellExplanation.class);

    @Override
    public String getExplanation(Cell cell, ServiceProvider provider, Locale locale) {
        return getExplanation(cell, false, provider, locale);
    }

    @Override
    public String getExplanationAsHtml(Cell cell, ServiceProvider provider, Locale locale) {
        return getExplanation(cell, true, provider, locale);
    }

    /**
     * Get the explanation string in the specified format.
     * 
     * @param cell the cell to create an explanation for
     * @param html if the format should be HMTL, otherwise the format is just
     *            text
     * @param provider the service provider, if available
     * @param locale the locale for the explanation, to be matched if content is
     *            available
     * @return the explanation or <code>null</code>
     */
    protected abstract String getExplanation(Cell cell, boolean html, @Nullable ServiceProvider provider,
            Locale locale);

    /**
     * Format an entity for inclusion in an explanation.
     * 
     * @param entity the entity, may be <code>null</code>
     * @param html if the format should be HMTL, otherwise the format is just
     *            text
     * @param indexInFront whether index conditions should be in front of the
     *            property name or behind in brackets
     * @param locale the locale for the explanation, to be matched if content is
     *            available
     * @return the formatted entity name or <code>null</code> in case of
     *         <code>null</code> input
     */
    protected String formatEntity(Entity entity, boolean html, boolean indexInFront, Locale locale) {
        if (entity == null)
            return null;

        return formatEntity(entity.getDefinition(), html, indexInFront, locale);
    }

    /**
     * Format an entity for inclusion in an explanation.
     * 
     * @param entityDef the entity definition, may be <code>null</code>
     * @param html if the format should be HMTL, otherwise the format is just
     *            text
     * @param indexInFront whether index conditions should be in front of the
     *            property name or behind in brackets
     * @param locale the locale for the explanation, to be matched if content is
     *            available
     * @return the formatted entity name or <code>null</code> in case of
     *         <code>null</code> input
     */
    protected String formatEntity(EntityDefinition entityDef, boolean html, boolean indexInFront, Locale locale) {
        if (entityDef == null)
            return null;
        // get name and standard text
        String name = entityDef.getDefinition().getDisplayName();
        String text = quoteName(name, html);

        // modify text with filter
        List<ChildContext> path = entityDef.getPropertyPath();
        // different output than AlignmentUtil in case of property with index
        // condition
        if (path != null && !path.isEmpty() && path.get(path.size() - 1).getIndex() != null) {
            if (indexInFront) {
                text = MessageFormat.format(getBaseMessage("index_pre", locale),
                        formatNumber(path.get(path.size() - 1).getIndex() + 1, locale), text);
            } else {
                text += " " + MessageFormat.format(getBaseMessage("index_post", locale),
                        formatNumber(path.get(path.size() - 1).getIndex() + 1, locale));
            }
        } else {
            String filterString = AlignmentUtil.getContextText(entityDef);
            if (html) {
                filterString = StringEscapeUtils.escapeHtml(filterString);
            }
            if (filterString != null)
                text += " " + MessageFormat.format(getBaseMessage("filter", locale), quoteText(filterString, html));
        }
        return text;
    }

    /**
     * Returns an entity name without condition strings (e.g. "part1.part2").
     * 
     * @param entity the entity
     * @return the entity name
     */
    protected String getEntityNameWithoutCondition(Entity entity) {
        EntityDefinition entityDef = entity.getDefinition();
        if (entityDef.getPropertyPath() != null && !entityDef.getPropertyPath().isEmpty()) {
            List<String> names = new ArrayList<String>();
            for (ChildContext context : entityDef.getPropertyPath()) {
                names.add(context.getChild().getName().getLocalPart());
            }
            String longName = Joiner.on('.').join(names);
            return longName;
        } else
            return entityDef.getDefinition().getDisplayName();
    }

    /**
     * Checks whether the given entity has an index condition.
     * 
     * @param entity the entity to check
     * @return true, if the entity has an index condition
     */
    protected boolean hasIndexCondition(Entity entity) {
        List<ChildContext> path = entity.getDefinition().getPropertyPath();
        return path != null && !path.isEmpty() && path.get(path.size() - 1).getIndex() != null;
    }

    /**
     * Quote or otherwise format (in case of HTML) the given text.
     * 
     * @param text the text, may be <code>null</code>
     * @param html if the format should be HMTL, otherwise the format is just
     *            text
     * @return the quoted text or <code>null</code> in case of <code>null</code>
     *         input
     */
    protected String quoteText(String text, boolean html) {
        if (text == null)
            return null;
        if (html)
            return "<span style=\"font-style: italic;\">" + text + "</span>";
        else
            return "'" + text + "'";
    }

    /**
     * Quote or otherwise format (in case of HTML) the given value.
     * 
     * @param value the value to quote, may be <code>null</code>
     * @param html if the format should be HMTL, otherwise the format is just
     *            text
     * @return the quoted text or <code>null</code> in case of <code>null</code>
     *         input
     */
    protected String quoteValue(Object value, boolean html) {
        if (value == null)
            return null;
        if (html)
            return "<code>" + value + "</code>";
        else
            return "`" + value + "`";
    }

    /**
     * Quote or otherwise format (in case of HTML) the given name (e.g. an
     * entity or parameter name).
     * 
     * @param name the name to quote, may be <code>null</code>
     * @param html if the format should be HMTL, otherwise the format is just
     *            text
     * @return the quoted text or <code>null</code> in case of <code>null</code>
     *         input
     */
    protected String quoteName(String name, boolean html) {
        if (name == null)
            return null;
        if (html)
            return "<em>" + name + "</em>";
        else
            return "'" + name + "'";
    }

    private String formatNumber(int number, Locale locale) {
        switch (number) {
        case 1:
            return getBaseMessage("first", locale);
        case 2:
            return getBaseMessage("second", locale);
        case 3:
            return getBaseMessage("third", locale);
        case 4:
            return getBaseMessage("fourth", locale);
        case 5:
            return getBaseMessage("fifth", locale);
        case 6:
            return getBaseMessage("sixth", locale);
        default:
            return (number + 1) + ".";
        }
    }

    /**
     * Get a message for a specific locale.
     * 
     * @param key the message key
     * @param locale the locale
     * @return the message string
     */
    protected String getMessage(String key, Locale locale) {
        return getMessage(key, locale, getDefaultMessageClass());
    }

    /**
     * Get a message for a specific locale.
     * 
     * @param key the message key
     * @param locale the locale
     * @param messageClass the class the messages to retrieve are associated to
     * @return the message string
     */
    protected String getMessage(String key, Locale locale, Class<?> messageClass) {
        return ResourceBundle
                .getBundle(messageClass.getName(), locale, messageClass.getClassLoader(),
                        ResourceBundle.Control.getNoFallbackControl(ResourceBundle.Control.FORMAT_PROPERTIES))
                .getString(key);
    }

    /**
     * Get the class used to retrieve messages via
     * {@link #getMessage(String, Locale)}
     * 
     * @return the default message class
     */
    protected Class<?> getDefaultMessageClass() {
        return getClass();
    }

    /**
     * Get a message for a specific locale that is stored with the
     * {@link AbstractCellExplanation} explanation base class.
     * 
     * @param key the message key
     * @param locale the locale
     * @return the message string
     */
    private String getBaseMessage(String key, Locale locale) {
        return ResourceBundle
                .getBundle(AbstractCellExplanation.class.getName(), locale,
                        AbstractCellExplanation.class.getClassLoader(),
                        ResourceBundle.Control.getNoFallbackControl(ResourceBundle.Control.FORMAT_PROPERTIES))
                .getString(key);
    }

    @Override
    public Iterable<Locale> getSupportedLocales() {
        try {
            return findLocales(getDefaultMessageClass(), getDefaultMessageClass().getSimpleName(), "properties",
                    getDefaultLocale());
        } catch (IOException e) {
            log.error("Error determining supported locales for explanation", e);
            return null;
        }
    }

    /**
     * Get the default locale assumed for resources with an unspecified locale.
     * 
     * @return the default locale assumed for messages
     */
    protected Locale getDefaultLocale() {
        return Locale.ENGLISH;
    }

    /**
     * Determine the locales a resource is available for.
     * 
     * @param clazz the clazz the resource resides next to
     * @param baseName the base name of the resource
     * @param suffix the suffix of the resource file, e.g.
     *            <code>properties</code>
     * @param defaultLocale the default locale to be assumed for an unqualified
     *            resource
     * @return the set of locales the resource is available for
     * @throws IOException if an error occurs trying to determine the resource
     *             files
     */
    public static Set<Locale> findLocales(final Class<?> clazz, final String baseName, final String suffix,
            Locale defaultLocale) throws IOException {
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(
                clazz.getClassLoader());
        String pkg = clazz.getPackage().getName().replaceAll("\\.", "/");
        String pattern = pkg + "/" + baseName + "*." + suffix;
        return Arrays.stream(resolver.getResources(pattern)).map(resource -> {
            String fileName = resource.getFilename();

            if (fileName != null && fileName.startsWith(baseName)) {
                fileName = fileName.substring(baseName.length());
                if (fileName.endsWith("." + suffix)) {
                    if (fileName.length() == suffix.length() + 1) {
                        // default locale file
                        return defaultLocale;
                    } else {
                        String localeIdent = fileName.substring(0, fileName.length() - suffix.length() - 1);

                        String language = "";
                        String country = "";
                        String variant = "";

                        String[] parts = localeIdent.split("_");
                        int index = 0;
                        if (parts.length > index && parts[index].isEmpty()) {
                            index++;
                        }

                        if (parts.length > index) {
                            language = parts[index++];
                        }

                        if (parts.length > index) {
                            country = parts[index++];
                        }

                        if (parts.length > index) {
                            variant = parts[index++];
                        }

                        return new Locale(language, country, variant);
                    }
                } else {
                    log.error("Invalid resource encountered");
                    return null;
                }
            } else {
                log.error("Invalid resource encountered");
                return null;
            }
        }).filter(locale -> locale != null).collect(Collectors.toSet());
    }

    /**
     * Create a string enumerating the given items.
     * 
     * @param items the collection of items
     * @param locale the locale
     * @return the joined string
     */
    protected String enumerateJoin(List<String> items, Locale locale) {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < items.size(); i++) {
            result.append(items.get(i));

            if (i == items.size() - 2) {
                result.append(' ');
                result.append(getBaseMessage("and", locale));
                result.append(' ');
            } else if (i < items.size() - 2) {
                result.append(", ");
            }
        }
        return result.toString();
    }

    /**
     * Build a replacement table (HTML only).
     * 
     * @param varToProperty variable expressions mapped to the entities that
     *            replace them
     * @param locale the locale
     * @return the replacement table as string
     */
    protected String buildReplacementTable(Map<String, String> varToProperty, Locale locale) {
        StringBuilder sb = new StringBuilder();
        sb.append("<br /><br />");
        sb.append(getBaseMessage("rt_intro", locale));
        sb.append("<br />");
        sb.append("<table border=\"1\"><tr><th>");
        sb.append(getBaseMessage("rt_variable", locale));
        sb.append("</th><th>");
        sb.append(getBaseMessage("rt_property", locale));
        sb.append("</th></tr>");
        for (Entry<String, String> entry : varToProperty.entrySet()) {
            sb.append(String.format("<tr><td>%s</td><td>%s</td></tr>", entry.getKey(), entry.getValue()));
        }
        sb.append("</table>");
        return sb.toString();
    }

}