import java.awt.*; import java.io.Serializable; import java.io.InputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.Method; import java.util.Enumeration; import java.util.Hashtable; import java.util.Properties; import java.util.StringTokenizer; /** * a layout manager used to layout a gui based upon the information held in a properties file */ public class SkinLayout implements LayoutManager, java.io.Serializable { /** * the properties file for this layout */ Properties layoutProperties; /** * components laid out by this layout */ Hashtable components = new Hashtable(); /** * construct a new layout. Load the properties file from the specified InputStream */ public SkinLayout(InputStream in) throws IOException { setLayoutFile(in); } /** * construct a new layout. Load the properties file using the specified filename */ public SkinLayout(String layoutFile) throws FileNotFoundException, IOException { setLayoutFile(layoutFile); } /** * load properties from the specified FileInputStream */ public void setLayoutFile(InputStream in) throws IOException { if (layoutProperties == null) layoutProperties = new Properties(); else layoutProperties.clear(); layoutProperties.load(in); in.close(); } /** * load properties using the specified filename */ public void setLayoutFile(String layoutFile) throws FileNotFoundException, IOException { InputStream in = new FileInputStream(layoutFile); setLayoutFile(in); } /** * add a component to this layout. The constrains object must be a name (string) so the component * may be identified in the properties. */ public void addLayoutComponent(Component comp, Object constraints) { synchronized (comp.getTreeLock()) { if ((constraints != null) && (constraints instanceof String)) { addLayoutComponent((String)constraints, comp); } else { throw new IllegalArgumentException("cannot add to layout: constraint must be a string"); } } } /** * add a component to this layout with the specified name. Certain attributes MUST be present in * the properties for this component to be successfully added (xpos, ypos, width, height). */ public void addLayoutComponent(String name, Component comp) { synchronized (comp.getTreeLock()) { if (layoutProperties.containsKey(name + ".xpos") && layoutProperties.containsKey(name + ".ypos") && layoutProperties.containsKey(name + ".width") && layoutProperties.containsKey(name + ".height")) { components.put(name,comp); System.out.println("component " + name + " added"); } else { throw new IllegalArgumentException("cannot add " + name + " to layout: missing argument in properties"); } } } /** * remove a component from the layout */ public void removeLayoutComponent(Component comp) { synchronized (comp.getTreeLock()) { Enumeration e = components.keys(); while (e.hasMoreElements()) { String s = e.nextElement().toString(); Component c = (Component)components.get(s); if (comp == c) { components.remove(s); break; } } } } /** * return the minimum size based upon the width and height attributes in the properties */ public Dimension minimumLayoutSize(Container target) { synchronized (target.getTreeLock()) { Dimension dim = new Dimension(0, 0); if (layoutProperties.containsKey("width") && layoutProperties.containsKey("height")) { try { dim.width = (new Integer(layoutProperties.get("width").toString())).intValue(); dim.height = (new Integer(layoutProperties.get("height").toString())).intValue(); } catch (NumberFormatException e) { throw new IllegalArgumentException("cannot calculate minimum size: invalid argument in properties"); } } else { throw new IllegalArgumentException("cannot calculate minimum size: missing argument in properties"); } return dim; } } /** * preferred size is the same as minimum size for this layout */ public Dimension preferredLayoutSize(Container target) { synchronized (target.getTreeLock()) { return minimumLayoutSize(target); } } public void invalidateLayout(Container target) { } /** * layout the container according to information found in the layout properties file */ public void layoutContainer(Container target) { synchronized (target.getTreeLock()) { try { // call processAttributes for the container itself ("" = no component name) if (layoutProperties.containsKey("attributes")) { processAttributes("", target); } Enumeration e = components.keys(); while (e.hasMoreElements()) { String s = e.nextElement().toString(); if (!components.containsKey(s)) { System.out.println("error. component missing"); } Component c = (Component)components.get(s); // minimum attributes need to layout a component int xpos = new Integer(layoutProperties.get(s + ".xpos").toString()).intValue(); int ypos = new Integer(layoutProperties.get(s + ".ypos").toString()).intValue(); int width = new Integer(layoutProperties.get(s + ".width").toString()).intValue(); int height = new Integer(layoutProperties.get(s + ".height").toString()).intValue(); c.setLocation(xpos,ypos); c.setSize(width,height); // visible is not a mandatory attribute, so we check if it's there first if (layoutProperties.containsKey(s + ".visible") && layoutProperties.get(s + ".visible").toString().equalsIgnoreCase("false")) { c.setVisible(false); } // now check for custom attributes if (layoutProperties.containsKey(s + ".attributes")) { processAttributes(s + ".", c); } } } catch (Exception ex) { ex.printStackTrace(); } } } /** * For a specified component name, find its string of attributes, then attempt to find a corresponding * set method in the component for each attribute. The set method must take a string as its parameter. * eg. for an attribute string gobutton.attributes=image=temp.gif,text=Test we will attempt to find a setImage and * setText method in the component 'gobutton' */ private void processAttributes(String s, Component c) throws Exception { StringTokenizer st, attr_st; // retrieve the list of methods Method methods[] = c.getClass().getMethods(); if (methods != null && methods.length > 0) { // tokenize the attributes, delimited by comma's st = new StringTokenizer(layoutProperties.get(s + "attributes").toString(),","); // loop for each individual attribute while (st.hasMoreTokens()) { attr_st = new StringTokenizer(st.nextToken(),"="); if (attr_st.countTokens() != 2) { throw new Exception("invalid attribute string"); } String attribute = attr_st.nextToken(); String value = attr_st.nextToken(); // find the associated method // if found, then invoke it with the attribute value boolean found = false; for (int i = 0; i < methods.length; i++) { if (methods[i].getName().equalsIgnoreCase("set" + attribute) && methods[i].getParameterTypes().length == 1 && methods[i].getParameterTypes()[0].getName().equals("java.lang.String")) { found = true; Object values[] = { value }; methods[i].invoke(c,values); } } if (!found) { System.out.println("could not find a corresponding method for set" + attribute + " in [" + s + "]"); } } attr_st = null; } st = null; } public String toString() { return getClass().getName() + "[]"; } }