
com.inexas.util.TextBundle Maven / Gradle / Ivy
package com.inexas.util;
import java.io.*;
import java.util.*;
import java.util.jar.*;
/**
*
* This class allows a set of property files to be used like resource bundles
* for SQL or other similar applications where there are several dialects or
* versions of a single dialect. An example is worth a thousand words:
*
*
*
* sql.properties====
* # one might expect an SQL 92 definition here
* select=select * from mytable
* insert=insert into tab(id,name)values('%s',%d)
* ==================
*
* sql_db2.properties====
* # here's the version for the DB2 database
* select=SELECT * FROM DB2TAB
* ==================
*
*
* An application would construct a TextBundle using "sql_db2" as the name. On
* loading the properties file this class would discover that a predecessor
* existed and load that. The property files are used in the load order to
* service requests.
*
* @author Keith Whittingham
* @version $Revision: 1.1 $
*/
public class TextBundle extends Properties {
// todo JUnit me
private static final long serialVersionUID = 1832358303485280887L;
private final String baseName;
/**
* Load a set of properties as a text bundle. The base name is referenced
* from the home directory as retrieved from Config and may include a
* directory structure. E.g. a base name of config/ab_cd_ef will cause a
* search of config/ab.properties, config/ab_cd.properties and
* config/ab_cd_ef.properties to be searched for and loaded if present.
*
* @param baseName
* the base name of the text bundle
*/
public TextBundle(String baseName) {
assert baseName != null && baseName.length() > 0;
this.baseName = baseName;
final Collection filenames = generateFilenames(baseName);
if(filenames.size() == 0) {
throw new RuntimeException("No text bundle for base name: " + baseName);
}
boolean isFirst = true;
final Iterator i = generateFilenames(baseName).iterator();
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
String filename = null;
Properties properties = null;
while(i.hasNext()) {
filename = i.next();
try(final InputStream is = loader.getResourceAsStream(filename)) {
if(is == null) {
throw new FileNotFoundException(filename);
}
properties = process(is, i.hasNext(), properties);
isFirst = false;
} catch(final FileNotFoundException e) {
if(isFirst) {
throw new RuntimeException("No text bundle for filename: " + filename, e);
}
// ignore files not found...
} catch(final IOException e) {
throw new RuntimeException("Error loading file: " + filename, e);
}
}
}
/**
* The text bundle name is assumed to be in the format "name{_dlt]*" where
* name is the base name of the resource bundle and dlt is a specialization
* in a similar way to language resource bundles. If the name is
* "config/abc_def_ghi" is passed then we attempt to load three properties
* files: abc.properties, abc_def.properties and abc_def_ghi. properties.
*
* @param jarFilename
* the name of the JAR file to read
* @param baseName
* the resource bundle name to read in
*/
public TextBundle(String jarFilename, String baseName) {
boolean found = false;
this.baseName = baseName;
try {
// tokenize the class path and look for the right file name...
final String classpath = System.getProperty("java.class.path");
final String pathSeparator = System.getProperty("path.separator");
final StringTokenizer st = new StringTokenizer(classpath, pathSeparator);
while(st.hasMoreTokens()) {
final String s = st.nextToken();
if(s.endsWith(jarFilename)) {
found = true;
// got it, create the jarfile...
try(JarFile jf = new JarFile(s)) {
// generate a list of jar entries...
final List jarEntries = new ArrayList<>();
final Iterator i = generateFilenames(baseName).iterator();
while(i.hasNext()) {
final String filename = i.next();
// try and load the properties file for this one...
final JarEntry je = jf.getJarEntry(filename);
if(je != null) {
jarEntries.add(je);
}
}
// now process the jar entries....
Properties properties = null;
JarEntry je = null;
final Iterator j = jarEntries.iterator();
while(j.hasNext()) {
je = j.next();
try(final InputStream is = jf.getInputStream(je)) {
properties = process(is, i.hasNext(), properties);
} catch(final FileNotFoundException e) {
// ignore files not found...
} catch(final IOException e) {
jf.close();
throw new RuntimeException(
"Error loading jar entry: " + je.getName(), e);
}
}
}
}
}
if(!found) {
// test
// File wFile = new File("abcdefg");
// FileWriter fw = new FileWriter("abcdefg");
// fw.write("hello Keith");
// fw.close();
// Iterator i = generateFilenames("/sql").iterator();
// Properties properties = null;
// while (i.hasNext()) {
// String filename = null;
// try {
// filename = (String)i.next();
// InputStream is = new FileInputStream(filename);
// properties = process(is, i.hasNext(), properties);
// is.close();
// } catch (FileNotFoundException e) {
// // ignore files not found...
// } catch (IOException e) {
// throw new InexasRuntimeException("Error loading file: " +
// filename, e);
// }
// }
// // if we didn't found the jarfile so far we try as last
// chance
// // to load the properties like this (fix for tomcat)
final ResourceBundle rb = ResourceBundle.getBundle(baseName);
final Enumeration en = rb.getKeys();
while(en.hasMoreElements()) {
final String key = en.nextElement();
this.put(key, rb.getString(key));
}
}
} catch(final IOException e) {
throw new RuntimeException("Error accessing JAR file: " + jarFilename, e);
}
}
public String getString(String key, String defaultValue) {
return super.getProperty(key, defaultValue);
}
/**
* Get a string from the bundle given a key
*
* @param key
* the key of the string
* @return the string matching the given key
* @exception RuntimeException
* if no string for the key is found
*/
public String getString(String key) {
final String value = super.getProperty(key);
if(value == null) {
throw new RuntimeException("Missing key in TextBundle " + baseName + " key: " + key);
}
return value;
}
/**
* Get a string from the bundle given a key.
*
* @param key
* The key of the string to retrieve.
* @param parameters
* Key/value pairs like ..., "{Name}", "Keith"
* @return The string matching the given key.
* @exception RuntimeException
* If no string for the key is found.
*/
public String get(String key, String... parameters) {
assert parameters.length > 0 && parameters.length % 2 == 0 : "Next 2, 4, 6... parameters";
assert parametersExist(key, parameters);
String result = getString(key);
for(int i = 0; i < parameters.length;) {
final String name = parameters[i++];
final String value = parameters[i++];
result = result.replace(name, value);
}
return result;
}
@Deprecated
@Override
public String getProperty(String key) {
return super.getProperty(key);
}
private Properties process(InputStream is, boolean notLast, Properties properties)
throws IOException {
final Properties returnValue;
// [first] <--defaults-- [ ... ] <--defaults-- [last=this]
if(properties == null) {
if(notLast) {
// first, but not last...
returnValue = new Properties();
} else {
// first *and* last...
returnValue = this;
}
} else {
if(notLast) {
// not first or last...
returnValue = new Properties(properties);
} else {
// not first but last...
defaults = properties;
returnValue = this;
}
}
returnValue.load(is);
return properties;
}
private List generateFilenames(String theBaseName) {
final List list = new ArrayList<>();
// split up the baseName into path and filename and tokenize the
// latter...
final int slash = theBaseName.lastIndexOf('\\');
final int backSlash = theBaseName.lastIndexOf('/');
final int pathLength = 1 + slash > backSlash ? slash : backSlash;
String path, filename;
if(pathLength == -1) {
path = "";
filename = theBaseName;
} else {
path = theBaseName.substring(0, pathLength);
filename = theBaseName.substring(pathLength);
}
final StringTokenizer st = new StringTokenizer(filename, "_");
String name = path + st.nextToken();
list.add(name + ".properties");
while(st.hasMoreTokens()) {
name += "_" + st.nextToken();
list.add(name + ".properties");
}
return list;
}
/**
* This method checks that there is at least one occurrence of each
* parameter in the string. It is only called by an assert so take our time
*
* @param sql
* @param parameters
* @return true if all is well
*/
private boolean parametersExist(String sqlKey, String[] parameters) {
final String sql = getString(sqlKey);
final TextBuilder sb = new TextBuilder();
for(int i = 0; i < parameters.length; i += 2) {
final String parameterName = parameters[i];
if(!(parameterName.startsWith("{") && parameterName.endsWith("}"))) {
throw new RuntimeException("Parameter name missing {Brackets}: " + parameterName);
}
if(sql.indexOf(parameterName) < 0) {
if(sb.length() > 0) {
sb.append(',');
}
sb.append(parameterName);
}
}
if(sb.length() > 0) {
throw new RuntimeException(
"Missing parameters in SQL template, (sqlKey:sqltemplate:missing-parameters): " +
sqlKey + ':' + sql + ':' + sb.toString());
}
return true;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy