All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ocpsoft.rewrite.transposition.LocaleTransposition Maven / Gradle / Ivy

/*
 * Copyright 2014 Université de Montréal
 *
 * 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 org.ocpsoft.rewrite.transposition;

import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentHashMap;

import org.ocpsoft.common.util.Assert;
import org.ocpsoft.rewrite.bind.Binding;
import org.ocpsoft.rewrite.config.Operation;
import org.ocpsoft.rewrite.context.EvaluationContext;
import org.ocpsoft.rewrite.event.Rewrite;
import org.ocpsoft.rewrite.param.Constraint;
import org.ocpsoft.rewrite.param.Parameter;
import org.ocpsoft.rewrite.param.Parameters;
import org.ocpsoft.rewrite.param.Transposition;

/**
 * A {@link Transposition} and/or {@link Constraint} responsible for translating values from their matching translation
 * from a {@link ResourceBundle}. The lookup in the properties file is case-sensitive. The properties file should use
 * ISO-8859-1 encoding as defined by {@link PropertyResourceBundle}.
 * 
 * Requires inverted properties files in the form of translated_name=name_to_bind.
 * 
 * TODO Should we allow to load the properties file in a different encoding? TODO Allow to enable case insensitive
 * and/or ascii-folding on property lookup?
 * 
 * @author Christian Gendreau
 */
public class LocaleTransposition implements Transposition, Constraint
{
   // shared thread safe map between a String(representing the language) and a ResourceBundle.
   private static Map bundleMap = new ConcurrentHashMap();

   private final String languageParam;
   private final String bundleName;

   private Operation onFailureOperation;

   private LocaleTransposition(final String languageParam, final String bundleName)
   {
      Assert.notNull(languageParam, "Language must not be null.");
      Assert.notNull(bundleName, "Bundle must not be null.");

      // TODO ensure that we can find a resource bundle with the provided name in the classpath

      this.languageParam = languageParam;
      this.bundleName = bundleName;

   }

   /**
    * Translate a value into the matching one from a resource bundle in specified language.
    * 
    * @param lang
    * @param value
    * @return translated value or null if no bundle can be found for the specified language or no entries in the bundle
    *         match the given value.
    */
   private String translate(String lang, String value)
   {
      String translatation = null;
      if (value != null)
      {
         if (!bundleMap.containsKey(lang))
         {
            Locale locale = new Locale(lang);
            try
            {
               ResourceBundle loadedBundle = ResourceBundle.getBundle(bundleName, locale,
                        ResourceBundle.Control.getNoFallbackControl(ResourceBundle.Control.FORMAT_DEFAULT));

               bundleMap.put(lang, loadedBundle);
            }
            catch (MissingResourceException e)
            {
               return null;
            }
         }

         try
         {
            // can we received more than one path section? e.g./search/service/
            translatation = bundleMap.get(lang).getString(value);
         }
         catch (MissingResourceException mrEx)
         {
            // ignore
         }
      }
      return translatation;
   }

   /**
    * Create a {@link Binding} to a {@link ResourceBundle}, where the initial bound {@link Parameter} value is used as
    * the bundle lookup key. The resultant value of the bundle lookup is stored as the new bound {@link Parameter}
    * value.
    * 

* For example, consider the following URL-based rule: * *

    * Configuration config = ConfigurationBuilder.begin()
    *          .addRule(Join.path("/{lang}/{path}").to("/{path}"))
    *          .where("path").transposedBy(LocaleTransposition.bundle("org.example.Paths", "lang"));
    * 
*

* In the above scenario, "org.example.Paths" is the resource bundle name. The value of the {@link Parameter} "lang" * is extracted from the inbound request URL, and used as the bundle {@link Locale}. The initial value of the * {@link Parameter} "path" is used as the lookup key, and is transposed to the value of the corresponding resource * bundle entry. Once transposition has occurred, after rule evaluation, subsequent references to the "path" * {@link Parameter} will return the value from the {@link ResourceBundle} entry. *

* When this example is applied to a URL of: "/de/bibliotek", assuming a bundle called "org.example.Paths_de" exists * and contains the an entry "bibliotek=library", the rule will forward to the new URL: "/library", because the value * of "path" has been transposed by {@link LocaleTransposition}. * * * @param bundleName Fully qualified name of the {@link ResourceBundle} * @param localeParam The name of the {@link Parameter} that contains the ISO 639-1 language code to be used with * {@link Locale}. * * @return new instance of LocaleBinding */ public static LocaleTransposition bundle(final String bundleName, final String localeParam) { return new LocaleTransposition(localeParam, bundleName); } /** * Specify an {@link Operation} to be added as a preOperation in case the {@link Transposition} failed. Failure * occurs when no {@link ResourceBundle} can be found for the requested language or when a value can not be * transposed due to a missing key in the resource bundle. * * @param onFailureOperation * @return */ public LocaleTransposition onTranspositionFailed(Operation onFailureOperation) { this.onFailureOperation = onFailureOperation; return this; } @Override public String transpose(Rewrite event, EvaluationContext context, String value) { // Retrieve the value of lang from the context String targetLang = (String) Parameters.retrieve(context, this.languageParam); String transposedValue = translate(targetLang, value); if (transposedValue == null) { if (onFailureOperation != null) { context.addPreOperation(onFailureOperation); } // if language is not defined, do not translate and keep original value. transposedValue = value; } return transposedValue; } @Override public boolean isSatisfiedBy(Rewrite event, EvaluationContext context, String value) { // Retrieve the value of lang from the context String targetLang = (String) Parameters.retrieve(context, this.languageParam); String translation = translate(targetLang, value); return (translation != null); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy