Sms.java

package fr.sii.ogham.sms.message;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import fr.sii.ogham.core.message.Message;
import fr.sii.ogham.core.message.capability.HasContentFluent;
import fr.sii.ogham.core.message.capability.HasRecipients;
import fr.sii.ogham.core.message.capability.HasRecipientsFluent;
import fr.sii.ogham.core.message.capability.HasToFluent;
import fr.sii.ogham.core.message.content.Content;
import fr.sii.ogham.core.message.content.StringContent;
import fr.sii.ogham.core.message.fluent.SingleContentBuilder;
import fr.sii.ogham.core.util.Loggable;
import fr.sii.ogham.core.util.EqualsBuilder;
import fr.sii.ogham.core.util.HashCodeBuilder;
import fr.sii.ogham.core.util.StringUtils;

/**
 * SMS message that contains the following information:
 * <ul>
 * <li>The content of the SMS (see {@link Content} and sub classes for more
 * information)</li>
 * <li>The sender information (name and phone number). Name is optional</li>
 * <li>The list of recipients (name and phone number). Name is optional</li>
 * </ul>
 * 
 * @author Aurélien Baudet
 *
 */
public class Sms implements Message, HasContentFluent<Sms>, HasRecipients<Recipient>, HasRecipientsFluent<Sms, Recipient>, HasToFluent<Sms>, Loggable {
	/**
	 * The number of the sender
	 */
	private Sender from;

	/**
	 * The list of recipients of the SMS
	 */
	private List<Recipient> recipients;

	/**
	 * The content of the SMS
	 */
	private Content content;

	private final SingleContentBuilder<Sms> messageBuilder;

	public Sms() {
		super();
		recipients = new ArrayList<>();
		messageBuilder = new SingleContentBuilder<>(this);
	}

	// ----------------------- Getter/Setters -----------------------//

	/**
	 * Get the sender of the SMS
	 * 
	 * @return SMS sender
	 */
	public Sender getFrom() {
		return from;
	}

	/**
	 * Set the sender of the SMS
	 * 
	 * @param from
	 *            SMS sender to set
	 */
	public void setFrom(Sender from) {
		this.from = from;
	}

	/**
	 * Set the sender phone number of the SMS as string.
	 * 
	 * @param phoneNumber
	 *            SMS sender phone number to set
	 */
	public void setFrom(String phoneNumber) {
		setFrom(new Sender(phoneNumber));
	}

	/**
	 * Get the recipients (to) of the SMS
	 * 
	 * @return SMS recipients
	 */
	public List<Recipient> getRecipients() {
		return recipients;
	}

	/**
	 * Set the recipients (to) of the SMS
	 * 
	 * @param to
	 *            SMS recipients
	 */
	public void setRecipients(List<Recipient> to) {
		this.recipients = to;
	}

	@Override
	public Content getContent() {
		if (content != null) {
			return content;
		}
		// NOTE: normally it can't be null but EqualsVerifier uses reflection to
		// set it to null
		if (messageBuilder != null) {
			return messageBuilder.build();
		}
		return null;
	}

	@Override
	public void setContent(Content content) {
		this.content = content;
	}

	// ----------------------- Fluent API -----------------------//

	/**
	 * Set the content of the message.
	 * 
	 * <p>
	 * You can use this method to explicitly set a particular {@link Content}
	 * instance. For example:
	 * 
	 * <pre>
	 * {@code
	 * .content(new TemplateContent("path/to/template", obj));
	 * }
	 * </pre>
	 * 
	 * <p>
	 * If you prefer, you can instead use the fluent API to set the message of
	 * the SMS:
	 * 
	 * <pre>
	 * {@code
	 * .message().template("path/to/template", obj)
	 * }
	 * </pre>
	 * 
	 * @param content
	 *            the content of the message
	 * @return this instance for fluent chaining
	 * @see #message()
	 */
	public Sms content(Content content) {
		setContent(content);
		return this;
	}

	/**
	 * Set the content of the message. This is a shortcut to
	 * 
	 * <pre>
	 * {@code .content(new StringContent(content))}
	 * </pre>
	 * 
	 * <p>
	 * If you prefer, you can instead use the fluent API to set the message of
	 * the SMS:
	 * 
	 * <pre>
	 * {@code
	 * .message().string(content)
	 * }
	 * </pre>
	 * 
	 * 
	 * @param content
	 *            the content of the message
	 * @return this instance for fluent chaining
	 * @see #message()
	 */
	public Sms content(String content) {
		return content(new StringContent(content));
	}

	/**
	 * Set the message of the SMS.
	 * 
	 * <p>
	 * This method provides fluent chaining to guide developer. It has the same
	 * effect has using {@link #content(Content)}.
	 * 
	 * <p>
	 * If you also call either {@link #content(Content)},
	 * {@link #content(String)} or {@link #setContent(Content)} then this method
	 * has no effect.
	 * 
	 * @return the builder for building text part
	 * @since 3.0.0
	 */
	public SingleContentBuilder<Sms> message() {
		return messageBuilder;
	}

