Target Audience: this guided project is geared toward the beginning Java programmer who has some experience with Java programming from the command line.
Creating a good software system, one that people want to use, requires more than just writing code. Writing software is a process, called software engineering. Software engineering is a sub-area of computer science where practitioners use processes and practices to develop high quality, performant, and usable software that meet a set of customer requirements. Tools support these processes and practices.
After completing this Guided Project, students will be able to…
Implement and test a multi-class software system from a given set of requirements and design.
Use software engineering best practices like test-driven development, code coverage, static analysis, version control, continuous integration, and documentation with supporting tooling to implement and test object-oriented systems.
Best Practice: Integrated Development Environments (IDEs)
Integrated Development Environments (IDEs) facilitate software development by integrating multiple software development tools into a single application. In this module you will use the Eclipse IDE and tooling in Eclipse to develop and test software.
WolfScheduler System
You will implement and test PART of the WolfScheduler system. The WolfScheduler system provides a way for a student to determine which course schedule may be best for them in an upcoming semester.
You will develop the WolfScheduler project over the course of the three guided projects. The WolfScheduler requirements describe the fully implemented system. Guided Project 1 will focus on the following requirements:
You will complete functionality related to events and schedule conflicts in future Guided Projects. There are expectations from the Guided Projects that you must follow. Do NOT attempt to implement any functionality before a Guided Project tells you to do so!
As part of the Guided Project, you will be expected to run provided JUnit tests and acceptance tests to ensure that your implementation meets the requirements and design of the system.
Review the learning objectives and submit the assignment.
Total Estimated Time*:
9 hours 35 minutes
* Times are only estimates. It may take you less time or more time depending on how much debugging effort is required for your specific implementation. Use the relative numbers to help guide you: for example, a task labeled as "5 minutes" should not require as much effort as a task labeled "20 minutes", regardless of how much time it takes to actually complete the task.
Guided Task: Your First Eclipse Project
Learning Outcomes
Create an Eclipse project
Create a package
Create a class
Execute a program in Eclipse
Best Practice: Integrated Development Environments
When you first learned how to write Java programs, you probably started by writing code in a text file, compiling using the javac command, then running your program from the command line using the java command. This form of programming is what we’ll refer to as command-line programming, and it follows the practice of write/edit-compile-execute. While command-line programming has many advantages, it can be quite tedious. After all, your text editor doesn’t usually provide much outside of… well… editing. Professional developers gravitate toward integrated development environments (IDE) for their programming. The idea of an IDE is to integrate many different developer tools into a single program (such as code editing, compiling, running, file management, code navigation, documenting, version control, and so on). Programming with an IDE can provide some of the following advantages over command-line programming:
Compiling is built-in. Whenever you click Save, your code is compiled for you.
A controlled execution environment means that you can control how exactly your program will be executed without changing what your current system is configured for (such as running your program using different Java virtual machines).
A Graphical User Interface (GUI) provides immediate feedback on compile errors, warnings, and other problems with your project.
IDEs are highly customizable. From formatting and coloring preferences to programming in various languages and technologies, IDEs are extremely extensible.
Code editing and navigation is much easier because the IDE “understands” the language you’re working with (you’ll understand more of what this means with a few examples).
IDEs support large projects by providing many other tools not related directly to programming, but with software development in general.
In this Guided Project we’ll be covering Eclipse, a professional-grade, open-source development platform for Java (and many other languages!). While reviewing prerequisite materials, we’ll focus on some of the features found in Eclipse that are not found in command-line programming.
A note about audience. We assume the users for this tutorial are working on their own machines and can devote permanent space to their Eclipse work.
Installing Eclipse
See the Eclipse Installation Tutorial for instructions about installing Eclipse and the other plug-ins you will be using this semester.
Starting Eclipse
When you first start Eclipse, you are asked to select your workspace. The Eclipse workspace is a permanent location for your work. You can have many workspaces on one computer. A workspace is just a folder that holds a related collection of Eclipse projects and metadata (stored in the .metadata directory) about your custom settings for your workspace. We recommend using a single workspace for CSC216. Choose any location on your hard drive where you want your work to be stored and click OK to load the workspace.
The first time you start with a fresh workspace, the Welcome view opens. Close that view by clicking the X on the Welcome tab under the Eclipse menu.
The main Eclipse interface is called the Java Perspective (designated by the Java Perspective icon in the upper right of the user interface). A perspective provides tools (e.g., editors, problems list, outline, test results) appropriate to a major developer activity such as development or debugging. A perspective contains windows (called “Views”) that support the overarching activity.
The Java perspective has a tool bar of buttons along the top that enables you to create new items or run an application. The view on the left side of the perspective is the Package Explorer, which contains the project contents shown in a hierarchal manner. The middle view of the perspective is the editor, which contains tabs for each file open for editing. The Outline view, along the right of the perspective, lists key information about the file open in the editor. If the file is a Java file, the Outline View will list the methods. The upper right view is a Task List. The bottom of the perspective contains the Problems view and several other views as tabs. The Problems view lists compiler errors and warnings.
You can move around and resize the views that make up the perspective. You can stack views in an area of the screen and use tabs to switch between views. If you want to reset the perspective to how it originally looked, go to Window > Reset Perspective. If you are not in the Java perspective, use Window > Open Perspective > Other... > Java (NOT Java Browsing!). You can customize your perspective with additional views using Window > Show View and selecting a listed view or Other.
Create an Eclipse Project
Now that your Eclipse workspace is set up, start your first project. This project does not need to be submitted, but you will be expected to complete all of the activities in this section on your own later in the Guided Project.
An Eclipse project is a grouping of files that are all part of one application or program. Many Java files, compiled Java .class files, test code and supporting files, documentation, a project’s website, and other resources can all be in a single Eclipse project.
If you have never created a project in the workspace before, the Package Explorer will have the option to Create a Java project. If you have created projects before, you can right click in the Package Explorer and select New > Java Project.
A window pops up for selecting project settings. Type HelloWorld as the name of your project. Click the option to Create separate folders for sources and class files. This ensures that your *.java and *.class files are stored in different directories, allowing for a neater project folder. Uncheck the option to Create module-info.java file as circled below.
Your new project should be listed in the Package Explorer. Click on the arrow or plus sign next to the project name to expand and look inside your project.
Notice the src/ folder in your project. This is where all of your Java source code files will reside. If you do not have a src/ folder, right click on the project name and select New > Source Folder. In the resulting dialog, name the folder src/. A bin/ folder (which will store all your compiled class files) will be automatically created for you. The bin/ folder is not displayed in the Package Explorer. If you go to the project’s directory on your file system, you will see the bin/ folder with the *.class files.
Create a Package
Packages are a way of organizing java files into folders that contain related functionality. For example, you may have a package for your plain old java objects, a package for any file reading and writing functionality, and a package that contains the business logic of the application.
Packages are also used to name applications and Eclipse plug-ins. If you look in the <YOUR INSTALL DIRECTORY>/eclipse-install/eclipse/plugins directory, you will see a file with the name org.eclipse.equinox.launcher_1.5.400.v20190515-0925.jar. The first part, org.eclipse.equinox.launcher is the package name. The package name indicates that the entity that created the code is a organization (org), that the organization is eclipse, the project is equinox, and the code in the package is the launcher functionality. The numbers specify the version of launcher and the build date and time of the jar file.
Conceptual Knowledge: Access Control
In Java, you can provide modifiers to fields, constants, and methods that determine whether other classes are able to access those elements. The public modifier, typically used on methods, means that any other class can call the method on an instance of the object (or in the case of a static method, on the class itself). The private modifier encapsulates or hides fields from direct access by other classes. The private access modifier is helpful for auxiliary methods that do not need to be exposed to a client class.
There are two other modifiers that you need to know about: no modifier (or default level access) and protected. If a Java element has no modifier, it has default level access, which means that elements of the class itself can access the given element AND methods of any other class in the same package can access the elements with default level access! Protected level access means that elements of the class, the same package, and any subclass can access the protected element (you’ll work with subclasses in GP2). The table below summarizes the access control modifiers.
Modifier
Class
Package
Subclass
World
private
Y
N
N
N
No modifier
Y
Y
N
N
protected
Y
Y
Y
N
public
Y
Y
Y
Y
Create your package by
Right clicking on the src/ folder and selecting New > Package.
In the resulting dialog, enter edu.ncsu.csc216.hello_world for the package name. Verify that the source folder is HelloWorld/src/.
Click Finish.
The package name indicates that you are part of an educational organization (edu), specifically NCSU (ncsu), and completing work for CSC216 (csc216). The last part of the package name identifies this specific project (hello_world). Package names are typically all lower case with words separated by an underscore (_).
Best Practice: IDE Customization
IDEs allow for customization of the workspace to help developers be more productive. Eclipse is a highly configurable IDE. You may find the following customization useful if you find the package names are too long for disambiguation in the Package Explorer view.
You can shorten the way that package names are displayed in the Package Explorer by selecting Window > Preferences > Java > Appearance. Check the box next to Compress all package name segments, except the final segment and enter 0 (zero) in the text box. Click OK. This will shorten the packages in your project to the last segment, in our case hello_world. When you look at the folder structure on the file system and the package imports in the source code, the full package remains.
Create a Class
IDEs such as Eclipse provide shortcuts or wizards for creating common programming elements, like classes. You will use the New Java Class Wizard to help create your first Java class in Eclipse named HelloWorld that will be saved in a file HelloWorld.java. The class will be in your edu.ncsu.csc216.hello_world package.
Right-click on the edu.ncsu.csc216.hello_world package, and select New > Class.
In the Name box, type HelloWorld for the name of your class (you do not need the .java extension - Eclipse will add that for you automatically). Also, check the box that says public static void main(String [] args) as a method stub that you would like generate. Verify the following:
Source folder: HelloWorld/src
Package: edu.ncsu.csc216.hello_world
Name: HelloWorld
Modifiers: public
Superclass: java.lang.Object
Which method stubs would you like to create? At least public static void main (String [] args) should be selected.
You can optionally select the checkbox for Generate comments
Click Finish.
Eclipse generates a file named HelloWorld.java in the HelloWorld/src/edu/ncsu/csc216/hello_world folder with a main method. The file is opened in the Java editor so you can start working on it right away.
That means you will have a project folder called HelloWorld and a class in the projectHelloWorld saved in the file HelloWorld.java. Class names and project names do NOT need to be the same. In fact, class names must be distinct within packages, and unique names are encouraged within projects to ease maintenance.
Editing a Class
You are going to edit your HelloWorld class to include a statement that will print "Hello, World!" to the console.
Place a print statement for the String literal "Hello, World!" in the main method. You can delete the TODO comment when you add the print statement.
Click the Save button ( in the top menu (or press Ctrl+S - Cmd+S on Macs).
As you are typing the print statement into the editor, you may see a red x () to the left of your line. Eclipse is trying to compile your code as you type, and since you have an uncompleted statement, Eclipse shows a compilation error. Simply ignore the red x for now, and focus on finishing your statement. Saving the file compiles the program, which is equivalent to running the command javac HelloWorld.java on the command line. Any compiler errors will appear in the Problems View after you save your file. Additionally, any line of code with a compiler error will have a red x to the left, and there will be a red squiggly line under the code. Fix any compiler errors in your code.
Running a Java Program
You now have everything you need to execute your program!
If there are no compiler errors, right-click on HelloWorld.java in the Package Explorer and select Run As > Java Application.
The program output of Hello, World! is printed to the Eclipse Console view. If you do not see the Console view, select Window > Show View > Console. You may need to rerun your program for the output to display in the Console view.
You just ran your first Java program in Eclipse!
Independent Task: Create WolfScheduler
For the remainder of the tutorial, you’ll implement the first part of the WolfScheduler project.
Learning Outcomes
Create an Eclipse project
Create a package
Create a class
Best Practice: Integrated Development Environments (IDEs)
Use the Eclipse IDE to create a Java project, package, and class!
Complete the following tasks to set up your WolfScheduler project.
Create a new project called WolfScheduler. The project MUST have the exact name WolfScheduler with a capital W and S or the automated grading will not work.
Create a new package called edu.ncsu.csc216.wolf_scheduler.course in the src folder of the WolfScheduler project.
Create a new Java class called Course in the edu.ncsu.csc216.wolf_scheduler.course package of the WolfScheduler project
Check Your Progress
Verify that your project has the following structure as shown in text and screenshot form, below. Note that in the screenshot of the Package Explorer the only visible part of the package name is course due to IDE Customization.
Submission of all major course deliverables for Guided Projects, Labs, and Part 2s of Projects is through the Git version control system and NCSU’s Enterprise GitHub. You should go through the Git and GitHub Tutorial to familiarize yourself with the technologies before completing this Guided Task.
Learning Outcomes
Clone a GitHub repository
Share a project with a GitHub repository
Push changes to a remote GitHub repository
Best Practice: Version Control
A version control system stores the history of development on a given project in a central location. Additionally, a version control system provides mechanisms for teams to collaborate on the same code base. In a version control system, there is typically a remote repository that contains the main copy of the code and all of the development history or contributions of the developers. Developers have a local copy of the remote’s code where they can make changes to fix bugs or add new functionality. Those changes are then copied or pushed to the remote’s repository and are integrated into the main code base. Other team members can checkout or pull the changes from the remote to their local copies of the code and continue working on their development tasks. If there are major issues with a system, a development team can roll back to an earlier version of the code base.
When using Git, you will have a remote Git repository that exists on NCSU’s Enterprise GitHub server. That remote repository is where all of your files must end up for grading. You will have a local repository that is cloned from the provided remote repository. You will develop in your local repository and push your changes to the remote repository. If you’re working with a partner, your partner can pull your changes into their local repository and continue development. The workflow is similar if you’re using Git Bash or Eclipse EGit.
You will be using NCSU’s Enterprise GitHub, which is a web interface for working with Git repositories. The web interface provides a mechanism for viewing your remote Git repository (where all your work is submitted). Additionally, GitHub provides functionality for providing feedback on code and tracking issues.
To be assigned a repository, you must log into NCSU’s GitHub system with your unity id and password.
Completing a local configuration of Git is useful to streamline usage. We require that commits to NCSU’s GitHub are identified by the student’s unity id. Configure your local Git setup, so that you can ensure that you are using the correct identifiers so that we can give you credit for your work, especially on team assignments.
There are two ways to connect to GitHub: 1) HTTPS and 2) SSH.
If you connect to GitHub using HTTPS, you will connect through secure HTTP connections. This will require you to enter your username and password with each interaction with the remote NCSU Enterprise GitHub server.
If you connect to GitHub using SSH, you will use a secure key on your local system to connect with a public key that you have associated with your GitHub account. SSH stand for secure shell (it’s also Dr. Heckman’s initials!) This will serve as your authentication and provide a secure connection; you won’t have to enter your user name and password with every interaction. We strongly encourage you to follow the directions provided by GitHub to set up your local system to use SSH to connect to GitHub.
Reference: Cloning a Repository
For additional information on cloning a repository, see:
To work with a new remote repository that is assigned to you for submission of your CSC 216 work, you must first clone the remote repository to your local file system.
Open the dropdown menu under your username in the top left of the page. Choose the CSC 216 organization for the given semester. For example, Fall 2021’s organization is engr-csc216-fall2021 and Spring 2022’s organization is engr-csc216-spring2022.
Select the repository for the given assignment. For example, a repository for Guided Project 1 may be named csc216-GP1-XXX-YYY, where XXX is your section number and YYY is your repository number.
First select if you would like to copy the SSH clone URL or the HTTPS clone URL in the drop down menu. Then click the copy button. Only select the SSH URL if you have set up your SSH key.
Clone the repository locally. Do ONE of the following.
Change directory to the location on your file system where you want your cloned repository to live. Remember this location!
Execute the command:
git clone <copy clone URL here>
If you are using HTTPS, enter your unity id and password.
This will create a directory structure on your file system for the local copy of the Git repository. However, this does not add the Git repository information to your Eclipse workspace. To add the repository to Eclipse, do the following:
Open the Git Repositories view (if it is not already open) by selecting Window > Show View > Other > Git > Git Repositories. Click OK.
Select the option to Add and existing local Git repository.
Eclipse will search your filesystem for folders that are Git repositories (these are folders that contain a .git/ folder). Select the folder that corresponds to the location where you cloned the repo.
Click Add. Your repository will now be in your Git Repositories view in your Eclipse workspace.
Open the Git Repositories view (if it is not already open) by selecting Window > Show View > Other > Git > Git Repositories. Click OK.
Select the option to Clone a Git Repository.
The Clone Git Repository wizard will display. If you have the Git URL in your computer’s memory, it will populate the URI, Host, and Repository path fields. If you do not, copy the URL and paste it into the URI field. The Host and Repository fields will populate. Click Next.
Make sure that the main branch is selected. Click Next.
Browse for the location where you want the local copy of the repository to be located on your file system. The default location is a git/ directory under your home directory.
After you have selected the location, click Finish. The repository will now be listed in the Git Repositories view.
Reference: Adding (Staging) a Project
For additional information on adding (staging) a project to a repository, see:
Adding (Staging) WolfScheduler to a Git Repository
Now that you have a local copy of your remote submission repository, you need to add your project to the local repository. Since you created the project in Eclipse BEFORE cloning your repository, we recommend using EGit to add (stage) your WolfScheduler project to your local Git Repository. This is because EGit will handle the copying of the WolfScheduler project to the repository location on your file system while maintaining the project in Eclipse.
Right click on the WolfScheduler project and select Team > Share Project…..
Select your repository from the drop down menu and click Finish.
The project name in the Package Explorer should now be followed with the name of the repository in square brackets.
To add the files to the index, right click on WolfSchduler and select Team > Add to Index.
Reference: Adding (Staging) a Project
For additional information on committing staged changes, see:
Committing Local Changes and Pushing to the Remote Repository
Now that your project is staged in the repository, you can commit the changes to the local repository and push the changes to the remote repository and submit your work (so far).
Commit and push your changes. Do one of the following:
First, commit your changes using the following command:
git commit -am"Submitting a skeleton of WolfScheduler."
Second, push the changes to the remote repository using the following command:
git push
In Eclipse, click the refresh button in the Git Repositories view for the commit to be recognized by Eclipse.
Commit/push using EGit
Right click on WolfScheduler and select Team > Commit.
In the Git Staging view, do the following:
Check that all files you want pushed to the remote repository are in the Staged Changes window. Select any files that should be committed/pushed in the Unstaged Changes window, right click and select Add to Index.
If there are any files that you do not want pushed to the remote repository in the Staged Changes window, right click on the files and select Remove from Index.
Make sure that the following files (at least) are staged:
.classpath
.gitignore
.project
Course.java
Enter a commit message like “[Skeleton] Submitting a skeleton of WolfScheduler”.
Click Commit and Push.
If your files aren’t showing up in the Staged Changes window, click the refresh button (in the red circle) in the upper left of the view.
After the push completes, there will be a confirmation window to show that the project was pushed to the remote correctly. If you see a message about fast-forward, there was an error. You have a merge conflict and you will need to resolve the conflict.
Check Your Progress
You should ALWAYS verify that your pushed changes are on the remote repository. You can do this by going to your repository on the GitHub website.
Select the appropriate organization and repository.
Verify that your latest commit message is listed and that all pushed files are in the repository.
You will follow this process every time you want to submit your code. We recommend frequent pushes to the remote repository to save your work or when you have a problem. It is ok to push code that doesn’t compile or pass all of the tests. When the teaching staff helps you, it is easiest for us to check the code on your repository (assuming you provide a link to your repo for us to use).
Guided Task: Java Source Generation
Eclipse provides source generation of commonly written code. You typically use source generation when creating plain old java objects (POJOs) or when overriding methods from the Object class.
Learning Outcomes
Create fields
Generate getters and setters for fields
Generate a constructor for the fields
Generate overridden Object methods equals(), hashCode(), and toString().
Best Practice: IDE Code Generation
Many IDEs have tools that help with common tasks, and some of those tools can help you generate code. You learned how to write these code statements manually in your introductory programming class so that you would fully understand their purpose. Now you can use Eclipse’s code generation tools to help you write common statements. A benefit of using code generation tool, besides saving time, is that the code is more likely to be correct. However, you should always test the generated code to make sure that it meets the requirements of the larger program.
Create State for Course
Source code generation is useful only if you have specified data (or state) for your object. You will create state for Course as defined in UC 1, Processing Course Information. A Course should keep track of its name (e.g., CSC 216), title (e.g., Software Development Fundamentals), section (e.g., 001), credit hours (e.g., 4), instructor’s unity id (e.g., sesmith5), meeting days (e.g., MW), start time (e.g., 1330), and end time (e.g., 1445).
An important concept in object-oriented programming is the idea of encapsulation. Encapsulation is the protection of an object’s state. Encapsulated classes are achieved through private fields. If a field is private, that means it can be changed only through the object’s methods (or behaviors). The methods provide a controlled way to change the object’s field and can help ensure that any object invariants (e.g., rules about the object’s state that must be true at all times) are not violated by a client.
That’s why all of the fields listed below are private.
Add the following code to Course to create fields (instance variables). You may copy and paste from the Guided Project.
/** Course's name. */privateStringname;/** Course's title. */privateStringtitle;/** Course's section. */privateStringsection;/** Course's credit hours */privateintcredits;/** Course's instructor */privateStringinstructorId;/** Course's meeting days */privateStringmeetingDays;/** Course's starting time */privateintstartTime;/** Course's ending time */privateintendTime;
Generate Getters and Setters
Each field that you just created has a yellow warning beside its declaration to indicate that the field is never used locally. The light bulb next to the warning triangle indicates a quick fix. One fix will delete the field. Another will generate the getter (accessor) and setter (mutator) for the field. Typically when creating fields, you will want to generate getters and setters as appropriate to your class.
The Quick Fix tool provides functionality for creating BOTH getters and setters for ONE field. A different feature of Eclipse provides a more general code generation of getter and/or setter methods for all fields in a class and allows for more control over the method visibility.
Conceptual Knowledge: Encapsulation - Getters and Setters
Since methods provide the mechanism of mutating or changing the contents of a field, you should write them carefully in order to control the change that a client can make to the private fields. One example is to make fields that might mutate an object’s data private or not add them to the class.
As you design a class, you should think about when a class should and should not be able to access and change a field and plan accordingly.
For your Course class, you will provide getters and setters for all the fields. The setters will be implemented to ensure that clients of Course are unable to change Course fields to invalid values.
Right click anywhere in the Editor view. Select Source > Generate Getters and Setters.
A window opens to display all of the Course fields. Click Select All to generate getters and setters for all of the fields. Use the arrows to see which of the methods will be created for each field. To save a little more coding time, check Generate method comments. Additionally, you can change the insertion point so that the new methods are positioned after the fields. Click Generate when you are done selecting options. The newly generated code will display in the Editor view. Now save Course to remove the earlier warnings associated with each field.
Add method level comments to all of the getters and setters. An example of what the comments might look like for the Course.name field are provided below.
/**
* Returns the Course's name.
* @return the name
*/publicStringgetName(){returnname;}/**
* Sets the Course's name.
* @param name the name to set
*/publicvoidsetName(Stringname){this.name=name;}
Generate the Course Constructor
Now that you have state for your Course class, you need to create a constructor for setting that state! You could require that clients of Course use the default constructor with no parameters and setters to set up an instance of the Course class. But a client may not set up the Course correctly or miss an important data item. A better solution is to write a constructor in which the calling code passes in values for each Course field (or a subset of fields) and sets the fields by calling the appropriate setter methods.
Constructors provide a concrete mechanism for creating a new instance of a class correctly and ensuring that the values stored in the field are appropriate for the class’s abstraction.
Eclipse can generate a constructor for you. Make sure Course is displayed in the editor, right click the editor and select Source > Generate Constructor using Fields.
Eclipse opens a dialog for selecting which fields to include in the constructor parameters. (Just because you have a field in your class does not require that a value is passed in as a parameter to the constructor, so you can leave fields unchecked. You should handle initialization of those fields within the constructor to an appropriate default value.) Again, check Generate constructor comments. Also, check the option to Omit call to default constructor super(). Optionally change the insertion point so that the constructor is positioned between the field declarations and the getter methods. Click Generate.
After generating the constructor, update the comments!
Use the code generation tool to create a Course constructor with only the name, title, section, credits, instructorId, and meetingDays fields. This constructor is used when the course is arranged and there are no start/end meeting times. After generating the constructor, update the comments! When you are done, the constructor should look like the following.
/**
* Creates a Course with the given name, title, section, credits, instructorId, and meetingDays for
* courses that are arranged.
* @param name name of Course
* @param title title of Course
* @param section section of Course
* @param credits credit hours for Course
* @param instructorId instructor's unity id
* @param meetingDays meeting days for Course as series of chars
*/publicCourse(Stringname,Stringtitle,Stringsection,intcredits,StringinstructorId,StringmeetingDays){this.name=name;this.title=title;this.section=section;this.credits=credits;this.instructorId=instructorId;this.meetingDays=meetingDays;}
Best Practice: Comment Code as You Implement!
Commenting code is important to let others know what your classes, fields, and methods do. It is also important for reminding future you about you implementation.
Do not wait until the last minute to comment your code; comment as you work on things. Even better, you can write your code skeleton and comment the skeleton before you begin writing code. That helpe you think about what a method should do or what a field means before you start trying to implement something!
Generate equals() and hashCode()
The Object class is the parent of all Java classes. It has some frequently used methods: equals(), hashCode(), and toString(). However, Object’s implementation of these methods may not be what you want for your classes. Eclipse can help override these method definitions.
The equals() method compares two objects to determine if they are equivalent. Its default implementation compares the locations of the objects in memory. If the objects are in the same location, then they are the same object, and therefore equivalent. But typically, objects are considered equivalent if some or all of their fields have the same values (for example, if two Book objects have the same title and author, we could consider the Books equal). You can override equals() to define two Courses to be equivalent if their fields match.
If you override equals() using code generation, then you should also override hashCode() to ensure that equivalent objects hash to the same value (you’ll discuss hashes in data structures).
Conceptual Knowledge: The Java Invariant of Overriding equals() and hashCode() Methods
The specifications of the Java programming language state that both the equals() and hashCode() methods should be overridden. Additionally, the same fields considered for equality in the equals() method should be used to generate the object’s hashCode().
A hashCode is a numerical value for an Object that is generated from the Object’s fields. The algorithm to create the hashCode is such that every distinct object should hash to a different value and objects that are the same should hash to the same value. This means that every Object can be identified by its unique hashCode.
The default implementation of hashCode() uses the Object’s memory location to generate the hashCode(). But if we want to provide a different definition of equality to our objects, we should also ensure that those same objects have the same hashCode!
To ensure this, Eclipse requires us to override equals() and hashCode() at the same time using the same fields.
To override equals() and hashCode(), open the Course in the Editor. Right click in the Editor, and select Source > Generate hashCode() and equals(). In the resulting dialog, check which fields are required for object equality. For Course, all fields should be selected. Be sure to check Generate method comments. You can optionally change the insertion point so that the new methods are positioned at the bottom of the getter methods. Click Generate.
See that the methods were generated in your source. Don’t forget to update the comments to be specific to Course and then save your changes!
Best Practice: Comment equals() and hashCode()!
You should always comment overridden methods to provide details about the specific of the override and why the method was overridden. This will help others (and future you) understand the customizations of the overridden method for your class.
Implement toString()
Eclipse code generator also has the option of overriding toString(). Check the fields that are relevant for toString(), generate method comments, then click Generate.
However, you’ll end up using the output of toString() later in the project when writing Course information to a file, which means that you require a specific type of output as per the requirements. Use the following toString() code.
/**
* Returns a comma separated value String of all Course fields.
* @return String representation of Course
*/@OverridepublicStringtoString(){if(meetingDays.equals("A")){returnname+","+title+","+section+","+credits+","+instructorId+","+meetingDays;}returnname+","+title+","+section+","+credits+","+instructorId+","+meetingDays+","+startTime+","+endTime;}
You’ve made a lot of changes to your Course class by adding fields, methods, and constructors. Before moving on to the next portion of the Guided Project, complete the following tasks:
Comment the Course class.
Make sure that all fields, methods, and constructors are commented.
Commit and push the Course class to GitHub. Remember to use a meaningful commit message describing how you have changed the code. For example, “[Implementation] Added fields, getters/setters, and Object methods to Course.”.
Guided Task: Encapsulation and Reducing Redundancy
Code is redundant where there are multiple locations with the same or almost the same statements. By reducing redundancy, you can put functionality in one location in our programs and access that functionality through calling methods. That means if there’s a bug, you have one place to look to find and fix the problem!
Learning Outcomes
Use private methods to hide setters from a client
Call setter methods from a constructor
Follow one path of construction paradigm
Best Practice: Reducing Redundancy
You learned in your introductory programming course that methods or procedures were created to provide a name for a related set of program statements that are likely to be used over and over. While there is a small cost to every method call, the gains in code readability, understandability, and maintenance are critical for teams of software developers working on large programming projects. When working on code, you should remove redundant code as often as you can!
Further Encapsulate the Course.name Field
A Course shouldn’t ever change its name (CSC 216 will always be CSC 216). You want to have a method that will set the name field and do all the appropriate checks that the value is correct when constructing the object, but you don’t want to allow clients of your code to change the field after construction. You need to make the setName() method private to prevent clients from modifying the field. There will be a warning about not using the setName() method, but you can ignore it for now. You’ll use the method shortly.
Best Practice: Use the Outline
The Outline View provides a list of the fields and methods in the class that is currently in focus in the Editor. You can sort the members listed in the outline view in several different ways. By clicking a member in the outline view, the editor will be updated to display that element. It’s a lot faster to click a method in the outline view than to scroll through your code. Give it a try to select setName().
Change the setName() method to use the private access identifier as shown below. Notice the outline view changes the icon next to the method from a green circle (indicating public) to a red square (indicating private).
/**
* Set's the Course's name.
* @param name the name to set
*/privatevoidsetName(Stringname){this.name=name;}
Setter methods are convenient for wrapping up all the (potentially complex) functionality for checking that a value is valid for storage in a class’s field. However, you may not want to expose that functionality to a client. private methods provide a mechanism for wrapping up functionality in a method that can be used internally within the class as a helper method for setting the values for a given field.
Reducing Redundancy in the Course Constructors
The Eclipse constructor generation code assigns the parameters to the fields directly. However, there is error checking that you must complete on the constructor’s parameters before assigning them to fields (you’ll actually write the error checking code in the next step). The parameters of all setter methods for Course should also be checked. For example, a Course should not be created if the name is null or an empty string.
To reduce duplicate code, you can place the code to check for invalid parameters in each of the setter methods and call the setter methods from the constructor.
Conceptual Knowledge: Reducing Redundancy
Another consideration when designing classes is that if you do have code that checks if a field receives a correct value, you should have that code in only one place. A setter method for a field may be appropriate. Other methods that change the field can call the setter with the appropriate value and handle an incorrect value as needed for the calling method’s goals.
The constructor calls each of the setters for the fields, as shown below.
/**
* Constructs a Course object with values for all fields.
* @param name name of Course
* @param title title of Course
* @param section section of Course
* @param credits credit hours for Course
* @param instructorId instructor's unity id
* @param meetingDays meeting days for Course as series of chars
* @param startTime start time for Course
* @param endTime end time for Course
*/publicCourse(Stringname,Stringtitle,Stringsection,intcredits,StringinstructorId,StringmeetingDays,intstartTime,intendTime){setName(name);setTitle(title);setSection(section);setCredits(credits);setInstructorId(instructorId);setMeetingDays(meetingDays);setStartTime(startTime);setEndTime(endTime);}/**
* Creates a Course with the given name, title, section, credits, instructorId, and meetingDays for
* courses that are arranged.
* @param name name of Course
* @param title title of Course
* @param section section of Course
* @param credits credit hours for Course
* @param instructorId instructor's unity id
* @param meetingDays meeting days for Course as series of chars
*/publicCourse(Stringname,Stringtitle,Stringsection,intcredits,StringinstructorId,StringmeetingDays){setName(name);setTitle(title);setSection(section);setCredits(credits);setInstructorId(instructorId);setMeetingDays(meetingDays);}
However, you still have some redundant code in your constructors - the calls to setName(), setTitle(), setSection(), setCredits(), setInstructorId(), and setMeetingDays(). You want to follow the paradigm of one path of construction to further reduce redundant code.
Conceptual Knowledge: One Path of Construction
When writing a class, you want to minimize redundancy. That is one reason why you want to create setters for checking that the values stored in a field are appropriate for the class. Another way to minimize redundancy is to create one path of construction for a class. In Java, a class can have multiple constructors with different sets of parameters to initialize a class. One constructor should call another constructor (with appropriate default values for any missing fields) so that there is the main construction code in only one location. And that code can call (or delegate to) the appropriate setters for setting field values so that the code for checking for a valid field value is in only one location.
You call another constructor in a Java class using the keyword this().
By having one location for functionality, it’s much easier to maintain your code. If there’s a bug, you only have one place that you need to look to find and fix it!
You will update your Course constructor with six parameters so that it calls the Course constructor with all eight parameters. You’ll pass in default parameters for the course times. We’ll use 0 to represent the idea of a course with no meeting time.
/**
* Creates a Course with the given name, title, section, credits, instructorId, and meetingDays for
* courses that are arranged.
* @param name name of Course
* @param title title of Course
* @param section section of Course
* @param credits credit hours for Course
* @param instructorId instructor's unity id
* @param meetingDays meeting days for Course as series of chars
*/publicCourse(Stringname,Stringtitle,Stringsection,intcredits,StringinstructorId,StringmeetingDays){this(name,title,section,credits,instructorId,meetingDays,0,0);}
You’ve made a lot of changes to your Course class by reducing redundancy. Before moving on to the next portion of the Guided Project, complete the following tasks:
Update the comments on the Course class.
Make sure that all fields, methods, and constructors are commented.
Commit and push the Course class to GitHub. Remember to use a meaningful commit message describing how you have changed the code. For example, “[Refactoring] Reduced redundancy in Course”.
Guided Task: A Very Brief Introduction to Unit Testing
Larger systems with many interacting classes only have one main method. If you can’t start a program from a main method, you are unable to run black box tests on the program.
You could wait until all of the classes are implemented to start testing, but that is very hard debug - and there are always bugs! Instead, you should test as you go along to ensure that your program’s smaller units (classes and methods) work the way they are supposed to. For the WolfScheduler project, the teaching staff are providing a suite of JUnit tests that you can run to ensure that your implementation meets the specified WolfScheduler requirements and design.
Learning Outcomes
Create a test/ source folder
Create a package in the test/ folder
Create a class for testing
Run JUnit tests
Best Practice: Unit Testing
During the test phase of development, you execute the program against a variety of inputs to evaluate if the program generates the correct or expected output. You can determine the expected output by reading the requirements as well as the design documentation and APIs that define what the smallest program units (methods) should do.
Unit testing focuses on testing the smallest units of a program: methods and classes. The premise is that by ensuring the smallest portions of the programs work correctly, you can have increased confidence that the whole program will work correctly.
When you unit test for CSC 216/217, you are actually doing a combination of unit testing and integration testing. Integration testing is when you test things in combination. You will test methods in combination (e.g., adding something to a list and then removing it) and you will test methods that call other methods (e.g., a constructor calls a setter method). However, your focus is on the very local functionality of that method or combinations of methods within a class and not the program as a whole.
Tool: JUnit
JUnit is an automated unit testing tool for Java. JUnit is a set of libraries that include classes, like TestCase, and methods, like assertEquals(), that help with unit testing Java code.
For Guided Project 1, all unit tests will be provided so you will be able to assess the quality of your work as you progress through the tasks. Later, you will be expected to write your own tests and your implementation will be exercised against a hidden suite of teaching staff tests that will assess how well you meet the teaching staff design on the project!
Reference: Creating Packages and Classes in Eclipse
You’ll start by setting up the WolfScheduler project for testing so you can test Course.
Create a new source folder, called test, which will contain all JUnit test files. Right click on the project and select New > Source Folder. Enter test in the Folder name text field.
Create a new package in the test source folder named edu.ncsu.csc216.wolf_scheduler.course. Notice that the package is the same as Course’s package.
Create a new class in the test source folder edu.ncsu.csc216.wolf_scheduler.course package named CourseTest. Notice you use the same class name (Course) and append the word Test to the end of the class name.
You need to add the JUnit library to your project. Right click on the WolfScheduler project and select Properties. In the left menu, select Java Build Path. In the right portion of the screen, select the Libraries tab. Select Classpath and then click Add Library…. Select JUnit and click Next. Select JUnit 5 in the drop down and click Finish followed by Apply and Close. When you are done, the JUnit 5 library will be added to the project’s build path and the compiler errors should be resolved.
With JUnit 5, we can parameterize our tests with information stored in comma-separated value (CSV) files. Right-click on the test folder and select New > Folder. Enter resources in the Folder name text field. Click Finish. This will create a package icon that appears empty, but it is a folder!
Download and move the following files into your test/resources/ folder. You can also create new files with a .csv extension and copy in the text.
Run the provided JUnit tests by right clicking on the test source folder and selecting Run As > JUnit Test. By running the command on the test source folder, you will run all the test classes in the folder. You can also run all the tests in a package or a single test class by right clicking on that artifact.
After running the tests, several will be passing (testCourseStringStringStringIntStringString(), testEqualsObject(), testHashCode(), toString()), but several will be failing! That’s Ok! You’ll fix that soon!
You’ve added your CourseTest class. Before moving on to the next portion of the Guided Project, complete the following tasks:
Add CourseTest, course-meeting-days-and-times-valid.csv, course-meeting-days-and-times-invalid.csv to the index.
Commit and push the CourseTest class, the CVS files, and any Course changes to GitHub. Remember to use a meaningful commit message describing how you have changed the code. For example, “[Test] Added CourseTest and related files”.
Guided Task: Run Static Analysis Tools
There are many potential things that can be done incorrectly when programming large applications. The compiler reveals syntactical mistakes in your code. However, once code compiles there can be other potential problems. Program analysis tools, like static analysis tools, can help you identify problems with your code early. Static analysis is the evaluation of your source code without execution. This is beneficial since errors may be found in places that are not executed during automated testing, but a limitation is that the analysis is complex and some notifications may not be important or really a problem.
You will use three different static analysis tools to find potential problems in your code. The reason for three static analysis tools is that each tool has an area of specialization. Those three tools are:
SpotBugs: SpotBugs finds potential security vulnerabilities and dangerous code patterns that may lead to runtime exceptions.
PMD: PMD can find some code problems and style issues. You’ll focus on using PMD for finding style problems and incorrect JUnit tests.
CheckStyle: CheckStyle finds problems with style issues: naming, indentation, commenting, etc.
You will use static analysis tools to help remember good style and prevent careless mistakes while developing software. These tools will need to be configured for each project and they may be set up to run automatically with every save.
Any un-addressable static analysis notifications, unless otherwise allowed by the teaching staff, will result in a deduction from your style grade. Make sure that you are using the CheckStyle and PMD configuration files for this class (details are in CSC216 Environment Setup - PMD and CheckStyle Settings). If you are not using the provided style files, PMD and CheckStyle will generate an abundance of additional notifications on items that the teaching staff does not require you to fix.
SpotBugs
You will need to configure SpotBugs for each project and then run using the instructions below. You should remove all notifications that are labeled Scariest and Scary before submitting your code. This will prevent potential silly mistakes.
Configure SpotBugs
Do the following to configure SpotBugs for your project:
Right click on the project and select Properties > SpotBugs.
Check the boxes labeled Enable project specific settings, Run automatically, and (also on full build).
In the Reporter Configuration tab, move the slider so that the text 9 (Scary) is listed below the slider.
Leave the default selection of Reported (visible) bug categories (Bad practice, Correctness, Performance, Dodgy code, and Multithreaded correctness).
Select the drop down boxes next to Scariest and Scary to contain Error.
Click Apply and Close.
Click OK on the pop-up box about Full SpotBugs build required.
Run SpotBugs
Do the following to run SpotBugs on your project:
Right click on the project and select SpotBugs > Find Bugs.
The counts of the number of SpotBugs notifications will be in parentheses next to each project, package, and class. Details about the notifications will be listed in the Problems view of the Java perspective.
Selecting a notification in the Problems view will take you to the associated line of code in the editor. Selecting the notification icon in the left gutter of the editor will open a Bug Info view that will provide additional details about the notification.
Run PMD
Do the following to run PMD on your project:
Right click on the project and select PMD > Check Code. This will run PMD on your code.
After PMD runs, you will automatically switch to the PMD perspective, where you can browse the PMD results. (Switch back to the Java perspective by clicking the Java perspective icon in the upper right of the workbench.) You can also look at the PMD results in the Problems view of the Java perspective.
Best Practice: Use the Java Perspective
Running PMD will switch you to the PMD perspective, which doesn’t have some of the helpful tools for developing Java code. Switch back to the Java Perspective after running PMD!
If you want PMD to run every time your program compiles, right click on your project and select Properties > PMD > Enable PMD.
Run CheckStyle
Do the following to run CheckStyle on your project:
Right click on the project and select CheckStyle > Check Code with CheckStyle. This will run CheckStyle on your code.
CheckStyle alerts are listed in the Problems view of the Java perspective.
If you want CheckStyle to run every time your program compiles, right click on your project and select Properties > CheckStyle > CheckStyle active for this project.
Removing all of the CheckStyle notifications will help you as you write and eventually Generate Javadoc.
Static Analysis Tool Meta Data
Each static analysis tool will create its own set of meta data or configuration files in your project folder. These files MAY be pushed to GitHub.
If you would prefer to leave them off, you can add the files to the .gitignore. If you’re using the Eclipse Git Staging view, right click on the file and select Ignore. This will add the file to .gitignore and add the .gitignore file to the list of unstaged changes.
Best Practice: Using .gitignore
We recommend adding the configuration files associated with the static analysis tools to your project’s .gitignore file. This is especially helpful when working on teams!
You can edit .gitignore directly by opening the Project Explorer, selecting the filter icon, and unchecking the .* resources item. The dot-files will then display in the Project Explorer. Switch back to the Package Explorer when developing.
Also note that your .project file also changed. The .project file contains information about the configuration of your project and has been updated to reflect your use of static analysis tools. DO NOT IGNORE THE .project FILE!! If the .project file is not pushed to GitHub, the teaching staff will not be able to import your project into Eclipse for manual grading of the black box tests! You will receive a deduction for any manual configuration of your project required to grade your work. The same applies to the .classpath file even though it was not modified by using static analysis tools.
Ensure that you have removed all static analysis notifications from your code. If you have questions about a notification, take a look at the online documentation for each tool.
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Commit and push the Course class to GitHub. Remember to use a meaningful commit message describing how you have changed the code. For example, “[Static Analysis] Removed static analysis notifications”.
Independent Task: Finish Course
Using the WolfSchedulerrequirements along with the provided JUnit tests, you will finish implementing the Course class to pass the tests!
There are seven setter methods (you’ll see why it is seven shortly) and one getter method that need to be fully implemented to pass all of the provided JUnit tests. The implementation details below provide additional details about how to implement the WolfScheduler requirements and will help you pass the test cases. Note that when a method receive an invalid value in a parameter, you want to throw an IllegalArgumentException. That way the client can catch and handle the error.
You’ll walk through an example of implementing setName() before leaving the other methods for independent implementation. The other methods will review prerequisite concepts.
Learning Outcomes
Write setter methods for Course to pass the unit tests and meet the requirements.
Best Practice: Test Driven Development (TDD)
Test driven development is a software development practice where you write your test cases for a class using the requirements BEFORE you write the code for the class. The failing tests then drive the development of your class! You keep fixing your code until your tests pass.
The teaching staff has provided a set of unit tests that you must now pass!
In TDD practice, you may not always write your test cases first. Iterating between code and test (and not putting off testing) is critical for writing high quality code!
Constants
In the Guided Project 1 design there are several constants listed for the Course class. Constants are used to provide names, and therefore meaning, to values in our programs that do not change. Add the constant values to Course and then use the constant values where appropriate. Remember that constants are static and final. These constants should also be private as indicated by the red square in the UML diagram. See the Conceptual Knowledge block about UML Class Diagrams and Notation for more information.
Comment your constants using Javadoc!
Conceptual Knowledge: UML Class Diagrams and Notation
We use a Unified Modeling Language (UML) Class Diagram to provide a visual representation of our design. Class diagram show the classes, their fields, and methods. Class diagrams also show the connection or relationships between classes. By interpreting the icons and information in the class diagram we can create a code skeleton of the classes, fields, and methods for our program. We’ll use the information in the Course class box to create our constants.
The constants are in the middle portion of the Course box, which is labeled as part of the edu.ncsu.csc216.wolf_scheduler.course package. The constants are noted in the left portion of each line by the red outline of a square labeled with an ‘S’ in the upper left corner and ‘F’ in the upper right corner. We interpret the red outline of a square as the keyword private. The ‘S’ represents static. The ‘F’ represents final. The name of the constant is next; an example is SECTION_LENGTH. This if followed by the type of int and the value it is assigned of 3. We would interpret the line in code as private static final int SECTION_LENGTH = 3;.
The other icons in the class diagram tell use different things. The outlines of circles, squares, and triangles represent fields. The solid circle, squares, and triangles represent methods. Green circles are public, red squares are private, and yellow triangles are protected. ‘S’ is static, ‘F’ is final, ‘C’ is a constructor.
The lines connecting the classes represent fields. We’ll discuss those in further detail when we get to the WolfScheduler class.
Provided Tests
The teaching staff tests have been provided for you. The provided tests should NOT change in any way. Be careful that you are not using Eclipse Quick Fixes that might change the provided tests.
The automated grading system (this will be explained in much more detail later) will overwrite your copy of the tests with the teaching staff version. If you changed the provided tests then your code may not compile when you look at the automated grading system feedback. For now, try to avoid changing the provided tests. There will be information about resolving any issues when you reach the section of the Guided Project on automated grading.
Implement Course Methods
Now that we have our constants, fields, and constructors set up, we need to implement the Course class’ methods to meet the requirements defined in Use Case 1: Load Course Catalog. While Use Case 1 defines the information about how to load a file with course schedule information, the list of what makes a course record in the file invalid helps us determine the implementation of the Course object. We want to define Course so that if any of these invalid field characteristics are passed in as parameters, we can let the client (or class that is working with Course objects) know that there is a problem. We will notify the clients’ of Course about the problem by throwing exceptions!
Conceptual Knowledge: Exceptions
Exceptions are objects that we can throw to let a calling method (or the client of your method) know about a problem. By throwing an exception to a client when your method can no longer execute, you are letting the client determine how to handle the problem in that class’ context.
When we want to throw an exception, we must first construct it and then use the throw keyword to send the exception to the caller (see line 9 in the code snippet below). When we see the throw keyword, the flow of control switches from the current method to the caller (if an exception is throw in setInfo(), the flow of control would switch back to line 19 and immediately transfer to the first enclosing catch block). If the caller doesn’t catch or handle the exception, then the exception propagates to the the next caller. If an exception is never caught, then program execution will end.
/**
* An example of throwing an exception.
* @param info additional information for the instance of a class
* @throws IllegalArgumentException when the parameter info is null or an empty string
*/publicvoidsetInfo(Stringinfo){if(/* something is wrong with info */){// error path - info is null or an empty stringthrownewIllegalArgumentException();// construct the exception and throw}// continue with happy path}/**
* The calling class - it's calling a method that may thrown an exception
*/publicvoidprocessInfo(){// note that this method could be in another class, including a test class!try{// wrap around code that may throw an exceptionStrings=setInfo();// continue with happy path}catch(IllegalArgumentExceptione){// handle exception}}
Best practice is to catch and handle exceptions rather than let them escape and stop program execution, but you don’t want to wrap all your code in try/catch blocks. There’s a balance to working with exceptions. If your code is trying things that are likely to throw an exception, then wrap in a try/catch. Otherwise, if the exception is unchecked, meaning that you are not required to handle it for compilation, you could chose not to explicitly try to catch the exception.
You should always provide documentation about exceptions that your method might throw to help clients identify if they should catch a possible exception. Use the @throws tag to document. You can also describe error paths in the main comment body.
A course record is invalid if one of more of the following are true:
the course name is null or an empty string
the course name is contains less than 5 characters or more than 8 characters
the course name does not contain one space between letter characters and number characters
the course name does not start with 1 to 4 letter characters
the course name does not end with three digit characters
Note: Testing Basics
By describing valid and invalid names, we are starting to identify tests to ensure that our method is implemented correctly. We have a test method that considers valid names (e.g., testSetNameValid()) and a test method that considers invalid names (e.g., testSetNameInvalid()).
The requirements describe the characteristics for a course name. For example, “CSC 216” would be valid, but “CSC216” would not be because there is no space. Other valid and invalid examples are provided below. The provided test cases provide additional examples.
“E 11” - length less than 5, incorrect number of digits
“HESFQ 101” - prefix has too many characters
“101” - length less than 5, no leading characters, no space
“CSC 21” - less than 3 digits
“CSC\t216” - no space
Reminder: Characters and the `Character` Class
When processing the name, you will need to look at individual characters. You can get a character from a String at index i using the String.charAt(i) method.
Once you have a character, you can use the methods of the Character class to determine if the character is a letter (Character.isLetter(c)) or a digit (Character.isDigit(c)).
For the implementation of setName() you will need to compare directly with the ` ` (space) character. Using Character.isWhitespace(c) considers more than just a space! Luckily, we have a test to ensure that you’re not using Character.isWhitespace(c).
There are several checks that we need to consider to implement setName(). You should consider them one or two at a time rather than in one big if statement. The one big if statement is harder to debug! Also, by considering one or two checks at a time, we can customize our error messages. The pseudocode below shows one possible implementation of setName().
Implementation Considerations
Constants
MIN_NAME_LENGTH
MAX_NAME_LENGTH
MIN_LETTER_COUNT
MAX_LETTER_COUNT
DIGIT_COUNT
Exception Messages
“Invalid course name.”
if name is null
if length is incorrect
if name structure is incorrect
Testing
Run your tests and make sure that testSetNameValid() and testSetNameInvalid() passes.
All previously passing tests should continue to test
/**
* Sets the Course's name. If the name is null, has a length less than 5 or more than 8,
* does not contain a space between letter characters and number characters, has less than 1
* or more than 4 letter characters, and not exactly three trailing digit characters, an
* IllegalArgumentException is thrown.
* @param name the name to set
* @throws IllegalArgumentException if the name parameter is invalid
*/privatevoidsetName(Stringname){//Throw exception if the name is nullifnameisnullthrownewIllegalArgumentException("Invalid course name.");//Throw exception if the name is an empty string//Throw exception if the name contains less than 5 character or greater than 8 charactersifname'slengthislessthanMIN_LENGTHorname'slengthismorethanMAX_LENGTHthrownewIllegalArgumentException("Invalid course name.");//Check for pattern of L[LLL] NNNinitializecounterfornumberoflettersinitializecounterfornumberofdigitsinitializebooleanflagforfindingaspacetofalseforeachcharacterinnameifaspacehasnotyetbeenfoundifthecharacteratiisaletterincrementthelettercounterelseifthecharacteratiisaspacespaceflagshouldbesettotrueotherwisethrownewIllegalArgumentException("Invalid course name.");elseifaspaceisfoundifthecharacterisadigitincrementthedigitcounterotherwisethrownewIllegalArgumentException("Invalid course name.");//Check that the number of letters is correctiflettercounterislessthanoneormorethan4thrownewIllegalArgumentException("Invalid course name.");//Check that the number of digits is correctifdigitcounterisnot3thrownewIllegalArgumentException("Invalid course name.");setthis.name(field)toname(parameter)}
Both conditionals check if title is null using ==, which is appropriate when doing null checks.
Conditional 1 checks for equality of the title with the empty string. Since Strings are objects, we must use .equals() to check for equality. Additionally, we put the string literal of "" first because a string literal will never be null and the convention is put string literals first. If you swap the order, you’ll get a CheckStyle notification!
Conditional 2 checks the length of the title string. If the length is zero, the string is empty.
Implementation Considerations
Exception Messages
“Invalid title.” - if title is null or empty string
Testing
Run your tests and make sure that testSetTitleValid() and testSetTitleInvalid() passes.
All previously passing tests should continue to test
A course record is invalid if one of more of the following are true:
the section number is NOT exactly three digits
Section numbers can start with a leading zero, so section is a String. That means that section cannot be null. Remember that you can use the Character class methods to help you with determining if a character is a digit.
Implementation Considerations
Constants
SECTION_LENGTH
Exception Messages
“Invalid section.”
if section is null or not three characters
if any of section’s three characters are not digits
Testing
Run your tests and make sure that testSetSectionValid() and testSetSectionInvalid() passes.
All previously passing tests should continue to test
A course record is invalid if one of more of the following are true:
the credit hours are not a number
the credit hours are less than 1 or greater than 5
You actually don’t need a specific test here to check if the credit hours are not a number. The parameter type of int guarantees that the value will be a number. But later on, you will need to handle this case.
Implementation Considerations
Constants
MIN_CREDITS
MAX_CREDITS
Exception Messages
“Invalid credits.” - if credits is out of bounds
Testing
Run your tests and make sure that testSetCreditsValid() and testSetCreditsInvalid() passes.
All previously passing tests should continue to test
A course record is invalid if one of more of the following are true:
meeting days consist of any characters other than ‘M’, ‘T’, ‘W’, ‘H’, ‘F’, or ‘A’
meeting days have a duplicate character
if ‘A’ is in the meeting days list, it must be the only character
the start time is not between 0000 and 2359 an invalid military time
the end time is not between 0000 and 2359 or an invalid military time
the end time is less than the start time (i.e., no overnight classes)
a start time and/or end time is listed when meeting days is ‘A’
We’re going to interpret the requirements in the following way. If the meeting days for the Course is arranged (e.g., "A"), then startTime and endTime should be 0. If the meeting days for Course are not arranged, then there MUST be a valid startTime and endTime. We could create separate setters for each, but there are too many dependencies and considerations. Instead, we will create a single method to ensure the changes are consistent with all three values.
Before you start your method implementation, do the following:
Remove setMeetingDays()
Remove setStartTime()
Remove setEndTime()
Create a method public void setMeetingDaysAndTime(String meetingDays, int startTime, int endTime)
Comment the method to describe the functionality.
Update the Course constructor to use the new method
Uncomment the method in CourseTest called testSetMeetingDaysAndTimesValid()
Uncomment the method in CourseTest called testSetMeetingDaysAndTimesInvalid()
This method can have lots of decision points, so pseudocode is provided to get you started on the problem. There are multiple ways to implement this method; you do not need to implement is as per the pseudocode.
To make the method easier to understand and to reduce redundancy, you may want to create helper methods to break out some of the work. If you create helper methods, they must be private!
Implementation Considerations
Hint
When working with military time as an integer, you can get the hours by dividing by 100 and the minutes by mod-ing by 100.
Constants
UPPER_HOUR
UPPER_MINUTE
Exception Messages
“Invalid meeting days and times.”
if meeting days is null, empty, or contains invalid characters
if an arranged class has non-zero start or end times
if start time is an incorrect time
if end time is an incorrect time
if end time is less than start time
Comments
Comment the method, including all parameters.
Testing
Run your tests and make sure that testSetMeetingDaysAndTimeValid() and testSetMeetingDaysAndTimeInvalid() passes.
All previously passing tests should continue to test
// TODO - Add Javadoc here!publicvoidsetMeetingDaysAndTime(StringmeetingDays,intstartTime,intendTime){ifmeetingDaysisnulloremptystringthrowIAE("Invalid meeting days and times.")// IAE = IllegalArgumentExceptionifmeetingDaysis"A"// ArrangedifstartTimeisNOT0ORendTimeisNOT0throwIAE("Invalid meeting days and times.")setmeetingDaystotheparameter;startTimeandendTimeto0otherwise//not arrangedcreatelocalvariablestoholdthecountsforeachweekdayletterforallcharactersinmeetingDaysincrementweekdaylettercounterifyoufindtheletter// this will take several lines of code ifanyinvalidlettersthrowIAE("Invalid meeting days and times.")ifanyweekdaylettercountsaremorethanone// checks for duplicatesthrowIAE("Invalid meeting days and times.")breakapartstartTimeandendTimeintohoursandminutes//several lines of codeifstartHourisinvalid// not between 0 and 23, inclusivethrowIAE("Invalid meeting days and times.")ifstartMinisinvalid// not between 0 and 59, inclusivethrowIAE("Invalid meeting days and times.")ifendHourisinvalid// not between 0 and 23, inclusivethrowIAE("Invalid meeting days and times.")ifendMinisinvalid// not between 0 and 59, inclusivethrowIAE("Invalid meeting days and times.")//everything is valid and works together!setfieldsformeetingDays,startTime,andendTime}
Implement getMeetingString()
UC 3: View Course Information and UC 9: Display Final Schedule both discuss providing meeting information as a string in standard time format rather than in military format. The getMeetingString() method provides this functionality.
To get started, do the following:
Create a method public String getMeetingString()
Uncomment the method in CourseTest called testGetMeetingString()
To make the method easier to understand and to reduce redundancy, you may want to create helper methods to break out some of the work. If you create helper methods, they must be private! The teaching staff solution has a helper method, getTimeString(int time), to convert military time to standard time.
Implementation Considerations
Hints
When working with military time as an integer, you can get the hours by dividing by 100 and the minutes by mod-ing by 100.
If the hour is military time is over twelve, subtract 12 to convert to standard time hours (PM).
If the hour in military time is 0, the hour is 12 (AM) in standard time.
Minutes less than ten will need a leading ‘0’ character concatenated to the minute value when building the String to return.
Comments
Comment the method, including all parameters.
Testing
Run your tests and make sure that testGetMeetingString() passes.
All previously passing tests should continue to test
Testing and Debugging
At this time, all tests should be passing with a green bar! (You should make sure that you didn’t change the teaching staff tests. You can copy of the test file again to ensure there are no changes.)
If you have failing tests, that’s ok! The tests are there to help you find problems in your code. By working incrementally and using test-driven development, that keeps us from implementing too much of our project before finding fundamental problems. If the Course class doesn’t work, a list of Courses won’t work.
There are several ways to debug your code. You can trace your code via pencil and paper. Start with the failing test and keep track of how the variables change as you move through each statement. You can also add print statements to your code and execute each test to see how the values change. If you want to run a single test, you can right click on that test in the JUnit results view and select Run. That will help you narrow down to a single problem.
Best Practice: Ask for Help on Piazza or in Office Hours
For additional help, ask a question on Piazza or come to office hours. When asking a question, please do the following:
Push all your source and test code to GitHub, even if it’s not working. The teaching staff can take a look.
Provide a link to your GitHub repository in the question.
Provide the name of the test method that is failing and provide the specific line number of the failure.
Provide the expected and actual output from the test.
Provide a explanation of why you think the test is failing.
A course record is invalid if one or more of the following are true:
an item is missing
a course with the same same name and section
You do a basic check for null or empty string items that helps with the “an item is missing” statement, but there is more that will need to be done there when you handle processing the lines of Course information from an input file.
The other item that a record is invalid if “a course with the same same name and section” isn’t something that you can implement at this point. A Course only knows about itself; not about other Courses in the system. You’ll implement that statement when you start working with a collection of Courses.
Conceptual Knowledge: Object Design
A requirement describes what the system must do. Use cases describe the system’s behaviors from the context of a user of the system. When the user interacts with a system, they are not interacting with a specific object, instead, they are interacting with several objects. When designing systems from requirements, a single object may describe the behavior of part of a single use case or parts of many use cases. Part of system design is identifying the objects that are necessary in the system and the relationships between those objects.
You’ve made a lot of changes to your Course class by implementing the required functionality to pass the provided test cases. Before moving on to the next portion of the Guided Project, complete the following tasks:
Make sure that all constants, fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Fix test failures. DO NOT CHANGE THE PROVIDED TEST CODE!
Commit and push your code changes with a meaningful commit message. For example, “[Implementation][Test] Completed Course functionality”.
Guided Task: Code Editing Tools
Now that you have seen the power of source code generation and quick fix, you can explore some other Eclipse code editing tools that may be useful as you develop code.
Many IDEs have tools that help with common tasks and some of those tools can help you generate code. You learned how to write these code statements manually in your introductory programming class so that you would fully understand their purpose. Now, you can use Eclipse’s code generation tools to help in writing common statements. A benefit of using code generation tool, besides saving time, is that the code is more likely to be correct. However, you should always test the generated code so make sure that it meets the requirements of the larger program.
Best Practice: Code Formatting and Style
Formatting of code is also important. Well formatted code is easier to read and maintain. Additionally, it easier for others to read - especially if you need help. Most companies have a set of style guidelines that they expect software engineers to follow when working on a code base to ensure that everyone can read and understand code quickly. That is why we have a set of style guidelines for code in CSC216 and enforce those guidelines through the static analysis tool, CheckStyle.
Auto-Complete
Eclipse can fix your simple errors. It can also guess what you want to type via the auto-complete tool. The auto-complete tool is especially handy for remembering class, method, and variable names especially when your code has long or complicated variable or method names.
To use auto-complete, start typing a variable or method name and press Ctrl + Space or Cmd + Space. The auto-complete context menu provides several options for code generation. Select the best option and press Enter to generate the code.
Hint: Any time you want to finish something that you think Eclipse can guess, it’s worth pressing Ctrl + Space or Cmd + Space to see if it does. Eclipse will find things like method names, variables in the current scope, class names, or even make suggestions for a variable you’re declaring! The idea is that you don’t have to remember everything about the program you’re writing. You can select the options with the mouse, but it’s probably faster to use the arrow keys and the Enter key.
Code Templates
Eclipse provides several code templates for commonly used methods and statements.
To use a code template, start typing in the code template keyword and then press Ctrl+Space.
Some keywords for code templates are:
Typing main will create a main method
Typing sysout will create a System.out.println() statement.
There are several for code templates that will create for-loops.
The foreach template will search upward from your current location and find the nearest Iterable type and create an enhanced for-loop over it.
Typing /** before a method will generate Javadoc, inferring the parameters to your method. You still have to write the comments that describes what the method does and provide information about the parameters to the method. Don’t write junk! We do read your comments to ensure they describe your code.
Code Formatter
Additionally, Eclipse can help you keep your code well formatted (which is nice for avoiding loss of style points on your programming assignments).
Once you know that your program works, take a look at your code. Methods should be indented within classes, and statements should be indented within methods. If your code does not follow proper indentation let Eclipse help. Press Ctrl + Shift + F (or go to the Source > Format in the menu; Mac users try Cmd + Shift + F).
Customizing Code Templates and the Formatter
If you want to add/edit code templates for the way your code gets formatted, select Window > Preferences (Mac Users select Eclipse > Preferences).
For code templates, select Java > Editor > Templates. Alternatively, start typing “Code templates” in the top text search box to find the menu.
For the formatter, select Java > Code Style > Formatter. Alternatively, start typing “Formatter” in the top text search box to find the menu.
Eclipse defaults to using tabs for indentation. If you want to switch the tab key to enter spaces rather than a tab, do so by editing the style defined in the Formatter properties menu.
Guided Task: Eclipse Quick Fix Tool
The Eclipse IDE knows how to identify and fix lots of common errors. By storing patterns of common errors and their solutions, Eclipse can generate a “Quick Fix” for your problems. Quick Fixes are errors and warnings that have a lightbulb on the icon.
Learning Outcomes
Correct a compiler error using an Eclipse Quick Fix
Best Practice: Quick Fix for Error-Driven Development
One way to use Quick Fix is to first write your code ignoring all of your declarations. If you want to call a class you haven’t written yet, just use it as if it were already created, then apply quick fix to create your class declaration. It takes some getting used to, but this technique enables you to write code very quickly in the end because you don’t have to write so much from scratch. Some developers like to write code in this way because they can simply write what they’re currently thinking about without having to stop and deal with details that can be Quick-fixed. You might find that this way of writing code is not so different from writing pseudo code first!
We’re going to start working on implementing Use Case 1: Load Course Catalog where we can process a course catalog file. We typically call working with files I/O or input/output. Since I/O is so different from our Course object, we will create a new package for our I/O implementation.
To demonstrate the use of Quick Fix, test-driven development, and Eclipse code generation, we’ll start by creating our package in the **test** folder. Follow the steps below:
Create a new package called edu.ncsu.csc216.wolf_scheduler.io in the test folder of the WolfScheduler project.
Create a new Java class called CourseRecordIOTest in the edu.ncsu.csc216.wolf_scheduler.io package of the test source directory of the WolfScheduler project.
Note: Automatic Compile and Rebuilding/Cleaning a Project
If your Problem view fails to display the error, select Project > Build Automatically from the top level menu. It should be checked on. You can also clean your project by selecting Project > Clean. Cleaning your project removes all *.class files and rebuilds your project.*
Compiler Errors in Eclipse
There’s a compiler error in CourseRecordIOTest! You can tell this in several ways:
There is a red squiggly/dotted line under the CourseRecordIO type in CourseRecordIOTest.
There is a little red X along the left-hand side of the editor in the gutter. The icon also contains a light bulb, which lets us know that Eclipse has a Quick Fix.
The Problems view displays the errors.
The compiler errors state that CourseRecordIO cannot be resolved. We need a CourseRecordIO type for working with reading and writing files that store information about the Courses offered in a given semester! Therefore, you’re going to have to create your own CourseRecordIO class.
Quick Fix
To use Quick Fix, put your cursor anywhere inside the red squiggly of one of the compiler errors. The command for Quick Fix is Ctrl + 1 (that’s the number ONE; Mac users, try Cmd +1). This opens the quick-fix context menu. There are many possible options. Select the first option, to create class CourseRecordIO, by pressing Enter. Note that you can see a preview of what the created class will look like.
Create CourseRecordIO Class
Eclipse opens a wizard for creating a new class. Eclipse will generate what it expects the source folder, package, and class names to be from the context of how the class is used in the source code.
We do need to make one change to the source folder. Change WolfScheduler/test to WolfScheduler/src. The CourseRecordIO class is part of the implementation and therefore belongs in the src folder. The rest of the information should be correct. You can also select the option for Generate comments.
Create CourseRecordIO Methods
You still have compiler errors, even after creating the CourseRecordIO class. That is because CourseRecordIOTest is calling CourseRecordIO methods that don’t yet exist.
Use Quick Fix to generate the readCourseRecords() and writeCourseRecords() methods. The methods that are generated will be static methods. We’ll discuss why in the next section.
CourseRecordIO Method Exceptions
The Eclipse Quick Fix tool can be very powerful, but it is limited to very common and easily-solvable problems. Quick Fix will work only on compiler errors, not logic errors since Quick Fix doesn’t know your requirements. Quick Fix may not always fix your code exactly how you want it, so it’s not a good idea to completely rely on this feature. Common fixes include adding an import, creating a class/method/variable, public/private access, fixing parameters in a method, and so on.
You still have compiler errors, even after generating the CourseRecordIO methods. In this case, Quick Fix will not help us. The suggested fixes are to remove the catch clause or replace catch with throws from the test methods. However, you know that CourseRecordIO will deal with file input and output. That means you need to handle common file I/O exceptions like FileNotFoundException and IOException. We will pass those exceptions on to the client (which right now is the test class) so they can handle as appropriate for reading from or writing to a file in their context. That means the test class should NOT be changed - the expected behavior will be to catch the exceptions that CourseRecordIO will throw.
Conceptual Knowledge: Checked Exceptions
Both FileNotFoundException and IOException are checked exceptions. That means we need to add information to the method headers in CourseRecordIO to state that we will be throwing these exceptions. Javadoc should describe when these exceptions are thrown so the client knows to handle them. Because they are checked exception, the client must handle the exceptions in some way. This can mean that we catch them using try/catch or we could throw to the client’s client using a throws clause on the method header.
You can add throws clauses to the CourseRecordIO method headers, which will show that the methods throw exceptions and will also let our code compile. The finished (and commented) methods should look like the code below. You may copy in the provided comments.
(*Note: You will need to import java.io.FileNotFoundException and java.io.IOException for CourseRecordIO to compile. That’s a great use of Quick Fix!)
packageedu.ncsu.csc216.wolf_scheduler.io;importjava.io.FileNotFoundException;importjava.io.IOException;importjava.util.ArrayList;importjava.util.List;importedu.ncsu.csc216.wolf_scheduler.course.Course;/**
* Reads Course records from text files. Writes a set of CourseRecords to a file.
*
* @author Sarah Heckman
*/publicclassCourseRecordIO{/**
* Reads course records from a file and generates a list of valid Courses. Any invalid
* Courses are ignored. If the file to read cannot be found or the permissions are incorrect
* a File NotFoundException is thrown.
* @param fileName file to read Course records from
* @return a list of valid Courses
* @throws FileNotFoundException if the file cannot be found or read
*/publicstaticArrayList<Course>readCourseRecords(StringfileName)throwsFileNotFoundException{// TODO Auto-generated method stubreturnnull;}/**
* Writes the given list of Courses to
* @param fileName file to write schedule of Courses to
* @param courses list of Courses to write
* @throws IOException if cannot write to file
*/publicstaticvoidwriteCourseRecords(StringfileName,ArrayList<Course>courses)throwsIOException{// TODO Auto-generated method stub}}
Renaming Identifiers
You may have noticed that some of the parameter names in the code above are different from the names that Eclipse generated for you. When Eclipse generates methods, it has to guess on the names that you might find useful for a given type. For example, writeCourseRecords()’s first parameter was named string because Eclipse had no context of what that parameter is. That parameter should really be called fileName.
In Java, identifiers are the names that you give classes, methods, and variables. That means they can be used all over your code. Renaming an identifier can take a while to complete, but the compiler will let you know where there are problems. However, renaming an identifier is a very common problem to have and Eclipse has a tool that helps with renaming the selected identifier and all other uses of that identifier in the entire project! Renaming an identifier is a refactoring, which is a small change to the code that improves readability or design but doesn’t change the functionality of the code.
To rename an identifier, first select the identifier and then do one of the following to refactor:
Right click on the identifier and select Refactor > Rename.
Press Alt+Shift+R.
An editing box will open. Make your edit and press Enter. The name will change in all locations, including the Javadoc!
You’ve added several new files. Before moving on to the next portion of the Guided Project, complete the following tasks:
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Add new files to the index. Commit and push your code changes with a meaningful commit message.For example, “[Implementation][Test] Added CourseRecordIO and CourseRecordIOTest”.
Guided Task: Testing with Files
Now that you have the skeleton of CourseRecordIO, you’ll implement it to pass the tests in CourseRecordIOTest. As part of that, you’ll discover how to test file I/O.
Learning Outcomes
Set up WolfScheduler for testing file I/O
Best Practice: Testing File I/O
Testing File Input
When testing file input functionality, you need files that test the valid input and files that test invalid input. The files may be broken down so that one distinct record is in each file or so that several valid/invalid records are in the same file. Distinct records are a little easier to debug since you can narrow down to a specific failing file, but finding a failing line in a larger file can be done by printing out each line and including print messages to help you debug and narrow to a specific problem line. Later this can be more easily accomplished with a debugger.
If you know what the file input is, you can check that the input generated the correct objects with the correct state for each object. That information can be part of your test case.
Testing File Output
When testing file output, you will be generating a file. You could read the generated file back in and compare with a String of expected results. Another option is to create a file that contains the expected results and compare that the two files are the same. CourseRecordIOTest does the latter and reads in both files and compares them line by line.
Changing Expected Files
If there is a possibility that a test or incorrect code under test may manipulate a file that is used for input in another test, you should make sure that you “reset” the test file before using in another test. To do that, you can maintain a file that contains the starting information and copy it into your test file before each test. CourseRecordIO.setUp() is an example of copying a starter file to an input file.
Setting up for File I/O Testing
You should keep your Eclipse projects organized. Folders keep random files from cluttering the top level of the project. When working with file I/O functionality, you need a place to store your test files. These test files are different than the *.csv files that we used for CourseTest. These test files are files that a user would provide as actual input to the program; they are not the contents of a parameterized test. Therefore, we’ll store these test files in a different location.
Create an Eclipse folder to store your test files by right clicking on the WolfScheduler project and selecting New > Folder. Name the folder test-files.
Creating Test Files
Right click on the link to download each of the provided files and copy the downloaded file into your Eclipse project test-files directory. DO NOT copy the text from the file into an empty text file. That will cause issues with encoding.
There are several ways to save files to your computer. The files may automatically be downloaded to a folder on your computer or you may be able to save your files directly to the test-files folder in your Eclipse project in your local copy of the Git repository.
If you save your files to a downloads or some other folder that is NOT your Eclipse project, you can drag and drop the files into the test-files folder of your project in Eclipse.
If you save your files to the Eclipse project on your computer’s file system, the files will not be immediately available inside of Eclipse. You must refresh your Eclipse project for the files to be available inside of Eclipse. To refresh, right click on your Eclipse project and select Refresh. This will sync your Eclipse workspace information with the actual files in the file system.
You can also interact with your Eclipse file structure from the file system. Your Eclipse project folder structure is in your local git repository. This is usually a path like ~/git/csc216-GP1-001-001/WolfScheduler. You can interact with the files in your Eclipse project directly! However, be careful. There are certain files in this directory structure that are important for working with Eclipse. The .project and .classpath files are used by Eclipse to know that your project is an Eclipse project and the information to help build and work with your project. Please don’t edit these files unless you have merge conflicts. If you do have merge conflicts in these files, we strongly recommend that you work with the teaching staff to resolve the issue!
When programming complex systems, most software engineers don’t start from scratch. Instead, they take advantage of existing libraries of common functionalities when writing their programs. You work with libraries that are part of the Java Development Kit (JDK) all the time when writing programs. For example, any time you print to the console using System.out.println(), you are working with the System class’ PrintStream object named out, which has a method println() that provides the functionality for writing to standard output stream (or the console).
When reading from files, you use the Scanner class (which is actually in the java.util package because Scanner can scan other things than files). The CourseRecordIO.readCourseRecords() method will use the Scanner class to read the file. readCourseRecords() receives a parameter specifying the name of the file that will be read.
Scanner has several constructors that accept parameters specifying different resources that Scanner can parse. When working with Files, Scanner requires a parameter of type File or FileInputStream. For CourseRecordIO we will use FileInputStream so that the files are processed as bytes rather than characters. You will use the Scanner as you normally would. Passing in a String type will lead to a logic error because Scanners can also be used to parse Strings!
The Scanner(File) constructor may throw a checkedFileNotFoundException if there is a problem reading the file. One way to handle is that to surround the code with a try/catch block. However, that would hide the error within the CourseRecordIO class. Instead, the CourseRecordIO.readCourseRecords() throws a FileNotFoundException out to the client. That means you do not need to check for the FileNotFoundException in CourseRecordIO.
Copy the code below into your CourseRecordIO.readCourseRecords() method. This code should replace the entire method body. Delete the TODO comment. It’s no longer needed because you are implementing the method.
You will have compiler errors under the Scanner and FileInputStream classes. Use Eclipse’s quick fix tool to import those classes (Ctrl+1 or Cmd+1).
When passed a File as a parameter, a Scanner is a wrapper for an operating system resource called a file handle. Every time you create a FileScanner, you use one of the OS’s file handles. File handles are a limited resource, so when you use a file handle, you should make sure it’s released when you’re done. This is done through the Scanner.close() method.
Scanner has a set of hasNext*() methods that let you check to see if the next value is of the type that you want. Once you know that the next token is the value you want, you can use a next*() method to get the value of the given type. From the requirements for UC 1: Load Course Catalog, you know that the Course records are stored in a text file with one Course record per line. You will want to get each line from the file and then create a Course or skip that line if the record is not a valid Course.
The following code sets up the loop for processing each line. You will use a private helper method, readCourse(), to process each Course. You can use Eclipse’s Quick Fix to create the readCourse() helper method.
When writing object-oriented code, you may find that public methods (those that a client may want to use) can quickly grow complex. When that happens, it is useful to put some of the functionality in a private method. By creating smaller methods, the code is easier to maintain. The cost of calling another method is very small compared to readable code in large systems.
File Output
You can use the PrintStream class (the same type as System.out) to write to a File by providing a File object as an actual parameter. CourseRecordIO.writeCourseRecords() writes information about each Course to the file using a PrintStream as shown below.
Like Scanner, PrintStreams should also be closed when they are done. Note that you are also using the Course.toString() method to create the comma-separated output in the same format as the input!
Copy the following code into your CourseRecordsIO.writeCourseRecords() method.
The Java Collections Framework (JCF) provides functionality for working with collections of objects. In CSC216, you will focus on the List collection type.
Conceptual Knowledge: ArrayList and Arrays
ArrayList provides List functionality using the underlying storage of an array. That means ArrayList is a class that wraps additional functionality around an array so that it is easier for clients (like us) to work with it.
Like an array, ArrayList provides functionality for adding elements, removing elements, getting elements, finding the size, and several other useful methods. The table below shows the relationship between arrays and ArrayList.
Function
Array
ArrayList
Constructing
String [] array = new String[size]
ArrayList<String> array = new ArrayList<String>()
Adding an element
array[0] = "apple";
array.add(0, "apple");
Removing an element
array[0] = null;
array.remove("apple");
Getting an element
return array[0];
return array.get(0);
Size
array.length
array.size()
CourseRecordIO.readCourseRecords() returns an ArrayList of Courses. That means the method should construct an empty ArrayList and add each Course to the ArrayList. You can also plan for the error handling that needs to occur when there’s an invalid Course record. If there’s a problem when constructing a Course object, an IllegalArgumentException is thrown. You can catch the exception and skip adding that line to the coursesArrayList. Additionally, you need to handle the requirement in [UC1, Processing Course Information] where a course record is invalid if the name and section is the same as another course in the list. That means only the first course in an input file with a given name, title, and section is added to the list. Others are ignored.
Copy this code into your CourseRecordsIO.readCourseRecords() method. You should overwrite the code we copied in earlier. The comment provide additional details about what is happening in the class.
publicstaticArrayList<Course>readCourseRecords(StringfileName)throwsFileNotFoundException{ScannerfileReader=newScanner(newFileInputStream(fileName));//Create a file scanner to read the fileArrayList<Course>courses=newArrayList<Course>();//Create an empty array of Course objectswhile(fileReader.hasNextLine()){//While we have more lines in the filetry{//Attempt to do the following//Read the line, process it in readCourse, and get the object//If trying to construct a Course in readCourse() results in an exception, flow of control will transfer to the catch block, belowCoursecourse=readCourse(fileReader.nextLine());//Create a flag to see if the newly created Course is a duplicate of something already in the list booleanduplicate=false;//Look at all the courses in our listfor(inti=0;i<courses.size();i++){//Get the course at index iCoursecurrent=courses.get(i);//Check if the name and section are the sameif(course.getName().equals(current.getName())&&course.getSection().equals(current.getSection())){//It's a duplicate!duplicate=true;break;//We can break out of the loop, no need to continue searching}}//If the course is NOT a duplicateif(!duplicate){courses.add(course);//Add to the ArrayList!}//Otherwise ignore}catch(IllegalArgumentExceptione){//The line is invalid b/c we couldn't create a course, skip it!}}//Close the Scanner b/c we're responsible with our file handlesfileReader.close();//Return the ArrayList with all the courses we read!returncourses;}
Note that in the CourseRecordIO.writeCourseRecords() code in the last section, you already used an ArrayList. for loops are an excellent tool for working with the elements of an ArrayList. Instead of using the array’s length, you get the ArrayList’s size(). You can use the get() method to get the element at index i.
You’ve added functionality to CourseRecordIO, but it’s not yet complete. Before moving on to the next portion of the Guided Project, complete the following tasks:
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Commit and push your code changes with a meaningful commit message. Try writing your own commit message this push. We suggest that you label the commit with the “[Implementation]” tag to describe the type of commit. This will help future you (and the teaching staff or any future teammates) understand what you did in this commit.
Independent Task: Finish CourseRecordIO
Now that we have the basic outline of CourseRecordIO you’ll implement the readCourse() method to process a single line from the input file and create an instance of the Course class. A correct implementation will pass the tests in CourseRecordIOTest. As part of our implementation of readCourse(), we’ll discuss how to test file I/O.
Learning Outcomes
Implement CourseRecordIO.readCourse()
Implement CourseRecordIO.readCourse()
To finish the CourseRecordIO class and pass all of the CourseRecordIOTests, you need to implement CourseRecordIO.readCourse(). The method receives a String which is a line from the input file. An example line might be:
CSC 116,Intro to Programming - Java,001,3,jdyoung2,MW,0910,1100
or
CSC 216,Software Development Fundamentals,601,3,jctetter,A
You can use a Scanner to process the String parameter. You must break the line up into tokens by using the comma character (e.g., ',') as a delimiter. A delimiter is a string that is used to separate or break apart a larger string. The default delimiter for Scanner is white space. You can change the delimiter using the Scanner.useDelimiter() method. After updating the delimiter, any call to Scanner.next() will return the token before the next delimiter. So for our example,
CSC 216,Software Development Fundamentals,601,3,jctetter,A
the first call to Scanner.next() would return a string "CSC 216", the second call to Scanner.next() would return a string "Software Development Fundamentals", etc. When reading the values for credits, startTime, or endTime, you can use Scanner.nextInt().
Once you break the line into tokens, you can construct a Course object and return it from the method.
There are two places where code that you work with may lead to an exception for invalid values in the input string:
NoSuchElementException or InputMismatchException: if you receive these when processing the input, catch them and throw an IllegalArgumentException to the caller (which in this case is CourseRecordIO.readCourseRecords()).
IllegalArgumentException: when constructing a Course with invalid values, the Course class will throw an IllegalArgumentException. In this case DO NOT catch the exception! Instead, let the exception propagate to the caller (which in this case is CourseRecordIO.readCourseRecords())
Use the fields and constructors of the Course class to help you determine what type each token should be. The tokens will always be in the order of:
If the meeting days string is NOT “A”, then you’ll need to read in the last two tokens. If there are any additional tokens after the expected last one, an IllegalArgumentException should be thrown.
The high level structure of the method is provided in the pseudocode, below. You do not have to implement the method this way, but it will be useful for getting started.
//TODO - Add Javadoc here!privatestaticCoursereadCourse(Stringline){constructScannertoprocessthelineparametersetthedelimiterto","try{readintokensforname,title,section,credits,instructorId,andmeetingDaysandstoreinlocalvariablesifmeetingDaysis"A"iftherearemoretokensthrowIAEotherwisereturnanewlyconstructedCourseobjectotherwisereadintokensforstartTimeandendTimeiftherearemoretokensthrowIAEreturnanewlyconstructedCourseobject}catchanyexceptionsandthrowanewIllegalArgumentException//Remember to close the scanner on all possible paths out of the method - that's not in the pseudocode}
Make sure that all your tests are passing before continuing with the Guided Project!
File I/O Testing Overview
Let’s take a quick look at the CourseRecordIOTest class to discuss how we are testing CourseRecordIO.
There are three test methods and a private helper method.
Two of the test methods test the CourseRecordIO.readCourseRecords() method. CourseRecordIOTest.testReadValidCourseRecords() considers an input file with valid lines and one duplicate. When the file is processed there should be 13 Course objects in the ArrayList and their toString() output should match the constants validCourse* at the top of the test class.
CourseRecordIOTest.testReadInvalidCourseRecords() considers an input file with invalid lines. Each line is invalid in a different way as per the requirements. The README.txt file describes why each line is invalid and may be useful for finding any bugs in your code.
The last test method, CourseRecordIOTest.testWriteCourseRecords() tests the CourseRecordIO.writeCourseRecords() method. The private helper method checkFiles() will check that the actual output file matches the expected output file. The expected output file was provided. You can review the tests in more detail as testing is covered in the class. You will see file I/O again and these tests will be a useful reference for future projects!
Troubleshooting
There are several places where you may run into trouble when implementing the CourseRecordIO.readCourse() method. The troubleshooting section provides an overview of how to resolve the challenges. You should also refer to your course text book on the chapters related to File I/O, post questions to Piazza, and attend office hours to help with the implementation of this method!
Unable to Reset Files
You may run into a java.lang.AssertionError: Unable to reset files when executing the CourseRecordIOTest.setUp() method. This method is resetting course_records.txt with the starting values needed for the tests. Other tests in the system may overwrite course_records.txt, so the starter_course_records.txt file is used to rest the original file. There are several possibilities that you should investigate if you run into this error:
Close all of the files that may be open in Eclipse or in your file system. If the file is being processed by another application, the JVM may not be able to open it.
Close the Scanner in all possible paths in CourseRecordIO. If you do not release the Scanner on the file, the file may still be considered in use for a time, even if the test stopped execution.
Make sure that all files use the *.txt extension.
Restart Eclipse. This will release any file handles that are attached to open files.
Resource Leak
If you receive a CheckStyle notification about a resource leak on your Scanner, make sure that you separate any program statement Scanner s = new Scanner(nextLine).useDelimiter(","); into two program statements. Using the two methods chained together leads to CheckStyle misunderstanding the flow of the methods.
We’ve finished CourseRecordIO. Before moving on to the next portion of the Guided Project, complete the following tasks:
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Fix test failures.
Commit and push your code changes with a meaningful commit message. Label your commit with “[Implementation]” for future you!
Independent Task: Implement WolfScheduler
The last model class that you need to implement is WolfScheduler. WolfScheduler reads in and stores as a list all of the Course records stored in a file (UC 1: Load Course Catalog). Additionally, WolfScheduler creates a schedule for the current user (a student) and provides functionality for naming the schedule ([UC 2: Rename Schedule]), adding a Course to the schedule ([UC 4: Add Course to Schedule]), removing a Course from the schedule ([UC 5: Remove Course from Schedule]), resetting the schedule ([Use Case 8: Reset Schedule]). You will start the work needed to pass the appropriate error messages to the graphical user interface (GUI). However, at this time you will NOT be implementing the conflict functionality.
Note that there are several steps that you will need to complete to fully implement WolfScheduler. The tasks in this section are a review of prerequisite materials or of topics covered earlier in the Guided Project. Where appropriate, we will provide links to earlier sections that will help as you complete the tasks in this portion of the Guided Project!
Use Eclipse’s tools to set up the classes for WolfSchedulerTest and WolfScheduler. Follow these steps:
Create a new package called edu.ncsu.csc216.wolf_scheduler.scheduler in the test folder of the WolfScheduler project.
Create a new Java class called WolfSchedulerTest in the edu.ncsu.csc216.wolf_scheduler.scheduler package of the test source directory of the WolfScheduler project.
Using the Quick Fix tool, create a new Java class called WolfScheduler in the edu.ncsu.csc216.wolf_scheduler.scheduler package of the src source directory of the WolfScheduler project.
Using the Quick Fix tool, create WolfScheduler’s public methods from the compiler errors in WolfSchedulerTest. You can adjust the parameter names as you work on implementing each method in later steps. You may also need to update the return type if the generated return type is Object.
Update the return types of getCourseCatalog(), getScheduledCourses(), and getFullScheduledCourses()String[][].
Update the return types of addCourseToSchedule() and removeCourseFromSchedule() to boolean.
Update the return type of getCourseFromCatalog() to Course.
Update the return type of getScheduleTitle() to String.
The WolfScheduler class has three pieces of state. Write the code to create the three fields for WolfScheduler (there’s no Quick Fix for this step). You will need to import both ArrayList and Course using Quick Fix.
a course catalog (an ArrayList of Courses) called catalog
a schedule (an ArrayList of Courses) called schedule
a schedule title (a String) called title
Implement WolfScheduler Constructor and Methods
There are several WolfScheduler methods that you must implement so that WolfScheduler will pass the tests in WolfSchedulerTest. The sections below provide implementation details for each of the methods. You’ll pass more tests as you implement each of the methods.
Best Practice: Comment as you Code
Writing comments as you write your code can help you understand the problem that you’re working on and ensures that future you will understand you code in a few months. Writing the comments first can help you better understand what a method should do. Before implementing each method, write the Javadoc comments for the method. Then implement. Finally, revisit the comments so that they match your implementation.
Don’t put off commenting until the end of the project!
The WolfScheduler constructor has a parameter, which is the filename for the course records that should be read in and stored in the ArrayList of Courses. You will likely need to change the name of the parameter from the one created via Quick Fix. You can use the rename refactoring to rename both the parameter and the Javadoc name.
The constructor should complete the following tasks:
construct an empty ArrayList to hold the schedule in the schedule field
set the title field to the default “My Schedule” as described in the requirements
try to add the Course objects from the input file to the catalog. An IllegalArgumentException should be thrown with the message of “Cannot find file.” if CourseRecordIO.readCourseRecords() throws an exception.
To add the Course objects from the input file to the catalog, you will need to work with the CourseRecordIO class. Note that all the CourseRecordIO methods are static. You work with them through the class itself (e.g., CourseRecordIO.readCourseRecords(filename);). You can see examples of working with the CourseRecordIO class in CourseRecordIOTest!
After implementing the constructor, you can run your tests, but they should all fail. The constructor test depends on other methods in WolfScheduler working properly. You may have to revisit the constructor to debug issues.
Best Practice: Exception Messages
All Exception classes have a constructor with a String parameter for a message. That message is passed with the Exception and may be retrieved through a call to getMessage(). By providing error messages as part of an Exception, the error functionality is tied to the model. Then any front end classes will use the same error message!
Implement getCourseCatalog()
getCourseCatalog() returns a 2D String array of the catalog. This array is used in the GUI to create the table of course catalog information. There is a row for each Course and three columns for name, section, and title. If there are no Courses in the catalog, an empty 2D String array is returned.
The test testGetCourseCatalog() should pass after you implement this method.
Implement getScheduledCourses()
getScheduledCourses() returns a 2D String array of the schedule. This array is used in the GUI to create the table of course catalog information. There is a row for each Course and three columns for name, section, and title. If there are no Courses in the schedule, an empty 2D String array is returned.
Implement getFullScheduledCourses()
getFullScheduledCourses() returns a 2D String array of the schedule with all information. This array is used in the GUI to create the table of course catalog information. There is a row for each Course and six columns for name, section, title, credits, instructorId, and the meeting days string (e.g., getMeetingString(). If there are no Courses in the schedule, an empty 2D String array is returned.
getCourseFromCatalog() has two parameters: a course name and section. Since a Course in the catalog is distinct by name and section we can use those two items to find a Course. If the Course with the given name and section does not exist in the catalog, return null.
You will iterate through all the elements of the catalogArrayList and try to find a Course with the given name and section. Once you find it, you can return it immediately. If you reach the end of the loop without finding the Course, return null.
The following tests should pass after you implement this method:
testGetCourseCatalog()
testGetCourseFromCatalog()
Implement addCourseToSchedule()
addCourseToSchedule() returns true if the given Course (represented by the name and section) meets the following criteria: 1) the course exists in the catalog and 2) the course is successfully added to the student’s schedule.
If the Course is not in the catalog, it cannot be added to the schedule and the method returns false.
A Course with the same name as another Course already in the schedule cannot be added to the schedule. This means a student can’t be enrolled in both Section 001 and 002 of CSC216 at the same time. If a Course with the same name is already in the schedule, an IllegalArgumentException with the error message of “You are already enrolled in <course name>” is thrown.
The following tests should pass after you implement this method:
testGetCourseCatalog()
testGetCourseFromCatalog()
testGetScheduledCourses()
testGetFullScheduledCourses()
testAddCourse()
Implement removeCourseFromSchedule()
removeCourseFromSchedule() returns true if the given Course (represented by the name and section) can be removed from the student’s schedule. The Course should be removed.
The method returns false if the Course isn’t in the schedule.
The following tests should pass after you implement this method:
testGetCourseCatalog()
testGetCourseFromCatalog()
testGetScheduledCourses()
testGetFullScheduledCourses()
testAddCourse()
testRemoveCourseFromSchedule()
Implement resetSchedule()
resetSchedule() creates a empty ArrayList for the schedule.
The following tests should pass after you implement this method:
testGetCourseCatalog()
testGetCourseFromCatalog()
testGetScheduledCourses()
testGetFullScheduledCourses()
testAddCourse()
testRemoveCourseFromSchedule()
testResetSchedule()
Implement getScheduleTitle() and setScheduleTitle()
getScheduleTitle() returns the schedule title. setScheduleTitle() throws an IllegalArgumentException if the title is null with an error message of “Title cannot be null.”
The following tests should pass after you implement this method:
testGetCourseCatalog()
testGetCourseFromCatalog()
testGetScheduledCourses()
testGetFullScheduledCourses()
testAddCourse()
testRemoveCourseFromSchedule()
testResetSchedule()
testSetScheduleTitle()
Implement exportSchedule()
exportSchedule() receives a String parameter that is the filename where the student’s schedule will be saved to. You will use the CourseRecordIO.writeCourseRecords() to export the file. If CourseRecordIO.writeCourseRecords() throws an IOException, catch it and throw a new IllegalArgumentException with the message of “The file cannot be saved.”.
Note that all the CourseRecordIO methods are static. You work with them through the class itself (e.g., CourseRecordIO.readCourseRecords(filename);). You can see examples of working with the CourseRecordIO class in CourseRecordIOTest!
All tests should pass after you implement this method:
Before moving on to the next portion of the Guided Project, complete the following tasks:
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Fix test failures.
Commit and push your code changes with a meaningful commit message. Label your commit with “[Implementation]” for future you!
Guided Task: WolfSchedulerGUI
Up to this point, you’ve been working with model classes. Model classes represent the data and business logic of an application and are part of the Model-View-Controller (MVC) design pattern. MVC provides a separation of the major areas of a large application.
Model: data and business logic
View: view that the users interact with
Controller: connection between the view and the model
In Java, you can create Graphical User Interfaces (GUIs) using the Swing libraries. The Swing libraries provide both the view and the controller aspects of MVC. In Swing, the view is the look and feel of the application and includes form elements and how they are organized. The controller is the functionality that connects to the underlying model when the user interacts with the GUI by clicking a button or interacts with other form elements.
You are still learning all of the skills needed to write a GUI. You’ll have the opportunity to write your own (small) GUI later this semester. Until then, the teaching staff will provide you with GUIs for your Guided Projects, projects, and labs.
You’ll need to create a package for the GUI class, which we typically put in a package with ui in the package name. This helps separate the user interface or GUI code from the model portion of the program.
Create edu.ncsu.csc216.wolf_scheduler.ui Package
Create a new package called edu.ncsu.csc216.wolf_scheduler.ui in the src folder of the WolfScheduler project.
Create WolfSchedulerGUI Class
Create a new Java class called WolfSchedulerGUI in the edu.ncsu.csc216.wolf_scheduler.ui package of the src source directory of the WolfScheduler project.
Before moving on to the next portion of the Guided Project, complete the following tasks:
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Fix test failures.
Commit and push your code changes with a meaningful commit message. Label your commit with “[GUI]” for future you!
Guided Task: Run System Tests
Now that you’re completed the model classes with passing unit tests and added the GUI (view/controller) to the WolfScheduler program, you can run your system level tests. These tests ensure that the entire system is working end to end. When we run system tests, we consider the system as a whole where the internal details are not know. The system is like a closed box, where we put input in and get actual results out. If the actual results of execution are different than what was expected, the test fails and we can start debugging. The expected results of execution are defined in the requirements!
The main() method for the WolfScheduler project is in the GUI class. To run the WolfScheduler project, right click on WolfSchedulerGUI and select Run As > Java Application. The program will start and a FileChooser dialog box titled “Load Course Catalog” will open. Select test-files/course-records.txt and click Select.
The main GUI will load. There are four sections:
Course Catalog: The catalog contains 13 classes in the same order as the file. (Note: the file has 14 lines, one line (line 4) is invalid.) The name, section, and title for each are displayed.
Actions: The actions section has a set of buttons and the Schedule Title text field.
My Schedule: Lists the courses that have been added to the schedule.
Course Details: Displays the details of any course selected in the Course Catalog table.
Running System Tests
The teaching staff have created 13 system tests for your to run. Each test should be independent, but some may have preconditions that rely on earlier tests. For example, the test to add a course to the schedule relies on the test to load a valid course catalog. To save a little time, the tests are ordered so that you can go through them all without closing the GUI and starting over for each test, starting with test 2. However, if you run into issues, you should close the GUI and start over on the next test (which is what the teaching staff will do during grading).
Download the System Test Plan document as a Word document by select File > Download > Microsoft Word in Google Drive. Create a new folder in your WolfScheduler project called project_docs by right clicking on WolfScheduler and selecting New > Folder. Save your system test plan as a Word document (*.doc or *.docx) in the project_docs folder. As you run each test, report the results of execution in the system test plan in the actual results column. DO NOT record, “Passed”, “Failed”, or copy the expected results. Instead, describe the results in your own words.
Make sure all your system tests pass! However, if you run out of time and are unable to fix all the bugs in your project, report the actual results of execution - EVEN IF THEY ARE FAILING! You’ll earn some points on the system test portion of the grading rubric for reporting actual, failing results.
Before moving on to the next portion of the Guided Project, complete the following tasks:
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Fix test failures.
Commit and push your code changes with a meaningful commit message. Label your commit with “[Test]” for future you!
Guided Task: Generate Javadoc
Commenting your code is important! Comments describe what the code is supposed to do. At a minimum, you should comment all your classes, fields, and methods. All methods should be Javadoc-ed, including methods that were automatically generated by Eclipse. This is because you should provide the client of your code details about why you overrode the method and the specific overridden functionality! When working with CSC 216/217 projects, you should delete any automatically generated non-Javadoc documentation and replace it with Javadoc appropriate for the overridden method.
Java provides the Javadoc tool to generate a set of web pages that display the comments for your code.
Every time you use the Java API, you are using web pages generated with the Javadoc tool. The Java developers have done an excellent job of commenting the class files in the Java SDK. You can use the API to find classes and methods that may help you implement the program you’re writing. You should return the favor to those who may use your code (as well future you) by writing comments. By writing the comments in a Javadoc format, you’ll be able to generate an API for your code that will benefit future programmers.
Learning Outcomes
Generate Javadoc API pages for WolfScheduler
Check your Comments
Double check your Javadoc comments. Are they correct? Do they describe the functionality of methods, fields, and classes? Are you missing any key items? The CheckStyle static analysis tool can help you find missing items in your Javadoc. Make sure that you have resolved all notifications from CheckStyle.
The teaching staff (actual people) do read your comments to ensure they are appropriate for the classes, fields, and methods. Please do not include anything that would be considered inappropriate (e.g., no bad language; no disparaging remarks about you, your classmates, the teaching staff, or the class; no threatening or offensive text).
Configure and Run Javadoc for your Project
After you have written your Javadoc in your class files, you can run the Javadoc tool on your code by selecting Project > Generate Javadoc.
In the resulting dialog, you must configure the Javadoc command if you have not already done so. To configure Javadoc, click Configure. Browse to the location where you installed your JDK. The javadoc tool is in the bin folder.
Additionally, you can select which projects/packages/classes/and files you want to run Javadoc on. Most of the time, you only want to generate Javadoc for classes in the source folder since they represent a deliverable that others may use.
Check the option to Use standard doclet. Browse for your project as the destination and then append a doc folder to the end of the path.
Javadoc Checks!
Make sure that you browse to your current project’s location! The Javadoc tool remembers locations from other projects. You don’t want to generate your Javadoc in the wrong location!
Always verify that your generated Javadoc is in your project AND that the generated Javadoc is pushed to the correct GitHub repository via the browser!
Click Next and Next. On the third menu, ensure that the JRE Source Compatibility is set to 11.
Click Finish to generate the Javadoc. If your project does not already have a doc folder, Eclipse prompts you to create the destination folder. Click Yes To All.
When the Javadoc has been created, refresh your project listing in the Package Explorer if it has not been done automatically. You can do this by selecting your project and pressing F5. Your project should now show a doc directory.
Fix Javadoc Errors and Warnings
Console output is generated when creating Javadoc. You should scroll back through the output to see if there are any errors or warnings that you need to fix. An example warning about a missing description on a @throws tag is shown. Fix any warnings or errors and regenerate.
Check Generated Javadoc Pages
You can view your Javadoc by opening the index.html within the doc directory. When you open an *.html file inside Eclipse, a browser will be opened in the Eclipse editor showing your newly generated Javadoc.
Any time you update your Javadoc, you should regenerate the HTML files!
Always select the entire src/ folder. Sometimes the javadoc tool will only run on the currently selected class! Double check that the correct files were created! Push all the changes to GitHub.
Before moving on to the next portion of the Guided Project, complete the following tasks:
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Fix test failures.
Commit and push your code changes with a meaningful commit message. Label your commit with “[Documentation]” for future you!
Guided Task: Continuous Integration and Automated Grading
A common software engineering practice is continuous integration. That means developers are continuously committing/pushing their code to version control and evaluating the code by running tests and other analysis tools against their code. The evaluation of the code is typically completed by a continuous integration system, which is a software system that will automatically build, test, run analysis tools (like static analysis), and deploy software. Since this is what the teaching staff does when evaluating your work, we can use a continuous integration system to evaluate your work every time you push to GitHub. That means you should be able to estimate your grade on an assignment from the feedback from Jenkins, the continuous integration server we use for CSC216. On future assignments, Jenkins will also run hidden teaching staff tests and provide you feedback on how well you meet the teaching staff’s design!
Learning Outcomes
Utilize results of a continuous integration system to improve program quality
Check Jenkins Results
You can access the Guided Project and Project Jenkins via https://csc216-jenkins.csc.ncsu.edu/jenkins/. (Labs will have their own Jenkins servers.) When you log in, your Jenkins job will be listed using the pattern of GP1-<repo-name>. If you don’t see a project, then email the teaching staff!
The Jenkins build for Guided Project 1 will copy the provided teaching staff test cases into your project, overwriting the ones that you were provided. This is to make sure that you didn’t accidentally modify the teaching staff tests through an Eclipse QuickFix. If you did modify a test, that will likely lead to a red X on Jenkins, which means code didn’t compile.
If your code doesn’t compile, you can see the compiler errors in the Console Output for the build. To get to the Console Output, select your Jenkins job, the latest build in the box to the lower left, then select Console Output from the left menu. Scroll down to see the compiler errors. Use those to resolve any issues.
One way to resolve any issues with the provided tests is to recopy them into your local test files. The provided tests are:
All assignments have a rubric that you can use to estimate your grade. Use the Jenkins feedback and your black box test results to estimate your grade for Guided Project 1.
Use the feedback from Jenkins to make changes to your code. Any time you make a change, push to GitHub and check the Jenkins results.
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Fix test failures.
Commit and push your code changes with a meaningful commit message. Label your commit with “[Implementation]” for future you!
Check Jenkins results for a green check! Fix any Jenkins issues.
Guided Task: Conclusion
A common software engineering practice is continuous integration. That means developers are continuously committing/pushing their code to version control and evaluating the code by running tests and other analysis tools against their code. The evaluation of the code is typically completed by a continuous integration system, which is a software system that will automatically build, test, run analysis tools (like static analysis), and deploy software. Since this is what the teaching staff does when evaluating your work, we can use a continuous integration system to evaluate your work every time you push to GitHub. That means you should be able to estimate your grade on an assignment from the feedback from Jenkins, the continuous integration server we use for CSC216. On future assignments, Jenkins will also run hidden teaching staff tests and provide you feedback on how well you meet the teaching staff’s design!
Check Jenkins Results
You can access the Guided Project and Project Jenkins via https://csc216-jenkins.csc.ncsu.edu/jenkins/. (Labs will have their own Jenkins servers.) When you log in, your Jenkins job will be listed using the pattern of GP1-<repository-name>. If you don’t see a project, then email the teaching staff!
The Jenkins build for Guided Project 1 will copy the provided teaching staff test cases into your project, overwriting the ones that you were provided. This is to make sure that you don’t accidentally modify the teaching staff tests through an Eclipse QuickFix. If you did modify a test, that will likely lead to a red X on Jenkins, which means code didn’t compile. You’ll want to use the console output (as described in the Jenkins tutorial) on the build to help you find the compiler error.
Estimate Your Grade Against the Rubric
All assignments have a rubric that you can use to estimate your grade. Use the Jenkins feedback and your black box test results to estimate the grade for Guided Project 1.
Final Tasks
Before you complete your final submission to GitHub, you should ensure the following:
Make sure that all code is pushed to GitHub by the assignment deadline. There are deductions for any late work up to 48 hours.
Grading Rubric
Your Wolf Scheduler Guided Project 1 will be evaluated on the following items:
Overall Rubric
Phase
Grade Item
Points
Details
Total Points
0
Deductions
Grade Item
Points
Details
Misnamed file or incorrect project structure
-5
Incorrect names of files or incorrect project structure. This can include problems when importing the project to Eclipse for acceptance testing, incorrect location of the system test file, incorrect file extension, etc.
Other deductions
-5
If the project has to be manually graded due, you will receive a 5 point deduction. Make sure that your project builds on Jenkins!
Use the feedback from Jenkins to make changes to your code. Any time you make a change, push to GitHub and check the Jenkins results.
Make sure that all fields, methods, and constructors are commented.
Resolve all static analysis notifications.
Fix test failures.
Commit and push your code changes with a meaningful commit message. Label your commit with “[Implementation]” for future you!
Check Jenkins results for a green check! Fix any Jenkins issues.
Congratulations!
You’ve finished the first Guided Project and a good portion of the WolfScheduler requirements. Great work on reviewing prerequisite materials and using the new suite of development tools for CSC 216/217! You’re off to a great start. Take time to celebrate your accomplishment!
Now, onwards to Guided Project 2. [Note: Don’t start Guided Project 2 until you’re assigned a repository by the teaching staff.]
Grading Rubric
Your Wolf Scheduler Guided Project 2 will be evaluated on the following items:
Overall Rubric
Phase
Grade Item
Points
Details
Total Points
0
Deductions
Grade Item
Points
Details
Misnamed file or incorrect project structure
-5
Incorrect names of files or incorrect project structure. This can include problems when importing the project to Eclipse for acceptance testing, incorrect location of the system test file, incorrect file extension, etc.
Other deductions
-5
If the project has to be manually graded due, you will receive a 5 point deduction. Make sure that your project builds on Jenkins!
Javadoc Rubric
Item
Strong - 3 points
Adequate - 2 points
Inadequate - 1 point
Jenkins Server
We are using the Jenkins Continuous Integration system to automatically evaluate your work and provide feedback on your submission. Go to the [Guided Project and Project Jenkins Server by using https://csc216-jenkins.csc.ncsu.edu. NOTE: The Jenkins servers for CSC216 are self signed and are maintained by the CSC216 teaching staff and CSC IT. Please select the option to accept the signed certificate. Usually, there’s a link for Advanced that will display an option to trust the certificate.