/* * @(#)SortItem.java 1.17f 95/04/10 James Gosling * * Copyright (c) 1994-1995 Sun Microsystems, Inc. All Rights Reserved. * * Permission to use, copy, modify, and distribute this software * and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and * without fee is hereby granted. * Please refer to the file http://java.sun.com/copy_trademarks.html * for further important copyright and trademark information and to * http://java.sun.com/licensing.html for further important licensing * information for the Java (tm) Technology. * * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES. * * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). SUN * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR * HIGH RISK ACTIVITIES. */ import java.awt.*; import javax.swing.*; import java.awt.event.*; /** * A simple applet class to demonstrate a sort algorithm. * You can specify a sorting algorithm using the "alg" * attribyte. When you click on the applet, a thread is * forked which animates the sorting algorithm. * * @author James Gosling * @version 1.17f, 10 Apr 1995 * * 27 Nov 2006: (bwbecker@uwaterloo.ca) Changed the overall architecture to * use model-view-controller. Re-examined algorithms to ensure that counting * was consistent across all algorithms (the quicksorts used to ignore most * of the comparisons in the partitioning, for example). Added controls to * pause the sort and display counts. */ public class SortItem extends JApplet implements IView { /** * The component displaying the sort algorithm's progress. */ private SortDisplay sortDisplay; private SortAlgorithm model; private JButton startButton = new JButton("Start"); private JButton resetButton = new JButton("Reset"); private JRadioButton dataRandom = new JRadioButton("Random", true); private JRadioButton dataInorder = new JRadioButton("Inorder"); private JRadioButton dataReverse = new JRadioButton("Reverse"); private JLabel compares = new JLabel("0"); private JLabel movements = new JLabel("0"); private JLabel other = new JLabel("0"); /** * Initialize the applet. */ public void init() { String at = getParameter("alg"); if (at == null) { at = "BubbleSort"; } String algName = at + "Algorithm"; try { this.model = (SortAlgorithm) Class.forName(algName) .newInstance(); this.sortDisplay = new SortDisplay(this.model); this.layoutView(); this.registerControllers(); this.model.addView(this); } catch (Exception e) { throw new Error(e); } } private void layoutView() { ButtonGroup bg = new ButtonGroup(); bg.add(this.dataRandom); bg.add(this.dataInorder); bg.add(this.dataReverse); JPanel controls = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.fill = GridBagConstraints.HORIZONTAL; gbc.gridx = 0; gbc.gridy = GridBagConstraints.RELATIVE; gbc.anchor = GridBagConstraints.NORTH; gbc.gridwidth = 2; controls.add(this.startButton, gbc); controls.add(this.resetButton, gbc); controls.add(this.dataRandom, gbc); controls.add(this.dataInorder, gbc); controls.add(this.dataReverse, gbc); JLabel cLabel = new JLabel("Compare:"); JLabel mLabel = new JLabel("Moves:"); JLabel oLabel = new JLabel("Other:"); cLabel.setHorizontalAlignment(SwingConstants.TRAILING); mLabel.setHorizontalAlignment(SwingConstants.TRAILING); oLabel.setHorizontalAlignment(SwingConstants.TRAILING); this.compares.setHorizontalAlignment(SwingConstants.TRAILING); this.movements.setHorizontalAlignment(SwingConstants.TRAILING); this.other.setHorizontalAlignment(SwingConstants.TRAILING); gbc.gridwidth = 1; controls.add(cLabel, gbc); controls.add(mLabel, gbc); controls.add(oLabel, gbc); gbc.gridx = 1; gbc.gridy = 5; controls.add(this.compares, gbc); gbc.gridy++; controls.add(this.movements, gbc); gbc.gridy++; controls.add(this.other, gbc); Dimension d = this.startButton.getPreferredSize(); d.width = d.width + d.width/2; this.startButton.setMinimumSize(d); this.startButton.setPreferredSize(d); JPanel p = new JPanel(new BorderLayout()); p.add(this.sortDisplay, BorderLayout.CENTER); p.add(controls, BorderLayout.EAST); this.setContentPane(p); } /** Update the view with the most recent information from the model. * @param totalOps The total number of comparisons, movements, and * other array operations since the last update. Used to control the * length of the pause in the display. */ private SortState updateViewPrevState = null; public void updateView(int totalOps) { this.compares.setText(""+this.model.getTotalCompares()); this.movements.setText(""+this.model.getTotalMoves()); this.other.setText(""+this.model.getTotalOther()); if (this.updateViewPrevState != this.sortDisplay.state) { switch(this.sortDisplay.state) { case RUNNING: this.startButton.setText("Pause"); break; case PAUSED: this.startButton.setText("Resume"); break; case STOPPED: this.startButton.setText("Start"); break; } this.updateViewPrevState = this.sortDisplay.state; } } private void registerControllers() { this.startButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { switch (sortDisplay.state) { case RUNNING: sortDisplay.pauseSort(); break; case PAUSED: sortDisplay.resumeSort(); break; case STOPPED: sortDisplay.startSort(); break; } updateView(0); } }); this.resetButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { sortDisplay.stopSort(); updateView(0); } }); this.dataInorder.addActionListener(new DataOrderController(DataOrderEnum.INORDER)); this.dataRandom.addActionListener(new DataOrderController(DataOrderEnum.RANDOM)); this.dataReverse.addActionListener(new DataOrderController(DataOrderEnum.REVERSE_ORDER)); } /** * Stop the applet. Kill any sorting algorithm that * is still sorting. */ public void stop() { this.sortDisplay.stopSort(); } private class DataOrderController implements ActionListener { private DataOrderEnum order; public DataOrderController(DataOrderEnum o) { this.order = o; } public void actionPerformed(ActionEvent e) { sortDisplay.setDataOrder(this.order); updateView(0); } } } enum DataOrderEnum { RANDOM, INORDER, REVERSE_ORDER; } enum SortState { STOPPED, PAUSED, RUNNING; } class SortDisplay extends JComponent implements Runnable, IView { /** * The thread that is sorting (or null). */ private Thread kicker; /** * What's the current state of the animation? */ SortState state = SortState.STOPPED; /** * The array that is being sorted. */ int arr[]; /** * How long to sleep at each pause (milliseconds); */ int sleepTime = 10; /** * The sorting algorithm (or null). */ SortAlgorithm algorithm; DataOrderEnum dataOrder = DataOrderEnum.RANDOM; SortDisplay(SortAlgorithm alg) { this.algorithm = alg; this.algorithm.addView(this); this.addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { if (SortDisplay.this.getWidth() > 0 && state == SortState.STOPPED) { scramble(); repaint(); } } }); } /** Update the view with the most recent information from the model. * @param totalOps The total number of comparisons, movements, and * other array operations since the last update. Used to control the * length of the pause in the display. */ public synchronized void updateView(int totalOps) { this.repaint(); try { while (this.state == SortState.PAUSED) { this.wait(); } Thread.sleep(totalOps * this.sleepTime); } catch (InterruptedException e) { System.out.println("caught exception"); } } /** * Resume sorting after the sort was paused. */ synchronized void resumeSort() { this.state = SortState.RUNNING; this.notifyAll(); } /** * Pause the sort until the user resumes it. The actual call to wait is * in updateView. */ void pauseSort() { this.state = SortState.PAUSED; } void setDataOrder(DataOrderEnum order) { this.dataOrder = order; this.stopSort(); this.scramble(); this.repaint(); } synchronized void stopSort() { this.state = SortState.STOPPED; this.algorithm.stop(); this.algorithm.hiMarker = -1; this.algorithm.lowMarker = -1; this.algorithm.activeMarker = -1; this.scramble(); this.repaint(); } /** * For a Thread to actually do the sorting. This routine makes * sure we do not simultaneously start several sorts if the user * repeatedly clicks on the sort item. It needs to be * synchronoized with the stop() method because they both * manipulate the common kicker variable. */ synchronized void startSort() { this.algorithm.init(); this.state = SortState.RUNNING; kicker = new Thread(this); kicker.start(); } /** * Fill the array with random numbers from 0..n-1. */ void scramble() { int a[] = new int[this.getWidth() / 2]; switch (this.dataOrder) { case RANDOM: int h = this.getHeight(); for(int i=0; i= 0; x -= 2) { g.drawLine(x, h, x, h-arr[i]); } if (this.algorithm.hiMarker >= 0) { g.setColor(Color.red); x = this.algorithm.hiMarker * 2 + 1; g.drawLine(x, h, x, 0); } if (this.algorithm.lowMarker >= 0) { g.setColor(Color.blue); x = this.algorithm.lowMarker * 2 + 1; g.drawLine(x, h, x, 0); } if (this.algorithm.activeMarker >= 0) { g.setColor(Color.GREEN); x = this.algorithm.activeMarker * 2 + 1; g.drawLine(x, h, x, 0); } } /** * Run the sorting algorithm. This method is * called by class Thread once the sorting algorithm * is started. * @see java.lang.Thread#run */ public void run() { try { algorithm.init(); algorithm.sort(arr); } catch (Exception e) { System.out.println(e); } this.kicker = null; } }