org.fao.geonet.api.records.formatters.SchemaLocalizations.java Source code

Java tutorial

Introduction

Here is the source code for org.fao.geonet.api.records.formatters.SchemaLocalizations.java

Source

/*
 * Copyright (C) 2001-2016 Food and Agriculture Organization of the
 * United Nations (FAO-UN), United Nations World Food Programme (WFP)
 * and United Nations Environment Programme (UNEP)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 *
 * Contact: Jeroen Ticheler - FAO - Viale delle Terme di Caracalla 2,
 * Rome - Italy. email: geonetwork@osgeo.org
 */

package org.fao.geonet.api.records.formatters;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableTable;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import com.vividsolutions.jts.util.Assert;

import groovy.util.slurpersupport.GPathResult;

import org.fao.geonet.ApplicationContextHolder;
import org.fao.geonet.api.records.formatters.groovy.CurrentLanguageHolder;
import org.fao.geonet.api.tools.i18n.LanguageUtils;
import org.fao.geonet.constants.Geonet;
import org.fao.geonet.domain.IsoLanguage;
import org.fao.geonet.kernel.SchemaManager;
import org.fao.geonet.languages.IsoLanguagesMapper;
import org.fao.geonet.repository.IsoLanguageRepository;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;

import jeeves.server.dispatchers.guiservices.XmlFile;
import jeeves.server.sources.ServiceRequestFactory;

import static jeeves.config.springutil.JeevesDelegatingFilterProxy.getApplicationContextFromServletContext;

/**
 * Contains methods for efficiently accessing the translations in a schema's labels and codelists
 * files.
 *
 * @author Jesse on 11/28/2014.
 */
public class SchemaLocalizations {
    public static final String LANG_CODELIST_NS = "http://www.loc.gov/standards/iso639-2/";
    /**
     * Localization files from schema plugin and the parents.  each file should be tried from the
     * first to last.
     */
    private final List<SchemaLocalization> schemaLocalizations;
    private final CurrentLanguageHolder languageHolder;
    private final IsoLanguageRepository languageRepo;

    public SchemaLocalizations(ApplicationContext context, CurrentLanguageHolder languageHolder, String schema,
            String extraSchemas) throws IOException, JDOMException {
        this.languageHolder = languageHolder;
        this.languageRepo = context.getBean(IsoLanguageRepository.class);

        ArrayList<SchemaLocalization> tmpLocalizations = Lists.newArrayList();
        final SchemaManager schemaManager = context.getBean(SchemaManager.class);
        final Map<String, SchemaLocalization> allLocalizations = getSchemaLocalizations(context, schemaManager);
        tmpLocalizations.add(allLocalizations.get(schema));
        addParentLocalizations(schemaManager, allLocalizations, tmpLocalizations,
                getConfigFile(schemaManager, schema).dependOn());
        addParentLocalizations(schemaManager, allLocalizations, tmpLocalizations, extraSchemas);
        this.schemaLocalizations = Collections.unmodifiableList(tmpLocalizations);
    }

    /**
     * Get the strings.xml, codelists.xml and labels.xml for the correct language from the schema
     * plugin
     *
     * @return Map(SchemaName, SchemaLocalizations)
     */
    static Map<String, SchemaLocalization> loadSchemaLocalizations(ApplicationContext context,
            SchemaManager schemaManager) throws IOException, JDOMException {

        Map<String, SchemaLocalization> localization = Maps.newHashMap();
        final Set<String> allSchemas = schemaManager.getSchemas();
        for (String schema : allSchemas) {
            Map<String, XmlFile> schemaInfo = schemaManager.getSchemaInfo(schema);
            localization.put(schema, new SchemaLocalization(context, schema, schemaInfo));
        }

        return localization;
    }

    public static SchemaLocalizations create(String schema) throws IOException, JDOMException {
        Object obj = RequestContextHolder.getRequestAttributes();

        ServletRequestAttributes attributes = (ServletRequestAttributes) obj;
        HttpServletRequest request = attributes.getRequest();

        final ApplicationContext appContext = ApplicationContextHolder.get();
        final String lang3 = appContext.getBean(LanguageUtils.class).getIso3langCode(request.getLocales());
        final String lang2 = appContext.getBean(IsoLanguagesMapper.class).iso639_2_to_iso639_1(lang3);
        CurrentLanguageHolder languageHolder = new CurrentLanguageHolder() {
            @Override
            public String getLang3() {
                return lang3;
            }

            @Override
            public String getLang2() {
                return lang2;
            }
        };
        return new SchemaLocalizations(appContext, languageHolder, schema, null);
    }

