To make sure that the report designer stays clean and maintainable over time, I'm enforcing a strict rulership over the code rejecting code that is not "clean" according to my standards.
Clean, what does that mean:
- No anonymous inner classes.
anonymous inner classes are only ok for primitive one-line event forwardings. For executing larger portions of code, create named inner classes (so that the code can be documented and has a name that tells what the code is doing) instead.
- Separation of concerns, encapsulation
Code that works together on the same task, should go together and stay in the same class. When writing event-handlers or actions, keep the code dealing with one task in the same inner class.
Example: A action (as an example of a ActionListener) that can only succeed when a item on a list is set, should also take care of the ListSelection-event. Even if more than one action has to listen to the same event, they should be separate listener-instances. This way, when we month later have to change the action, we have all code dealing with the action in one place and don't have to search/change event handlers all over the place.
- Prefer javax.swing.Action instead of maintaining the button/menu states manually.
- For non-trivial dialogs, create a data model. Even if the data model you create does not fully match the "perfect" model, it is easier to tweak a dialog's data model to new or changed requirements than o tweak a dialog that stores all data only in the components itself.
- Use real data models in the code
A non-trivial "data model" that consists only of presentation data (or worse, of localized strings) and generic collections is not acceptable. A maintainable data model must be a type-safe model implementation that maintains and guarantees the integrity of its internal state. When creating GUI elements for the model use custom renderers and editors if the model does not create user-friendly representations on its "toString()" methods.
Likewise, I prefer that GUI elements are bound to the model instead of creating a incomprehensible web of event-listener dependencies between UI-components.
- Encapsulation II: There is no way how I could ever accept non-private members on a class. Likewise, I do not accept public non-static inner classes, as these constructs easily lead to memory leaks (as every non-static class holds a implicit reference to its parent-class instance)
- Separation II: It is no shame to create a separate class for each dialog or larger task to be done. Maintaining a 1000-lines monster class is no fun, and usually the local complexity can be greatly reduced by splitting such monsters into separate areas of concern.
- Be error-resistant:
Each public method must and each protected method should check all incoming parameters for Null-Values or other invalid values and should throw explicit Exceptions in that case.
This helps us to discover errors early and prevents that classes can enter a invalid state without getting noticed. When we (again) months later have to change the dialog for whatever reasons, then any error introduced by us will be easily spotted, which reduces our maintenance efforts.
- Write the code clean enough that an average student has a realistic chance to understand and work with the code.
For users of IntelliJ IDEA:
- Inspections Profile A inspection profile that detects most of the common style and coding errors I consider bad style. To import it. copy the file into "$home/.IntelliJIdea70/config/inspections"
- Inspections Profile A code-style profile matching the reporting-engine coding guidelines. To import it. copy the file into "$home/.IntelliJIdea70/config/codestyles"
Whenever more than one developer comes together to work on a shared project, questions of style consistency arise. As every developer has his own habit of formating code, naming methods and variables, the resulting mix becomes confusing and hard to read, as soon as two incompatible styles get mixed up. The only way to avoid such a chaos, is to define mandatory rules, which every developer has to follow.
This style guide defines such a set of common style rules for JFreeReport and all adjacent projects.
Some of the given rules are mandatory and must not be ignored or violated. Other rules given in this document can be considered recommendations, they should be followed as closely as possible, but can be violated if there is a known (and documented) reason for it.
As Java is a complex language, which gives you a great freedom in writing your code, there will be many fields, which remain uncovered by the rules given in this document. Where ever possible, we will try to give examples for such known cases to reduce the number of different variations of these cases.
This source code style guide uses the same structure as the Java style guide written by Sun. Where ever possible, we will refer to the rules in that Guide.
Project Directory Structure and File Names
All projects should share a common structure. All files for a project must reside below a single directory, which must be named after the project. All Java source files must be contained in a directory called "source", which must be located directly under the project's root directory. Jar-Files the project might depend on must be located inside a directory called "lib", which also must be located directly under the project's root directory.
All projects must provide ANT-build scripts, which create the release distribution files. The build scripts must be self contained and should not have any private dependencies outside the project's directory. All external dependencies on the build script must be documented and must not include non-open source components. Every user of the project must be able to fully rebuild the complete project.
Every project should provide at least a ChangeLogfile, which documents the development progress and a README file, which describes the purpose of the project, how to get started with the files in the project and which gives pointers to where to find additional documentation, if available.
Finally, to comply with the GPL or LGPL, every project must contain a copy of its licence in the root of its project directory.
Java File Structure
Each Java source file must contain a single public class or interface. Every package level class must be a public class, which is defined in it's own Java source file. Package protected or private outer classes must not be used, every use of these classes should be replaced by the use of either public outer classes or inner classes.
Java source files must have the following ordering:
- Beginning comment
- Package and import statements
- Public Class or Interface declaration
All Java source files should be encoded using the ISO-8859-1 encoding. Non-ASCII Unicode characters should be escaped.
All source files must begin with a c-style comment that holds the copyright and licence information, the class name, original author and contributors and a ChangeLog covering the milestones in the file's development history.
This header comment must always include a CVS-ID tag.
Package and Import Statements
The first non-comment line of the file must be the package statement. No project class should be contained in the default package.
The following package spaces should be used for the JFreeReport projects:
- org.jfree.report for the JFreeReport core project
- org.jfree.report.ext for the JFreeReport extension project
- org.jfree.pixie for the Pixie image library
- org.jfree.report.dev for the JFreeReport development tools
Package imports (*-imports) must not be used. This method of importing all classes of a certain package is dangerous, as it might cause naming conflicts between two classes with the same name, which were imported from different packages. Additionally, it is no longer obvious to the human reader, which classes get used by a certain Java source file, thus making it harder to track dependencies.
All package names must be written in lowercase ASCII-characters. Although Java allows almost any Unicode character in the names of packages as well as in class names, the correct representation of such names in the file system of the user and developers cannot be guaranteed for non-ASCII names. Therefore the safest bet is, to avoid such things altogether.
Package names in Java are case-sensitive, a package named "com" is not the same as a package named "COM". Windows, for example does not have a case-sensitive filesystem, therefore the package directories for "COM" and "com" will be mapped to the same filesystem entry and thus making it hard to work with these packages in the same project.
If the IDE used for development supports it, the import statements should be sorted alphabetically in each block. Import statements for the java and javax packages should be written first, followed by a blank line and finally followed by all other imports.
Public Class or Interface Declaration
Every Java source file must contain only one public outer scope class or interface declaration.
Every class or interface declaration must be preceded by a JavaDoc class comment describing the purpose of the class. The documentation comment should contain examples on how to use that class, where ever it seem applicable. The JavaDoc @author tag is mandatory. Multiple @author tags are allowed.
The class documentation must be followed by a valid public class or interface declaration.
If necessary, a non JavaDoc comment might follow immediately after the class declaration, describing internal details of the implementation of this class.
The order of the class members should be as follows.
- Constant declarations (public, protected or private static final variables).
Only immutable classes should be used as constant values.
The final keyword protects the local variable from any changes. As Java always stores references when dealing with objects only the reference is now write-protected. That means, you cannot change the object's reference (i.e. let the variable point to a different object), but nothing prevents you from changing the internals of the referenced object (assumed that the object's implementation allows it). Array contents are always mutable - there is no way to make the contents of an array immutable.
Classes are considered immutable, if there exists no operation that changes the public behaviour of the class. An immutable class always returns the same results on calls to its public or protected methods whenever these methods are called with the same parameters. Immutable classes do not change their behaviour.
- Java Language Specification: The final keyword
- Immutable Objects
- Inner classes.
Inner classes should always be used in favor of anonymous inner classes.
If possible, such classes should be static, as this greatly reduces the chances of creating memory leaks when passing these objects around. (All non-static inner classes hold an invisible reference to their outer class instance, and therefore the garbage collector cannot free the outer class unless all inner classes can be garbage collected.)
- Static member variables
All non-final static member variables must be private. If necessary, create protected accessor methods for these variables. Static variables should be avoided, except for Singleton implementations, as such variables make it hard to trace bugs in a managed application (like Servlet or EJB-Containers).
All access to such variables must be properly synchronized.
- Non-static member variables
All member variables must be private. If you need to access these variables from a derived class, create protected accessor methods for them.
Every class must declare at least one constructor. If there are more than one constructor, the default constructor should always be the first in the list of constructors, even if it is private.
If the class is an utility class and does only contain static methods, then a private default constructor should be added. This prevents unnecessary object creation of such classes.
The methods should be grouped by functionality rather than by visibility or name. The main goal should always be to make the source code as readable as possible.
Indentation and Braces
To conserve space, two spaces should be used as unit of indentation. All indentations must be done with spaces.
Tab characters must not be used. How Tab characters will be displayed primarily depends on the settings of the user's editor. Usually this leads to inconsistent indentation across the source files once two developers with different Tab-settings edit that document. To avoid such troubles at the first place, the use of Tab characters is forbidden. This way, all files look consistent, no matter whether the tab size is 2 or 8.
- Tabs versus Spaces: An Eternal Holy War
Forced Line Breaks
On some constructs of the Java language, we require mandatory line breaks.
First of all, and most important: Every statement should be placed on an own line. The old times, where code must be written in a way, that as less as possible paper was used for a program are long time over. There is plenty of space in a Java source file, so there is no reason to press more than one statement on a single line of code.
To increase the readability of the code, a blank line should be inserted after each structural element, like method, constructor or class or member variables.
If an overly long expression does not fit on a single line, break it according to these general principles:
- Break after a comma
- Break after an operator
- Prefer higher-level breaks to lower-level breaks.
- Align the new line with the beginning of the expression at the same level on the previous line.
- If the above rules lead to confusing code or to code that's squished up against the right margin, just indent 8 spaces instead.
Whenever possible, line breaks within expressions should be avoided. If the expression is used in an assignment and the right value of the assignment would fit on the next line, the line break should be inserted after the assignment operator.
But after all the best way to handle such cases is to avoid them altogether. Overly long lines of code make your source code hard to read and can be usually split into several separate statements. Introduce local variables to split your code or to compute complex parameters. This will greatly increase the readability of the source code.
The following keywords must always be placed on a new line:
Note: An else followed immediately by an if, can be kept on the same line as the if statement.
Placement of Braces
All curly braces should be placed at the beginning of the next line. The braces itself should have the same indention as their preceding statement. All statements inside the curly braces must be indented by two more spaces than the statements outside the braces. The closing brace must be at the same horizontal position as the corresponding opening brace.
All code blocks must be held in curly braces, even if it is just a single line of an if, for or while statement.
A curly brace must always be the only non-whitespace character on the line.
Using a single line for every curly brace makes it harder to overlook these characters and greatly increase the readability of the source code. The structure of even deeply nested code can be recognized by a short look at it. This way, it is easy to identify the blocks of code of a certain method. Having braces around all of these code blocks makes it easier to add statements without accidentally introducing bugs due to forgetting to add these braces.
Here is some properly aligned example code that shows how to apply the above rules:
All source code should be commented.
Java offers several types of comments. The most noticeable comments are the JavaDoc comments ( /** .. */). The other ones are the line comments (//) and the C-style comments (/* .. */). The later comment types should be used to document the implementation and design decisions leading to that implementation.
We require valid and complete JavaDoc comments for every class, method and member variable of the source code.
All classes must have at least a short documentary comment describing the purpose of the class. An @authortag is required for every top level class. For inner classes, the it is assumed, that the author of the top level class also wrote the inner class.
When applicable, the documentation should contain a textual description on how and where to use that class and how this class fits into the big picture. Example code is always a good addition to illustrate the comments.
Methods must at least document their parameters, exceptions and return values using the corresponding JavaDoc tags. For every object parameter, it should be documented, whether null values are allowed. Runtime exceptions, which are known to be thrown by that method should also be documented in the JavaDoc comment (together with the condition that might trigger the exception).
The methods JavaDoc documentation should not contain implementation details, except this particular knowledge is a requirement to successfully use the method. Be aware, that this is almost always an indication for a design error. It should be considered to refactor such methods or classes to clarify that problematic case.
All member variables must have at least a short comment documenting their purpose.
Implementation comments should always be added to the code where ever it increases the readability of the code.
However, if you find yourself documenting methods, which are hard to read or understand, you should consider to rewrite such tricky or unclear code by using meaningful (or so-called 'speaking') variable names. In general you should strive for self-documenting code by using well-known patterns and simple structures.
Class and Interface Declarations
Top level classes must always be declared as public classes. The package protected access level must never be used. Package protected classes cannot be used outside the package, therefore they cannot be used or extended from an foreign implementor. The package protected access level leaves the classes open to be abused by unrelated classes from the same package. It is better to either lock down the class using the private modifier or to open it to all child classes by using the protected modifier.
Methods declared in interfaces must be explicitly marked by the keyword public.
As a general rule, there should be only one variable declaration per line.
Class and member variables must always have an explicit access level modifier. The use of the package protected access level is not allowed in any code conforming to this style guide. Variables which are not declared 'static final' must always have the private access level. There is no sane reason to allow any other code to modify the internal state of a class. If it should be allowed to modify the value of a class or member variable, accessor methods should be provided for that purpose.
The visibility of variables should be as little as possible. This leads to the rule, that variables should be declared whenever they are used. Declaring variables at the beginning of a method, as done in C or Pascal leads to confusing code, as it is hard to see, where in a method such a variable might have been already used. If a variable is only used in a certain code block, make sure, that it is declared in that block only.
Variables, which are assigned only once in their lifetime should be declared as final variables. This way, it will be easier for the compiler to generate optimized code. An other positive side effect is, that you can always be sure about the value of that particular variable, which in return leads to better and easily understandable code.
Non-static member variables should not be initialized in their declaring statement. If the initial value of a variable differs from the default value for that variable (false for boolean variables, zero for numeric variables and null for object references), the variable should be initialized in the constructor(s).
Generally, identifiers in your program should be speaking identifiers. A reader of your code should be able to recognize the purpose of the variable or method by the its name. A name should be preferably short, but long enough to clearly describe the purpose without being either chatty nor cryptic. Choosing the right name is an art, but always worth the effort, as it (like nothing else) increases the readability and maintainability of the source code.
For the sake of compatibility with non-unicode systems, package and class names must be restricted to ASCII characters only. To avoid encoding troubles, we also require, that all method and variable names must be chosen in a way that only ASCII characters get used.
The prefix of a unique package name is always written in all-lowercase ASCII letters and should be one of the top-level domain names, currently com, edu, gov, mil, net, org, or one of the English two-letter codes identifying countries as specified in ISO Standard 3166, 1981.
Subsequent components of the package name vary according to an organization's own internal naming conventions. Such conventions might specify that certain directory name components be division, department, project, machine, or login names.
All package names must be in lower case.
Class and Interface Names
Class names should be nouns, in mixed case with the first letter of each internal word capitalized. Try to keep your class names simple and descriptive. Use whole words-avoid acronyms and abbreviations (unless the abbreviation is much more widely used than the long form, such as URL or HTML).
Names for methods should begin with an English verb (as methods usually 'do' something) possibly extended by a noun or adjective. The name of the method should be speaking, so that the name itself gives a correct indication on what the method will do. Examples for a method that computes a layout would be 'compute', 'computeLayout or 'computeReportLayout'. Which of these names would be the best depends on the context where it was declared and where it is going to be used.
Method names should be mixed case with the first letter lowercase, with the first letter of each internal word capitalized. Underscore characters must not be used.
Accessor methods to class or instance variables should follow the Java Beans name style. Methods to set the variable XXX should be called 'setXXX' and methods to read the variable should be called 'getXXX'.
Except for constants, all instance, class, and class variables are in mixed case with a lowercase first letter. Internal words start with capital letters. Variable names should not start with underscore _ or dollar sign $ characters, even though both are allowed.
When choosing identifiers for your program, you should use full English descriptions for names. Avoid using abbreviations. Use names like firstName, lastName, and middleInitial rather than the shorter versions fName, lName, and mi.
As a thumb rule, names for variables should be an English noun, possibly preceded by an adjective. The name should match the purpose of the member variable. Examples for such names would be limit, upperLimit or lowerLimit.
One-character variable names should be avoided except for temporary "throwaway" variables. Common names for temporary variables are i, j, k, m, and n for integers; c, d, and e for characters.
The names of variables declared class constants should be all uppercase with words separated by underscores ("_"). REPORT_DATE_PROPERTY would be an example for a valid name for a constant variable.
Finally, this guide provides some best-practices when writing Java code. Most of these practices should be common among all programmers and all of them are a requirement for code that should be acceptable for JFreeReport and its related projects.
A failure to obey to these rules typically leads to bugs, so that the failure to obey to the rule itself is considered a bug.
Always Define a Constructor
Every class should have an explicit constructor definition. If the class is an utility class and defined only static methods and variables, this constructor should be private to avoid unnecessary object creations.
Although Java creates an invisible default constructor for classes, which do not define their own constructor, an explicit declaration is more obvious to the causal reader and provides a convenient place to initialize the instance variables.
Providing Access to Class or Instance Variables
Class or instance variables must never be accessible directly. If you need to read or modify the variables, define suitable accessor methods for them.
Non-private member variables make it impossible for a certain implementation to maintain a valid internal state. Foreign code could easily modify the variable in an invalid way causing state-dependent bugs, which are particular hard to trace. If accessor methods are used, the implementor of these methods can check the given parameters for validity, refusing to alter the classes internal state.
When providing access to mutable internal variables, you might as well open a big black hole in your code, causing similar bugs as if you'd have allowed direct non-accessor access to the internal variable itself. Avoid returning mutable shared instances of private variables.
For performance reasons, mutable variables might be returned to derived classes under the assumption, that the creator of the derived class is aware of the possible side effects. Still, this is dangerous and it should be considered to introduce a special container class for that purpose, which protects the internal state actively or to introduce additional methods to avoid that situation altogether.
Not every internal variable need to have accessor methods defined. As a general rule you should minimize the public or protected interface of an class as much as applicable.
Referring to Class Variables and Methods
Avoid using an object to access a class (static) variable or method. Use a class name instead. For example:
Avoid Declaring Constant Pools in Interfaces
Sometimes, it is necessary to define a set of common constants to be used in several, otherwise unrelated classes.
A common practice among Java Programmers to solve this problem seems to be, to define that set of constants in an Interface declaration. Later that interface is implemented in all classes, which want to use these constants.
Especially when dealing with a large set of (possibly inherited) classes, it is hard to trace, where a certain constant is originally defined. This problem can be avoided by defining all constants in a final class, which only has a private constructor, to disallow object creation of this class.
Using the class name to access the constants (Referring to Class Variables and Methods), makes it is always obvious, where a particular constant is defined.
Avoid assigning several variables to the same value in a single statement. It is hard to read.
Do not use the assignment operator in a place where it can be easily confused with the equality operator. Example:
should be written as
Do not use embedded assignments in an attempt to improve run-time performance. This is the job of the compiler. Example:
should be written as
Always Check Parameter Values
When ever a public or protected method accepts parameters, these parameters should be checked for validity.
Most of the hard to trace bugs in a program are caused by an invalid object state. The only way, a valid object implementation can get into an invalid state, is by accepting invalid parameter in methods, which alter the state of the object. That mean, if methods do never accept invalid parameters and the instance variables cannot changed directly, the object cannot get into an invalid state.
Designing stable programs begins at the very basic level of the program. Checking parameters and failing fast is a very safe strategy to ensure that the contract between the object designer and the object user is not violated. Do hope, that the caller will only provide valid values, as there will be at least one time in your life, where he will not.
Common tests for parameters include range checks or checks, whether a value is null.
Don't repair invalid input.
Never assume, that you can read the original programmers mind in a safe way, so that you will be able to fix invalid parameters. You won't. If a caller provides invalid values, fail. Often the caller is not even aware that the given values are invalid and as long as the program does not crash (or at least an exception is thrown), he might not notice his fault. If you want to provide self-repairing abilities for your code, provide it in a safe and separate way, so that a caller has to validate (and possibly repair) the input in a separate step. This way, the programmer will be aware, that the input got possibly modified before it is acceptable for the object.
Avoid Overly Long or Complex Methods
A method should not be longer than 150 lines of code.
Long methods are usually a clear indication, that the code of this method should be split into separate sub methods. A common example for such long methods can be found in the various GUI builders out there. The JBuilder, for instance puts all GUI initialization code into a single method, called jbinit(). Once the GUI is more complex than a HelloWorld example, the code in there becomes confusing and hard to read and understand. Splitting the method into several sub methods helps to make the code easier to read. Staying with the GUI example, one could, for instance, separate the initialization of the tool bars, panels and menus into their own methods. Developers, who want to work with Menu-code won't have to cope with panels or tool bars anymore.
Prefer Simple Structures
"Complex structures contain complex bugs. Simple structures contain simple bugs."
Complex, deeply nested functions are very hard to read and most programmers will not understand their purpose, effects and error conditions, unless they spent hours and hours to study them. Functions, which are not fully understandable by the very first look at it, should be redesigned. Complex nested loop bodies should be moved into own methods, so that readers of the code don't have to care about the details, unless they are really interested in them.
Expressions with many, many parentheses or numerous inline parameter computations are also hard to read and harder to understand. Simplify them. The structure of Java allows almost self-documenting code. Use these options. Write small and simple methods, possibly by outsourcing code to other methods.
The main aim for all programmers should always be to write maintainable and understandable code.
Java, as most other modern languages, uses the Exception-model as preferred way to handle unexpected error conditions. Once Exceptions are thrown, they interrupt the current method and transfer the control to a registered error handling code in one of the calling methods. If no error handler was found, the current thread will be finished.
Error state variables should be avoided. As by definition all programmers are lazy, error states often remain unchecked. Exceptions should always be used to signal, that something unexpected happened. Error return values should be used in case the error was expected. For instance, trying to add an element to a list containing only unique values, which does already contain that element, should not trigger an exception. It is fully sufficient to indicate success or failure by using a certain return value. But, adding an element to a list, which does not expect this kind of element (adding a string to a list of numbers), might throw an exception. Trying to add an invalid type might in this case indicates a programming error and is prevented.
As a thumb rule: If the error is expected and no invalid state is caused by it, use return values. If the error is unexpected or would cause an invalid program state if ignored, then throw an exception.