Adding Existing Source Code in NetBeans CS288, Autumn 2005 Lab 002 Purpose This document will show how to incorporate existing source code within a NetBeans project. It will also introduce the concept of calling methods from a different class. You have already set up the Building class within the CS288 Java project using NetBeans before starting this exercise (Lab 000 and Lab 001). To continue with this exercise start NetBeans with the CS288 project open. Note: In using this document it is possible to paste text directly into NetBeans. That means when defining new terms in NetBeans their value can be copied directly from here, which will remove the possibility of mistyping values. If you are reading the PDF version of the document, then clicking on the ‘Select’ icon activates text selection: Items can be selected and copied in the usual way after choosing this tool. The DialogInput Class The source code that we will add to the project is in the file DialogInput.java located on the course web site in the Lab002 section. This file needs to be copied to the same folder that NetBeans uses to store the source code for the CS288 project. Right click on the CS288 project in NetBeans and select project properties. This tells you the main project folder location. The source code for the CS288 project in this example is in the 'src\CS288' subfolder of the project folder. I.e. for the example shown above, the source code lives in the C:\Bill\Java\NetBeans\CS288\src\cs288 folder. Note: if you did not name your project CS288 then it will have a different path. If you have found the correct subfolder it will already contain the file Building.java. Copy DialogInput.java to the 'src\CS288' folder. Note: the copied file MUST have exactly the same name and extension as the original file or NetBeans will not correctly include the class within the project. Also the copied file name must have exactly the same case structure as shown. If you have copied the file correctly then NetBeans will automatically update the project to include the new class and methods. If this is so the project view will also be updated and should look something like the illustration on the right. Double click the askUserForString method in the DialogInput class. The purpose of the DialogInput class is to provide a user friendly way to get input strings during the execution of the Building application. The askUserForString method creates a basic dialog box with a single text field for the user to fill in. The argument to askUserForString is used as the prompt in the dialog box to display to the user. We will use this method in the Building class instead of the command line arguments that we used in the previous lab exercise. At this point of the course we have not covered enough material to understand what the code in the DialogInput class does. However we will still be able to use the methods. As another check that the new class has been correctly added to the project it is useful to compile the file and see that no errors are reported. Right click on DialogInput.java in the project view and select 'Compile File'. If the class has been added to the project without errors then the Output pane should show something like this: init: deps-jar: Compiling 1 source file to C:\Bill\Java\NetBeans\CS288\build\classes compile-single: BUILD SUCCESSFUL (total time: 0 seconds) The DialogInput.java was designed as a stand alone Java class, and will not work correctly with the NetBeans project structure without some modification. Double click the DialogInput.java item in the project view. In the editor pane add the line: package cs288; Illustration 1:Updated Project View at the start of the file. Note: If you did not call your project cs288 then you will need to use the appropriate name at this point. This 'package' statement ensures the NetBeans compilation script understands that the DialogInput class is meant to be part of the same project. Strictly this is not required by the Java programming environment when all source files are contained within the same folder, however NetBeans does not completely comply with the Java standard. Using the askUserForString method Add a new private field to the Building class. To do this you can choose the 'Add Field' option from the menu given by right clicking the Fields table in the project view. It is also possible to define a new field by simply typing directly into the editor pane. Open the Building.java editor window and type in (or copy) the following line: private DialogInput dlg = new DialogInput("Building Object"); For clarity it is best to add this line to the same part of file where the other fields have been declared. As a check click on the 'Clean and Build Main Project' icon. If the changes were correctly made you should see output something like this: init: deps-clean: Deleting directory C:\Bill\Java\NetBeans\CS288\build Deleting directory C:\Bill\Java\NetBeans\CS288\dist clean: init: deps-jar: Created dir: C:\Bill\Java\NetBeans\CS288\build\classes Compiling 2 source files to C:\Bill\Java\NetBeans\CS288\build\classes compile: Created dir: C:\Bill\Java\NetBeans\CS288\dist Building jar: C:\Bill\Java\NetBeans\CS288\dist\CS288.jar jar: BUILD SUCCESSFUL (total time: 0 seconds) The new line creates an object of the class DialogInput and assigns it to the variable 'dlg'. Having created this object we can use its methods to provide a more friendly method for user input with the askUserForString method. Double click the main method in the Building.java branch of the cs288 project view. Either delete or comment out the statements that use the command line arguments to initialise the field values. Immediately after the 'Building b1 = new Building();' line that creates the 'b1' object type the text 'dlg.' (do not forget to type the period '.'). NetBeans will now pop up a dialogue box listing all the fields and methods that are available for the 'b1' object. Scroll down to askUserForString method and double click it. Replace the 'disString' text in the method call with the string "Enter a value for the street name" (remember to include the quotation marks). Add a ';' to correctly terminate the statement. The source file should now contain this statement: dlg.askUserForString("Enter a value for the street name"); Error in Source File At this point we have an error in the source code. The new statement we have added is underlined in red. If you click on the line and leave the mouse stationary for a moment, the following dialogue box should appear: Even though we have not explicitly compiled the project, NetBeans has checked the code against the compiler in the background and is highlighting an error that will occur if we try to compile the project. The error occurs because the main method must be static in a Java program. A static method is only created once for the entire class and is not copied to new objects that are created during execution of the application. The Java compiler does not allow static methods to directly refer to non static fields. A non static field may not exist at runtime because there do not have to be any objects of the corresponding class created during runtime. However, static methods can be executed at any point during runtime, whether objects for a class exist or not. If a static method attempted to access some non static field when there were no corresponding objects then their would be a runtime error. Error Resolution (1) We only want to create a DialogInput object to access its methods. In particular so that we can create a simple dialogue box for the user to enter a string. One solution is not to have a 'dlg' field at all as part of the Building class. Instead we could create a DialogInput object for use just within the main method. Right click the 'dlg' field item for the Building class in the project view. Choose the delete option. Now double click the main method. Modify the body of the method so that it looks like this: public static void main(String[] args) { Building b1 = new Building(); DialogInput dlg = new DialogInput("Building Object"); dlg.askUserForString("Enter a value for the street name"); System.out.println("Occupant name is " + b1.getOccupantName()); System.out.println("Street Name is " + b1.getStreetName()); System.out.println("Street Number is " + b1.getStreetNumber()); } Note we have now declared 'dlg' as a local variable which only has meaning within the main method, and not as a field that can be accessed from any part of the class. Check that the code correctly compiles by clicking the 'Clean and Build Main Project' icon. Run the application. You should see a dialogue box something like this: Enter any value and click OK. The output should look like: init: deps-jar: compile: run: Occupant name is null Street Name is null Street Number is null BUILD SUCCESSFUL (total time: 9 seconds) Note because we have not assigned any value to the Building fields they remain 'null'. Modify the main method to this: public static void main(String[] args) { Building b1 = new Building(); DialogInput dlg = new DialogInput("Building Object"); String tmp_string = dlg.askUserForString("Enter a value for the street name"); b1.setStreetName(tmp_string); System.out.println("Occupant name is " + b1.getOccupantName()); System.out.println("Street Name is " + b1.getStreetName()); System.out.println("Street Number is " + b1.getStreetNumber()); } Notice we have now assigned the string value returned by the dlg.askUserForString method to the local variable tmp_string. Then we have used the setStreetName method to assign a value to the b1 object field streetName. Run the application again. This time the value you type into the dialogue box will appear in the output view. Now modify the code so that the streetNumber and occupantName are also assigned values by using the dlg.askUserForString method. Error Resolution (2) For this solution we will keep 'dlg' as a field in the Building class. Right click the fields item in the project view and add a new field 'dlg' of type DialogInput. Then edit the 'dlg' declaration so that it becomes: private DialogInput dlg = new DialogInput("Building Object"); Next define a new method for the Building class. Right click the methods item in project view and select new method. In the dialogue box enter userSetStreetName and click OK. Double click the new method in the project view. Edit the body of the method so that it looks like this: public void userSetStreetName() { String tmp_string = dlg.askUserForString("Enter a value for the street name"); setStreetName(tmp_string); } Now edit the main method to this: public static void main(String[] args) { Building b1 = new Building(); b1.userSetStreetName(); System.out.println("Occupant name is " + b1.getOccupantName()); System.out.println("Street Name is " + b1.getStreetName()); System.out.println("Street Number is " + b1.getStreetNumber()); } Clean and build the application, then run. This time whatever value you type in the dialogue box will be assigned to the streetName field. To see how this works try adding breakpoints in the main method and then running the debugger as we did in Lab000. Add other new methods to the Building class in the same way, so that the user can input values for the Street Number and Occupant Name. Call these new methods userSetOccupantName and userSetStreetNumber. Note: the second method requires some conversion between types in order to use an input String to define the Double field value of streetNumber. If you get stuck with this there is a solution near the end of this document. The error was resolved in this case by removing direct access to the 'dlg' field and wrapping it in a method call of the 'b1' object. The 'b1' object will always exist since it is created as part of a static method, namely the main method of the class. Error Resolution (3) One other solution to the original error is to make the DialogInput methods askUserForString and showInFrame be declared static. Then they can be called without creating an object for the DialogInput class. For example by invoking DialogInput.askUserForString(“Some String or Other”) Edit the DialogInput class and the main method of the Building class to implement this solution. Hint: in changing the declaration of these methods to static, you will then cause other errors to occur which will then require various fields in the DialogInput class to also be declared static. User Output For this section it is assumed you have implemented Resolution 2 above for the static error. The DialogInput class has a method for displaying an output string in a simple frame. This section shows how to use this to display the field values of the 'b1' object. Double click the main method for the Building class in the project view. Comment out the lines that print the field values to standard output. Right click the methods item and select new method. Call the new method showStreetName: Edit the body of the new method so that it looks like this: public void showStreetName() { dlg.showInFrame( "The Street Name is", streetName); } This method can now be called for the 'b1' object in the main method. Double click the main method, and edit to this: public static void main(String[] args) { Building b1 = new Building(); b1.userSetStreetName(); b1.showStreetName(); } The main method indirectly calls the methods for the DialogInput class from methods for the 'b1' object. Clean build and run the application. Assuming you enter 'Smith St' in the dialogue box for entering the street name, then you should see a frame something like this after you click OK: Now modify the Building class so that it has new methods that show the street number and occupant name in their own frames. Call these new methods showStreetNumber and showOccupantName. The userSetStreetNumber and showStreetNumber methods The definition of the userSetStreetNumber and showStreetNumber methods are non-trivial since they require defining a 'Double' value from a given 'String' value and vice-versa. In your final solution your methods should look something like this: public void userSetStreetNumber() { String tmp_string = dlg.askUserForString("Enter the street number"); Double tmp_Dbl = new Double(tmp_string); setStreetNumber(tmp_Dbl); } public void showStreetNumber() { dlg.showInFrame("The Street Number is:", Double.toString(streetNumber)); } In the first method notice that we create a new object 'tmp_dbl' of type 'Double'. Then we use the value of this new 'Double' object as the street number in the setStreetNumber method. Since users are limited to inputting values as String objects this kind of conversion within a method is quite typical. In the second method we can not pass the streetNumber value directly to the dlg.showInFrame method since it is not a String. To convert the value to a String object we apply the static Double method toString. Summary The lab has shown how to add existing source code to the NetBeans project, and modified the code so that it will compile correctly with the configuration scripts of the NetBeans IDE. The lab has shown how to create an object of one class within another and then access its methods. The lab has also illustrated the difference between static and non static use of methods and fields.