    private void addParentLocalizations(SchemaManager schemaManager,
            Map<String, SchemaLocalization> allLocalizations, ArrayList<SchemaLocalization> tmpLocalizations,
            String dependantSchema) throws IOException {
        if (dependantSchema == null) {
            return;
        }

        final SchemaLocalization schemaLocalization = allLocalizations.get(dependantSchema);
        if (schemaLocalization != null) {
            tmpLocalizations.add(schemaLocalization);
        }

        final ConfigFile parentConfig = getConfigFile(schemaManager, dependantSchema);
        addParentLocalizations(schemaManager, allLocalizations, tmpLocalizations, parentConfig.dependOn());
    }

    @VisibleForTesting
    protected ConfigFile getConfigFile(SchemaManager schemaManager, String schema) throws IOException {
        final Path schemaDir = schemaManager.getSchemaDir(schema)
                .resolve(FormatterConstants.SCHEMA_PLUGIN_FORMATTER_DIR);
        return new ConfigFile(schemaDir, false, null);
    }

    @VisibleForTesting
    protected Map<String, SchemaLocalization> getSchemaLocalizations(ApplicationContext context,
            SchemaManager schemaManager) throws IOException, JDOMException {
        return loadSchemaLocalizations(context, schemaManager);
    }

    /**
     * Obtain a translation for the given node by looking up the elements name in the the schema's
     * labels.xml file
     *
     * @param node the node to get a translation for.
     */
    public String nodeLabel(GPathResult node) throws Exception {
        String parentNodeName = parentNodeName(node);
        return nodeLabel(node.name(), parentNodeName);
    }

    /**
     * Look up a translation in the schema's labels.xml file
     *
     * @param qualifiedNodeName       the name to use as a key for the lookup
     * @param qualifiedParentNodeName the name of the parent, used as the second lookup key.  This
     *                                can be null and the default value will be returned
     */
    public String nodeLabel(String qualifiedNodeName, String qualifiedParentNodeName) throws Exception {
        return nodeTranslation(qualifiedNodeName, qualifiedParentNodeName, "label");
    }

    /**
     * Obtain the description for the given node by looking up the elements name in the the schema's
     * labels.xml file
     *
     * @param node the node to get a description for.
     */
    public String nodeDesc(GPathResult node) throws Exception {
        String parentNodeName = parentNodeName(node);
        return nodeDesc(node.name(), parentNodeName);
    }

    protected String parentNodeName(GPathResult node) {
        GPathResult parentNode = node.parent();
        String parentNodeName = null;
        if (parentNode != node) {
            parentNodeName = parentNode.name();
        }
        return parentNodeName;
    }

    /**
     * Look up a description in the schema's labels.xml file
     *
     * @param qualifiedNodeName       the name to use as a key for the lookup
     * @param qualifiedParentNodeName the name of the parent, used as the second lookup key.  This
     *                                can be null and the default value will be returned
     */
    public String nodeDesc(String qualifiedNodeName, String qualifiedParentNodeName) throws Exception {

        return nodeTranslation(qualifiedNodeName, qualifiedParentNodeName, "description");
    }

    public String nodeTranslation(String qualifiedNodeName, String qualifiedParentNodeName, String type)
            throws Exception {
        if (qualifiedParentNodeName == null) {
            qualifiedParentNodeName = "";
        }

        for (SchemaLocalization schemaLocalization : this.schemaLocalizations) {
            final ImmutableTable<String, String, Element> labelIndex = schemaLocalization
                    .getLabelIndex(this.languageHolder.getLang3());
            Element element = labelIndex.get(qualifiedNodeName, qualifiedParentNodeName);
            if (element == null) {
                element = labelIndex.get(qualifiedNodeName, "");
            }
            if (element == null) {
                final ImmutableCollection<Element> values = labelIndex.row(qualifiedNodeName).values();
                if (!values.isEmpty()) {
                    element = values.iterator().next();
                }
            }
            if (element != null) {
                return element.getChildText(type);
            }
        }

        return qualifiedNodeName;
    }

