UQ Students should read the Disclaimer & Warning
Note: This page dates from 2005, and is kept for historical purposes.
COMP2801 – Assignment Three – Software Modification
I achieved twenty-three out of a possible thirty-five marks.
Software Documentation Project
ASSIGNMENT 3
Project Team
Stephen Gordon
Stephen Jenks
Ned Martin
Lachlan Smith
Tuesday 8am Group A
Table of Contents
|
|
1. Introduction
Software products follow a set life cycle that generally follows the pattern of analysis, design, implementation, and maintenance; this pattern can be managed in many ways such as the waterfall model and the phased model, amongst others. The maintenance phase, however, is consistent throughout all models, in that it is an ongoing process that lasts until the software product’s life is terminated.
The maintenance of a software product can include many different operations being performed on the code. Some possibilities are the removal of bugs that were previously undetected during testing, alterations to the programs functionality by extensions to current features or the inclusion of new features, the improvement of in-code documentation, or re-factoring of the code. Re-factoring can be used to improve the internal structure of the code to allow the program to run more efficiently, to improve the usability of the code by loosening coupling between classes, or just to improve the codes’ general structure.
In prior research done by this group on the UMLet program, several areas were identified as candidates for improvement and a list of these can be found in the previously tabled report. Several of these improvements have now been implemented and it is the purpose of this document to give an account of these changes. The changes made to the UMLet program include extra features, and re-factoring of the program’s internal code. As well as the changes to be made, methods were determined for testing the implementations of the changes, and these will also be outlined in this document.
The extra features are the inclusion of a tip of the day generator, that appears upon original execution of the program or from a request in the menu bar, the ability to print UML diagrams from the drawing canvas, colour control of the UML entities, and a right click function to update UML relations. Code re-factoring was used to improve the object-oriented nature of the program by implementing a common interface for the save mechanisms that generate different file types from UMLet drawings.
2. Code Changes
2.1. Feature Selection
During an earlier stage of this project, a list of desirable features that could be added to the existing UMLet code base was presented with the goal being to implement these at some time in the future. As part of this next stage this list was re-examined and while some of those features listed have indeed been implemented some others have not. In this section, the reasons behind these decisions will be discussed.
2.1.1 Code Documentation
One of the major issues noted during the original examination of the UMLet source code was that for the most part it contained little or no commenting whatsoever. A result of this was that often building an understanding of the code or implementing changes to it was made much more difficult than would otherwise have been necessary. Unfortunately, the UMLet code package was found to be far too large to be documented in the short time available. As a result, the group chose instead to devote time to the implementation of other features and changes. The goal of improving code documentation was however achieved through the fact that all changes were fully labelled and documented. Where possible all documentation of implemented features was undertaken using full JavaDoc syntax. This can of course later be parsed to Hypertext Mark-up Language (HTML) if desired, resulting in readable and user friendly documentation for both current and future editors of the code.
2.1.2. Toolbar
Another suggestion made after the initial code examination was that it might prove useful to add a toolbar to the UMLet program. While it is indeed quite possible that this would still be beneficial it was decided that such a modification would provide too little benefit as compared to the amount of change required. As such further developments in the area of improving the UMLet user interface were directed elsewhere.
2.1.3. Extending save and open functionality
The original suggestion for changes in this area was to add additional capabilities by enabling the program to save to and open from additional file formats. This was to be done by allowing, for instance, loading from the Scalable Vector Graphics (SVG) format. Since this time it has however been decided that with the ability to save to no less than five different file formats, including UMLet’s own format, which it can also load from, the software is more than capable enough in this area. It was however noted that the inability of the program to print was a somewhat major deficiency and as such, the group went about implementing functionality in this area of output instead. This new direction was also chosen because of the level of additional interest it provided for the development team. None of the team members had until now used the Java language’s printing support at all and as such, this presented an opportunity to learn something new while also working towards obtaining the goals of the project.
2.1.4. Additional UML icons and improvement of the palette.
No additional UML objects were added to the palette as had originally been suggested. After some consideration, however, it was decided that it would be beneficial to provide the ability to change the colour of items placed on the drawing canvas. It was felt that such a change would be beneficial in allowing users to highlight specific items in a drawing rather than force the use of UMLet’s default colour scheme, which is a bland black and white. Other changes to the palette were felt unnecessary as UMLet provides most, if not all, required UML drawing entities as is. Even if other drawing entities were required, their addition is not really a major programming task.
2.1.5. Code Re-factoring
During the planning for this stage of the project, it was suggested that some re-factoring of the UMLet code base might prove beneficial to both the team currently developing upon it and those who might choose to do so in the future. After some discussion, it was decided that the com.umlet.control.io package was most in need of attention. This was the most obvious area for re-factoring as a large number of classes found in this package accomplish very similar tasks yet are presented as completely separate classes rather than being grouped in some manner to indicate similarity of purpose. As such, this area was re-factored to better convey its purpose and workings. Details of the implementation of these changes are provided later in this document.
2.1.6. Online Help
Another feature planned for the development stage of this project was the addition of some form of online help service, however basic. As originally suggested a ‘Tip of the Day’ system has been added to the program and once again, the implementation of this is detailed later in this document. Unfortunately, due to both time constraints and also to the somewhat limited nature of UMLet’s feature set no other help facility has yet been added. This is mostly attributed to the fact that most users would more than likely benefit more from a well written online manual than any help ‘feature’ added to the code base. This is an area which could however be re-examined by future development teams.
2.1.7. User Interface (UI) Enhancement
Yet one more extra feature added to the UMLet program is a right-click activated popup menu. The menu contains options to change the UML relations wherever they exist, which is in both the drawing panel and in the palette panel. The contained options are to change the relation’s body into either a solid or a dotted line, or to change either end of the relation to indicate a dependency, an inheritance, an aggregation, or a composition. Only one aspect of the relation can be changed at a time and any further changes will require a successive right click on the particular relation requiring change. As each change is made the properties panel is updated to reflect the change made to the current relation.
The purpose of adding this functionality is that the only method for altering the relations previously was a text-based entry in the property panel. Since the UMLet program uses a GUI based look and feel, the addition of a right-click method for relation alterations does improve upon this theme. The added functionality does not entirely remove the need for the text-based method, however, as not all possible alterations to the relations are accessible from the popup menu, although this would just require extra programming, perhaps along the lines of a multi-tiered style menu. As with all other changes discussed in this section, implementation specifics are detailed later in this document.
2.2. Feature Implementation
2.2.1. Tip of the Day
To facilitate the addition of some user help functionality, as described previously in this document, it was decided to add the capability to display a ‘tip’ each time the program started as well as further tips on demand if required. As an example of the reusable nature of Object-Orientated (OO) design the majority of the code for the addition of this feature is entirely self contained and is connected to the main program with only a minor number of changes to the existing code base. As such it would be quite a simple matter to simply copy the new classes across to any other Java project and use them to provide the same functionality without having to rewrite the ‘tip of the day’ code. To facilitate the addition of tips without requiring a recompilation of the UMLet code all such data was stored in a new line delimited text file. While this is quite a basic storage method, it has proven to be quite adequate for the job. These factors combined mean that adding this functionality to a new project will, in most cases, not even need a recompile of the TipGenerator and TipContainer classes let alone any editing of the actual code they contain.
2.2.2. Printing
Surprisingly, the addition of the ability to print from UMLet was relatively simply accomplished. After some research was completed, it was found that while the Java language was unable to provide full printing, control libraries were provided to allow for most common operations in this area. To provide this functionality a new PrintPage class was created. This new class was built to implement the Printable interface as provided by the java.awt.print package in the Java libraries. After examining the requirements of the interface and the manner in which other classes responsible for outputting the contents of the drawing canvas worked the class methods were then created.
The following methods have been provided within the new PrintPage class:
- public static PrintPage getInstance()
This provides a way for a program, in this case UMLet, to access the class, which is singleton.
- public void doPrint()
The doPrint() method creates a PrinterJob which is set to call this class’s print method when required. It then proceeds to present the user with a dialog box to select printing options before then attempting to call the job’s print(…) method to perform actual printing of the document, if necessary. Any exceptions related to printing are also caught here. It is also worth noting that the provision of an actual dialog box for printing options is handled by the host operating system as such can and will vary between platforms.
- public int print (Graphics graphics, PageFormat format, int page)
The bulk of the actual work undertaken by the class is contained in this method. As specified by the Printable interface it requires a graphics object to draw upon, a PageFormat object with all the required settings for the page and an integer representing the number of the page. As the method is actually called by the PrinterJob set to print this Printable object all of these parameters can in fact be provided with no real intervention from the programmer if required. Once called the method simply translates all objects on the drawing canvas onto the actual Graphics2D object to be printed and, if successful, returns the PAGE_EXISTS constant so that the PrinterJob may proceed to send the page to the printer.
This new class was very simply hooked into the menu structure of UMLet via the addition of a new menu item and an appropriate change to the ActionListener attached to it. While the simplicity of this might give the impression that the PrintPage class could be reused in any other project this is unfortunately not the case. This is due to the fact that the print(…) method makes use of an UMLet specific call to find out what entities are on the drawing panel to print. As such to re-use this particular section of code in another project it would be necessary to make some alterations to build the data to be printed into an appropriate format. This however remains an example of how easy it can be to build basic but useful functionality onto a program using only the Java standard libraries and some minor code production.
2.2.3. Re-factoring – com.umlet.control.io
As part of the overall re-working of the UMLet code base undertaken in this assignment it was decided that some re-factoring of the com.umlet.control.io package would be beneficial. The goal of this action was to make the code layout to better reflect its actual purpose to anyone intent on further modifying the code base in the future.
To facilitate this, an interface ‘Generator’ was created to be implemented by the existing GenEPS, GenPDF and GenSVG classes and any further classes used for file output. This interface specifies that all classes implementing it must provide the following methods:
- public void createAndOutputToFile(String filename)
- public void createToStream(OutputStream ostream)
These two methods provide all the outward facing functionality required for UMLet to be able to access and use a given class for file output. All other methods required by a given file output class to complete its task can be kept completely separate from the main UMLet code base. On further investigation of the package, it was also found that the GenPDF class was responsible for the output of both PDF and JPG files due to some shared code. To provide a more consistent design in this area it was undertaken to separate this functionality by creating a new GenJPG class and moving the shared methods to the new DimensionCalculator class, which are called statically as required by both GenJPG and GenPDF. As a result of the above changes, some minor changes were made to the section of the Umlet class where the above classes and their respective methods are called from.
So in summary, as a result of this re-factoring the following classes were changed or created:
- Umlet (Changed)
- GenEPS (Changed)
- GenSVG (Changed)
- GenPDF (Changed)
- GenJPG (Created)
- DimensionCalculator (Created)
The end product of this is a package layout that much better reflects the actual capabilities and functionality of the package and provides a slightly wider scope for future re-use of code.
2.2.4 User Interface (UI) Improvement
The UniversalListener class is used to handle mouse events occurring in any one of the panels or in the top menu bar during the runtime of the UMLet program. The addition of a right-click menu for the altering of UML relations was entirely implemented in the one source file, UniversalListener.java in the com.umlet.control package. The only methods called from other classes in the additional code are the decomposeStrings method from the
Constants class, and the setPropertyPanelToEntity method of the Umlet class. Both the Constants class and the Umlet class were already being called from within the UniversalListener class, so no new dependencies were created and as such, the coupling of classes was not made any tighter by the new implementation.
To implement the right-click popup menu two of the existing methods in the UniversalListener class needed to be extended, the mousePressed method and the actionPerformed method. Also, a couple of new methods were added, the alterArrow method and the getUMLSymbol method.
The first of the extended methods was mousePressed. This method is called whenever the mouse is clicked on one of UMLet’s components. The functionality added to draw the right-click popup menu is called upon when the mouse is pressed if the component pressed on is an instance of a UML relation, and if the mouse button pressed is the right hand button. The popup menu contains several menu items relating to the different possible types that a UML relation can be. Each of these menu items has an ActionListener associated with them, and whenever an ActionListener detects an action, it calls the actionPerformed method.
The second method extended was the actionPerformed method. The actionPerformed method in the UMLet program only handles events sent from menu items so extending it to handle menu items from a popup menu was not a difficult task, as it merely needed to have the extra menu item identification added. Each of the new menu items added to the actionPerformed method calls upon the alterArrow method.
2.2.5 Colour Support
Java’s polymorphic nature has made it both easy, and hard, to add colour support to UMLet. The initial difficulty was in understanding exactly how UMLet worked, and a basic introduction to this will be necessary to explain how colour support was added.
All the entities UMLet displays are instances of the aptly named Entity class, or more strictly, various subclasses of this. Each subclass is responsible for the unique attributes of that particular Entity, such as its unique shape and any particular quirks that entity may have, such as arrow heads if it is a relation between other entities. Each Entity has a stored state, which is a combination of its unique attributes, and several common attributes such as its location, width and height. The unique attributes for each Entity are stored as a String, which is displayed via an editable palette on the UMLet interface, and it is through this properties palette that users are able to make changes to that particular Entity’s properties.
Each Entity’s class parses its state from the properties palette, generally from their paint() method, and reacts accordingly. One of the problems encountered was that part of the state of each Entity is actually drawn as text to that Entity, while other parts of an Entity’s state modify properties of the Entity and aren’t actually displayed. This is handled by a simple if/else within each Entity, where its state is parsed, and any state that doesn’t correspond to a hidden modifier is then output to the Entity as text.
The first step in giving UMLet colour support was adding a hidden colour modifier to each Entity. The syntax used follows that used by UMLet’s existing property modifiers, “modifier=property”, which, in the case of colours, is “c1=r g b” and “c2=r g b”, where “c1” and “c2” are the foreground and background colours respectively, and “r”, “g” and “b” are integers between 0 and 255 representing red, green and blue. The problem with using this method is that colour support is not unique to each Entity type, and as such should be included within the Entity class, rather than duplicated within each of its subclasses, but the current method used within UMLet would mean that the modifiers would then be output to each Entity as text, rather than hidden. To counter this problem, a paint() method was added to the Entity class, and each subclass makes a call to Entity’s paint() method from within its paint() method, and its state is initially parsed within Entity’s paint() method, rather than its own.
A new class, Colour, was introduced to handle the various functions related to providing colour support, and contains the following publicly useable methods:
- public static Object[] parseActive(String palletteProperties)
This method accepts a String representing the palette properties for the current Entity, and returns an array of Objects. The first element within the array is a Vector representation of the palette properties String, which returned to the Entity for further parsing, stripped of any hidden colour modifiers. The second and third Objects in the returned array are the foreground and background colours parsed out of the palette properties String.
- public static Color decode(String colour)
This method accepts a String and returns the colour represented by the String. It expects the String to be in the format “r g b” where “r”, “g” and “b” are String representations of integers from 0 to 255, representing the amount of red, green and blue respectively. This method gracefully handles “invalid” input not in the expected format, returning the Color black if it is unable to parse the input String into valid red, green and blue components, or tending towards black if it is unable to parse only a portion of the input String. This method is used to generate the foreground colours.
- public static Color decodeFill(String colour)
This method is identical to the one above, except that it tends towards the Color white on invalid input. This method is used to generate the background colours.
Two other slight modifications were made, one to support the existing feature where an Entity, when selected, assumes a different colour, and a new feature, where an Entity has a bolded border when selected, bringing UMLet inline with many other applications. The colour change on select is handled in Entity’s paint() method along with the colour parsing, simply providing a different colour if the Entity is currently selected by the use of a simple test. This was previously handled by an onSelect() method within Entity, which set a global variable to the suitable colour for use by the subclass’ paint() method whenever an Entity was selected, and then called repaint() to repaint that Entity. This method has been preserved, but now only calls repaint() to ensure an Entity is repainted when its selected status changes. The bolding is handled in each subclass of Entity, due to the unique drawing requirements of each type of Entity, and is also a simple test if the Entity is currently selected, changing the drawing attributes in that particular subclass’ paint() method if it is selected.
3. Testing
3.1. Tip of the Day
3.1.1. Goals
The ‘Tip of the Day’ feature that has been added to UMLet is intended to complete a very specific task and as such testing in this particular case proved relatively straight forward. The goals for the testing of these classes were:
- Check a tip is displayed on program start.
- Check that a tip is displayed when requested from the menu.
- Check that tips are loaded correctly from the ‘tips.txt’ file.
- Check that tip to be displayed is selected randomly from those in the aforementioned file.
Methodology
Despite the Graphical User Interface (GUI) nature of the actual tip of the day functionality, it was found to be possible to make use of a test script to test the new TipContainer class automatically. The test class created for this purpose is TestTipContainer in the new com.umlet.testing package. This class makes use of the JUnit test engine to automatically test the capabilities of the TipContainer. To do this the test script accesses the container in various states before and after adding new tips. This basic testing simply provides a way to eliminate fence post errors and the like, which are liable to result in a NullPointerException at run time if not handled correctly.
All further testing of the class was undertaken manually by starting the program multiple times to check that a randomly selected tip was displayed each time, starting the program when no tips.txt file was present and accessing the menu option multiple times, once again to check random tips were being displayed each and every time.
Results
Due to the relatively simple nature of this feature, it proved quite possible to successfully test all of the added functionality with the simple testing outline above. As a result of this, several errors were located and removed including a minor error with tip selection in the original code.
3.2. Printing
3.2.1 Goals
When testing the printing class it was felt that the group could ensure that all entities on the palette could be printed and that the newly added colour support would also be reflected in print outs.
3.2.2 Methodology
Due to the very nature of the added printing functionality, in that it is called by a GUI action and expects a drawing to be present for printing, it proved impossible to write an automated test script. As such all testing of this new feature was undertaken manually by creating drawings with each of the UML entities represented and then testing various variations with and without colour. While tests were sent all the way to the printer on several occasions, it proved beneficial to use the ‘print to file’ option of the host operating system to save to PostScript. These PostScript files were then examined for correctness using the GhostScript tool under the UNIX environment. Worth noting is that testing of printing functionality occurred on both the Windows and UNIX platforms but as no member of the team has direct access to a Macintosh, performance on this platform is as yet untested. As Java relies heavily on the host operating system to provide printing support this is a rather unfortunate but unavoidable omission in testing. Theoretically, if the functionality works correctly under the Java Virtual Machine (JVM) on one platform, as it does in this case, then it should also perform correctly on all other platforms, in reality however, this is not always the case.
3.2.3. Results
The printing functionality proved to work correctly in all manual tests undertaken including those where use had been made of the newly added colours support. In these tests in was found that there are some problems with varying margins between printers. Unfortunately, as yet no fix has been found for this problem, but it does not prevent the user from printing out most, if not all, basic diagrams as it only becomes a problem as the size of the diagram grows towards that of a full page.
3.3. Re-factoring
3.3.1. Goals
After the re-factoring of the IO package all classes involved in the generation of files were expected to require testing. The list of classes changed and likely to require at least some testing as a result included:
· GenEPS
· GenJPG
· GenPDF
· GenSVG
· DimensionCalculator
3.3.2. Methodology
Of the classes in the package listed as requiring re-testing, only the new DimensionCalculator class cannot be tested directly but rather is called by GenJPG and GenPDF. Unfortunately, once again all the Gen* classes rely on an a full image being provided as input and the only output goes to a file rather than being a returned value which can be tested. As such, manual testing was once again undertaken for these classes. This involved drawing up a series of diagrams making use of all entities on the UMLet drawing palette and saving to each of the available output formats. Once this was complete each newly created file was opened in an appropriate editor and checked for correctness.
3.3.3. Results
All of the modified output classes were found to work correctly using the manual testing methods detailed above. Even with special attention given to the GenJPG and GenPDF classes where the largest changes were made, no problems were found and as such, the com.umlet.control.io package is considered to be of production quality.
3.4. User Interface (UI) Improvements
3.4.1. Goals
Testing for the right-click popup menu was done manually to show several events being completed successfully upon request. These events and the order in which they were expected to occur are:
- The popup menu is only to appear when a relation is right clicked on.
- Once on-screen, the relation’s popup menu is to contain all the required selectable choices.
- If a selection is made the relation is updated with the expected change.
- The change made is reflected in the properties panel.
- The change made is persistent after the relation is deselected.
3.4.2. Methodology
Testing of this new feature was not an extensive task and did not require the creation of a large test harness to help ensure that each new addition to the code maintained Umlet in a working state. This is because these new features work at runtime and require user interaction rather than methods returning variables for calculations of lists. Although there are classes provided with Java and third party programs that are able to test user interaction processes, the extra speed gained by using these compared to manual checking would not outweigh the time taken to implement them on this relatively small additional feature.
To satisfy each of the points mentioned in the goals for testing this new feature to UMLet, testing occurred on each of the steps aforementioned in order by first checking that the program could be compiled, and then trying to use the feature up to the point of its implementation so far.
Since the code for mouse listeners is from a standard Java library this was not tested. What was tested was that if a right-click was made, the conditional statement for separating a right click on a relation from other mousePressed() events was made, this was tested by simply inserting a call to print to the screen a particular indicating statement. From this statement, it was also possible to tell if, when multiple relations were clicked on, that they were different relation instances. This indicating statement was removed from the code when it was determined that the implemented code worked.
3.4.3. Results
As the steps for implementing the right-click popup menu were performed consecutively, there was an assurance that once one part of the implementation was deemed to be working correctly the next step of the implementation process could be attempted. This method follows the waterfall method with a slight twist by adding an iteration of the implementation and testing phase, this seemed the most logical method for such a minor change that had little effect on any other parts of the program.
3.5. Colours
3.5.1. Goals
The Colour class provides a specific functionality to Umlet, namely it allows a user to specify foreground and background colours for a selected Entity. Testing had to ensure that the class was able to accept and gracefully handle any input a user might give, producing logical results, and that all existing Entity’s still behaved as expected, except displayed the appropriate colours when expected. Specifically, the Colour class should accept any given String and return a valid Color. If the String is a valid representation of three integers in the ranges 0 to 255, then the Color returned should match that given by interpreting the String as a series of red, green and blue values. Furthermore, if the String is intended to represent a foreground colour, the Color produced should tend to black. That is, if invalid input is given, the Color output should be black, and if partially valid input is given, such as a String containing representing a single integer, the remaining values should tend to zero. Similarly, if the String is intended to represent a background colour, the Color output should tend towards white.
3.5.2. Methodology
A testing script TestColour, using the JUnit testing framework, was used to automate the testing of the Colour class. As external interaction with the Colour class is limited to a String input and three known outputs, the test script simply sends a series of randomly chosen but valid and randomly chosen but invalid String inputs and tests that the required outputs are returned and that they match the expected outputs for that String input. Despite sounding simple, this task was complicated by the robust design of the Colour class, as it will accept many partially valid String inputs and still return a valid output.
In addition to the automated testing, manual testing was performed to test such aspects as the integration with the rest of Umlet and to ensure that Entity’s still behave as expected. Several instances of each type of Entity were created, set to various colours, and each of their properties changed. The workspace was saved to a file at various stages, and reloaded at a later date, and saved to each of the available output formats, including the newly added printing support.
3.5.3. Results
Automated testing showed that the Colour class is able to handle all possible input while still producing the expected output, and will not produce any unexpected behaviour under normal operating conditions.
The manual testing demonstrated with a reasonable degree of certainty that the existing functionality has not been altered, and that Entity’s behave as expected when coloured.
4. Journals
4.1. Stephen Gordon
[Removed for privacy]
4.2. Stephen Jenks
[Removed for privacy]
4.3. Ned Martin
4.3.1. Original Journal
Apparently, I’m supposed to have been keeping a “team learning journal”, but I haven’t been so I’m currently in the same situation as the proverbial. To rectify this situation, I have decided to paraphrase various parts of another journal I have been keeping, interspersed with my own (occasionally accurate) recollections, and attempt to formulate from this something that vaguely matches the description of what a team journal should be.
First, a little about myself – I am Ned. Hear me roar. That done, I guess I may as well begin at the beginning, way back in July – Thursday, 15th July, to be precise.
Thursday 15 July – Sign On
I woke up at a horribly early hour, to sign on for my COMP2801. I’ve gone from having an all but perfect timetable, to a pretty average timetable, to a rather bad timetable with two eight o’clock starts and a five o’clock finish on Friday – due to tutoring. Hopefully it’s worth it, and everything works out ok.
Monday 26 July – First Lecture
The next COMP2801 thing of any import was the first lecture, which I attended quite too bright and early one Monday morning. I believe I’ve dutifully attended all lectures since, and found them instrumental in helping me understand UML, along with the tutorials. This has been particularly handy, as I don’t have any UML textbooks.
Tuesday 27 July – Practical 1
I woke up when my alarm went off, at six o’clock, and again eight minutes later. Eight o’clock starts aren’t my favorite things. Nevertheless, I managed to get to uni on time, and it wasn’t even that cold. I found the right room for my practical, and formed a group with the three people sitting nearest me – all of whom I happened to know, vaguely. I had been a little worried that I’d end up being automatically assigned a group of dumb people – less intellectually endowed people, I should say, or people who wouldn’t do any work, couldn’t speak English, were intolerable, had multiple open wounds, etc. I just wasn’t quite sure what people would go to an 8 AM practical, because, by my logic, no sane person would intentionally sign up for something that early. Fortunately, though, I think the group I’ve managed to get are reasonably intelligent, willing and able to try, well versed in basic English and other associated communication skills, including, but not limited to, the ability to write and talk in an understandable manner, and so on, and so forth. In other words, I’m hopeful that I’m in a good group.
We then proceeded to “bond”, by attempting to survive a plane crash in snowy, sub-zero northern Canada, in a wooded area crisscrossed with creeks and roughly 20 miles from the nearest town. Luckily, we had some vital survival equipment, such as a lighter with no fluid, a ball of steel wool, and two dead pilots. We had to individually rank these (and several other) items in order of importance to our survival, and then do the same again but as a group. We then compared our results to that of an “expert”, who, I suspect, would accidentally kill himself (and several innocent bystanders) while trying to survive a trip to the local supermarket. Nevertheless, we bonded, and, apart from one member who took the whisky and headed towards town and certain death, and another member who didn’t place enough importance on the lighter and froze to death, and myself and another member who left the steel wool behind and not only froze to death but were also unable to signal for help due to the unreflective properties of Crisco shortening, we all survived.
Tuesday 3 August – Practical 2
Today, at the ungodly time of 8 AM, I learnt how to use ANT – which beats learning how to use make files. I also learnt that I should follow instructions exactly as given – as I had several problems and had to ask for help a few times, and each time it was because I’d failed to read the instructions properly, or had missed a step. So, I guess you could say today has been a productive day.
Tuesday 10 August – Practical 3
I had to tutor for another subject, and attend my eight o’clock group practical, so didn’t want to be late. I had also stayed up most of the night, as usual, so had to use my patent-pending psychological trick to wake up – setting the computer’s alarm to play something loud five minutes after my alarm, so when mine goes off, I have to get up to turn the computer’s one off before it goes off and wakes everyone else, who will then kill me. It was very cold – one of the coldest mornings so far, but I managed to attend my prac, and probably learnt something there, although I can’t remember what it was.
Tuesday 17 August – Practical 4
We had a group meeting during our prac, which went quite well. We managed to delegate tasks to ourselves in a way that seems to make everyone happy, without really meaning to, while telling lots of doubtful jokes and stuff. It seems Flash (Steve Gordon) is Mr. CVS, Stephen (Jenks) is Mr. PowerPoint Designer, and Lachlan (Smith) and I are your presenters – with Lachlan being more comfortable talking, and me being less comfortable talking, so I’m Mr. Click Things and he’s the inimitable Dr Voice. It’s all going good.
Friday 20 August – Group Meeting
We had a group meeting today. I almost forgot about it, and because my tutoring ran overtime, I was late. The meeting went for all of three minutes, during which time we unanimously decided to have another group meeting at a later date, due to lack of motivation and a dire lack of a lack of assignments in other subjects.
I am so busy right now - it’s not good. I have an assignment due before 2 PM Monday, this one due during my two-hour eight o’clock practical on Tuesday and another due on Friday. I also have a fourth assignment that I should be doing, but I haven’t even looked at it so don’t know when it’s due. Then, on top of that, there’s a few things relating to my tutoring that I have to do, several little things around here I need to do, paperwork and computer stuff, and I need to go shopping and do some washing.
Tuesday 24 August – Practical 5
I headed down to the labs, via the refectory where I bought a terrible pretending pizza thing that was so bad I had to throw it out. After three quarters of our group finished not preparing in the labs, we went up to the other lab and, after the fourth quarter arrived we presented our program – “UMLet”, a Java based UML drawing program, which I insist is pronounced as “omelet”. It was a ten-minute presentation, during which Lachlan spoke and I cleverly manipulated our PowerPoint slides and the actual program. This was followed by rapturous applause (or so I insist on remembering), and then Stephen (the Gordon one) took a break from losing sleep over Slashdot to demonstrate his immeasurable CVS skills – and that was that. The rest of the groups ran through their presentations ranging from one who had a simple script and read it out without any computer involvement to one who had a fully scripted presentation with all four participating. We were the only group who ran to time (or at least the only group who had a two-minute warning given), and successfully covered all points on the criteria sheet with a live demonstration of the program, so I’m hoping for marks. We might even get an excellence and innovation mark for Stephen’s installation and run script.
Tuesday 31 August – Practical 6
Today’s practical was supposed to teach us how to use Rational Rose, but after a quick perusal of that program, I’ve decided that “use” is not the appropriate word – performing even the most basic tasks requires immense effort, although perhaps it wouldn’t be so bad if I actually understood what I was trying to do – something I must aim for. I did, however, learn how to create complex-looking (and sometimes meaningful) UML diagrams from Java without expending too much effort – something that’s very useful considering that’s part of what I have to do for the next assignment. My only other exposure to UML had been through Visio, while attempting to draw ER diagrams for an INFS assignment, I came across some UML templates and had a bit of a play around, but I didn’t realize the full depth and breadth, and the rich variety of diagrammatical styles possible. Now I do. The tutorials on UML have been instrumental in aiding my understanding as well, as I’ve found some aspects of UML, particularly the way it’s only semi-formal and a lot of discretion is required to determine precisely how to model something, quite confusing.
Tuesday 7 September – Practical 7
I pretended the world was round and that today was just another day, and headed into university. I attended my prac, during which we planned how we are going to try to do our assignment, and after that, I repressed all thoughts of assignments for as long as possible.
Tuesday 14 September – Practical 8
Our original plan had us all completing our individual parts of assignment two by today, and beginning the group parts. What actually happened, though, was that I went to Sydney over the weekend and didn’t complete my part on time, and we realized that some aspects of the individual work we’d assigned ourselves is better done in a group environment. Basically, we each found that we had to read half of the java files assigned to other group members in order to be able to figure out what the code in our files did – so we’ve modified our schedule a little. It is now more realistic and goes something like “finish everything as fast as possible – definitely before the due date”.
Wednesday 15 September
So here am I, sitting down in the lab typing all this. Now that I’ve given what is, I hope, an accurate overview of my experiences with COMP2801 so far, I would like to reflect on what I’ve actually learnt so far, and how I’ve gone about it.
UML – I had a vague idea about it, mostly garnered from using Visio to draw diagrams during a previous INFS course. I’ve now learnt quite a lot about UML, and can actually use Rational Rose – in a way.
Teamwork – This is the third teamwork-based assessment I’ve done here at UQ, but the first where I’ve really learnt how to cooperate and trust others. I’ve had to, as I haven’t had enough time to check work others are doing to see if it matches my “standards”.
Java – I don’t really think I’ve learnt anything I didn’t already know about Java and programming itself, everything has been straightforward code so far and I believe I’m becoming adept at spotting situations that could benefit from re-factoring. I have, however, had to struggle to understand and identify design patterns, as I don’t find their discovery intuitive.
CVS – This course is the first time I’ve used CVS. I’d heard about it a lot from various open-source places, but never actually had to use it myself. I can’t say I’m overly impressed, it doesn’t seem to be much more than a glorified versioning archive to me – but it’s handy to know how to use it should I ever need to.
ANT – Having only used make files before, ANT came as a nice surprise. It’s quite powerful, relatively easy to learn, and uses XML for that up-to-the-minute effect. I didn’t have much input into the ANT build file used by our team, but the ANT practical helped.
4.3.2. Updated Journal
Friday 17 September – Assignment Submitted
I headed off to uni to complete our assignment. I am supposed to have gone to uni earlier, but, for some strange reason (possibly related to not getting to bed until early this morning), I felt quite sleepy when I woke up and didn’t want to go. Today is the day of the grand submission, and our document is a proud 42 pages, much of which was written at ridiculous hours of the night, so I corrected a few silly mistakes when proof reading – and we submitted it. It should definitely get us a pass, if not better.
Tuesday 21 September – Sick
I attended my eight o’clock prac, at great risk to my health. I’m feeling sick and terrible. We made up a list of a few things to consider for assignment three – re-factoring the code (an obvious and generic choice), adding print functionality, adding colours, documenting the code (another generic choice), and adding a tip of the day module (which Steve has already written). This doesn’t sound like much, and isn’t very exciting, but it should fulfil the assignment criteria. Adding colours, if that’s a plausible option, would involve quite a bit of object-orientated nastiness, as changes would cascade across stacks of classes. The print option should, hopefully, be plausible and shouldn’t be too hard to add on without modifying too much existing code. Steve has gone home for the holidays, and the rest of us have other assignments due sooner, so we’re giving them a higher priority.
Friday 1 October – Woe
I spent all night at uni last night, and have been working hard on three assignments for my three other subjects all week, all of which are due on the same day. COMP2801 isn’t receiving any attention at all right now, and won’t until these are submitted.
Tuesday 5 October – Wherein I discover I can no longer afford to sleep
I went to bed shortly after three o’clock, and I left for uni around a quarter to seven – that’s less than three hours sleep, and it isn’t enough, but I had to be at uni by eight o’clock for my group practical, during which we discussed our fairly limited progress so far. I then spent a normal day at uni, doing normal things. That is, I spent all day sitting in the computer labs programming for my other assignments. COMP2801 is still on the backburner.
Wednesday 6 October – Assignment Two Returned
I went to uni and worked on my COMS3200 assignment. It is turning into a nightmare. It is excessively complex for the marks given, and most of it isn’t anything to do with networking. I also went and got our marked assignment two for COMP2801 back. We’ve got 18 out of 20. The average mark is 12.9 and the median 11.5. Only one other group, who happened to be from our practical, got a higher mark – at 19, so I think we’ve done quite well. I wasn’t sure what mark we’d get for this, as there were other groups talking about immensely long documents, and I wasn’t sure that we’d really completed what was expected, but now that we’ve got a good mark, I’d like to try to get a good mark for the next assignment and see if I can’t get a good overall result for this course.
Sunday 10 October – All Night
I have to finish COMS3200 and start and complete COMP3502 by tomorrow. COMP3502 is due at 2 PM, and COMS3200 at 10 AM. COMP2502, also due tomorrow, is already completed. With this in mind, I headed into and spent the night working on them. Once these are submitted, for better or worse, I can at least stop worrying about them and get on with COMP2801, which I’ve been ignoring totally.
Tuesday 12 October – Of which little is said
I had to be at uni by eight o’clock at the wrong end of the day for my practical, so I was. The rest of the day followed along, as if by clockwork, one moment at a time and never two at once. By the end, it was over and ready to begin again.
Wednesday 13 October – My day not off
Today used to be my day off, back before I realised that uni is harder than before. Because I don’t have any lectures, I did sleep in, but still went to uni in the evening to do some work on our assignment. I had wanted to go see a movie tonight, but a few friends convinced me to wait until tomorrow, so I stayed at uni until late instead, trying to figure out where and how to add colour support to Umlet – not an easy task given its complex polymorphic structure.
Thursday 14 October – Shaun of the Dead
Another exciting day of fun, study and Java – but that is not all. Today, a few friends and I went to the movies. We saw “Shaun of the Dead”. The movie is a successful synthesis of British humour and American gore, managing to be both amusing and entertaining, but isn’t really related to COMP2801, so that’s all I’ll say about it. After the movie, I started on my time management reflection, which I have, ironically, left to the last minute, and that ended the uni-related portion of my day. It was actually quite interesting writing my time management reflection, as it gave me an opportunity to look back and consider my progress at uni so far – something I haven’t really done before.
Saturday 16 October – Another Normal Day
Another particularly normal and uneventful day, during which I present myself at uni in order to fully add colour to Umlet, fail to do so, and so on. I also read perhaps the funniest geek joke ever – two strings walk into a bar. The first string says to the bartender, “Bartender, I’ll have a beer. u.5n$x5t?*&4ru!2[sACCErJ”. The second string says, “Pardon my friend, he isn’t NULL terminated”. After a day of largely unsuccessful programming, that really is quite hilarious.
Sunday 17 October – Rain & Little Else
I slept in for a while and then caught a train into uni where I did some coding for our team project, talked to a few friends, and then headed home again, after buying a falafel roll from the kebab place down the Ville. It’s now raining and a bit cool, and I still haven’t got colour support fully working, but I’m getting there. I understand what I need to do now, which is probably a good start, I just have to figure out some way of doing it that doesn’t involve editing every single subclass of Entity (as I currently have) – something the perfectionist in me refuses to accept.
Tuesday 19 October – Uni
After four hours sleep, or something woeful like that, I had to get up and go to uni for my practical, during which my group cleverly divided the remaining tasks. Once again we’ve managed to delegate everything in what seems to be a logical way which everyone agrees on, without any problems.
Sunday 24 October – Gauls & Arguments
Today has been another really hot day. I spent the morning in bed asleep, and the evening at uni. I met up with Flashpix (Steve Gordon, so called from his last name) and Insomnix (Lachlan, the man who never sleeps), two indomitable Gauls still holding out against the house drawing, team project and sprinkler-mapping invaders that have overtaken all the other labs. We did much great teamwork, which can be summarised roughly as “Hey yeah, so we better do stuff, but Coles shuts soon and I haven’t had breakfast yet so I’ll see you later ok? Yeah, I’ll do some work tonight and we can compare tomorrow. Cool, see you then”. There was also something I was supposed to do tonight, so that we can compare it tomorrow, but I can’t remember what it is so I shan’t be doing it.
Monday 25 October – University is as University Does
I spent the morning attending lectures and tutoring, and the evening with Lachlan preparing for our presentation tomorrow. Lachlan is against “letting slides do the talking”, so our slides are just a method of displaying some UML diagrams to backup Lachlan’s talk, which is up to several pages of notes now, and will hopefully be impressive.
Tuesday 26 October – Presentation Woes
After a little less than four hours sleep, I got up and headed into uni so I’d be there by half past seven, to finish preparing for our presentation. Not surprisingly, I didn’t feel fantastic, and I don’t think Lachlan had had anymore sleep than I.
Our group presented third. The first group had a comprehensive and highly detailed slideshow – there was probably too much information on it, as they didn’t cover it all. The second and fourth groups had what I’d call average, but good, presentations, although the second group was too pre-planned and choreographed, as they were in the first presentation. Our presentation went terribly, I felt. Despite Lachlan and I having done a reasonable amount of preparation yesterday, coupled with both our lack of sleep, it simply wasn’t enough. I don’t know what our presentation looked like from the observer’s perspective, but I was very aware that we didn’t cover much of what I’d hoped to cover, and what we did was confused, leaving me waiting for planned cues that never came. I am, however, very impressed with Lachlan’s ability to make up stuff on the spot, something I don’t think I could do. All I can do is hope, vainly I fear, that Doug is also impressed.
Wednesday 27 October
With our assignment’s due date rapidly approaching, the time has again come for me to stop being slack and write this. Unlike last time, I have actually been keeping something of a journal as I go, but it’s been pretty factual, with little in the way of reflection, so I plan to have a quick retrospective look back at the course, and see what I think.
Overall, I think I’ve been fortunate to have a good group, who are all able to work well together, and capable of working on their own but still meeting group goals. I think we’ve done fairly well, although our marks for this assignment will show whether this is so. We have approached this assignment in exactly the same way we approached the first – which seemed like a good idea given that we got good marks for it. One thing that didn’t work out though, was the presentation, but that’s the problem with on the spot things like that – there’s always the chance that it won’t work out as planned when the time comes. The assignment, on the other hand, is something we can attempt, look at, and redo if necessary – and I think, or hope, we’ve met all the assignment goals. I am hoping for a reasonable mark given the effort we’ve expended in getting this far, but it’s hard to tell exactly what we should be doing, and whether we’ve done enough – I guess we won’t know until we get our marks, but I do feel that we’ve done a good job. In retrospect, we should have prepared a much more detailed slideshow, and rehearsed it more thoroughly, but other than that, I think we’ve done well and there’s nothing I’d change.
I’ve used the computer labs heavily, but haven’t really used any of the information we’ve been taught in lectures, because most of my work has been with Java code, with the other team members handling the other aspects of our assignments, so I’ve mainly used online Java help pages and the Java documentation. In fact, that is also perhaps the only thing I’ve thought of that could be changed to improve this course. The current structure allows perhaps too great a disparity between various teams and team members – something that’s probably realistic from a real-world perspective, but probably not the best from a learning perspective. I feel that, had we all been given the same carefully pre-picked software to work on, and the assignments been structured in such a way that a broader contribution would be required from each team member, we would all learn a lot more. Looking back, I can say that so far I have learnt more about Java from this course than I have about software design, team work, UML, or any of the other aspects I should have been learning – because I felt I would be able to contribute more to our group in this way. While this has allowed us, as a group, to delegate tasks to members more able to complete them, it has also meant that individual members haven’t been developing a wide a range of skills as the course requires. A change like this might also reduce the severity of the exam at the end of the course, something else I’d like to see.
Despite the above, I’d say this has been a good course, and my advice to any future students undertaking a similar project would be to ensure they’ve got a good, working group and don’t leave anything to the last minute like I always do.
4.4. Lachlan Smith
[Removed for privacy]
5. Appendix A – Evidence of Code Changes
5.1. PrintPage.java
Figure 5.1.1. Shows the new class PrintPage and its relationship with the existing UniversalListener class.
package com.umlet.control;
import java.awt.*;
import java.awt.geom.*;
import java.awt.print.*;
import java.util.*;
import com.umlet.element.base.*;
/**
Provides Umlet with the ability to print the current drawing pane.
@author Stephen Gordon <a href="mailto:stephen.a.gordon@gmail.com">stephen.a.gordon@gmail.com</a>
*/
public class PrintPage implements Printable {
private static PrintPage _instance;
/**
Returns an instance of the class.
*/
public static PrintPage getInstance() {
if(_instance == null) {
_instance = new PrintPage();
}
_instance;
}
/**
When UMLet wishes to print this method is called and directs
all further print related activitie.
*/
public void doPrint() {
PrinterJob job = PrinterJob.getPrinterJob ();
job.setPrintable(this);
if (job.printDialog()) {
try {
job.print();
} catch (Exception PrintException) {
PrintException.printStackTrace();
}
}
}
/**
Does the actual job of sending a page to print.
@param Graphics the graphics object to print.
@param PageFormat the page format sturcture which is used to
set various printing options.
@param page The number of the page.
@return int PAGE_EXISTS or NO_SUCH_PAGE
*/
public int print (Graphics graphics, PageFormat format, int page) {
Graphics2D graphics2D;
if (page == 0) {
graphics2D = (Graphics2D)graphics;
graphics2D.setColor(Color.black);
graphics2D.translate(format.getImageableX(),
format.getImageableY());
Vector v=Selector.getInstance().getAllEntitiesOnPanel();
for (int i=0; i<v.size(); i++) {
Entity e=(Entity)v.elementAt(i);
graphics2D.translate(e.getX(), e.getY());
e.paint(graphics2D);
graphics2D.translate(-e.getX(), -e.getY());
}
(PAGE_EXISTS);
} else {
(NO_SUCH_PAGE);
}
}
}
5.2. TipContainer.java
Note: because this class is considered to be aggregated by TipGenerator, both classes are diagrammed together in Section 5.3.
package com.umlet.control;
/**
Stores a collection of 'tips' for display to the user.
@author Stephen Gordon <a href="mailto:stephen.a.gordon@gmail.com">stephen.a.gordon@gmail.com</a>
*/
public class TipContainer {
/** The TipContainer currently uses a string array as it's
storage method. */
private String [] _tips;
/**
Default constructor simply initialises the storage structure.
*/
public TipContainer() {
_tips = new String[0];
}
/**
Adds a new tip to the container. As the current implementation
uses an array this operation is actually quite intensive.
@param String newTip The string which will be displayed to the
user as a 'tip'.
@return int Returns the index of the newly added tip in the
structure.
*/
public int add(String newTip) {
String [] temporary = new String[_tips.length + 1];
for(int count = 0; count < _tips.length; count++) {
temporary[count] = _tips[count];
}
temporary[temporary.length - 1] = newTip;
_tips = temporary;
.length;
temporary}
/**
Returns the tip with the given index.
@param int n The index of the tip to get.
@return String Returns the string representation of a tip.
*/
public String getTip(int n) {
_tips[n];
}
/**
Gets the number of tips in the container.
@return int Returns the number of tips in the container.
*/
public int getLength() {
_tips.length;
}
}
5.3. TipGenerator.java
Figure 5.3.1. Shows the new classes, TipGenerator and TipContainer, and their relationship to each other and the rest of UMLet.
Figure 5.3.2. Shows the call to TipGenerator.getInstance.show() appended Umlet.init(), the method which initialises the UMLet application.
Figure 5.3.3. Shows the call to TipGenerator.getInstance.show() added to the UniversalListener.actionEvent() method, the action handler for menu selections in UMLet.
package com.umlet.control;
import javax.swing.*;
import java.io.*;
/**
Facilitates the generation and display of 'tips' for program usage.
@author Stephen Gordon <a href="mailto:stephen.a.gordon@gmail.com">stephen.a.gordon@gmail.com</a>
@version 1
*/
public class TipGenerator {
private TipContainer _tipContainer;
private static TipGenerator _instance;
/**
Provides a way for other classes to get an instance of the
TipGenerator.
@return TipGenerator Returns a new TipGenerator.
*/
public static TipGenerator getInstance() throws IOException {
if(_instance == null) {
_instance = new TipGenerator("tips.txt");
}
_instance;
}
/**
TipGenerator constructor, allows loading of tips from file and
populates the TipContainer.
@param fileanem String representing the file to load tips from.
@throws IOException
*/
public TipGenerator(String filename) throws IOException {
try {
BufferedReader br = new BufferedReader(
New FileReader(filename)
);
String inputData = null;
_tipContainer = new TipContainer();
do {
inputData = br.readLine();
if(inputData != null) {
_tipContainer.add(inputData);
}
} while(inputData != null);
} catch(IOException e) {
System.err.println("ERR: Unable to open tips file.");
}
}
/**
Displays a dialog box with a randomly chosen tip.
@return void
*/
public void show() {
if(_tipContainer.getLength() > 0) {
JOptionPane.showMessageDialog(null,
_tipContainer.getTip((int)(Math.random() *
(_tipContainer.getLength() - 1))));
} else {
System.err.println("ERR: No tips found.");
}
}
}
5.4. TestTipContainer.java
package com.umlet.testing;
import junit.framework.*;
import com.umlet.control.TipContainer;
/**
Provides a way to test the basic functionality of the TipContainer
which is used for 'tip of the day' functionality.
*/
public class TestTipContainer extends TestCase {
/**
Simple constructor, simply feeds straight through to the parent
class which is part of the JUnit framework.
@param name String
*/
public TestTipContainer(String name) {
super(name);
}
/**
Test TipContainer creation.
@return void
*/
public void testContainerCreate() {
TipContainer tmpTip = new TipContainer();
assertEquals(tmpTip.getLength(), 0);
}
/**
Test adding an item to the tipContainer.
*/
public void testContainerAdd() {
TipContainer tmpTip = new TipContainer();
tmpTip.add("test");
assertEquals(tmpTip.getLength(), 1);
assertEquals(tmpTip.getTip(0), "test");
}
/**
Test accessing an item which does/should not exist.
*/
public void testNonexistantIndex() {
TipContainer tmpTip = new TipContainer();
try {
String heh = tmpTip.getTip(0);
fail("Should throw ArrayIndexOutOfBoundsException.");
} catch(ArrayIndexOutOfBoundsException e) {
}
}
}
5.5. AllTests.java
package com.umlet.testing;
import junit.framework.*;
public class AllTests {
public static void main(String [] args) {
junit.textui.TestRunner.run(testRun());
}
public static TestSuite testRun() {
TestSuite theSuite = new TestSuite();
theSuite.addTestSuite(TestTipContainer.class);
theSuite.addTestSuite(TestColour.class);
theSuite;
}
}
5.6. Colour.java
Figure 5.6.1. Shows the relationship between the new Colour class and the existing Entity class.
Figure 5.6.2. Shows the paint method added to the Entity class, which calls the Colour class to parse the information the user typed into the textual palette.
package com.umlet.element.base;
import java.awt.*;
import java.util.*;
import com.umlet.control.*; // Constants
/**
* Provides decode and decodeFill methods to decode a string
* and create a colour from that string.
*
* @author Ned Martin
* @version 19-OCT-2004
*/
public class Colour
{
/**
* Decodes a given string and returns a colour, defaulting to
* black.
*
* @param String to decode.
* @return Color representation of input.
*/
public static Color decode
(
String colour
)
{
// split string into r, g, b
int[] rgb = integerize(colour, 0);
new Color(rgb[0], rgb[1], rgb[2]);
} // end decode
/**
* Parses the palette attributes of an Entity and returns an
* array containing the attributes minus any colour
* information, and the foreground and background colours.
*
* @param String pallette attributes.
* @return Object[] containing the palette attributes minus
* any colour information as a vector, and the
* foreground and background colours as Color.
*/
public static Object[] parseActive
(
String palletteProperties
)
{
Vector input = Constants.decomposeStrings(palletteProperties, "");
String borderColour = "",
borderLine = "",
fillColour = "",
fillLine = "";
Object[] output = new Object[3];
for (int ii = 0; ii < input.size(); ii++)
{
String s = (String)input.elementAt(ii);
if (s.startsWith("c1=") & s.length() > 3)
{
// we have a foreground colour attribute
borderColour = s.substring(3, s.length());
borderLine = s;
}
if (s.startsWith("c2=") & s.length() > 3)
{
// we have a background colour attribute
fillColour = s.substring(3, s.length());
fillLine = s;
}
}
// remove the colour attributes
input.remove(borderLine);
input.remove(fillLine);
// we have a custom foreground colour
if (borderColour != null && borderColour.length() > 0)
{
output[2] = decode(borderColour);
}
// otherwise we don't have a custom foreground colour
else
{
// set to the default background colour
output[2] = decode("");
}
// we have a custom background colour
if (fillColour != null && fillColour.length() > 0)
{
output[1] = decodeFill(fillColour);
}
// otherwise we don't have a custom background colour
else
{
// set to the default foreground colour
output[1] = decodeFill("");
}
output[0] = input;
output;
} // end parseActive
/**
* Decodes a given string and returns a colour, defaulting to
* white.
*
* @param String to decode.
* @return Color representation of input.
*/
public static Color decodeFill
(
String colour
)
{
// split string into r, g, b
int[] rgb = integerize(colour, 255);
new Color(rgb[0], rgb[1], rgb[2]);
} // end decodeFill
/**
* Splits a string into an array of three integers less than
* 255.
*
* @param String to split.
* @return int[] of integers.
*/
private static int[] integerize
(
String input,
int baseColour
)
{
int bufferPosition = 0;
int[] output = new int[3];
for (int ii = 0; ii < 3; ii++)
{
String temp = "";
int out = baseColour;
// skip any leading white space.
while (bufferPosition < input.length() &&
(input.charAt(bufferPosition) == ' ' ||
input.charAt(bufferPosition) == ''))
{
// advance the buffer position.
bufferPosition++;
}
// build a string of consecutive integers.
while (bufferPosition < input.length() &&
Character.isDigit(input.charAt(bufferPosition)))
{
// concatenate current character to the previous one.
temp += input.charAt(bufferPosition);
// advance the buffer position.
bufferPosition++;
}
try
{
out = Integer.parseInt(temp);
}
// error parsing integer
catch (Exception e)
{
// integer was out of bounds
}
// ensure the output is not greater than 255
output[ii] = (out <= 255)?out:baseColour;
}
// return the integer value of the concatenated
// characters.
output;
} // end nextDouble
} // end Colour
5.7. UniversalListener.java (extract)
public void actionPerformed(ActionEvent e) {
JMenuItem b=(JMenuItem)e.getSource();
if (b.getText()=="Delete") {
Vector v=Selector.getInstance().getSelectedEntitiesOnPanel();
if (v.size()>0) {
Controller.getInstance().executeCommand(new RemoveElement(v));
}
} else if (b.getText()=="Undo") {
Selector.getInstance().deselectAll();
Controller.getInstance().undo();
} else if (b.getText()=="Redo") {
Controller.getInstance().redo();
} else if (b.getText()=="Open") {
Umlet.getInstance().doOpen();
} else if (b.getText()=="Save") {
Umlet.getInstance().doSave();
} else if (b.getText()=="Save as..") {
Umlet.getInstance().doSaveAs();
} else if (b.getText()=="New") {
Umlet.getInstance().doNew();
} else if (b.getText()=="Save as JPG..") {
Umlet.getInstance().doSaveAsJPG();
} else if (b.getText()=="Save as SVG..") {
Umlet.getInstance().doSaveAsSvg();
} else if (b.getText()=="Save as EPS..") { //LME
Umlet.getInstance().doSaveAsEps();
} else if (b.getText()=="Save as PDF..") {
Umlet.getInstance().doSaveAsPdf();
} else if (b.getText()=="Tip of the day") {
try {
TipGenerator.getInstance().show(); // ADDED BY STEPHEN GORDON
} catch(Exception ex) {
System.err.println("ERR: Unable to display tip.");
}
} else if(b.getText()=="Print...") {
PrintPage.getInstance().doPrint();
}
//rightclick menu actionevents
else if (b.getText()=="Solid Line ") {
alterArrow(1);
}else if (b.getText()=="Dotted Line ") {
alterArrow(2);
}else if (b.getText()=="Dependence < ") {
alterArrow(3);
}else if (b.getText()=="Dependence > ") {
alterArrow(4);
}else if (b.getText()=="Inheritance << ") {
alterArrow(5);
}else if (b.getText()=="Inheritance >> ") {
alterArrow(6);
}else if (b.getText()=="Aggregation <<< ") {
alterArrow(7);
}else if (b.getText()=="Aggregation >>> ") {
alterArrow(8);
}else if (b.getText()=="Composition <<<<") {
alterArrow(9);
}else if (b.getText()=="Composition >>>>") {
alterArrow(10);
}
}
// the method for changing an arrow with a right click
// it alters the drawing palette and then updates the properties palette
public void alterArrow(int umlSelected){
Relation tmp = (Relation)rc;
Vector atts = Constants.decomposeStrings(tmp.getPanelAttributes(), "");
for(int ii = 0; ii < atts.size(); ii++){
String s = atts.elementAt(ii).toString();
if(s.startsWith("lt=")){
atts.setElementAt(getUMLSmbol(s, umlSelected), ii);
}
}
String concat = "";
for(int ii = 0; ii < atts.size(); ii++){
String s = atts.elementAt(ii).toString();
concat = concat + s+ "";
}
tmp.setState(concat);
Umlet.getInstance().setPropertyPanelToEntity(tmp);
}
5.8. Generator.java
package com.umlet.control.io;
import java.awt.*;
import java.io.*;
/**
The Generator interface provides a common front for all classes used to create
file output from UMLet.
@author Stephen Gordon <a href="mailto:stephen.a.gordon@gmail.com">stephen.a.gordon@gmail.com</a>
*/
public interface Generator {
public void createAndOutputToFile(String filename);
public void createToStream(OutputStream ostream);
}
5.9. DimensionCalculator.java
package com.umlet.control.io;
import java.awt.*;
import java.util.*;
import org.apache.batik.dom.*;
import com.umlet.control.*;
import com.umlet.element.base.Entity;
public class DimensionCalculator {
public static Dimension calculateCanvas() {
int maxx=50;
int minx=0;
int maxy=50;
int miny=0;
Vector v=Selector.getInstance().getAllEntitiesOnPanel();
for (int i=0; i<v.size(); i++) {
Entity e =(Entity)v.elementAt(i);
maxx=Math.max(maxx,e.getX()+e.getWidth()+20);
maxy=Math.max(maxy,e.getY()+e.getHeight()+20);
minx=Math.min(minx,e.getX()-20);
miny=Math.min(miny,e.getY()-20);
}
new Dimension(maxx-minx,maxy-miny);
}
public static Dimension calculateTranslate() {
int maxx=50;
int minx=0;
int maxy=50;
int miny=0;
Vector v=Selector.getInstance().getAllEntitiesOnPanel();
for (int i=0; i<v.size(); i++) {
Entity e =(Entity)v.elementAt(i);
maxx=Math.max(maxx,e.getX()+e.getWidth()+20);
maxy=Math.max(maxy,e.getY()+e.getHeight()+20);
minx=Math.min(minx,e.getX()-20);
miny=Math.min(miny,e.getY()-20);
}
new Dimension(minx,miny);
}
}
5.10. GenEPS.java (Similar changes to GenJPG, GenPDF, GenSVG)
/*
* Created on 18.02.2004
*
* To change the template for this generated file go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
package com.umlet.control.io;
import java.awt.*;
import java.io.*;
import java.util.*;
import org.jibble.epsgraphics.*;
import com.umlet.control.*;
import com.umlet.element.base.Entity;
/**
Generate EPS files from the UMLet drawing panel. If colour support
is in use this will also be saved correctly.
@author Stephen Gordon <a href="mailto:stephen.a.gordon@gmail.com">stephen.a.gordon@gmail.com</a>
*/
public class GenEps implements Generator {
/**
Paints all entities on the panel to a provided Graphics2D object.
@param Graphics2D g2d The object to paint the entities on.
@return void
*/
private void paint(Graphics2D g2d) {
Vector v=Selector.getInstance().getAllEntitiesOnPanel();
for (int i=0; i<v.size(); i++) {
Entity e=(Entity)v.elementAt(i);
g2d.translate(e.getX(), e.getY());
e.paint(g2d);
g2d.translate(-e.getX(), -e.getY());
}
}
/**
Create an EPS image and output to a file.
@param String filename The name of the file to save to.
@return void
*/
public void createAndOutputToFile(String filename) {
try {
OutputStream ostream = new FileOutputStream(filename);
createToStream(ostream);
} catch (Exception e) {
System.out.println("IO Exception.");
}
}
/**
Create an EPS image and output it to a stream.
@param OutputStream ostream The stream to output data to.
@return void
*/
public void createToStream(OutputStream outputStream) { //create and write stream to file
try {
PrintWriter bw = new PrintWriter(outputStream);
EpsGraphics2D g = new EpsGraphics2D("umlet_diagram");
paint(g);
bw.print(g.toString());
bw.flush();
bw.close();
} catch (Exception fnfe) {
System.out.println("UMLet: Error: Exception in GenEps.createToStream: "+fnfe);
}
}
}
5.11 TestColour.java
package com.umlet.testing;
import junit.framework.*;
import java.awt.*;
import java.util.*;
import com.umlet.element.base.Colour;
/**
* Tests the Colour class by passing various input to the
* three public functions within the Colour class, and testing that
* the returned values are the expected values for those input.
*
* @author Ned Martin, s4052992 at the University of Queensland
* www.nedmartin.org/uni/
* @copyright Copyright (c) 2004 Ned Martin
* @version 2004-10-28-SUBMIT
*/
public class TestColour extends TestCase
{
/**
* Simple constructor, simply feeds straight through to the
* parent class which is part of the JUnit framework.
*
* @param String name.
*/
public TestColour(String name)
{
super(name);
} // end TestColour
/**
* Tests the Colour.parseActive() method by passing it a series
* of different values and comparing the returned values.
*
* Various random valid and invalid strings are passed to
* Colour.parseActive() and the two Color's returned are tested
* to ensure that they match the expected return values.
*
* Note that should the random string generator generate a
* valid String in the format c1=number or c2=number, then this
* test will fail, but the likelihood of this occuring is very
* slight.
*/
public void testParseActive()
{
// test with random string
String randomString = makeRandom();
Object[] returned = Colour.parseActive(randomString);
// test that Vector holding string still holds string
assertEquals((String)((Vector)returned[0]).elementAt(0),
randomString);
// test that fill colour c2 equals the right Color
assertEquals(returned[1], Color.white);
// test that border colour c1 equals the right Color
assertEquals(returned[2], Color.black);
String validString;
Color validColor;
Object[] object;
// get a valid colour set
object = validColourString();
validString = (String)object[0];
validColor = (Color)object[1];
// reset returned
returned = new Object[3];
// test with a valid c1 string
returned = Colour.parseActive("c1=" + validString);
// ensure the Vector returned is empty
assertEquals(((Vector)returned[0]).size(), 0);
// ensure the Color's returned are correct
assertEquals(returned[1], Color.white);
assertEquals(returned[2], validColor);
// reset returned
returned = new Object[3];
// test with a valid c2 string
returned = Colour.parseActive("c2=" + validString);
// ensure the Vector returned is empty
assertEquals(((Vector)returned[0]).size(), 0);
// ensure the Color's returned are correct
assertEquals(returned[1], validColor);
assertEquals(returned[2], Color.black);
// reset returned
returned = new Object[3];
// test with a valid c1 and c2 string
String c = "c1=" + validString + "" + "c2=" + validString;
returned = Colour.parseActive(c);
// ensure the Vector returned is empty
assertEquals(((Vector)returned[0]).size(), 0);
// ensure the Color's returned are correct
assertEquals(returned[1], validColor);
assertEquals(returned[2], validColor);
// reset returned
returned = new Object[3];
// test with a valid c2 and c1 string
c = "c2=" + validString + "" + "c1=" + validString;
returned = Colour.parseActive(c);
// ensure the Vector returned is empty
assertEquals(((Vector)returned[0]).size(), 0);
// ensure the Color's returned are correct
assertEquals(returned[1], validColor);
assertEquals(returned[2], validColor);
// test with a randomly padded valid c1 and c2 string
c = randomString + "=" + validString + "" + randomString + "" + "c2=" + validString + "" + randomString;
returned = Colour.parseActive(c);
// ensure the Vector returned contains 3 elements
assertEquals(((Vector)returned[0]).size(), 3);
assertEquals(returned[1], validColor);
assertEquals(returned[2], validColor);
} // end testParseActive
/**
* Tests that Colour.decode() returns the expected Color when
* given both valid and invalid String input. The Color
* returned should be black if the input is invalid.
*/
public void testDecode()
{
// test with a random string
String randomString = makeRandom();
Color colour = Colour.decode(randomString);
// if the first char of the random string is a digit then
// a non-black colour should be parsed, otherwise it is
// black.
if (Character.isDigit(randomString.charAt(0)))
{
assertNotSame(colour, Color.black);
}
else
{
assertEquals(colour, Color.black);
}
// test with a valid colour string
// get a valid colour set
Object[] object = validColourString();
String validString = (String)object[0];
Color validColor = (Color)object[1];
colour = Colour.decode(validString);
assertEquals(colour, validColor);
} // end testDecode
/**
* Tests that Colour.decodeFill() returns the expected Color
* when given
* both valid and invalid String input. The Color returned
* should be white if the input is invalid.
*/
public void testDecodeFill()
{
// test with a random string
String randomString = makeRandom();
Color colour = Colour.decodeFill(randomString);
// if the first char of the random string is a digit then
// a non-black colour should be parsed, otherwise it is
// black.
if (Character.isDigit(randomString.charAt(0)))
{
assertNotSame(colour, Color.white);
}
else
{
assertEquals(colour, Color.white);
}
// test with a valid colour string
// get a valid colour set
Object[] object = validColourString();
String validString = (String)object[0];
Color validColor = (Color)object[1];
colour = Colour.decodeFill(validString);
assertEquals(colour, validColor);
} // end testDecodeFill
/**
* Returns a pseudo-random String of a pseudo-random length,
* where the length is between 1 and 80 inclusive, containing
* only characters within the standard ASCII range (ASCII 32 to
* 126 inclusive). It is still possible for a valid string to
* be generated, but it is infrequent.
*
* @return String pseudo-randomnly generated
*/
private String makeRandom()
{
Random randomizer = new Random();
String returned = "";
for (int ii = 0; ii < randomizer.nextInt(80) + 1; ii++)
{
// add an ASCII value from 32 to 126 inclusive
returned += new Character((char)(randomizer.nextInt(95) + 32)).toString();
}
returned;
} // end makeRandom
/**
* Returns an Object array containing a valid String and the
* Color that valid String should create.
*
* @return Object[] containing two elements, a valid String
* and the Color produced by that valid String.
*/
private Object[] validColourString()
{
Random randomizer = new Random();
Object[] returned = new Object[2];
int r = randomizer.nextInt(256),
g = randomizer.nextInt(256),
b = randomizer.nextInt(256);
returned[0] = r + " " + g + " " + b;
returned[1] = new Color(r, g, b);
returned;
} // end validColourString
} // end TestColour
29-Dec-2004