/* * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Sun Microsystems nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.sun.swingset3; import java.awt.Component; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.lang.reflect.Method; import java.net.URL; import java.util.ArrayList; import java.util.logging.Level; import javax.swing.Icon; import javax.swing.ImageIcon; // PENDING JW: remove dependency (used for logger only) import org.jdesktop.swingxset.SwingXSet; /** * Wrapper class which encapsulates a GUI component to be displayed * as a SwingSet3 demo. * @author Amy Fowler */ public class Demo { public enum State { UNINITIALIZED, INITIALIZING, INITIALIZED, RUNNING, STOPPED, FAILED } private static final String IMAGE_EXTENSIONS[] = {".gif", ".png", ".jpg"}; public static String deriveCategoryFromPackageName(String className) { String parts[] = className.split("\\."); // return the right-most package name return parts.length >= 2? parts[parts.length-2] : "general"; } public static String deriveCategoryFromPackageName(Class demoClass) { String packageName = demoClass.getPackage() != null? demoClass.getPackage().getName() : null; if (packageName != null) { // if root package is swingset3, then remove it String swingsetPackageName = Demo.class.getPackage().getName(); if (packageName.startsWith(swingsetPackageName + ".demos.")) { packageName = packageName.replaceFirst(swingsetPackageName + ".demos.", ""); } } return packageName != null? packageName : "general"; } public static String deriveNameFromClassName(String className) { String simpleName = className.substring(className.lastIndexOf(".")+1, className.length()); return convertToDemoName(simpleName); } public static String deriveNameFromClassName(Class demoClass) { String className = demoClass.getSimpleName(); return convertToDemoName(className); } private static String convertToDemoName(String simpleClassName) { StringBuffer nameBuffer = new StringBuffer(); if (simpleClassName.endsWith("Demo")) { nameBuffer.append(simpleClassName.substring(0, simpleClassName.indexOf("Demo"))); nameBuffer.append(" "); nameBuffer.append("Demo"); } return nameBuffer.toString(); } private Class<?> demoClass; private String name; private String category; private String shortDescription; // used for tooltips private String iconPath; private Icon icon; private String[] sourceFilePaths; private URL[] sourceFiles; private Component component; private State state; private Exception failException; private PropertyChangeSupport pcs; public Demo(Class<?> demoClass) { this.demoClass = demoClass; initializeProperties(); } protected void initializeProperties() { // First look for DemoProperties annotation if it exists DemoProperties properties = demoClass.getAnnotation(DemoProperties.class); if (properties != null) { this.name = properties.value(); this.category = properties.category(); this.shortDescription = properties.description(); this.iconPath = properties.iconFile(); this.sourceFilePaths = properties.sourceFiles(); } else { this.name = deriveNameFromClassName(demoClass); this.category = deriveCategoryFromPackageName(demoClass); this.shortDescription = "No demo description, run it to find out..."; } state = State.UNINITIALIZED; pcs = new PropertyChangeSupport(this); } public Class getDemoClass() { return demoClass; } public String getName() { return name; } public String getCategory() { return category; } public Icon getIcon() { if (icon == null) { if (iconPath != null && !iconPath.equals("")) { // icon packageName was specified in DemoProperties annotation icon = getIconFromPath(iconPath); } else { // Look for icon with same name as demo class for(String ext : IMAGE_EXTENSIONS) { icon = getIconFromPath(getIconImagePath(ext)); if (icon != null) { break; } } } } return icon; } protected String getIconImagePath(String extension) { // by default look for an image with the same name as the demo class return "resources/images/" + demoClass.getSimpleName() + extension; } private Icon getIconFromPath(String path) { Icon icon = null; URL imageURL = demoClass.getResource(path); if (imageURL != null) { icon = new ImageIcon(imageURL); } return icon; } public String getShortDescription() { return shortDescription; } public URL getHTMLDescription() { // by default look for an html file with the same name as the demo class return demoClass.getResource("resources/" + demoClass.getSimpleName() + ".html"); } public URL[] getSourceFiles() { // If not already cached, then look them up if (sourceFiles == null) { ArrayList<URL> pathURLs = new ArrayList<URL>(); // Get any names registered in DemoProperties meta-data. // If meta-data is not specified then sourceFilePaths contains // one empty string. In this case we skip it. if (!(sourceFilePaths.length == 1 && sourceFilePaths[0].length() == 0)) { for (String path : sourceFilePaths) { URL url = getClass().getClassLoader().getResource(path); if (url == null) { SwingXSet.logger.log(Level.WARNING, "unable to load source file '" + path + "'"); } else { pathURLs.add(url); } } } this.sourceFiles = pathURLs.toArray(new URL[pathURLs.size()]); } return sourceFiles; } public void startInitializing() { setState(Demo.State.INITIALIZING); } void setDemoComponent(Component component) { if (component != null && !demoClass.isInstance(component)) { setState(State.FAILED); IllegalArgumentException e = new IllegalArgumentException("component must be an instance of " + demoClass.getCanonicalName()); failException = e; throw e; } Component old = this.component; this.component = component; if (component != null) { init(); } else { setState(State.UNINITIALIZED); } pcs.firePropertyChange("demoComponent", old, component); } public Component createDemoComponent() throws Exception { Component component = null; try { component = (Component)demoClass.newInstance(); setDemoComponent(component); } catch (Exception e) { System.err.println(e); e.printStackTrace(); failException = e; setState(State.FAILED); throw e; } return component; } public Component getDemoComponent() { return component; } public State getState() { return state; } protected void setState(State state) { State oldState = this.state; this.state = state; SwingXSet.logger.log(Level.FINE, "***>" +getName() + ":setState="+state); pcs.firePropertyChange("state", oldState, state); } public void addPropertyChangeListener(PropertyChangeListener pcl) { pcs.addPropertyChangeListener(pcl); } public void removePropertyChangeListener(PropertyChangeListener pcl) { pcs.removePropertyChangeListener(pcl); } private void init() { setState(State.INITIALIZED); try { Method initMethod = demoClass.getMethod("init", (Class[])null); initMethod.invoke(component, (Object[])null); } catch (NoSuchMethodException nsme) { // okay, no init method exists } catch (IllegalAccessException iae) { SwingXSet.logger.log(Level.SEVERE, "unable to init demo: "+demoClass.getName(), iae); failException = iae; setState(State.FAILED); } catch (java.lang.reflect.InvocationTargetException ite) { SwingXSet.logger.log(Level.SEVERE, "init method failed for demo: "+demoClass.getName(), ite); failException = ite; setState(State.FAILED); } catch (NullPointerException npe) { SwingXSet.logger.log(Level.SEVERE, "init method called before demo was instantiated: " +demoClass.getName(), npe); failException = npe; setState(State.FAILED); } } public void start() { try { Method startMethod = demoClass.getMethod("start", (Class[])null); startMethod.invoke(component, (Object[])null); setState(State.RUNNING); } catch (NoSuchMethodException nsme) { setState(State.RUNNING); // okay, no start method exists } catch (IllegalAccessException iae) { SwingXSet.logger.log(Level.SEVERE, "unable to start demo: "+demoClass.getName(), iae); failException = iae; setState(State.FAILED); } catch (java.lang.reflect.InvocationTargetException ite) { SwingXSet.logger.log(Level.SEVERE, "start method failed for demo: "+demoClass.getName(), ite); failException = ite; setState(State.FAILED); } catch (NullPointerException npe) { SwingXSet.logger.log(Level.SEVERE, "start method called before demo was instantiated: " +demoClass.getName(), npe); failException = npe; setState(State.FAILED); } } public void stop() { setState(State.STOPPED); try { Method stopMethod = demoClass.getMethod("stop", (Class[])null); stopMethod.invoke(component, (Object[])null); } catch (NoSuchMethodException nsme) { // okay, no stop method exists } catch (IllegalAccessException iae) { SwingXSet.logger.log(Level.SEVERE, "unable to stop demo: "+demoClass.getName(), iae); failException = iae; setState(State.FAILED); } catch (java.lang.reflect.InvocationTargetException ite) { SwingXSet.logger.log(Level.SEVERE, "stop method failed for demo: "+demoClass.getName(), ite); failException = ite; setState(State.FAILED); } catch (NullPointerException npe) { SwingXSet.logger.log(Level.SEVERE, "stop method called before demo was instantiated: " +demoClass.getName(), npe); } } }