    /**
     * Obtain a translation for the given codelist by looking up the codelist and codelist value in
     * the the schema's codelists.xml file * @param node a node containing a codeListValue attribute
     * and a codeList attribute
     */
    public String codelistValueLabel(GPathResult node) throws Exception {
        return codelistValueLabel(node.getProperty("@codeList").toString(),
                node.getProperty("@codeListValue").toString());
    }

    /**
     * Obtain a translation for the given codelist by looking up the codelist and codelist value in
     * the the schema's codelists.xml file.
     *
     * @param codelist the name of the codelist
     * @param value    the codelist value
     */
    public String codelistValueLabel(String codelist, String value) throws Exception {
        return codelistTranslation(codelist, value, "label");
    }

    /**
     * Obtain the description for the given codelist by looking up the codelist and codelist value
     * in the the schema's codelists.xml file.
     *
     * @param node a node containing a codeListValue attribute and a codeList attribute
     */
    public String codelistValueDesc(GPathResult node) throws Exception {
        return codelistValueDesc(node.getProperty("@codeList").toString(),
                node.getProperty("@codeListValue").toString());
    }

    /**
     * Obtain the description for the given codelist by looking up the codelist and codelist value
     * in the the schema's codelists.xml file.
     *
     * @param codelist the name of the codelist
     * @param value    the codelist value
     */
    public String codelistValueDesc(String codelist, String value) throws Exception {
        return codelistTranslation(codelist, value, "description");
    }

    public String codelistTranslation(String codelist, String context, String type) throws Exception {

        String codelistName = extractCodeListName(codelist);
        if (LANG_CODELIST_NS.equals(codelist) || "#LanguageCode".equals(codelist)
                || "LanguageCode".equals(codelistName)) {
            return translateLanguageCode(context);
        }

        for (SchemaLocalization schemaLocalization : this.schemaLocalizations) {
            Element codelistEl = schemaLocalization.getCodeListIndex(this.languageHolder.getLang3())
                    .get(codelistName, context);
            if (codelistEl != null) {
                return codelistEl.getChildText(type);
            }
        }
        return context;
    }

    public String translateLanguageCode(String value) {
        if (value == null) {
            return null;
        }
        List<IsoLanguage> lang;
        if (value.equals("deu")) {
            value = "ger";
        }

        if (value.length() == 2) {
            lang = this.languageRepo.findAllByShortCode(value.toLowerCase());
        } else {
            lang = this.languageRepo.findAllByCode(value.toLowerCase());
        }

        if (!lang.isEmpty()) {
            final IsoLanguage isoLanguage = lang.get(0);
            String label = isoLanguage.getLabel(languageHolder.getLang3());
            if (label == null) {
                label = isoLanguage.getLabel(Geonet.DEFAULT_LANGUAGE);
            }
            if (label == null) {
                label = value;
            }

            return label;
        }
        return value;
    }

    private String extractCodeListName(String codelist) {
        final int indexOfPound = codelist.lastIndexOf('#');
        if (indexOfPound > -1) {
            codelist = codelist.substring(indexOfPound + 1);
        }
        return codelist;
    }

    public Collection<String> codelist(String codelistName) throws Exception {
        int prefix = codelistName.indexOf(':');
        if (prefix > -1) {
            codelistName = codelistName.substring(prefix + 1);
        }

        Set<String> codelists = Sets.newHashSet();
        for (SchemaLocalization schemaLocalization : this.schemaLocalizations) {
            final ImmutableTable<String, String, Element> codeListIndex = schemaLocalization
                    .getCodeListIndex(this.languageHolder.getLang3());
            codelists.addAll(codeListIndex.row(codelistName).keySet());
        }
        return codelists;
    }

    /**
     * Translate a string in the schema's strings.xml file.  Each element in the key array is one
     * level deep in the xml tree.
     *
     * If there are two elements with the same name the second element will be ignored.
     *
     * @param key the lookup key of the codelist
     */
    public String schemaString(String... key) throws Exception {
        Assert.isTrue(key.length > 0, "There must be at least one key value");

        for (SchemaLocalization schemaLocalization : this.schemaLocalizations) {
            Element strings = schemaLocalization.getStrings(this.languageHolder.getLang3());
            for (int i = 0; i < key.length; i++) {
                strings = strings.getChild(key[i]);
                if (strings == null) {
                    break;
                }
            }
            if (strings != null) {
                return strings.getTextNormalize();
            }
        }

        return "[" + Joiner.on(',').join(key) + "]";
    }

}