ReferenceNumberGeneratorBuilder.java

package fr.sii.ogham.sms.builder.cloudhopper;

import java.util.Random;

import fr.sii.ogham.core.builder.Builder;
import fr.sii.ogham.core.builder.context.BuildContext;
import fr.sii.ogham.core.fluent.AbstractParent;
import fr.sii.ogham.sms.splitter.RandomReferenceNumberGenerator;
import fr.sii.ogham.sms.splitter.ReferenceNumberGenerator;

/**
 * In the cellular phone industry, mobile phones and their networks sometimes
 * support concatenated short message service (or concatenated SMS) to overcome
 * the limitation on the number of characters that can be sent in a single SMS
 * text message transmission (which is usually 160). Using this method, long
 * messages are split into smaller messages by the sending device and recombined
 * at the receiving end. Each message is then billed separately. When the
 * feature works properly, it is nearly transparent to the user, appearing as a
 * single long text message.
 * 
 * <p>
 * One way of sending concatenated SMS (CSMS) is to split the message into 153
 * 7-bit character parts (134 octets), and sending each part with a User Data
 * Header (UDH) tacked onto the beginning. A UDH can be used for various
 * purposes and its contents and size varies accordingly, but a UDH for
 * concatenating SMSes look like this:
 * 
 * <ul>
 * <li>Field 1 (1 octet): Length of User Data Header, in this case 05.</li>
 * <li>Field 2 (1 octet): Information Element Identifier, equal to 00
 * (Concatenated short messages, 8-bit reference number)</li>
 * <li>Field 3 (1 octet): Length of the header, excluding the first two fields;
 * equal to 03</li>
 * <li>Field 4 (1 octet): 00-FF, CSMS reference number, must be same for all the
 * SMS parts in the CSMS</li>
 * <li>Field 5 (1 octet): 00-FF, total number of parts. The value shall remain
 * constant for every short message which makes up the concatenated short
 * message. If the value is zero then the receiving entity shall ignore the
 * whole information element</li>
 * <li>Field 6 (1 octet): 00-FF, this part's number in the sequence. The value
 * shall start at 1 and increment for every short message which makes up the
 * concatenated short message. If the value is zero or greater than the value in
 * Field 5 then the receiving entity shall ignore the whole information element.
 * [ETSI Specification: GSM 03.40 Version 5.3.0: July 1996]</li>
 * </ul>
 * 
 * <p>
 * It is possible to use a 16 bit CSMS reference number in order to reduce the
 * probability that two different concatenated messages are sent with identical
 * reference numbers to a receiver. In this case, the User Data Header shall be:
 * 
 * <ul>
 * <li>Field 1 (1 octet): Length of User Data Header (UDL), in this case
 * 06.</li>
 * <li>Field 2 (1 octet): Information Element Identifier, equal to 08
 * (Concatenated short messages, 16-bit reference number)</li>
 * <li>Field 3 (1 octet): Length of the header, excluding the first two fields;
 * equal to 04</li>
 * <li>Field 4 (2 octets): 0000-FFFF, CSMS reference number, must be same for
 * all the SMS parts in the CSMS</li>
 * <li>Field 5 (1 octet): 00-FF, total number of parts. The value shall remain
 * constant for every short message which makes up the concatenated short
 * message. If the value is zero then the receiving entity shall ignore the
 * whole information element</li>
 * <li>Field 6 (1 octet): 00-FF, this part's number in the sequence. The value
 * shall start at 1 and increment for every short message which makes up the
 * concatenated short message. If the value is zero or greater than the value in
 * Field 5 then the receiving entity shall ignore the whole information element.
 * [ETSI Specification: GSM 03.40 Version 5.3.0: July 1996]</li>
 * </ul>
 * 
 * 
 * Configures reference number generation strategy.
 * 
 * 
 * @author Aurélien Baudet
 *
 */
public class ReferenceNumberGeneratorBuilder extends AbstractParent<MessageSplitterBuilder> implements Builder<ReferenceNumberGenerator> {
	private final BuildContext buildContext;
	private Random random;
	private ReferenceNumberGenerator custom;

	/**
	 * Initializes the builder with a parent builder. The parent builder is used
	 * when calling {@link #and()} method.
	 * 
	 * @param parent
	 *            the parent builder
	 * @param buildContext
	 *            for registering instances and property evaluation
	 */
	public ReferenceNumberGeneratorBuilder(MessageSplitterBuilder parent, BuildContext buildContext) {
		super(parent);
		this.buildContext = buildContext;
	}

	/**
	 * Uses a random number for reference number.
	 * 
	 * It uses an instance of {@link Random} to generate random numbers.
	 * 
	 * @return this instance for fluent chaining
	 */
	@SuppressWarnings("squid:S2245")
	public ReferenceNumberGeneratorBuilder random() {
		return random(new Random());
	}

	/**
	 * Uses a random number for reference number. The provided {@link Random}
	 * instance is used to generate random numbers.
	 * 
	 * <p>
	 * If this method is called several times, only the last {@link Random}
	 * instance is used.
	 * 
	 * <p>
	 * If random parameter is {@code null} then any previously registered
	 * {@link Random} instance won't be used.
	 * 
	 * @param random
	 *            the {@link Random} instance used to generate random numbers
	 * @return this instance for fluent chaining
	 */
	public ReferenceNumberGeneratorBuilder random(Random random) {
		this.random = random;
		return this;
	}

	/**
	 * Uses a custom reference number generation strategy. This strategy takes
	 * precedence over any other generation strategy.
	 * 
	 * <p>
	 * If this method is called several times, only the last registered
	 * generator is used.
	 * 
	 * <p>
	 * If custom parameter is {@code null}, the custom strategy is disabled.
	 * Other previously configured strategies (like {@link #random()}) will be
	 * used instead.
	 * 
	 * @param custom
	 *            custom reference number generation strategy
	 * @return this instance for fluent chaining
	 */
	public ReferenceNumberGeneratorBuilder generator(ReferenceNumberGenerator custom) {
		this.custom = custom;
		return this;
	}

	@Override
	public ReferenceNumberGenerator build() {
		if (custom != null) {
			return custom;
		}
		if (random != null) {
			return buildContext.register(new RandomReferenceNumberGenerator(random));
		}
		return buildContext.register(new RandomReferenceNumberGenerator());
	}
}