All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.eBus.messages.EField Maven / Gradle / Ivy

The newest version!
//
// 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 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




© 2015 - 2024 Weber Informatics LLC | Privacy Policy