View Javadoc
1   package org.codehaus.mojo.jaxb2.shared.environment.locale;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.plugin.MojoExecutionException;
23  import org.apache.maven.plugin.logging.Log;
24  import org.codehaus.mojo.jaxb2.shared.Validate;
25  import org.codehaus.mojo.jaxb2.shared.environment.AbstractLogAwareFacet;
26  
27  import java.util.Locale;
28  import java.util.StringTokenizer;
29  
30  /**
31   * EnvironmentFacet implementation which alters the default Locale for the
32   * remainder of the tool execution.
33   *
34   * @author <a href="mailto:lj@jguru.se">Lennart J&ouml;relid</a>, jGuru Europe AB
35   */
36  public class LocaleFacet extends AbstractLogAwareFacet {
37  
38      // Internal state
39      private Locale originalLocale;
40      private Locale newLocale;
41  
42      /**
43       * Compound constructor creating a LocaleFacet wrapping the supplied instances.
44       *
45       * @param log       The active Maven Log.
46       * @param newLocale The non-null Locale to be set by this LocaleFacet during execution.
47       */
48      @SuppressWarnings("WeakerAccess")
49      public LocaleFacet(final Log log, final Locale newLocale) {
50          super(log);
51  
52          // Check sanity
53          Validate.notNull(newLocale, "usedLocale");
54  
55          // Assign internal state
56          this.originalLocale = Locale.getDefault();
57          this.newLocale = newLocale;
58      }
59  
60      /**
61       * {@inheritDoc}
62       * <p>Changes the Locale during the execution of the plugin.</p>
63       */
64      @Override
65      public void setup() {
66  
67          if (log.isInfoEnabled()) {
68              log.info("Setting default locale to [" + newLocale + "]");
69          }
70  
71          try {
72              Locale.setDefault(newLocale);
73          } catch (Exception e) {
74              log.error("Could not switch locale to ["
75                      + newLocale + "]. Continuing with standard locale.", e);
76          }
77      }
78  
79      /**
80       * {@inheritDoc}
81       * <p>Restores the original locale following the plugin's execution.</p>
82       */
83      @Override
84      public void restore() {
85  
86          if (log.isInfoEnabled()) {
87              log.info("Restoring default locale to [" + originalLocale + "]");
88          }
89  
90          try {
91              Locale.setDefault(originalLocale);
92          } catch (Exception e) {
93              log.error("Could not restore locale to [" + originalLocale + "]. Continuing with ["
94                      + Locale.getDefault() + "]", e);
95          }
96      }
97  
98      /**
99       * Helper method used to parse a locale configuration string into a Locale instance.
100      *
101      * @param localeString A configuration string parameter on the form
102      *                     {@code &lt;language&gt;[,&lt;country&gt;[,&lt;variant&gt;]]}
103      * @param log          The active Maven Log. Cannot be null.
104      * @return A fully constructed Locale.
105      * @throws MojoExecutionException if the localeString was not supplied on the required form.
106      */
107     public static LocaleFacet createFor(final String localeString, final Log log) throws MojoExecutionException {
108 
109         // Check sanity
110         Validate.notNull(log, "log");
111         Validate.notEmpty(localeString, "localeString");
112 
113         final StringTokenizer tok = new StringTokenizer(localeString, ",", false);
114         final int numTokens = tok.countTokens();
115         if (numTokens > 3 || numTokens == 0) {
116             throw new MojoExecutionException("A localeString must consist of up to 3 comma-separated parts on the "
117                     + "form <language>[,<country>[,<variant>]]. Received incorrect value '" + localeString + "'");
118         }
119 
120         // Extract the locale configuration data.
121         final String language = tok.nextToken().trim();
122         final String country = numTokens > 1 ? tok.nextToken().trim() : null;
123         final String variant = numTokens > 2 ? tok.nextToken().trim() : null;
124 
125         // All done.
126         return new LocaleFacet(log, findOptimumLocale(language, country, variant));
127     }
128 
129     /**
130      * Helper method to find the best matching locale, implying a workaround for problematic
131      * case-sensitive Locale detection within the JDK. (C.f. Issue #112).
132      *
133      * @param language The given Language.
134      * @param country  The given Country. May be null or empty to indicate that the Locale returned should not
135      *                 contain a Country definition.
136      * @param variant  The given Variant. May be null or empty to indicate that the Locale returned should not
137      *                 contain a Variant definition.
138      * @return The optimally matching Locale.
139      */
140     @SuppressWarnings("All")
141     public static Locale findOptimumLocale(final String language, final String country, final String variant) {
142 
143         final boolean hasCountry = country != null && !country.isEmpty();
144         final boolean hasVariant = variant != null && !variant.isEmpty();
145 
146         final Locale[] availableLocales = Locale.getAvailableLocales();
147         for (int i = 0; i < availableLocales.length; i++) {
148 
149             final Locale current = availableLocales[i];
150 
151             // Extract the language/country/variant of the current Locale.
152             final String currentLanguage = current.getLanguage();
153             final String currentCountry = current.getCountry();
154             final String currentVariant = current.getVariant();
155 
156             // Check if the current Locale matches the supplied
157             final boolean isLanguageMatch = language.equalsIgnoreCase(currentLanguage);
158             final boolean isCountryMatch = (hasCountry && country.equalsIgnoreCase(currentCountry))
159                     || (!hasCountry && (currentCountry == null || currentCountry.isEmpty()));
160             final boolean isVariantMatch = (hasVariant && variant.equalsIgnoreCase(currentVariant))
161                     || (!hasVariant && (currentVariant == null || currentVariant.isEmpty()));
162 
163             if (isLanguageMatch && isCountryMatch && isVariantMatch) {
164                 return current;
165             }
166         }
167 
168         // Default to the default platform locale.
169         return Locale.getDefault();
170     }
171 }