Clover icon

Magnolia REST Content Delivery 2.1

  1. Project Clover database Fri Mar 16 2018 18:21:08 CET
  2. Package info.magnolia.rest.delivery.jcr.i18n

File I18nContainerRequestFilter.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart0.png
47% of files have more coverage

Code metrics

24
35
7
1
150
82
25
0.71
5
7
3.57

Classes

Class Line # Actions
I18nContainerRequestFilter 61 35 0% 25 66
0.00%
 

Contributing tests

No tests hitting this source file were found.

Source view

1    /**
2    * This file Copyright (c) 2018 Magnolia International
3    * Ltd. (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10    * This file is distributed in the hope that it will be
11    * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12    * implied warranty of MERCHANTABILITY or FITNESS FOR A
13    * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14    * Redistribution, except as permitted by whichever of the GPL
15    * or MNA you select, is prohibited.
16    *
17    * 1. For the GPL license (GPL), you can redistribute and/or
18    * modify this file under the terms of the GNU General
19    * Public License, Version 3, as published by the Free Software
20    * Foundation. You should have received a copy of the GNU
21    * General Public License, Version 3 along with this program;
22    * if not, write to the Free Software Foundation, Inc., 51
23    * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24    *
25    * 2. For the Magnolia Network Agreement (MNA), this file
26    * and the accompanying materials are made available under the
27    * terms of the MNA which accompanies this distribution, and
28    * is available at http://www.magnolia-cms.com/mna.html
29    *
30    * Any modifications to this file must keep this entire header
31    * intact.
32    *
33    */
34    package info.magnolia.rest.delivery.jcr.i18n;
35   
36    import info.magnolia.cms.i18n.I18nContentSupport;
37    import info.magnolia.objectfactory.Components;
38   
39    import java.io.IOException;
40    import java.util.Iterator;
41    import java.util.List;
42    import java.util.Locale;
43   
44    import javax.ws.rs.container.ContainerRequestContext;
45    import javax.ws.rs.container.ContainerRequestFilter;
46    import javax.ws.rs.core.UriInfo;
47    import javax.ws.rs.core.Variant;
48    import javax.ws.rs.ext.Provider;
49   
50    import org.apache.commons.collections4.CollectionUtils;
51    import org.apache.commons.lang3.LocaleUtils;
52    import org.apache.commons.lang3.StringUtils;
53   
54    /**
55    * This filter determines a locale by detecting "Accept-Language" HTTP header and the "lang" request parameter.
56    * "lang" parameter has higher priority than "Accept-Language" header.
57    * If the locale can't be determined, {@link I18nContentSupport} will do the job.
58    */
59    @Provider
60    @I18n
 
61    public class I18nContainerRequestFilter implements ContainerRequestFilter {
62    private static final String LANGUAGE_PARAM = "lang";
63   
64    private final I18nContentSupport i18nContentSupport;
65   
 
66  0 toggle public I18nContainerRequestFilter() {
67  0 this.i18nContentSupport = Components.getComponent(I18nContentSupport.class);
68    }
69   
 
70  0 toggle @Override
71    public void filter(ContainerRequestContext containerRequestContext) throws IOException {
72  0 Locale preferredLocale = determineLocaleFromQueryParameter(containerRequestContext.getUriInfo());
73  0 if (preferredLocale == null) {
74  0 preferredLocale = determineLocaleFromRequest(containerRequestContext);
75    }
76   
77  0 if (preferredLocale != null && isLocaleSupported(preferredLocale)) {
78  0 i18nContentSupport.setLocale(preferredLocale);
79    }
80    }
81   
 
82  0 toggle private Locale determineLocaleFromQueryParameter(UriInfo uriInfo) {
83  0 String localeString = uriInfo.getQueryParameters().getFirst(LANGUAGE_PARAM);
84  0 if (StringUtils.isEmpty(localeString)) {
85  0 return null;
86    }
87  0 Locale locale = Locale.forLanguageTag(localeString);
88  0 if (!isValidLocale(locale)) {
89  0 return null;
90    }
91  0 if (!isLocaleSupported(locale)) {
92  0 locale = getNextLocale(locale);
93    }
94  0 return locale;
95    }
96   
 
97  0 toggle private Locale determineLocaleFromRequest(ContainerRequestContext request) {
98  0 if (CollectionUtils.isEmpty(i18nContentSupport.getLocales()) || CollectionUtils.isEmpty(request.getAcceptableLanguages())) {
99  0 return null;
100    }
101    // Accepts only for available languages.
102  0 List<Variant> variantList = Variant.VariantListBuilder.newInstance().languages(i18nContentSupport.getLocales().toArray(new Locale[] {})).build();
103  0 Variant bestMatch = request.getRequest().selectVariant(variantList);
104   
105  0 if (bestMatch != null && bestMatch.getLanguage() != null) {
106  0 return bestMatch.getLanguage();
107    }
108  0 return null;
109    }
110   
 
111  0 toggle private boolean isValidLocale(Locale locale) {
112  0 if (locale == null || (locale != null && StringUtils.isEmpty(locale.getLanguage()))) {
113  0 return false;
114    }
115  0 return LocaleUtils.isAvailableLocale(locale);
116    }
117   
 
118  0 toggle private boolean isLocaleSupported(Locale locale) {
119  0 return locale != null && i18nContentSupport.getLocales().stream().anyMatch(supportedLocale -> supportedLocale.equals(locale));
120    }
121   
122    /**
123    * Returns the closest locale for which {@link #isLocaleSupported(Locale)} is true.
124    * <ul>
125    * <li>If the locale has a country specified (fr_CH) the locale without country (fr) will be returned.</li>
126    * <li>If the locale has no country specified (fr) the first locale with the same language but specific country (fr_CH) will be returned.</li>
127    * <li>If this fails the fall-back locale is returned</li>
128    * </ul>
129    * Warning: if you have configured both (fr and fr_CH) this method will jiter between this two values.
130    */
 
131  0 toggle private Locale getNextLocale(Locale locale) {
132    // if this locale defines a country
133  0 if (StringUtils.isNotEmpty(locale.getCountry())) {
134    // try to use the language only
135  0 Locale langOnlyLocale = new Locale(locale.getLanguage());
136  0 if (isLocaleSupported(langOnlyLocale)) {
137  0 return langOnlyLocale;
138    }
139    }
140    // try to find a locale with the same language (ignore the country)
141  0 for (Iterator<Locale> iter = i18nContentSupport.getLocales().iterator(); iter.hasNext(); ) {
142  0 Locale otherCountryLocale = iter.next();
143    // same lang, but not the same country as well or we end up in the loop
144  0 if (locale.getLanguage().equals(otherCountryLocale.getLanguage()) && !locale.equals(otherCountryLocale)) {
145  0 return otherCountryLocale;
146    }
147    }
148  0 return null;
149    }
150    }