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

com.force.i18n.settings.SharedKeyMapPropertyFileData Maven / Gradle / Ivy

There is a newer version: 1.2.30
Show newest version
/* 
 * Copyright (c) 2017, salesforce.com, inc.
 * All rights reserved.
 * Licensed under the BSD 3-Clause license. 
 * For full license text, see LICENSE.txt file in the repo root  or https://opensource.org/licenses/BSD-3-Clause
 */

package com.force.i18n.settings;

import java.io.Serializable;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * An implementation of property file data (e.g. label data) that
 * shares the section name keys and parameter name keys across
 * various languages.
 * 

* Some care needs to be taken with the thread safetyness of this class, * as the internal SharedKeyMap can be mutated by multiple threads. * * @author koliver */ public class SharedKeyMapPropertyFileData implements PropertyFileData, Serializable { /** * */ private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(SharedKeyMapPropertyFileData.class.getName()); private static final class SerializableLock implements Serializable { /** * */ private static final long serialVersionUID = 1L; } private final Locale locale; private final boolean isPrimary; /** * Note that the section-level maps are thread-safe. */ private SharedKeyMap> seed; private final SerializableLock seedLock = new SerializableLock(); private SharedKeyMap> data; private final Set publicSections; /** * @param locale The locale id should correspond * to a value that came from the g11n.xml file * @param isPrimary this is the only label set that can define new sections. * @param seed this is the shared key map that is shared across multiple locale's versions * of the data. * @param publicSections the names of the sections that are exposed externally (think global), so that they can be used in an API */ public SharedKeyMapPropertyFileData(Locale locale, boolean isPrimary, SharedKeyMap> seed, Set publicSections) { this.locale = locale; this.isPrimary = isPrimary; this.seed = seed; this.data = new SharedKeyMap>(seed); this.publicSections = publicSections; } @Override public Locale getLocale() { return this.locale; } @Override public Map getSection(String sectionName) { if (!this.data.containsKey(sectionName)) { return null; } return Collections.unmodifiableMap(this.data.get(sectionName)); } @Override @SuppressWarnings({"unchecked","rawtypes"}) public Set>> entrySet() { // This wants to return Entry> which is hard to get java to accept here Set entrySet = this.data.entrySet(); return Collections.unmodifiableSet((Set>>) entrySet); } @Override public Set getSectionNames() { return Collections.unmodifiableSet(this.data.keySet()); } @Override public boolean containsSection(String sectionName) { return this.data.containsKey(sectionName); } @Override public boolean contains(String sectionName, String paramName) { if (sectionName == null || paramName == null) { return false; } Map section = getSection(sectionName); if (section == null) { return false; } return section.containsKey(paramName); } @Override public Set getPublicSectionNames() { return Collections.unmodifiableSet(this.publicSections); } @Override public void setSectionAsPublic(String section) { if (!section.toLowerCase().equals(section)) { throw new RuntimeException("Public sections must have a lowercase name"); } this.publicSections.add(section); } @Override public Object put(String sectionName, String paramName, Object value) { SharedKeyMap section = this.data.get(sectionName); if (section == null) { // populate seed, lazily as needed SharedKeyMap seedSection; synchronized (this.seedLock) { seedSection = this.seed.get(sectionName); if (seedSection == null) { if (!this.isPrimary) { if (logger.isLoggable(Level.FINER)) { logger.finer("Only primary locale can create sections " + sectionName + "." + paramName); } return null; } // we need these to be thread safe as multiple threads could accessing // // ideally, we would just initialize all of these Maps once // with the default/english language. Then our structures would be read-only // from then on and we'd be okay sharing the read-only data structure from then on // without the need for synchronization. Map sharedParamToValue = new ConcurrentHashMap(1, 0.75f, 1); seedSection = new SharedKeyMap(sharedParamToValue, -1); this.seed.put(sectionName, seedSection); } } section = new SharedKeyMap(seedSection); this.data.put(sectionName, section); } return section.put(paramName, value); } public void compact() { synchronized (this.seedLock) { this.seed.trimToSize(); } this.data.trimToSize(); // we need to sync on seed here because iterating over this.data.values // ends up iterating over seed's keyset, which could have values added to it // in put, but in put we synchronize on seed to handle that. synchronized (this.seedLock) { for (SharedKeyMap section : this.data.values()) { section.trimToSize(); } } } @Override public void removeSection(String sectionName) { this.data.remove(sectionName); } @Override public Object remove(String sectionName, String paramName) { SharedKeyMap section = this.data.get(sectionName); if (section == null) { return null; } return section.remove(paramName); } @Override public void shareKeys(SharedKeyMap> seedKeyMap) { synchronized (this.seedLock) { // nothing for us to do if we're already sharing the same seeds if (this.seed == seedKeyMap) { return; } this.seed = seedKeyMap; } SharedKeyMap> unshared = this.data; this.data = new SharedKeyMap>(seedKeyMap); for (Map.Entry> unsharedSection : unshared.entrySet()) { String sectionName = unsharedSection.getKey(); for (Map.Entry entry : unsharedSection.getValue().entrySet()) { put(sectionName, entry.getKey(), entry.getValue()); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy