package com.plectix.simulator.gui.lib;
import java.awt.Color;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.Image;
import java.awt.Toolkit;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.UIManager;
import javax.swing.plaf.synth.SynthLookAndFeel;
/**
* Constants for basic colors, sizes, fonts, etc. defined in a properties file
* for easy customization, localization, branding, etc.
*
* @author ecemis
*/
public class UIProperties {
private static Properties props = new Properties();
private static Map<String, Color> colors = new HashMap<String, Color>();
private static Map<String, Font> fonts = new HashMap<String, Font>();
private static Map<String, Image> images = new HashMap<String, Image>();
private static Map<String, Object> otherObjects = new HashMap<String, Object>();
/**
* Initialize the UIProperties object from a .properties file. Any
* properties whose values look like "Color(R,G,B)",
* "Font(face,style,size)", or "Image(filename)" will be converted to
* java.awt.Color, java.awt.Font, or java.awt.Image objects respectively and
* saved in separate maps for faster retrieval.
*
* @see UIProperties#getColor(String)
* @see UIProperties#getFont(String)
* @see UIProperties#getImage(String)
*/
private UIProperties() throws Exception {
// Load properties file
loadPropertyFile(Settings.PROPERTIES_FILENAME);
// Set up look-and-feel
if (UIProperties.propertyExists("lookAndFeelResource")) {
setLookAndFeel(UIProperties.getString("lookAndFeelResource"));
} else {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
}
}
private static void loadPropertyFile(String propertyFilename) throws IOException {
// Load properties from resource
Properties properties = new Properties();
properties.load(new FileReader(propertyFilename));
// Add to master properties
props.putAll(properties);
// Save maps of Color, Font, and Image objects
for (Iterator it = properties.keySet().iterator(); it.hasNext();) {
String key = (String) it.next();
String value = properties.getProperty(key).trim();
if (value.startsWith("Color(")) {
// Skip if we've already made this color
if (colors.containsKey(value))
continue;
// Convert "Color(1,2,3)" to "1,2,3"
String temp = value.substring(6, value.length() - 1);
// Separate RGB values
String[] split = temp.split(",");
int red = Integer.parseInt(split[0].trim());
int green = Integer.parseInt(split[1].trim());
int blue = Integer.parseInt(split[2].trim());
// Save new Color object in map
if (split.length == 3)
colors.put(value, new Color(red, green, blue));
else
colors.put(value, new Color(red, green, blue, Integer.parseInt(split[3].trim())));
} else if (value.startsWith("Font(")) {
// Skip if we've already made this font
if (fonts.containsKey(value))
continue;
// Convert "Font(x,y,z)" to "x,y,z"
String temp = value.substring(5, value.length() - 1);
// Separate parameters
String[] split = temp.split(",");
String face = split[0].trim();
int style = Font.PLAIN;
if (split[1].trim().equalsIgnoreCase("bold"))
style = Font.BOLD;
else if (split[1].trim().equalsIgnoreCase("italic"))
style = Font.ITALIC;
float size = Float.parseFloat(split[2]);
// Save new Font object in map
// log.debug("Creating " + value);
Font font = makeFont(face, style, size);
fonts.put(value, font);
} else if (value.startsWith("Image(")) {
// Skip if we've already made this image
if (images.containsKey(value))
continue;
// Convert "Image(filename)" to "filename"
String temp = value.substring(6, value.length() - 1);
// Create the image object
String resourcePath = Settings.IMAGES_RESOURCE_PATH + temp;
URL url = UIProperties.class.getResource("/" + resourcePath);
if (url == null)
throw new FileNotFoundException(resourcePath);
// log.debug("Creating image " + resourcePath);
Image image = Toolkit.getDefaultToolkit().getImage(url);
// Force it to load now so the UI won't be glitchy later
// when the image is first displayed
image.getHeight(null);
// Save new Image object in map
images.put(value, image);
}
}
}
private static String getCheckedProperty(String property) {
String result = props.getProperty(property);
if (result == null) {
// log.error("UI properties key " + property + " not found");
throw new IllegalArgumentException(property);
}
return result.trim();
}
/**
* Return true if the specified property key exists.
* @param property property key to check
* @return true if property key exists
*/
private static boolean propertyExists(String property) {
return props.containsKey(property);
}
/**
* Get a string from the property map.
* @param property property key from conf/ui.properties
* @return String value of property
* @throws IllegalArgumentException if property key is not found
*/
public static String getString(String property) {
return getCheckedProperty(property);
}
/**
* Get an integer value from the property map.
* @param property property key from conf/ui.properties
* @return int value of property
* @throws IllegalArgumentException if property key is not found
*/
public static int getInt(String property) {
String result = getCheckedProperty(property);
return Integer.parseInt(result);
}
/**
* Get a double value from the property map.
* @param property property key from conf/ui.properties
* @return double value of property
* @throws IllegalArgumentException if property key is not found
*/
public static double getDouble(String property) {
String result = getCheckedProperty(property);
return Double.parseDouble(result);
}
/**
* Get a java.awt.Color object from the color map. The property
* should be a value like "Color(x,y,z)".
* @param property property key from conf/ui.properties
* @return java.awt.Color with specified values
* @throws IllegalArgumentException if property key is not found
*/
public static Color getColor(String property) {
String result = getCheckedProperty(property);
return colors.get(result);
}
/**
* Get a java.awt.Font object from the font map.
*
* @param property property key from conf/ui.properties
* @return java.awt.Font Font with specified values
* @throws IllegalArgumentException if property key is not found
*/
public static Font getFont(String property) {
String result = getCheckedProperty(property);
return fonts.get(result);
}
/**
* Get a java.awt.Font object from the font map, specified by
* name, style, and size. In almost all cases, you should call
* getFont(String) instead, to specify the font by property key.
*
* @param name Font name, e.g. "Gill Sans"
* @param style Font style
* @param size Font size
* @return Font matching name, style, and size, or null if not found
*/
public static Font getFont(String name, int style, int size) {
String styleString = "plain";
if (style == Font.BOLD)
styleString = "bold";
else if (style == Font.ITALIC)
styleString = "italic";
StringBuffer key = new StringBuffer();
key.append("Font(").append(name).append(",").append(styleString).append(",").append(size).append(")");
Font result = fonts.get(key.toString());
if (result == null) {
// log.warn("Unexpected font: " + key.toString() + " (make sure font is defined in ui.properties)");
}
return result;
}
/**
* Get a java.awt.Image object from the image map. The property
* should be a value like "Image(filename)". The filename should
* be relative to images/.
* @param property property key from ui.properties
* @return java.awt.Image image with specified filename
* @throws IllegalArgumentException if property key is not found
*/
private static Image getImage(String property) {
String result = getCheckedProperty(property);
return images.get(result);
}
/**
* Creates and returns an object. This method uses reflection
* to create them by name. Examples:
*
* <pre>
point.origin = java.awt.Point(int=0, int=0)
my.rectangle = java.awt.Rectangle(int=130, int=10, int=120, int=20)
my.dimension = java.awt.Dimension(int=800, int=600)
* </pre>
*
* Note that this function also shares the same objects
* through a hashmap.
*
* @param property a property, e.g. <code>point.origin</code>
* @return cashed or created object
*/
public static Object getObject(String property)
{
String objectString = getCheckedProperty(property);
Object ret = otherObjects.get(objectString);
if (ret != null) {
return ret;
}
int openParan = objectString.indexOf("(");
int closeParan = objectString.indexOf(")");
String className = objectString.substring(0, openParan);
String[] arguments = objectString.substring(openParan+1, closeParan).split(",");
Class[] args = new Class[arguments.length];
Object[] objs = new Object[arguments.length];
for (int i= 0; i< args.length; i++){
String[] argumentFields = arguments[i].trim().split("=");
// primitive Java types:
if (argumentFields[0].trim().equalsIgnoreCase("boolean")){
args[i] = boolean.class;
objs[i] = Boolean.parseBoolean(argumentFields[1].trim());
} else if (argumentFields[0].trim().equalsIgnoreCase("byte")){
args[i] = byte.class;
objs[i] = Byte.parseByte(argumentFields[1].trim());
} else if (argumentFields[0].trim().equalsIgnoreCase("char")){
args[i] = char.class;
objs[i] = argumentFields[1].trim().charAt(0);
} else if (argumentFields[0].trim().equalsIgnoreCase("short")){
args[i] = short.class;
objs[i] = Short.parseShort(argumentFields[1].trim());
} else if (argumentFields[0].trim().equalsIgnoreCase("int")){
args[i] = int.class;
objs[i] = Integer.parseInt(argumentFields[1].trim());
} else if (argumentFields[0].trim().equalsIgnoreCase("long")){
args[i] = long.class;
objs[i] = Long.parseLong(argumentFields[1].trim());
} else if (argumentFields[0].trim().equalsIgnoreCase("float")){
args[i] = float.class;
objs[i] = Float.parseFloat(argumentFields[1].trim());
} else if (argumentFields[0].trim().equalsIgnoreCase("double")){
args[i] = double.class;
objs[i] = Double.parseDouble(argumentFields[1].trim());
} else {
return null;
}
}
try {
ret = Class.forName(className).getConstructor(args).newInstance(objs);
otherObjects.put(objectString, ret);
return ret;
} catch (Exception e) {
e.printStackTrace();
// log.error("UI properties key " + property + " is ill-defined!");
throw new IllegalArgumentException(property);
}
}
/**
* Return an Icon for an image retrieved from getImage().
* @see #getImage(String)
* @param property property key from ui.properties
* @return icon for image with specified filename
*/
public static Icon getIcon(String property) {
return new ImageIcon(getImage(property));
}
private static void setLookAndFeel(String lookAndFeelResource) throws Exception {
SynthLookAndFeel synth = new SynthLookAndFeel();
InputStream lookAndFeelStream = UIProperties.class.getResourceAsStream("/" + lookAndFeelResource);
synth.load(lookAndFeelStream, UIProperties.class);
UIManager.setLookAndFeel(synth);
}
private static Font makeFont(String face, int style, float size) {
// Look for match in available fonts
Font[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
for (Font f : fonts) {
if (f.getName().equalsIgnoreCase(face))
return f.deriveFont(style, size);
}
// Use system font by default
// log.warn("Font " + face + " not found; using SansSerif");
return new Font("SansSerif", style, (int) size);
}
public static void setProperty(String name, int value) {
props.setProperty(name, Integer.toString(value));
}
public static void setProperty(String name, String value) {
props.setProperty(name, value);
}
public static void setProperty(String name, Color value) {
colors.put(name, value);
}
/**
* Format a message specified by a property key. The message is
* may use {0}, {1}, etc. as placeholders for the values supplied
* as additional parameters to this method.
* @see MessageFormat#format(String, Object[])
* @param propertyName property key name
* @param args variable-length list of replacement values for {0} etc.
* @return formatted message
*/
public static String getMessageString(String propertyName, Object... args) {
String message = UIProperties.getString(propertyName);
return MessageFormat.format(message, args);
}
}