Tetris in AspectJ By Gustav Evertsson,

Similar documents
AP CS Unit 11: Graphics and Events

Chapter 13 Lab Advanced GUI Applications

Theory Test 3A. University of Cape Town ~ Department of Computer Science. Computer Science 1016S ~ For Official Use

Chapter 13 Lab Advanced GUI Applications Lab Objectives. Introduction. Task #1 Creating a Menu with Submenus

RobotPlanning.java Page 1

Queens College, CUNY Department of Computer Science. CS 212 Object-Oriented Programming in Java Practice Exam 2. CS 212 Exam 2 Study Guide

Command-Line Applications. GUI Libraries GUI-related classes are defined primarily in the java.awt and the javax.swing packages.

Building Graphical User Interfaces. GUI Principles

More Swing. CS180 Recitation 12/(04,05)/08

Part 3: Graphical User Interface (GUI) & Java Applets

CSCI 136 Written Exam #2 Fundamentals of Computer Science II Spring 2015

DM550 / DM857 Introduction to Programming. Peter Schneider-Kamp

Building Graphical User Interfaces. Overview

Window Interfaces Using Swing Objects

Starting Out with Java: From Control Structures Through Objects Sixth Edition

Window Interfaces Using Swing Objects

Overview. Building Graphical User Interfaces. GUI Principles. AWT and Swing. Constructing GUIs Interface components GUI layout Event handling

TDDB84 Design Patterns Lecture 05. Builder, Singleton, Proxy. pelab

