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

com.techempower.gemini.internationalization.GeminiResources Maven / Gradle / Ivy

There is a newer version: 3.3.14
Show newest version
/*******************************************************************************
 * Copyright (c) 2018, TechEmpower, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name TechEmpower, Inc. nor the names of its
 *       contributors may be used to endorse or promote products derived from
 *       this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL TECHEMPOWER, INC. BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

package com.techempower.gemini.internationalization;

import java.util.*;

import com.techempower.gemini.*;
import com.techempower.helper.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Holds user interface resources (in the form of macro-enabled Strings) for
 * a Locale (such as US English).
 *   

* This class should cannot be instantiated directly by client code but rather * is instantiated by a ResourceManager such as the FileResourceManager, * which in turn is created by GeminiLocaleManager. *

* GeminiResources objects can have the lookup of Values cascade by setting a * parent reference. When asked to resolve a Value from a Key, the local * map will be referenced; and if the Key is not found, the parent will be * asked to fulfill the request. This can progress up the chain to the * "Default Resources" which are usually hard-coded as either a plain instance * of or a subclass of DefaultGeminiResources. */ public class GeminiResources { /** * A set of macro indicators that is reused for macro expansion. */ private static final String[] macroIndicators = { "$S1", "$S2", "$S3", "$S4", "$S5", "$S6", "$S7", "$S8", "$S9", "$SA", "$SB", "$SC", "$SD", "$SE", "$SF", "$SG", "$SH", "$SI", "$SJ", "$SK", "$SL", "$SM", "$SN", "$SO", "$SP", "$SQ", "$SR", "$SS", "$ST", "$SU" }; // // Member variables. // private final Logger log = LoggerFactory.getLogger(getClass()); private final Locale locale; private final GeminiResources parent; private Properties properties; // // Member methods. // /** * Default constructor. * * @param app The application reference. */ protected GeminiResources(GeminiApplication app) { this(app, new Properties(), null, null); } /** * Constructor. * * @param app The application reference. * @param properties Reference to the application's configuration. * @param locale the Locale to which these Resources are mapped. * @param parent a reference to the parent GeminiResources instance. */ protected GeminiResources(GeminiApplication app, Properties properties, Locale locale, GeminiResources parent) { this.properties = properties; this.locale = locale; this.parent = parent; } /** * Return all the resources as a Properties object. */ public Properties getAll() { return this.properties; } /** * Returns true if the resource has a mapping with the given key. */ public boolean hasKey(String key) { return this.properties.containsKey(key); } /** * Gets a localized string associated with the key, or the key itself if * no value for the given key exists. * * @param key The localization key */ public String get(String key) { // If the key doesn't exist in this resources file; add it as an empty // string. if (!hasKey(key)) { if (this.parent == null) { // There are no more parents to check. We're at the root. So let's // log the issue and return just the Key as if it were the Value. this.log.info("Key \"{}\" does not exist in the resources.", key); return key; } else { return this.parent.get(key); } } else { String value = (String)this.properties.get(key); if (StringHelper.isEmptyTrimmed(value)) { // The value for this key is empty, log it since this probably // shouldn't be the case. this.log.info("Value for key \"{}\" is empty.", key); } return value; } } /** * Gets a resource string, replacing macros as necessary. */ public String get(String key, String... macros) { String toExpand = this.get(key); if (macros != null) { return StringHelper.replaceSubstrings(toExpand, macroIndicators, macros); } return toExpand; } /** * Loads properties into this resource. These properties will overwrite any values with * the same key. */ public void loadFromProperties(Properties newProperties) { if (this.properties == null) { this.properties = newProperties; } else { for (Object objKey : newProperties.keySet()) { String key = objKey.toString(); if (!StringHelper.isEmptyTrimmed(newProperties.getProperty(key))) { this.properties.setProperty(key, newProperties.getProperty(key)); } } } } /** * Gets the Local with which this GeminiResources object is associated. */ public Locale getLocale() { return this.locale; } /** * Returns the parent GeminiResources */ public GeminiResources getParent() { return this.parent; } /** * Gets an updater reference for this GeminiResources object. This is used * to apply a set of updates in a single thread-safe event. */ public Updater updater() { return new Updater(); } /** * Standard toString. */ @Override public String toString() { return "GeminiResources [locale: " + (this.locale != null ? this.locale.getDisplayName() : "--") + "]"; } /** * A utility class allowing keys to be added to the resource map at runtime. * This is typically only used to update the DefaultGeminiResources subclass * to add resources used as classes are loaded. For example, as custom * FormElements are loaded, their static initializer */ public final class Updater { private final Map updates = new HashMap<>(); private String prefix = ""; private Updater() { } /** * Sets any String to prepend to subsequently-updated keys. */ public Updater prefix(String newPrefix) { this.prefix = newPrefix; return this; } /** * Adds a key-value update. */ public Updater put(String key, String value) { if (key == null) { throw new IllegalArgumentException("Key was null."); } else if (value == null) { throw new IllegalArgumentException("Value was null."); } this.updates.put((StringHelper.isNonEmpty(this.prefix) ? this.prefix : "") + key, value); return this; } /** * Commits the updates in one thread-safe event. */ public Updater commit() { synchronized (GeminiResources.this) { // Clone the existing properties so that we can do updates in a // thread-safe copy. Properties newProperties = (Properties)GeminiResources.this.properties.clone(); // Update. for (Map.Entry entry : this.updates.entrySet()) { newProperties.put(entry.getKey(), entry.getValue()); } // Switch the reference over. GeminiResources.this.properties = newProperties; } return this; } } } // End GeminiResources.





© 2015 - 2024 Weber Informatics LLC | Privacy Policy