001/* 002 * Units of Measurement Reference Implementation 003 * Copyright (c) 2005-2020, Jean-Marie Dautelle, Werner Keil, Otavio Santana. 004 * 005 * All rights reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without modification, 008 * are permitted provided that the following conditions are met: 009 * 010 * 1. Redistributions of source code must retain the above copyright notice, 011 * this list of conditions and the following disclaimer. 012 * 013 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions 014 * and the following disclaimer in the documentation and/or other materials provided with the distribution. 015 * 016 * 3. Neither the name of JSR-385, Indriya nor the names of their contributors may be used to endorse or promote products 017 * derived from this software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 026 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package tech.units.indriya.internal.format.l10n; 031 032import java.io.IOException; 033import java.io.InputStream; 034import java.util.Enumeration; 035import java.util.HashMap; 036import java.util.Map; 037import java.util.PropertyResourceBundle; 038import java.util.ResourceBundle; 039import java.util.Set; 040import java.util.Vector; 041 042/** 043 * Extends <code>ResourceBundle</code> with 2 new capabilities. The first is to store the path where the properties file used to create the 044 * <code>InputStream</code> is located and the second is to allow additional <code>ResourceBundle</code> properties to be merged into an instance. 045 * </p> 046 * <p> 047 * To allow a <code>SystemOfUnits</code> to locate and merge extension module properties files. 048 * </p> 049 * 050 * @author Werner Keil 051 * @version 1.0 052 */ 053public class MultiPropertyResourceBundle extends ResourceBundle { 054// also see https://github.com/vitorzachi/tcc-multitenancy/blob/master/tccMultitenancy/src/net/sf/trugger/util/MultiResourceBundle.java but that code might be older 055 056 /** 057 * <p> 058 * The location of the properties file that was used to instantiate the <code>MultiPropertyResourceBundle</code> instance. This field is set by the 059 * constructor. 060 * </p> 061 */ 062 private String resourcePath = null; 063 064 /** 065 * @return The location of the properties file that was used to instantiate the <code>MultiPropertyResourceBundle</code> instance. 066 */ 067 public String getResourcePath() { 068 return resourcePath; 069 } 070 071 /** 072 * <p> 073 * A {@link Map} containing all the properties that have been merged from multiple {@link ResourceBundle} instances. 074 * </p> 075 */ 076 private final Map<String, Object> resources = new HashMap<>(); 077 078 /** 079 * <p> 080 * A {@link StringBuilder} instance containing all the paths of the {@link ResourceBundle} instances that have been merged into this instance. This 081 * value is intended to be use to help generate a key for caching JSON formatted resource output in the {@link AbstractWebScript} class. 082 * </p> 083 */ 084 private final StringBuilder mergedBundlePaths = new StringBuilder(); 085 086 /** 087 * @return Returns the {@link StringBuilder} instance containing the paths of all the {@link ResourceBundle} instances that have been merged into 088 * this instance. 089 */ 090 public StringBuilder getMergedBundlePaths() { 091 return mergedBundlePaths; 092 } 093 094 /** 095 * <p> 096 * Instantiates a new <code>MultiPropertyResourceBundle</code>. 097 * </p> 098 * 099 * @param stream 100 * The <code>InputStream</code> passed on to the super class constructor. 101 * @param resourcePath 102 * The location of the properties file used to create the <code>InputStream</code> 103 * @throws IOException 104 */ 105 public MultiPropertyResourceBundle(InputStream stream, String resourcePath) throws IOException { 106 final ResourceBundle resourceBundle = new PropertyResourceBundle(stream); 107 this.resourcePath = resourcePath; 108 merge(resourceBundle, resourcePath); 109 } 110 111 /** 112 * <p> 113 * Constructor for instantiating from an existing {@link ResourceBundle}. This calls the <code>merge</code> method to copy the properties from the 114 * bundle into the <code>resources</code> map. 115 * 116 * @param baseBundle 117 * @param resourcePath 118 */ 119 public MultiPropertyResourceBundle(ResourceBundle baseBundle, String resourcePath) { 120 super(); 121 this.resourcePath = resourcePath; 122 merge(baseBundle, resourcePath); 123 } 124 125 /** 126 * <p> 127 * Merges the properties of a <code>ResourceBundle</code> into the current <code>MultiPropertyResourceBundle</code> instance. This will override any 128 * values mapped to duplicate keys in the current merged properties. 129 * </p> 130 * 131 * @param resourceBundle 132 * The <code>ResourceBundle</code> to merge the properties of. 133 * @param aResourcePath 134 */ 135 public void merge(ResourceBundle resourceBundle, String aResourcePath) { 136 if (resourceBundle != null) { 137 Enumeration<String> keys = resourceBundle.getKeys(); 138 while (keys.hasMoreElements()) { 139 String key = keys.nextElement(); 140 this.resources.put(key, resourceBundle.getObject(key)); 141 } 142 } 143 144 // Update the paths merged in this bundle 145 mergedBundlePaths.append(aResourcePath); 146 mergedBundlePaths.append(":"); 147 } 148 149 /** 150 * <p> 151 * Overrides the super class implementation to return an object located in the merged bundles 152 * </p> 153 * 154 * @return An <code>Object</code> from the merged bundles 155 */ 156 @Override 157 public Object handleGetObject(String key) { 158 if (key == null) { 159 throw new NullPointerException(); 160 } 161 return this.resources.get(key); 162 } 163 164 /** 165 * <p> 166 * Overrides the super class implementation to return an enumeration of keys from all the merged bundles 167 * </p> 168 * 169 * @return An <code>Enumeration</code> of the keys across all the merged bundles. 170 */ 171 @Override 172 public Enumeration<String> getKeys() { 173 Vector<String> keys = new Vector<>(this.resources.keySet()); 174 return keys.elements(); 175 } 176 177 /** 178 * <p> 179 * Overrides the super class implementation to return the <code>Set</code> of keys from all merged bundles 180 * </p> 181 * 182 * @return A <code>Set</code> of keys obtained from all merged bundles 183 */ 184 @Override 185 protected Set<String> handleKeySet() { 186 return this.resources.keySet(); 187 } 188 189 /** 190 * <p> 191 * Overrides the super class implementation to check the existence of a key across all merged bundles 192 * </p> 193 * 194 * @return <code>true</code> if the key is present and <code>false</code> otherwise. 195 */ 196 @Override 197 public boolean containsKey(String key) { 198 return this.resources.containsKey(key); 199 } 200 201 /** 202 * <p> 203 * Overrides the super class implementation to return the <code>Set</code> of keys from all merged bundles 204 * </p> 205 * 206 * @return A <code>Set</code> of keys obtained from all merged bundles 207 */ 208 @Override 209 public Set<String> keySet() { 210 return this.resources.keySet(); 211 } 212}