1.1 GUI. JFrame. import java.awt.*; import javax.swing.*; public class XXX extends JFrame { public XXX() { // XXX. init() main() public static

Proctors are unable to respond to queries about the interpretation of exam questions. Do your best to answer exam questions as written.

Layouts and Components Exam

Graphic User Interfaces. - GUI concepts - Swing - AWT

import javax.swing.*; import java.awt.*; import java.awt.event.*;

A Simple Text Editor Application

Midterm assessment - MAKEUP Fall 2010

The JFrame Class Frame Windows GRAPHICAL USER INTERFACES. Five steps to displaying a frame: 1) Construct an object of the JFrame class

G51PRG: Introduction to Programming Second semester Applets and graphics

CSEN401 Computer Programming Lab. Topics: Graphical User Interface Window Interfaces using Swing

Inheritance (cont) Abstract Classes

Programmierpraktikum

at 1.000,5.289 ljust "JLabel" at 3.438,5.289 ljust " JPanel" at 2.125,6.914 ljust "JFrame" at 2.250,9.039 ljust "content pane" at 2.125,7.

Proctors are unable to respond to queries about the interpretation of exam questions. Do your best to answer exam questions as written.

To gain experience using GUI components and listeners.

Exercise NMCGJ: Animated Graphics

Example 3-1. Password Validation

Control Flow: Overview CSE3461. An Example of Sequential Control. Control Flow: Revisited. Control Flow Paradigms: Reacting to the User

JRadioButton account_type_radio_button2 = new JRadioButton("Current"); ButtonGroup account_type_button_group = new ButtonGroup();

Advanced Java Unit 6: Review of Graphics and Events

Introduction to Graphical User Interfaces (GUIs) Lecture 10 CS2110 Fall 2008

Prototyping a Swing Interface with the Netbeans IDE GUI Editor

Programming Language Concepts: Lecture 8

Calculator Class. /** * Create a new calculator and show it. */ public Calculator() { engine = new CalcEngine(); gui = new UserInterface(engine); }

Systems Programming. Bachelor in Telecommunication Technology Engineering Bachelor in Communication System Engineering Carlos III University of Madrid

Attempt FOUR questions Marking Scheme Time: 120 mins

JLayeredPane. Depth Constants in JLayeredPane

RAIK 183H Examination 2 Solution. November 10, 2014

University of Cape Town Department of Computer Science Computer Science CSC1017F

Name: Checked: Learn about listeners, events, and simple animation for interactive graphical user interfaces.

CS108, Stanford Handout #22. Thread 3 GUI

Java Programming Lecture 6

User interfaces and Swing

Introduction to the JAVA UI classes Advanced HCI IAT351

Class 16: The Swing Event Model

State Application Using MVC

JAVA NOTES GRAPHICAL USER INTERFACES

1.00 Lecture 14. Lecture Preview

FirstSwingFrame.java Page 1 of 1

Datenbank-Praktikum. Universität zu Lübeck Sommersemester 2006 Lecture: Swing. Ho Ngoc Duc 1

Swing I CHAPTER EVENT-DRIVEN PROGRAMMING 921 Events and Listeners 921

Adding Buttons to StyleOptions.java

H212 Introduction to Software Systems Honors

Part I: Learn Common Graphics Components

DM503 Programming B. Peter Schneider-Kamp.

Frames, GUI and events. Introduction to Swing Structure of Frame based applications Graphical User Interface (GUI) Events and event handling

CS180 Spring 2010 Exam 2 Solutions, 29 March, 2010 Prof. Chris Clifton

AP CS Unit 12: Drawing and Mouse Events

MIT AITI Swing Event Model Lecture 17

Handout 14 Graphical User Interface (GUI) with Swing, Event Handling

Multiple Choice Questions: Identify the choice that best completes the statement or answers the question. (15 marks)

CSC 1051 Data Structures and Algorithms I. Dr. Mary-Angela Papalaskari Department of Computing Sciences Villanova University

Queen s University Faculty of Arts and Science School of Computing CISC 124 Final Examination December 2004 Instructor: M. Lamb

Today. cisc3120-fall2012-parsons-lectiii.3 2

Question 1. Show the steps that are involved in sorting the string SORTME using the quicksort algorithm given below.

Java Help Files. by Peter Lavin. May 22, 2004

!"# $ %&# %####' #&() % # # # #&* # ## +, # -

Introduction This assignment will ask that you write a simple graphical user interface (GUI).

Swing I Event-Driven Programming Buttons, Events, and Other Swing Basics Containers and Layout Managers 946

ANSWER KEY Exam 2 Computer Programming 230 Dr. St. John Lehman College City University of New York Thursday, 5 November 2009

Swing from A to Z Some Simple Components. Preface

CS 335 Graphics and Multimedia. Image Manipulation

Points Missed on Page page 1 of 8

APPENDIX. public void cekroot() { System.out.println("nilai root : "+root.data); }

G51PGP Programming Paradigms. Lecture 009 Concurrency, exceptions

Property Editors and Customizers. COMP434 Software Design. Property Editors. Property Editors and. Property Editors.

Swing - JTextField. Adding a text field to the main window (with tooltips and all)

Graphical User Interfaces (GUIs)

1 Looping Constructs (4 minutes, 2 points)

Graphics programming. COM6516 Object Oriented Programming and Design Adam Funk (originally Kirill Bogdanov & Mark Stevenson)

COMP Assignment #10 (Due: Monday, March 11:30pm)

An array is a type of variable that is able to hold more than one piece of information under a single variable name.

Multithread Computing

Practice Midterm 1. Problem Points Score TOTAL 50

AnimatedImage.java. Page 1

Programming Languages and Techniques (CIS120)

Chapter 8. Java continued. CS Hugh Anderson s notes. Page number: 264 ALERT. MCQ test next week. This time. This place.

Java.net - the Source for Java(tm) Technology Collaboration

Implementing Graphical User Interfaces

CSC System Development with Java Introduction to Java Applets Budditha Hettige

Java continued. Chapter 8 ALERT ALERT. Last week. MCQ test next week. This time. This place. Closed book. Assignment #2 is for groups of 3

CS Exam 3 - Spring 2010

Transcription:

Tetris in AspectJ 2003-01-20 By Gustav Evertsson, pt99gev@student.bth.se

1. Contents 1. CONTENTS... 1 2. INTRODUCTION... 2 PROBLEM DESCRIPTION... 2 WHAT IS TETRIS?... 2 3. DESIGN... 3 3.1. OVERALL ARCHITECTURE... 3 4. DIFFERENT ASPECTS... 4 4.1. DEVELOPMENT ASPECTS... 4 4.1.1 Design Checker... 4 4.2. GUI ASPECTS... 4 4.2.1 Game Info... 4 4.2.2. Menu... 5 4.3. HIGHSCORE ASPECTS... 6 4.3.1. Counter... 6 4.3.2. Level... 7 4.4. LOGIC ASPECTS... 8 4.4.1. New blocks... 8 4.4.2. Next block... 10 4. CONCLUSION... 11 5. REFERENCES... 12 6. APPENDIX... 13 6.1 ASPECTTETRIS.JAVA... 13 6.2 BLOCKS.JAVA... 16 6.3 TETRISIMAGES.JAVA... 20 6.4 TIMER.JAVA... 21 6.5 BLOCKPANEL.JAVA... 21 6.6 TETRISGUI.JAVA... 23 6.7 IEVENTLISTNER.JAVA... 25-1 -

2. Introduction This paper is done as a part of the course Advanced Software Engineering (pad004) at Blekinge Institute of Technology. As a compliment to the two previous reports [3, 4] that I have written about AspectJ have I done a small practical case study to see if the conclusions that I came up with are true. I have chosen to do this by programming a small game called Tetris. What I wanted to see is how AspectJ can be used to change an already complete program and some of the problems around this. The code has been compiled and tested with the following versions: AspectJ [1]: 1.1b2 Java [5]: 1.4.0.01 Problem description I started by programming the Tetris game without any thoughts about how to implement the aspects later on. A description about this can be found in chapter 3. The focus of this report is on how the aspect that I developed works. This is done in chapter 4. I decided to make the following aspects. I wanted to test different parts of AspectJ so they are made to show different things that can be made with aspects: Design checker I use it to check that the layer architecture that I use is correct. It will generate an error output during the compilation if it founds that something is wrong. (Chapter 4.1.1.) Game Info This is a Panel for showing info about the game. Other aspects then add the info to this panel. I use this aspect as an example about how the key dominates can be used. (Chapter 4.2.1.) Menu This aspect adds a menu to the gui and connects it to functionality that is already implemented. (Chapter 4.2.2.) Counter This aspect counts deleted lines. It uses the Game Info aspect to add a label to the gui. (Chapter 4.3.1.) Level This is the only aspect in this program that has a pointcut to another aspect. It uses the Counter to see how many lines that has been deleted and makes the game go faster when it reaches the next level. (Chapter 4.3.2.) New Blocks This aspect adds two new types of blocks to the game. The thing that is special about this aspect is that it use the proceed() call in the advices. (Chapter 4.4.1.) Next Block This is the only aspect that completely changes a method in the original program. It changes so the program shows what the next block is when the current block is dropped. (Chapter 4.4.1.) What is Tetris? The rules in Tetris are easy to understand. The goal is to pack the blocks so they become lines. The lines are then deleted so the user can add more blocks. The blocks are randomly generated at the top of the game board and are slowly dropped down until they reach the bottom. The game ends if the blocks reach to the top of the game board. - 2 -

3. Design The design for the game was made to be as easy as possible and no extra features were included in the game from start. These where later added with aspects. 3.1. Overall architecture The overall architecture is divided into three different parts. First the Main class that has all the game rules and handles the incoming events. The second is the Logic package that handles all the data manipulation. And the last is the gui package that draws everything on the screen. The main class creates the Logic and the gui classes and the gui classes then only calls the main class through the IEventLister interface. This is because I wanted to lower the coupling between the gui classes and the main class. TetrisGUI -JFrame theframe -gamepanel : BlockPanel -listner : IEventListner +TetrisGUI(in plistner : IEventListner, in gamesizex : int, in gamesizey : int) +start(in listner : IEventListner, in gamesizex : int, in gamesizey : int) BlockPanel -BLOCKSIZE : int -lines : int -columns : int -blocklines : int[][] -backgroundfile : string +BlockPanel(in columns : int, in lines : int, in backgroundfile : string) +paintcomponent(in Graphics g) +setblocks(in blocklines : int[][]) +setblock(in col : int, in line : int, in type : int) TetrisImages -names : string[] -Hashtable cache -instance : TetrisImages +preload() +TetrisImages() +setinstance() +getimage(in name : string) : <unspecified>image -loadimage(in name : string) : <unspecified> AspectTetris +GAMESIZEX : int +GAMESIZEY : int +BLOCKSIZEX : int +BLOCKSIZEY : int -gameboard : int[][] -currentblock : int[][] -currentxpos : int -currentypos : int -gui : TetrisGUI -blocks : Blocks -timer : Timer -ongoinggame : bool -paused : bool +starttetris() +incomingevent(in eventtype : int) -newblock() -deletelines() -gameover() -restartgame() -pausegame() +getrandomblock() : int +main() : int[] Timer -Thread timer -listner : IEventListner -sleeptime : int +Timer(in listner : IEventListner, in sleeptime : int) +setsleeptime(in sleeptime : int) +start() +stop() +run() «interface» IEventListner +incomingevent(in eventtype : int) Blocks +EMPTY : int +RED : int +DARKBLUE : int +CYAN : int +GREEN : int +GRAY : int +YELLOW : int +MAGENTA : int +NUMBEROFTYPES : int -blocksizex : int -blocksizey : int -gamesizex : int -gamesizey : int +Blocks(in blocksizex : int, in blocksizey : int, in gamesizex : int, in gamesizey : int) +combineblocks(in block : int[][], in board : int[][], in x : int, in y : int) : int[][] +deleteblocks(in block : int[][], in board : int[][], in x : int, in y : int) : int[][] +checkcombineblocks(in block : int[][], in board : int[][], in x : int, in y : int) : int[][] +turnblock(in oldblock : int[][]) : int[][] +deleteline(in line : int, in gameboard : int[][]) +getblock(in type : int) : int[][] +typetostring(in type : int) : string +typetocolor(in type : int) : <unspecified> +typetoimage(in type : int) : <unspecified> - 3 -

4. Different Aspects These are the aspects that I have developed for this project. 4.1. Development aspects 4.1.1 Design Checker This aspect was only used during the development phase. This aspect checks so the architecture is followed in the code. I use it to check that the layer architecture that I use is correct. It will generate an error output during the compilation if it founds that something is wrong. package Aspects.Development; public aspect DesignCheck { declare warning: call(gui.blockpanel.new(..)) &&!within(gui.*) &&!within(aspects..*): "Do not create BlockPanel outside the Gui package!"; declare warning: call(* AspectTetris.*(..)) &&!within(aspecttetris) &&!within(aspects..*): "Do not call AspectTetris outside the class, use the IEventListner interface!"; It only requires two lines of code to do this. The first one checks so the BlockPanel class is not created outside the Gui package. The second checks so the AspectTetris class is not called directly (it must be done through the IEventListner interface). 4.2. Gui aspects This is some aspects that change the graphical user interface in different ways. 4.2.1 Game Info This is a Panel for showing info about the game. Other aspects then add the info to this panel. It is marked as dominating the rest of the aspects because the panel must be created first (It will generate a null pointer exception if another aspects tries to add anything to the panel if not the GameInfo aspects is executed first). package Aspects.Gui; import java.awt.*; import javax.swing.*; import javax.swing.border.*; import Gui.*; public aspect GameInfo dominates Aspects.*.* { //initialization pointcut guiinit() : execution(gui.tetrisgui.new(..)); public static JPanel infopanel; before() : guiinit() { //System.out.println("Target: " + thisjoinpoint.gettarget()); //System.out.println("This : " + thisjoinpoint.getthis()); TetrisGUI thegui = (TetrisGUI)thisJoinPoint.getThis(); thegui.setlayout(new BorderLayout()); - 4 -

thegui.setborder(new EtchedBorder()); infopanel = new JPanel(); infopanel.setlayout(new BoxLayout(infoPanel, BoxLayout.Y_AXIS)); thegui.add(infopanel, BorderLayout.WEST); In the declaration is the keyword dominates used to control in what order the aspect is executed. It means in this case that it is always executed before all other aspects in the Aspects package. The guiinit() pointcut is true when the TetrisGui class is initialized. The before() advice is then executed and the panel is added to the gui. 4.2.2. Menu I added some functionality in form of New Game and Pause from start. This can be controlled with the keys F2 and F3. I wanted with this aspect show how a menu can be added and done to call the same functionality. package Aspects.Gui; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.border.*; import Gui.*; import EventInterface.*; public aspect Menu implements ActionListener { protected JMenuItem newgamemi; protected JMenuItem pausemi; protected JMenuItem exitmi; protected IEventListner tetris; //initialization pointcut guiinit() : execution(gui.tetrisgui.new(..)); pointcut tetrisinit(ieventlistner tetris) : execution(aspecttetris.new(..)) && target(tetris); before() : guiinit() { TetrisGUI thegui = (TetrisGUI)thisJoinPoint.getThis(); JMenuBar menubar = new JMenuBar(); JMenu filemenu = (JMenu) menubar.add(new JMenu("File")); newgamemi = (JMenuItem) filemenu.add(new JMenuItem("New Game")); pausemi = (JMenuItem) filemenu.add(new JMenuItem("Pause")); filemenu.add(new JSeparator()); exitmi = (JMenuItem) filemenu.add(new JMenuItem("Exit")); newgamemi.addactionlistener(this); pausemi.addactionlistener(this); exitmi.addactionlistener(this); thegui.add(menubar, BorderLayout.NORTH); before(ieventlistner tetris) : tetrisinit(tetris) { this.tetris = tetris; public void actionperformed(actionevent e) { if (e.getsource().equals(newgamemi)) { - 5 -

tetris.incomingevent(ieventlistner.newgame); else if (e.getsource().equals(pausemi)) { tetris.incomingevent(ieventlistner.pause); else if (e.getsource().equals(exitmi)) { System.exit(0); The first two pointcuts is used to get the instances of the AspectTetris and TetrisGui classes that are later used. The before guiinit() advice also adds the menu in the gui. The aspect implements the ActionListener interface in the same way as with a normal class. 4.3. Highscore aspects These aspects are made to make the game funnier by adding a counter and levels to it. 4.3.1. Counter The first thing to get the game a little bit funnier is to add a counter that counts the number of lines that is deleted. This aspect adds a label to the GameInfo aspect. package Aspects.Highscore; import java.awt.*; import javax.swing.*; import Aspects.Gui.*; public aspect Counter { pointcut guiinit() : execution(gui.tetrisgui.new(..)); pointcut deletelines() : call(* AspectTetris.deleteLines()); pointcut deleteline() : call(* Logic.Blocks.deleteLine(..)); pointcut newgame() : call(* AspectTetris.restartGame()); pointcut gameover() : call(* AspectTetris.gameOver()); protected int currentlines = 0; protected int totallines = 0; protected JLabel linelabel; before() : guiinit() { linelabel = new JLabel("Lines: 0"); GameInfo.infoPanel.add(lineLabel); before() : deletelines() { currentlines = 0; after() : deletelines() { totallines += currentlines; if(currentlines!= 0) //System.out.println("Deleted " + currentlines + " lines (Total: " + totallines + ")."); linelabel.settext("lines: " + totallines); before() : deleteline() { - 6 -

currentlines++; before() : newgame() { totallines = 0; currentlines = 0; linelabel = new JLabel("Lines: 0"); after() : gameover() { System.out.println("Deleted totally " + totallines + " lines."); It is made so it can count how many lines that is deleted in the same time. This can be used in the future if I decide to count score as well. 4.3.2. Level The level aspect is made so it count the lines and when it reach a certain number of lines will it change the sleeping time for the timer so the game will go faster and faster. This aspect is a little bit special in the way that it has a pointcut that listen to another aspect. package Aspects.Highscore; import java.awt.*; import javax.swing.jlabel; import Aspects.Gui.*; import EventInterface.*; import Logic.*; public aspect Levels { pointcut guiinit() : execution(gui.tetrisgui.new(..)); pointcut timerinit(timer timer, IEventListner listner, int sleeptime) : execution(logic.timer.new(ieventlistner, int)) && target(timer) && args(listner, sleeptime); pointcut deletelines(int totallines) : set(int Aspects.Highscore.Counter.totalLines) && args(totallines); pointcut newgame() : call(* AspectTetris.restartGame()); protected JLabel levellabel; protected int nextlevel = 5; protected int currentlevel = 1; protected int sleeptime; protected Timer timer; before() : guiinit() { levellabel = new JLabel("Level: " + currentlevel); GameInfo.infoPanel.add(levelLabel); after(int totallines) : deletelines(totallines) { if(totallines > nextlevel) { currentlevel++; levellabel.settext("level: " + currentlevel); nextlevel = nextlevel * 2; sleeptime = (int)((double)sleeptime * 0.8); timer.setsleeptime(sleeptime); //System.out.println("New sleeptime: " + sleeptime); - 7 -

before(timer timer, IEventListner listner, int sleeptime) : timerinit(timer, listner, sleeptime) { this.timer = timer; this.sleeptime = sleeptime; before() : newgame() { nextlevel = 5; currentlevel = 1; levellabel.settext("level: " + currentlevel); The two *Init pointcuts is used to get the instances to the classes in the same way as with the aspects above. The difference is in the pointcut deletelines() that listen to changes with the totallines variable in the Score aspect. 4.4. Logic aspects 4.4.1. New blocks This aspect adds two new types of blocks. It adds this functionality to the Blocks class. It use the around advice to do this. It first checks if the type if of the old types and if it is so is the normal method called. This makes it possible to modify a method but it is not needed to rewrite all the old code. package Aspects.Logic; import java.awt.*; import Logic.*; public aspect NewBlocks { public static final int WHITE = 7; public static final int BLUE = 8; pointcut getblock(int type) : call(int[][] Logic.Blocks.getBlock(int)) && args(type); pointcut typetostring(int type) : call(string Logic.Blocks.typeToString(int)) && args(type); pointcut typetocolor(int type) : call(color Logic.Blocks.typeToColor(int)) && args(type); pointcut typetoimage(int type) : call(image Logic.Blocks.typeToImage(int)) && args(type); pointcut numberoftypes() : get(static int Blocks.NUMBEROFTYPES); int around() : numberoftypes() { return 9; int[][] around(int type) : getblock(type) { if(type < 7) return proceed(type); int[][] newblock = new int[4][4]; for(int x = 0; x < 4; x++) { for(int y = 0; y < 4; y++) { newblock[x][y] = Blocks.EMPTY; - 8 -

if(type == WHITE ) { newblock[0][1] = WHITE; newblock[0][2] = WHITE; newblock[1][1] = WHITE; newblock[2][1] = WHITE; newblock[2][2] = WHITE; else if(type == BLUE ) { newblock[1][0] = BLUE; newblock[1][1] = BLUE; newblock[1][2] = BLUE; newblock[2][1] = BLUE; newblock[2][2] = BLUE; return newblock; String around(int type) : typetostring(type) { if(type < 7) return proceed(type); else if(type == WHITE) return "white"; else if(type == BLUE) return "blue"; else return ""; Image around(int type) : typetoimage(type) { if(type < 7) return proceed(type); if(type == WHITE) return TetrisImages.getImage("gray.gif"); else if(type == BLUE) return TetrisImages.getImage("magenta.gif"); else return null; Color around(int type) : typetocolor(type) { if(type < 7) return proceed(type); else if(type == WHITE) return Color.WHITE; else if(type == BLUE) return Color.BLUE; else return Color.BLACK; The numberoftypes() pointcut change the Blocks class so all classes start to count with the two new blocks. Then are the getblock() and the different type converting method overridden to include the two new blocks. - 9 -

4.4.2. Next block This is the only aspect that completely changes a method. In this case change the getrandomblock() method so it instead have a next block that is returned. It adds a BlockPanel the GameInfo aspect so the user can see what the next block is. package Aspects.Logic; import java.util.random; import java.awt.*; import javax.swing.*; import Aspects.Gui.*; import Gui.*; import Logic.*; public aspect NextBlock { pointcut guiinit() : execution(gui.tetrisgui.new(..)); pointcut getnextblock() : call(* AspectTetris.getRandomBlock()); protected BlockPanel nextblockpanel; protected int nextblock; before() : guiinit() { Random rn = new Random(); nextblock = rn.nextint(blocks.numberoftypes); nextblockpanel = new BlockPanel(4, 4, ""); GameInfo.infoPanel.add(nextBlockPanel); nextblockpanel.setblocks(blocks.getblock(nextblock)); int[][] around() : getnextblock() { int currentblock = nextblock; Random rn = new Random(); nextblock = rn.nextint(blocks.numberoftypes); nextblockpanel.setblocks(blocks.getblock(nextblock)); return Blocks.getBlock(currentBlock); The getrandomblock() method in the AspectTetris class is modified so it holds one block and in this way can output what the next block is. The guiinit() pointput adds the nextblockpanel to the gui. - 10 -

4. Conclusion First the positive sides that I found. I found it to be easier than I first had expected to add and/or modify the functionality with aspects. I found the code to be cleaner because the classes only needed to have the game logic. All extra features that can make the code more complex and harder to understand were placed in the aspects. The powerful pointcuts and advices made that the aspects didn t have to include much code but could be made small and straightforward. The feature to check the design with aspects under the development may not have been that useful here because one person can have control over all the code but I think this can be very useful in bigger projects there no one have full control over everything. One negative side of aspects that I found is that the developer must have full control over the code to know how the aspects affect the program. Problems may also occur if the original program is changed because it can be hard to know how the aspects react to the changes. It is just this uncertainness that I see as the big problem with aspects. The coupling between the classes and the different aspects are often low for different reasons but this makes it hard to know how they affect each other. Even the compiler itself changes so just because the developer feels finished with some part of the code it is possible that he must go back and modify it. So my core conclusion I found is that you must know how the program works to be able to make good aspects. It is not possible to make aspects without system design and the source code. However if the developer have this knowledge about the program can the maintenance be made easier with aspects. There are things that you can t make without aspects, checking the design for example. - 11 -

5. References 1. AspectJ, http://www.eclipse.org/aspectj/ 2. Ivan Kiselev, Aspect-Oriented Programming with AspectJ, 2001, Sams, ISBN: 0-672- 32410-5 3. Gustav Evertsson, Aspect Oriented Programming, 2002 4. Gustav Evertsson, Strategies in Aspect Oriented Programming with AspectJ, 2002 5. Java, http://www.java.sun.com - 12 -

6. Appendix This is the source code from the project. The folder structure shall be the same as the package structure. 6.1 AspectTetris.java import java.util.random; import Gui.*; import EventInterface.*; import Logic.*; public class AspectTetris implements IEventListner { public static final int GAMESIZEX = 10; public static final int GAMESIZEY = 20; public static final int BLOCKSIZEX = 4; public static final int BLOCKSIZEY = 4; protected int[][] gameboard; protected int[][] currentblock; protected int currentxpos = (GAMESIZEX / 2) - (BLOCKSIZEX / 2); protected int currentypos = 0; protected TetrisGUI gui; protected Blocks blocks; protected Timer timer; protected boolean ongoinggame = false; protected boolean paused = false; public void starttetris() { System.out.println("Starting AspectTetris..."); TetrisImages.preLoad(); blocks = new Blocks(BLOCKSIZEX, BLOCKSIZEY, GAMESIZEX, GAMESIZEY); // Creates an empty game board. gameboard = new int[gamesizex][gamesizey]; for(int x = 0; x < GAMESIZEX; x++) { for(int y = 0; y < GAMESIZEY; y++) { gameboard[x][y] = Blocks.EMPTY; // Creates an empty block. currentblock = new int[blocksizex][blocksizey]; for(int x = 0; x < BLOCKSIZEX; x++) { for(int y = 0; y < BLOCKSIZEY; y++) { currentblock[x][y] = Blocks.EMPTY; gui = TetrisGUI.start(this, GAMESIZEX, GAMESIZEY); gui.gamepanel.setblocks(gameboard); currentblock = getrandomblock(); timer = new Timer(this, 700); timer.start(); ongoinggame = true; public void incomingevent(int eventtype) { if(!ongoinggame && eventtype!= IEventListner.NEWGAME) - 13 -

return; if(paused && eventtype!= IEventListner.PAUSE && eventtype!= IEventListner.NEWGAME) return; //System.out.println("New event of type " + eventtype); if(eventtype == IEventListner.UP) { // Turn currentblock... if(blocks.checkcombineblocks(blocks.turnblock(currentblock), blocks.deleteblocks(currentblock, gameboard, currentxpos, currentypos), currentxpos, currentypos)){ currentblock = blocks.turnblock(currentblock); blocks.combineblocks(currentblock, gameboard, currentxpos, currentypos); gui.gamepanel.setblocks(gameboard); else if(eventtype == IEventListner.DOWN) { // Fast down... while(blocks.checkcombineblocks(currentblock, blocks.deleteblocks(currentblock, gameboard, currentxpos, currentypos), currentxpos, currentypos + 1)){ currentypos++; blocks.combineblocks(currentblock, gameboard, currentxpos, currentypos); currentypos); //System.out.println("The block has reached the bottom!"); blocks.combineblocks(currentblock, gameboard, currentxpos, deletelines(); newblock(); else if(eventtype == IEventListner.LEFT){ if(blocks.checkcombineblocks(currentblock, blocks.deleteblocks(currentblock, gameboard, currentxpos, currentypos), currentxpos - 1, currentypos)){ currentxpos--; blocks.combineblocks(currentblock, gameboard, currentxpos, currentypos); gui.gamepanel.setblocks(gameboard); else if(eventtype == IEventListner.RIGHT) { if(blocks.checkcombineblocks(currentblock, blocks.deleteblocks(currentblock, gameboard, currentxpos, currentypos), currentxpos + 1, currentypos)){ currentxpos++; blocks.combineblocks(currentblock, gameboard, currentxpos, currentypos); gui.gamepanel.setblocks(gameboard); else if(eventtype == IEventListner.TIMER) { if(blocks.checkcombineblocks(currentblock, blocks.deleteblocks(currentblock, gameboard, currentxpos, currentypos), currentxpos, currentypos + 1)){ currentypos++; blocks.combineblocks(currentblock, gameboard, currentxpos, currentypos); gui.gamepanel.setblocks(gameboard); else { //System.out.println("The block has reached the bottom!"); blocks.combineblocks(currentblock, gameboard, currentxpos, currentypos); deletelines(); newblock(); - 14 -

else if(eventtype == IEventListner.NEWGAME) { restartgame(); else if(eventtype == IEventListner.PAUSE) { pausegame(); protected void newblock() { currentxpos = (GAMESIZEX / 2) - (BLOCKSIZEX / 2); currentypos = 0; currentblock = getrandomblock(); currentypos)){ currentypos); if(!blocks.checkcombineblocks(currentblock, gameboard, currentxpos, else { gameover(); blocks.combineblocks(currentblock, gameboard, currentxpos, gui.gamepanel.setblocks(gameboard); protected void deletelines() { for(int i = 0; i < GAMESIZEY; i++) { boolean complete = true; for(int j = 0; j < GAMESIZEX; j++) { if(gameboard[j][i] == Blocks.EMPTY) complete = false; if(complete) { blocks.deleteline(i, gameboard); protected void gameover() { System.out.println("Game Over!"); ongoinggame = false; timer.stop(); protected void restartgame() { System.out.println("New Game..."); // Creates an empty game board. gameboard = new int[gamesizex][gamesizey]; for(int x = 0; x < GAMESIZEX; x++) { for(int y = 0; y < GAMESIZEY; y++) { gameboard[x][y] = Blocks.EMPTY; newblock(); timer.start(); paused = false; ongoinggame = true; - 15 -

protected void pausegame() { if(paused) { paused = false; timer.start(); else { paused = true; timer.stop(); System.out.println("Pause..."); public int[][] getrandomblock() { Random rn = new Random(); block."); int randomvalue = rn.nextint(blocks.numberoftypes); System.out.println("New " + Blocks.typeToString(randomValue) + " return blocks.getblock(randomvalue); public static void main(string[] args) { AspectTetris tetris = new AspectTetris(); tetris.starttetris(); 6.2 Blocks.java package Logic; import java.awt.*; public class Blocks { public static final int EMPTY = -1; public static final int RED = 0; public static final int DARKBLUE = 1; public static final int CYAN = 2; public static final int GREEN = 3; public static final int GRAY = 4; public static final int YELLOW = 5; public static final int MAGENTA = 6; public static int NUMBEROFTYPES = 7; protected static int blocksizex; protected static int blocksizey; protected static int gamesizex; protected static int gamesizey; { public Blocks(int blocksizex, int blocksizey, int gamesizex, int gamesizey) this.blocksizex = blocksizex; this.blocksizey = blocksizey; this.gamesizex = gamesizex; this.gamesizey = gamesizey; public int[][] combineblocks(int[][] block, int[][] board, int x, int y) { for(int i = 0; i < blocksizex; i++) { for(int j = 0; j < blocksizey; j++) { try { if(block[i][j]!= Blocks.EMPTY) { board[i + x][j + y] = block[i][j]; - 16 -

catch(exception e) { System.out.println("Com Error " + e); return board; public int[][] deleteblocks(int[][] block, int[][] board, int x, int y) { for(int i = 0; i < blocksizex; i++) { for(int j = 0; j < blocksizey; j++) { try { if(block[i][j]!= Blocks.EMPTY) { board[i + x][j + y] = Blocks.EMPTY; catch(exception e) { System.out.println("Del Error: " + e); return board; public boolean checkcombineblocks(int[][] block, int[][] board, int x, int y) { for(int i = 0; i < blocksizex; i++) { for(int j = 0; j < blocksizey; j++) { try { if(block[i][j]!= Blocks.EMPTY) { if(j + y >= gamesizey){ //System.out.println("Y SIZE FEL, y:" + y + ", j:" + j); return false; else if(i + x < 0 i + x >= gamesizex){ //System.out.println("X SIZE FEL, x:" + x + ", i:" + i); return false; else if(board[i + x][j + y]!= Blocks.EMPTY) { //System.out.println("BLOCK FEL"); return false; catch(exception e) { System.out.println("Check Error: " + e); return false; return true; public int[][] turnblock(int[][] oldblock) { int[][] newblock = new int[blocksizex][blocksizey]; for(int i = 0; i < blocksizex; i++) { for(int j = 0; j < blocksizey; j++){ newblock[j][i] = oldblock[3-i][j]; - 17 -

return newblock; public void deleteline(int line, int[][] gameboard) { for(int j = line; j >= 0; j--){ for(int i = 0; i < gamesizex; i++) { if(j == 0) gameboard[i][j] = EMPTY; else gameboard[i][j] = gameboard[i][j - 1]; public static int[][] getblock(int type) { // Reset current block first; int[][] newblock = new int[blocksizex][blocksizey]; for(int x = 0; x < blocksizex; x++) { for(int y = 0; y < blocksizey; y++) { newblock[x][y] = Blocks.EMPTY; if(type == RED ) { newblock[1][0] = RED; newblock[1][1] = RED; newblock[1][2] = RED; newblock[1][3] = RED; else if(type == DARKBLUE ) { newblock[1][2] = DARKBLUE; newblock[2][1] = DARKBLUE; newblock[2][2] = DARKBLUE; newblock[3][1] = DARKBLUE; else if(type == CYAN ) { newblock[1][1] = CYAN; newblock[2][1] = CYAN; newblock[1][2] = CYAN; newblock[2][2] = CYAN; else if(type == GREEN ) { newblock[1][1] = GREEN; newblock[2][1] = GREEN; newblock[2][2] = GREEN; newblock[3][2] = GREEN; else if(type == GRAY ) { newblock[0][1] = GRAY; newblock[1][1] = GRAY; newblock[1][2] = GRAY; newblock[2][1] = GRAY; else if(type == YELLOW ) { newblock[0][1] = YELLOW; newblock[0][2] = YELLOW; newblock[1][1] = YELLOW; newblock[2][1] = YELLOW; else if(type == MAGENTA ) { newblock[0][1] = MAGENTA; newblock[1][1] = MAGENTA; newblock[2][1] = MAGENTA; newblock[2][2] = MAGENTA; - 18 -

return newblock; public static String typetostring(int type) { if(type == Blocks.RED) return "red"; else if(type == Blocks.DARKBLUE) return "blue"; else if(type == Blocks.CYAN) return "cyan"; else if(type == Blocks.GREEN) return "green"; else if(type == Blocks.GRAY) return "gray"; else if(type == Blocks.YELLOW) return "yellow"; else if(type == Blocks.MAGENTA) return "magenta"; else return ""; public static Color typetocolor(int type) { if(type == Blocks.RED) return Color.red; else if(type == Blocks.DARKBLUE) return Color.blue; else if(type == Blocks.CYAN) return Color.cyan; else if(type == Blocks.GREEN) return Color.green; else if(type == Blocks.GRAY) return Color.gray; else if(type == Blocks.YELLOW) return Color.yellow; else if(type == Blocks.MAGENTA) return Color.magenta; else return Color.black; public static Image typetoimage(int type) { if(type == Blocks.RED) return TetrisImages.getImage("red.gif"); else if(type == Blocks.DARKBLUE) return TetrisImages.getImage("darkblue.gif"); else if(type == Blocks.CYAN) return TetrisImages.getImage("cyan.gif"); else if(type == Blocks.GREEN) return TetrisImages.getImage("green.gif"); else if(type == Blocks.GRAY) return TetrisImages.getImage("gray.gif"); else if(type == Blocks.YELLOW) return TetrisImages.getImage("yellow.gif"); else if(type == Blocks.MAGENTA) return TetrisImages.getImage("magenta.gif"); else return null; - 19 -

6.3 TetrisImages.java package Logic; import java.awt.*; import java.awt.image.*; import java.util.hashtable; import java.net.url; import java.net.urlclassloader; public class TetrisImages extends Component { private String[] names = { "cyan.gif", "background.gif", "red.gif", "darkblue.gif", "green.gif", "gray.gif", "yellow.gif", "magenta.gif"; private Hashtable cache; private static TetrisImages instance; public static void preload() { setinstance(); private TetrisImages() { cache = new Hashtable(names.length); for (int i = 0; i < names.length; i++) { cache.put(names[i], loadimage(names[i])); private static void setinstance() { if(instance == null) instance = new TetrisImages(); public static Image getimage(string name) { setinstance(); if (instance.cache!= null) { if(instance.cache.containskey(name)) return (Image)instance.cache.get(name); else { Image img = instance.loadimage(name); instance.cache.put(name, img); return img; return null; protected Image loadimage(string name) { URLClassLoader urlloader = (URLClassLoader)this.getClass().getClassLoader(); URL fileloc = urlloader.findresource("images/" + name); Image img = this.gettoolkit().createimage(fileloc); MediaTracker tracker = new MediaTracker(this); tracker.addimage(img, 0); try { tracker.waitforid(0); if (tracker.iserrorany()) { System.out.println("Error loading image " + name); catch (Exception ex) { ex.printstacktrace(); return img; - 20 -

6.4 Timer.java package Logic; import EventInterface.*; public class Timer implements Runnable { protected volatile Thread timer; protected IEventListner listner; protected int sleeptime; public Timer(IEventListner listner, int sleeptime) { this.listner = listner; this.sleeptime = sleeptime; public void setsleeptime(int sleeptime) { this.sleeptime = sleeptime; public void start() { timer = new Thread(this); timer.start(); public void stop() { timer = null; public void run() { Thread me = Thread.currentThread(); while (timer == me) { try { Thread.currentThread().sleep(sleepTime); catch (InterruptedException e) { listner.incomingevent(ieventlistner.timer); 6.5 BlockPanel.java package Gui; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import javax.swing.*; import Logic.*; public class BlockPanel extends JComponent { protected final int BLOCKSIZE = 15; protected int lines; protected int columns; protected int[][] blocklines; protected String backgroundfile; public BlockPanel(int columns, int lines, String backgroundfile) { this.lines = lines; this.columns = columns; this.backgroundfile = backgroundfile; blocklines = new int[columns][lines]; for(int x = 0; x < columns; x++) { for(int y = 0; y < lines; y++) { blocklines[x][y] = Blocks.EMPTY; - 21 -

//setbackground(color.gray); //setsize( 100, 100); public void paintcomponent(graphics g) { super.paintcomponent(g); if(blocklines == null) return; + 10); if(!backgroundfile.equals("")){ Image img = TetrisImages.getImage(backGroundFile); g.drawimage(img, 0, 0, (ImageObserver)this); else { //setbackground(color.black); g.setcolor(color.black); g.fillrect(0, 0, columns * BLOCKSIZE + 10, lines * BLOCKSIZE for(int x = 0; x < columns; x++) { for(int y = 0; y < lines; y++) { if(blocklines[x][y]!= Blocks.EMPTY){ // Draw colored blocks //g.setcolor(blocks.typetocolor(blocklines[x][y])); //g.fill3drect( x * BLOCKSIZE, y * BLOCKSIZE, BLOCKSIZE, BLOCKSIZE, true); // Draw images Image img = Blocks.typeToImage(blockLines[x][y]); g.drawimage(img, x * BLOCKSIZE + 5, y * BLOCKSIZE + 5, (ImageObserver)this); /** * Takes an array and update the panel from it. */ public void setblocks(int[][] blocklines) { this.blocklines = blocklines; repaint(new Rectangle(getPreferredSize())); public void setblock(int col, int line, int type) { blocklines[col][line] = type; repaint(new Rectangle(getPreferredSize())); public Dimension getminimumsize() { return getpreferredsize(); public Dimension getmaximumsize() { return getpreferredsize(); 10); public Dimension getpreferredsize() { return new Dimension(columns * BLOCKSIZE + 10, lines * BLOCKSIZE + - 22 -

6.6 TetrisGUI.java package Gui; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.beans.*; import Logic.*; import EventInterface.*; public class TetrisGUI extends JPanel { static JFrame theframe; public BlockPanel gamepanel; protected IEventListner listner; public TetrisGUI(IEventListner plistner, int gamesizex, int gamesizey) { this.listner = plistner; gamepanel = new BlockPanel(gameSizeX, gamesizey, "background.gif"); add(gamepanel); //gamepanel.setlocation( 75, 75); AbstractAction actionup = new AbstractAction() { public void actionperformed(actionevent e) { listner.incomingevent(ieventlistner.up); //System.out.println("Key: Up"); ; AbstractAction actiondown = new AbstractAction() { public void actionperformed(actionevent e) { listner.incomingevent(ieventlistner.down); //System.out.println("Key: Down"); ; AbstractAction actionleft = new AbstractAction() { public void actionperformed(actionevent e) { listner.incomingevent(ieventlistner.left); //System.out.println("Key: Left"); ; AbstractAction actionright = new AbstractAction() { public void actionperformed(actionevent e) { listner.incomingevent(ieventlistner.right); //System.out.println("Key: Right"); ; AbstractAction actionnewgame = new AbstractAction() { public void actionperformed(actionevent e) { listner.incomingevent(ieventlistner.newgame); //System.out.println("Key: Right"); ; AbstractAction actionpause = new AbstractAction() { public void actionperformed(actionevent e) { listner.incomingevent(ieventlistner.pause); //System.out.println("Key: Right"); ; try{ - 23 -

0); ).put(strokeup, "UP"); KeyStroke strokeup = KeyStroke.getKeyStroke(KeyEvent.VK_UP, gamepanel.getinputmap(jcomponent.when_in_focused_window gamepanel.getactionmap().put("up", actionup); KeyStroke strokedown = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0); gamepanel.getinputmap(jcomponent.when_in_focused_window ).put(strokedown, "DOWN"); gamepanel.getactionmap().put("down", actiondown); KeyStroke strokeleft = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0); gamepanel.getinputmap(jcomponent.when_in_focused_window ).put(strokeleft, "LEFT"); gamepanel.getactionmap().put("left", actionleft); KeyStroke strokeright = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0); gamepanel.getinputmap(jcomponent.when_in_focused_window ).put(strokeright, "RIGHT"); gamepanel.getactionmap().put("right", actionright); KeyStroke strokenewgame = KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0); gamepanel.getinputmap(jcomponent.when_in_focused_window ).put(strokenewgame, "NEWGAME"); gamepanel.getactionmap().put("newgame", actionnewgame); KeyStroke strokepause = KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0); gamepanel.getinputmap(jcomponent.when_in_focused_window ).put(strokepause, "PAUSE"); gamepanel.getactionmap().put("pause", actionpause); catch(exception e) { System.out.println("Problem setting up key events: " + e); public static TetrisGUI start(ieventlistner listner, int gamesizex, int gamesizey) { TetrisGUI gui = new TetrisGUI(listner, gamesizex, gamesizey); theframe = new JFrame("AspectTetris 1.0"); theframe.addwindowlistener(new WindowAdapter() { public void windowclosing(windowevent e) {System.exit(0); ); //theframe.getcontentpane().add("center", gui); theframe.getcontentpane().add(gui); theframe.pack(); theframe.setsize(300, 400); theframe.setvisible(true); return gui; - 24 -

6.7 IEventListner.java package EventInterface; public interface IEventListner { public static final int UP = 1; public static final int DOWN = 2; public static final int LEFT = 3; public static final int RIGHT = 4; public static final int TIMER = 5; public static final int NEWGAME = 6; public static final int PAUSE = 7; // This method is called when a new events is fired. public void incomingevent(int eventtype); - 25 -