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; import ballWorlds.World; /** * Pressing a BallButton constructs a Ball of the type whose name appears on the * BallButton. It does this using a Java feature called "reflection". * * @author David Mutchler, Salman Azhar, Curt Clifton and others, January 2005. * Modified September 2008, September 2009. */ public class BallButton extends JButton implements ActionListener { 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 BALLS_PACKAGE_NAME = "ballWorlds.ball"; private String ballType; private World world; /** * Associates a type of Ball and a World 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 world * the World for all Balls created by pressing this button */ public BallButton(String ballType, World world) { super(ballType); this.ballType = BALLS_PACKAGE_NAME + "." + ballType; this.world = world; 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) */ @SuppressWarnings("unchecked") public void actionPerformed(ActionEvent event) { try { // Get the Class for the Ball Class ballClass; try { ballClass = Class.forName(this.ballType).asSubclass(Ball.class); } catch (ClassCastException e) { // Oops. It's not a subclass of Ball. this.showErrorMessage(BallButton.ERROR_ClassIsNotABall); return; } // Make sure it is not abstract if (Modifier.isAbstract(ballClass.getModifiers())) { this.showErrorMessage(BallButton.ERROR_ClassIsAbstract); return; } // Use reflection to get the Ball's constructor. Constructor constructor; Class[] parameters = { World.class }; constructor = ballClass.getConstructor(parameters); // Construct a new instance of the ball and add it to the World Ball b = constructor.newInstance(this.world); this.world.addBall(b); } 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 put it somewhere other than the ballworlds.ball package?\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\nhas a constructor that has a single parameter of type BallEnvironment.\n" + "Your " + this.ballType + " class lacks such a constructor. Please add one."; 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); } }