001/* 002 * Units of Measurement Implementation for Java SE 003 * Copyright (c) 2005-2017, Jean-Marie Dautelle, Werner Keil, V2COM. 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-363 nor the names of its 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 tec.uom.se.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 */ 052public class MultiPropertyResourceBundle extends ResourceBundle { 053 054 /** 055 * <p> 056 * The location of the properties file that was used to instantiate the <code>MultiPropertyResourceBundle</code> instance. This field is set by the 057 * constructor. 058 * </p> 059 */ 060 private String resourcePath = null; 061 062 /** 063 * @return The location of the properties file that was used to instantiate the <code>MultiPropertyResourceBundle</code> instance. 064 */ 065 public String getResourcePath() { 066 return resourcePath; 067 } 068 069 /** 070 * <p> 071 * A {@link Map} containing all the properties that have been merged from multiple {@link ResourceBundle} instances. 072 * </p> 073 */ 074 private final Map<String, Object> resources = new HashMap<>(); 075 076 /** 077 * <p> 078 * A {@link StringBuilder} instance containing all the paths of the {@link ResourceBundle} instances that have been merged into this instance. This 079 * value is intended to be use to help generate a key for caching JSON formatted resource output in the {@link AbstractWebScript} class. 080 * </p> 081 */ 082 private final StringBuilder mergedBundlePaths = new StringBuilder(); 083 084 /** 085 * @return Returns the {@link StringBuilder} instance containing the paths of all the {@link ResourceBundle} instances that have been merged into 086 * this instance. 087 */ 088 public StringBuilder getMergedBundlePaths() { 089 return mergedBundlePaths; 090 } 091 092 /** 093 * <p> 094 * Instantiates a new <code>MultiPropertyResourceBundle</code>. 095 * </p> 096 * 097 * @param stream 098 * The <code>InputStream</code> passed on to the super class constructor. 099 * @param resourcePath 100 * The location of the properties file used to create the <code>InputStream</code> 101 * @throws IOException 102 */ 103 public MultiPropertyResourceBundle(InputStream stream, String resourcePath) throws IOException { 104 final ResourceBundle resourceBundle = new PropertyResourceBundle(stream); 105 this.resourcePath = resourcePath; 106 merge(resourceBundle, resourcePath); 107 } 108 109 /** 110 * <p> 111 * Constructor for instantiating from an existing {@link ResourceBundle}. This calls the <code>merge</code> method to copy the properties from the 112 * bundle into the <code>resources</code> map. 113 * 114 * @param baseBundle 115 * @param resourcePath 116 */ 117 public MultiPropertyResourceBundle(ResourceBundle baseBundle, String resourcePath) { 118 super(); 119 this.resourcePath = resourcePath; 120 merge(baseBundle, resourcePath); 121 } 122 123 /** 124 * <p> 125 * Merges the properties of a <code>ResourceBundle</code> into the current <code>MultiPropertyResourceBundle</code> instance. This will override any 126 * values mapped to duplicate keys in the current merged properties. 127 * </p> 128 * 129 * @param resourceBundle 130 * The <code>ResourceBundle</code> to merge the properties of. 131 * @param resourcePath 132 */ 133 public void merge(ResourceBundle resourceBundle, String resourcePath) { 134 if (resourceBundle != null) { 135 Enumeration<String> keys = resourceBundle.getKeys(); 136 while (keys.hasMoreElements()) { 137 String key = keys.nextElement(); 138 this.resources.put(key, resourceBundle.getObject(key)); 139 } 140 } 141 142 // Update the paths merged in this bundle 143 mergedBundlePaths.append(resourcePath); 144 mergedBundlePaths.append(":"); 145 } 146 147 /** 148 * <p> 149 * Overrides the super class implementation to return an object located in the merged bundles 150 * </p> 151 * 152 * @return An <code>Object</code> from the merged bundles 153 */ 154 @Override 155 public Object handleGetObject(String key) { 156 if (key == null) { 157 throw new NullPointerException(); 158 } 159 return this.resources.get(key); 160 } 161 162 /** 163 * <p> 164 * Overrides the super class implementation to return an enumeration of keys from all the merged bundles 165 * </p> 166 * 167 * @return An <code>Enumeration</code> of the keys across all the merged bundles. 168 */ 169 @Override 170 public Enumeration<String> getKeys() { 171 Vector<String> keys = new Vector<>(this.resources.keySet()); 172 return keys.elements(); 173 } 174 175 /** 176 * <p> 177 * Overrides the super class implementation to return the <code>Set</code> of keys from all merged bundles 178 * </p> 179 * 180 * @return A <code>Set</code> of keys obtained from all merged bundles 181 */ 182 @Override 183 protected Set<String> handleKeySet() { 184 return this.resources.keySet(); 185 } 186 187 /** 188 * <p> 189 * Overrides the super class implementation to check the existence of a key across all merged bundles 190 * </p> 191 * 192 * @return <code>true</code> if the key is present and <code>false</code> otherwise. 193 */ 194 @Override 195 public boolean containsKey(String key) { 196 return this.resources.containsKey(key); 197 } 198 199 /** 200 * <p> 201 * Overrides the super class implementation to return the <code>Set</code> of keys from all merged bundles 202 * </p> 203 * 204 * @return A <code>Set</code> of keys obtained from all merged bundles 205 */ 206 @Override 207 public Set<String> keySet() { 208 return this.resources.keySet(); 209 } 210}