package ballworlds.framework; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; import javax.swing.JButton; import javax.swing.JOptionPane; import ballworlds.ball.Ball; /** * Pressing a BallButton constructs a Ball of the type * whose name appears on the BallButton. * * @author David Mutchler, Salman Azhar and others, January 2005. * Modified September, 2008. */ public class BallButton extends JButton implements ActionListener { private String ballType; private BallEnvironment ballEnvironment; private static final int ERROR_ClassNotFound = 1; private static final int ERROR_ClassIsNotABall = 2; private static final int ERROR_ClassIsAbstract = 3; private static final int ERROR_NoConstructor = 4; private static final int ERROR_CouldNotConstructBall = 5; private static final String ballPackage = "ballworlds.ball"; /** * Associates a type of Ball and a BallEnvironment with this button, * labels this button with the type of Ball, and arranges * for this button to respond to its own button-presses. * * @param ballType the type of Ball to be created by pressing this button * @param ballEnvironment the BallEnvironment for all Balls created by pressing this button */ public BallButton(String ballType, BallEnvironment ballEnvironment) { super(ballType); this.ballType = ballType; this.ballEnvironment = ballEnvironment; this.addActionListener(this); } /** * Creates a Ball of the type whose name appears on this BallButton. * * @param event the Event that caused this method to be invoked * (usually, pressing this BallButton) */ public void actionPerformed(ActionEvent event) { try { // Get the Class for the Ball Class ballClass = Class.forName( BallButton.ballPackage + "." + this.ballType); // Make sure that it is a Ball class and not abstract. if (! Ball.class.isAssignableFrom(ballClass)) { this.showErrorMessage(BallButton.ERROR_ClassIsNotABall); return; } if (Modifier.isAbstract(ballClass.getModifiers())) { this.showErrorMessage(BallButton.ERROR_ClassIsAbstract); return; } // Construct the Ball, via the constructor that takes a BallEnvironment. // Don't add the ball to the World -- leave that to the Ball itself. Constructor constructor; Class[] parameters = {BallEnvironment.class}; constructor = ballClass.getConstructor(parameters); constructor.newInstance(this.ballEnvironment); } catch (ClassNotFoundException exception) { this.showErrorMessage(BallButton.ERROR_ClassNotFound); } catch (NoSuchMethodException exception) { this.showErrorMessage(BallButton.ERROR_NoConstructor); } catch (Exception exception) { exception.printStackTrace(); this.showErrorMessage(BallButton.ERROR_CouldNotConstructBall); } } /* * Displays an error message appropriate for the given error type. */ private void showErrorMessage(int errorType) { String message, error; switch (errorType) { case ERROR_ClassNotFound: // there is no class with this button's name message = "There is no " + this.ballType + " class in this project.\n" + "Did you forget to implement it?\n" + "Or did you misspell it? (Case matters!)"; error = "Error: class does not exist"; break; case ERROR_ClassIsNotABall: // the class with this button's name is not a Ball message = "The " + this.ballType + " class does not extend Ball.\n" + "Did you forget to declare it to do so?\n"; error = "Error: class is not a Ball"; break; case ERROR_ClassIsAbstract: // the class with this button's name is abstract message = "Your " + this.ballType + " class is abstract.\n" + "It must NOT be abstract -- you really must implement all the methods\n" + "exactly as specified in Drawable, Relocatable and Animate. Please do so.\n"; error = "Error: class is abstract"; break; case ERROR_NoConstructor: // the class with this button's name does not have an appropriate constructor message = "I cannot create a " + this.ballType + " unless the " + this.ballType + " class has a constructor that has a single parameter of type BallEnvironment.\n" + "Your " + this.ballType + " class lacks such a constructor. Please add one.\n" + "That constructor should add the Ball to its World (otherwise, the Ball is not displayed).\n"; error = "Error: class lacks an appropriate constructor"; break; case ERROR_CouldNotConstructBall: // I could not create the Ball, for unknown reasons. message = "I cannot construct a " + this.ballType + ".\n" + "Perhaps something is wrong with the code in your constructor?\n" + "Examine the error message in the output window and get help as needed."; error = "Error: could not construct the Ball"; break; default: // Should never get here. message = "Unknown error message type." + "Please show this message and your " + this.ballType + " class to your instructor.\n"; error = "Error: unknown error message type"; } JOptionPane.showMessageDialog(null, message, error, JOptionPane.ERROR_MESSAGE); } }