	/**
	 * Set the sender.
	 * 
	 * @param from
	 *            the sender
	 * @return this instance for fluent chaining
	 */
	public Sms from(Sender from) {
		setFrom(from);
		return this;
	}

	/**
	 * Set the list of recipients of the message
	 *
	 * @param recipients
	 *            the list of recipients of the message to set
	 * @return this instance for fluent chaining
	 */
	@Override
	public Sms recipients(List<Recipient> recipients) {
		setRecipients(recipients);
		return this;
	}

	/**
	 * Add a recipient for the message
	 *
	 * @param recipients
	 *            one or several recipient to add
	 * @return this instance for fluent chaining
	 */
	@Override
	public Sms recipient(Recipient... recipients) {
		this.recipients.addAll(Arrays.asList(recipients));
		return this;
	}

	/**
	 * Set the sender using the phone number as string.
	 * 
	 * @param phoneNumber
	 *            the sender number
	 * @return this instance for fluent chaining
	 */
	public Sms from(String phoneNumber) {
		return from(null, phoneNumber);
	}

	/**
	 * Set the sender using the phone number.
	 * 
	 * @param phoneNumber
	 *            the sender number
	 * @return this instance for fluent chaining
	 */
	public Sms from(PhoneNumber phoneNumber) {
		return from(null, phoneNumber);
	}

	/**
	 * Set the sender using the phone number as string.
	 * 
	 * @param name
	 *            the name of the sender
	 * @param phoneNumber
	 *            the sender number
	 * @return this instance for fluent chaining
	 */
	public Sms from(String name, String phoneNumber) {
		return from(name, new PhoneNumber(phoneNumber));
	}

	/**
	 * Set the sender using the phone number.
	 * 
	 * @param name
	 *            the name of the sender
	 * @param phoneNumber
	 *            the sender number
	 * @return this instance for fluent chaining
	 */
	public Sms from(String name, PhoneNumber phoneNumber) {
		return from(new Sender(name, phoneNumber));
	}

	/**
	 * Add a recipient specifying the phone number.
	 * 
	 * @param numbers
	 *            one or several recipient numbers
	 * @return this instance for fluent chaining
	 */
	public Sms to(PhoneNumber... numbers) {
		for (PhoneNumber number : numbers) {
			to((String) null, number);
		}
		return this;
	}

	/**
	 * Add a recipient specifying the phone number as string.
	 * 
	 * @param numbers
	 *            one or several recipient numbers
	 * @return this instance for fluent chaining
	 */
	public Sms to(String... numbers) {
		for (String num : numbers) {
			to(new Recipient(num));
		}
		return this;
	}

	/**
	 * Add a recipient specifying the name and the phone number.
	 * 
	 * @param name
	 *            the name of the recipient
	 * @param number
	 *            the number of the recipient
	 * @return this instance for fluent chaining
	 */
	public Sms to(String name, PhoneNumber number) {
		to(new Recipient(name, number));
		return this;
	}

	/**
	 * Add a recipient.
	 * 
	 * @param recipients
	 *            one or several recipients to add
	 * @return this instance for fluent chaining
	 */
	public Sms to(Recipient... recipients) {
		recipient(recipients);
		return this;
	}

	// ----------------------- Utilities -----------------------//

	/**
	 * Converts a list of phone numbers to a list of recipients.
	 * 
	 * @param to
	 *            the list of phone numbers
	 * @return the list of recipients
	 */
	public static Recipient[] toRecipient(PhoneNumber[] to) {
		Recipient[] addresses = new Recipient[to.length];
		int i = 0;
		for (PhoneNumber t : to) {
			addresses[i] = new Recipient(t);
			i++;
		}
		return addresses;
	}

	/**
	 * Converts a list of string to a list of recipients.
	 * 
	 * @param to
	 *            the list of phone numbers as string
	 * @return the list of recipients
	 */
	public static Recipient[] toRecipient(String[] to) {
		Recipient[] addresses = new Recipient[to.length];
		int i = 0;
		for (String t : to) {
			addresses[i] = new Recipient(t);
			i++;
		}
		return addresses;
	}

	@Override
	public String toString() {
		return toString(false);
	}

	@Override
	public String toLogString() {
		return toString(true);
	}

	@Override
	public int hashCode() {
		return new HashCodeBuilder().append(from, recipients, getContent()).hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		return new EqualsBuilder(this, obj).appendFields("from", "recipients", "content").isEqual();
	}

	private String toString(boolean includeContent) {
		StringBuilder builder = new StringBuilder();
		builder.append("Sms message\r\nFrom: ").append(from);
		builder.append("\r\nTo: ").append(StringUtils.join(recipients, ", "));
		builder.append("\r\n----------------------------------\r\n").append(includeContent ? getContent() : "<Content skipped>");
		builder.append("\r\n==================================\r\n");
		return builder.toString();
	}
}