ortus-java-coding-standards
Use this skill when writing, reviewing, or formatting any Ortus Solutions code (BoxLang, CFML, or Java) to ensure it follows the official Ortus coding standards: indentation, spacing, brace placement, naming, alignment, comments, and structural conventions.
Ortus Coding Standards
Overview
Ortus Solutions maintains consistent formatting rules across all BoxLang, CFML, and Java
source code. These rules are enforced via the OrtusJava Eclipse formatter profile
(ortus-java-style.xml) for Java, and via the conventions documented here for BoxLang
and CFML. When in doubt, match what already exists in the file you are editing.
Indentation
Use tabs for indentation โ never spaces for the leading indent.
- Class body: indented one tab from the class declaration.
- Method body: indented one tab from the method signature.
- Switch cases: indented one tab from
switch. - Case body: indented one tab from the
caselabel. - Continuation lines (wrapped expressions): indented one level.
// Java โ tab-indented, end-of-line braces
public class UserService {
public User findById( int id ) {
if ( id <= 0 ) {
throw new IllegalArgumentException( "id must be positive" );
}
return repository.find( id );
}
}
// BoxLang โ same indentation rules
class UserService {
function findById( required numeric id ) {
if ( id <= 0 ) {
throw( type = "InvalidArgument", message = "id must be positive" )
}
return variables.repository.find( arguments.id )
}
}
Spacing Inside Parentheses
Always place a space after ( and before ) in:
- Method declarations
- Method invocations
- Control flow keywords (
if,for,while,switch,catch,synchronized) - Casts
- Parenthesized expressions
// Java
public void process( String name, int count ) {
if ( count > 0 ) {
doThing( name );
}
var result = ( String ) rawValue;
}
// BoxLang
function process( required string name, required numeric count ) {
if ( count > 0 ) {
doThing( name )
}
}
Never place a space between a function name/keyword and its opening (:
// WRONG
doThing ( name );
// RIGHT
doThing( name );
No space inside empty parentheses:
// RIGHT
init()
list()
Brace Style
Use K&R / end-of-line brace placement โ opening brace on the same line as the statement, not on its own line.
// RIGHT
public void save( User user ) {
if ( user.isValid() ) {
repository.save( user );
} else {
throw new ValidationException( "invalid user" );
}
}
// WRONG โ Allman style, do not use
public void save( User user )
{
...
}
The same rule applies to:
- Class declarations
- Method declarations
if/else/else iffor/while/do-whiletry/catch/finallyswitch- Array initializers
Always Use Braces
Even for single-statement bodies, always use braces. Never place an if or loop
body on the same line as its condition.
// WRONG โ no braces, body on same line
if ( user == null ) return;
for ( var item : items ) process( item );
// RIGHT
if ( user == null ) {
return;
}
for ( var item : items ) {
process( item );
}
Spacing Around Operators
Place one space on each side of:
- Assignment operators (
=,+=,-=, etc.) - Comparison operators (
==,!=,<,>,<=,>=) - Arithmetic operators (
+,-,*,/,%) - Bitwise operators (
&,|,^,~) - Shift operators (
<<,>>,>>>) - String concatenation (
&in BoxLang/CFML,+in Java) - Lambda arrows (
->in Java,->/=>in BoxLang) - Ternary operator (
?and:)
// Java
var total = price * quantity;
var result = isValid ? "yes" : "no";
var upper = items.stream().map( s -> s.toUpperCase() ).collect( Collectors.toList() );
// BoxLang
var total = price * quantity
var result = isValid ? "yes" : "no"
var upper = items.map( ( s ) -> uCase( s ) )
No space for:
- Unary operators before their operand:
!condition,++i,-value - After
!:!isValid(no space between!andisValid) - Ellipsis:
String... values(no space before...)
Column Alignment
When multiple related declarations or assignments appear in a block, align on columns so they read like a table. This applies to:
- Field (property) declarations
- Local variable declarations
- Assignment groups
// Java โ aligned field declarations
private final UserRepository userRepository;
private final EmailService emailService;
private final int maxRetries;
// BoxLang โ aligned assignments
var firstName = "Alice"
var lastName = "Smith"
var email = "[email protected]"
var active = true
this.name = "My App"
this.sessionManagement = true
this.sessionTimeout = createTimespan( 0, 1, 0, 0 )
this.timezone = "UTC"
Apply alignment within logical groups (separated by blank lines). Do not force alignment across unrelated declarations.
Blank Lines
| Context | Rule |
|---|---|
| Between methods | 1 blank line |
| Between abstract methods | 1 blank line |
| Between fields | 0 blank lines (fields are grouped together) |
After package declaration | 1 blank line |
| First declaration inside a class body | 1 blank line |
| Beginning of a method body | 0 blank lines |
| End of a code block | 0 blank lines |
Before a code block (e.g., before {) | 0 blank lines |
// Java โ correct blank line usage
public class OrderService {
private final OrderRepository orderRepository;
private final PaymentService paymentService;
public OrderService( OrderRepository orderRepository, PaymentService paymentService ) {
this.orderRepository = orderRepository;
this.paymentService = paymentService;
}
public Order create( CreateOrderRequest request ) {
var order = buildOrder( request );
orderRepository.save( order );
return order;
}
private Order buildOrder( CreateOrderRequest request ) {
return new Order( request.getItems(), request.getUserId() );
}
}
Naming Conventions
| Item | Convention | Example |
|---|---|---|
| Java classes | PascalCase | UserService, OrderProcessor |
| Java interfaces | PascalCase | UserRepository, Serializable |
| Java methods | camelCase | getUserById(), processOrder() |
| Java fields | camelCase | userRepository, maxRetries |
| Java constants | UPPER_SNAKE_CASE | MAX_RETRIES, DEFAULT_TIMEOUT |
| Java packages | lowercase.dot.separated | ortus.boxlang.runtime.services |
| BoxLang classes | PascalCase | UserService.bx, OrderProcessor.bx |
| BoxLang functions | camelCase | getUserById(), processOrder() |
| BoxLang variables | camelCase | userProfile, orderTotal |
| BoxLang templates | camelCase or kebab-case | userProfile.bxm, order-detail.bxm |
| BoxLang scripts | camelCase | buildReport.bxs |
| BoxLang constants (local) | UPPER_SNAKE_CASE | var MAX_RETRIES = 3 |
Comments and Javadoc
Javadoc (Java)
Write Javadoc for all public classes and public/protected methods.
/**
* Finds a user by their unique identifier.
*
* @param id the user's primary key (must be positive)
*
* @return the matching {@link User}, or {@code null} if not found
*
* @throws IllegalArgumentException if {@code id} is not positive
*/
public User findById( int id ) {
...
}
Rules:
- Opening
/**and closing*/on their own lines. - One space after
*on every body line. @param,@return, and@throwstags on separate lines.- Do not join description lines โ keep each sentence on its own line.
@paramdescriptions aligned (not required but preferred for readability).
Inline Comments (Java)
// Use single-line comments for brief explanations above the line they describe.
// Never end-of-line comments for non-obvious things โ put them above instead.
// Calculate total including tax
var total = basePrice * ( 1 + taxRate );
Do not leave commented-out code in committed files.
BoxLang Documentation Comments
/**
* Finds a user by their unique identifier.
*
* @id The user's primary key. Must be a positive integer.
* @return struct containing user data, or an empty struct if not found.
*/
struct function findById( required numeric id ) {
...
}
BoxLang-Specific Conventions
Semicolons
Do not use semicolons on regular statements in BoxLang. They are optional and the standard is to omit them for cleaner code.
// RIGHT โ no semicolons
var result = calculate( x, y )
return result
// WRONG
var result = calculate( x, y );
return result;
Exceptions where semicolons ARE required:
- Property declarations:
property name="fieldName" type="string"; - Multiple statements on a single line (avoid this pattern)
Lambdas vs Closures
Use lambdas (->) when the function is deterministic and operates only on
its own arguments:
// Lambda โ only uses the item argument (no outer scope access)
var doubled = numbers.map( ( n ) -> n * 2 )
var upper = words.map( ( w ) -> w.uCase() )
Use closures (=>) when the function accesses variables from the enclosing
scope or calls external BIFs:
// Closure โ accesses outer variable `threshold`
var filtered = numbers.filter( ( n ) => n > threshold )
// Closure โ calls external BIF
var encoded = values.map( ( v ) => encodeForHTML( v ) )
Argument Scope in Closures
Inside a closure, do not use the arguments scope to access outer function
parameters. The arguments scope inside the closure refers only to the
closure's own parameters:
// WRONG โ arguments.name refers to the closure's arguments, not the outer function's
function process( required string name ) {
var found = items.filter( ( item ) => item.name != arguments.name )
}
// RIGHT โ use unscoped name; BoxLang finds it in the enclosing scope
function process( required string name ) {
var found = items.filter( ( item ) => item.name != name )
}
Function Call Spacing
// RIGHT โ spaces inside parentheses, space after comma
doThing( arg1, arg2 )
createUser( firstName = "Alice", active = true )
// WRONG โ no spaces inside parens
doThing(arg1, arg2)
Struct and Array Literals
// Preferred โ literal syntax with alignment
var config = {
host : "localhost",
port : 5432,
database: "myapp"
}
var roles = [ "admin", "editor", "viewer" ]
Multi-Line Method Calls
When a call does not fit on one line, wrap arguments with one argument per line and align them:
// Java
var result = userService.createUser(
request.getFirstName(),
request.getLastName(),
request.getEmail(),
request.getRoleId()
);
// BoxLang
var result = userService.createUser(
firstName = request.firstName,
lastName = request.lastName,
email = request.email,
roleId = request.roleId
)
Wrap before operators, not after:
// RIGHT โ operator at start of wrapped line
var isEligible = user.isActive()
&& user.hasVerifiedEmail()
&& !user.isSuspended();
Java-Specific Patterns
Import Order
Group imports in this order with one blank line between groups:
java.*andjavax.*- Third-party libraries
ortus.boxlang.*internal classes
Do not use wildcard imports (import java.util.*).
Null Checks
Prefer Objects.requireNonNull in constructors and public entry points:
public UserService( UserRepository repo ) {
this.repo = Objects.requireNonNull( repo, "repo must not be null" );
}
final Fields
Declare all injected dependencies and constructor-set fields as final:
private final UserRepository userRepository;
private final EmailService emailService;
Avoid Magic Numbers
Extract literal numbers and strings into named constants:
// WRONG
if ( retries > 3 ) { ... }
// RIGHT
private static final int MAX_RETRIES = 3;
if ( retries > MAX_RETRIES ) { ... }
Common Anti-Patterns to Avoid
| Anti-Pattern | Fix |
|---|---|
| Allman-style braces | Use K&R (end-of-line) braces |
No spaces inside ( ) in calls | Always doThing( arg ) not doThing(arg) |
| Semicolons on BoxLang statements | Remove unless a property declaration |
| Unaligned field groups | Align with spaces/tabs to column |
arguments.name inside closure accessing outer scope | Remove scope prefix |
| Comment-out dead code | Delete it; use version control |
| Wildcard imports in Java | Use explicit imports |
| Magic numbers | Extract to named constants |
Mutable public fields in Java | Use private final + accessor |
One-liner if without braces | Always use braces |