net.sf.eBus.messages.EField Maven / Gradle / Ivy
//
// Copyright 2013, 2016, 2019, 2020 Charles W. Rapp
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package net.sf.eBus.messages;
import java.io.Serializable;
import net.sf.eBus.util.Validator;
/**
* All application classes intended for use as an eBus messages
* field must extend {@code EField}. This allows instances of
* the application class to be serialized to a
* {@link java.nio.ByteBuffer}. The application class must also
* define {@code public static Builder builder()} and the
* {@code Builder} inner class. See
*
* Note: {@code EField} subclasses are limited
* to 31 fields. This is due to eBus binary serialization format.
*
* De-serialization
*
* {@link EMessageObject} shows how {@code EMessage} and
* {@code EField} need to define a builder inner class used to
* construct a subclass. But the example assumed that the
* subclass was {@code final}. This example shows how to define
* builders for a non-{@code final EField} class. The example
* also applies to {@code EMessage}. The following examples
* show two classes: {@code UserInfo} and its subclass
* {@code Employee}.
*
* public class UserInfo
extends EField
{
public final String name;
public final int age;
private UserInfo(final Builder<?, ?> builder) {
super (builder);
this.name = builder.mName;
this.age = builder.mAge;
}
// builder code explained below.
}
public final class Employee
extends UserInfo
{
public final String department;
private Employee(final Builder builder) {
super (builder);
this.department = builder.mDepartment;
}
// builder code explained below.
}
*
* Class {@code UserInfo} can be instantiated on its own or as
* {@code Employee} superclass. Because of this {@code UserInfo}
* is required to have two builder inner classes. The first is
* an abstract builder which the {@code Employee} builder
* extends. The second is a concrete builder used to create
* {@code UserInfo} instances.
*
* UserInfo abstract builder
* public static abstract class Builder<M extends UserInfo,
B extends Builder<M, ?>>
extends EField.Builder<M, B>
{
private String mName;
private int mAge;
// Protected constructor to allow subclass access.
protected Builder(final Class<? extends EMessageObject> targetClass) {
super (targetClass);
mAge = -1;
}
public B name(final String value) {
if (name == null || name.isEmpty()) {
throw (new IllegalArgumentException("value is null or empty"));
}
mName = value;
return ((B) this);
}
public B age(final int value) {
if (age < 0) {
throw (new IllegalArgumentException("value < zero"));
}
mAge = age;
return ((B) this);
}
@Override protected Validator validate(final Validate problems) {
return (super.validate(problems)
.requireNotNull(mName, "name")
.requireTrue(v -> (v ≥ 0), mAge, "age", Validator.NOT_SET);
}
// Since this class is abstract do not override buildImpl method.
}
*
* Since {@code Builder} is abstract it must use have the two
* class parameters {@code M} and {@code B} which are then passed
* to the superclass builder. Note that the setters use {@code B}
* in the method signature so that the subclass type is returned
* rather than {@code UserInfo.Builder}. Method {@code buildImpl}
* is not defined since that is only done in concrete builders.
*
* UserInfo concrete builder
*
* {@code UserInfo.ConcreteBuilder} extends
* {@code UserInfo.Builder} and defines {@code buildImpl}. There
* is nothing else for {@code ConcreteBuilder} to do since the
* setters and validation methods are provided by the superclass.
*
* public static final class ConcreteBuilder
extends Builder<UserInfo, ConcreteBuilder>
{
private ConcreteBuilder() {
super (UserInfo.class);
}
@Override protected UserInfo buildImpl() {
return (new UserInfo(this));
}
}
* UserInfo builder method
*
* {@code UserInfo.builder} method returns a
* {@code UserInfo.ConcreteBuilder} instance since that class is
* responsible for creating {@code UserInfo} instances. But the
* return type is the abstract {@code UserInfo.Builder}.
*
* public static Builder builder() {
return (new ConcreteBuilder());
}
* Employee builder
*
* {@code Employee.Builder} is as you expect: it extends
* {@code UserInfo.Builder}, defines the {@code department}
* setter method, validates the builder configuration, and
* builds the {@code Employee} instance.
*
* public static final class Builder
extends UserInfo.Builder<Employee, Builder>
{
private String mDepartment;
private Builder() {
super (Employee.class);
}
public Builder department(final String value) {
if (value == null || value.isEmpty()) {
throw (new IllegalArgumentException("value is null or empty"));
}
mDepartment = value;
return (this);
}
@Override protected Validator validate(final Validator problems) {
return (super.validate(problems)
.requireNotNull(mDepartment, department));
}
@Override protected Employee buildImpl() {
return (new Employee(this));
}
}
*
* @see EMessageObject
* @see EMessage
*
* @author Charles Rapp
*/
public abstract class EField
extends EMessageObject
implements Serializable
{
//---------------------------------------------------------------
// Member data.
//
//-----------------------------------------------------------
// Constants.
//
/**
* Serialization version identifier.
*/
private static final long serialVersionUID = 0x050200L;
//---------------------------------------------------------------
// Member methods.
//
//-----------------------------------------------------------
// Constructors.
//
/**
* Initiate field based on builder settings.
* @param builder eBus message field builder.
*/
protected EField(final Builder> builder)
{
super (builder);
} // end of EField(Builder<>)
//
// end of Constructors.
//-----------------------------------------------------------
//---------------------------------------------------------------
// Inner classes.
//
/**
* Used to create a new {@code EField} instance. Has no
* setter methods since {@code EField} has no data members.
* Defines the default
* {@link #validate(net.sf.eBus.util.Validator)}
* method
* which does nothing but return the {@code Validator}
* parameter.
*
* @param target message field class.
*/
public abstract static class Builder
extends EMessageObject.Builder
{
//-----------------------------------------------------------
// Member data.
//
//-----------------------------------------------------------
// Member methods.
//
//-------------------------------------------------------
// Constructors.
//
protected Builder(final Class extends EMessageObject> targetClass)
{
super (targetClass);
} // end of Builder(Class)
//
// end of Constructors.
//-------------------------------------------------------
//-------------------------------------------------------
// Abstract Method Overrides.
//
/**
* This method should be overridden by subclass message
* builders and called before doing its own
* validation. The first line in the subclass
* {@code validate} implementation should be
* {@code super.validate(problems);}.
*
* When overriding this method, be sure to add all
* discovered validation problems to the list. The
* validation method should consist of a series of
* individual {@code if} statements and not
* an {@code if/else if} chain. That way all problems
* are found and not just the first one.
*
*
* Please see {@link Validator} for an
* example of how to validate {@code Builder} settings.
*
* @param problems used to check field validity and
* collect discovered invalid fields.
* @return {@code problems} to allow for method chaining.
*
* @see Validator
*/
@Override
protected Validator validate(final Validator problems)
{
return (problems);
} // end of validate(Validator)
//
// end of Abstract Method Overrides.
//-------------------------------------------------------
} // end of class Builder
} // end of class EField