TariffHeading.java

package com.tradecloud.domain.duties;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang3.StringUtils;

import javax.persistence.Column;
import javax.persistence.Embeddable;
import java.io.Serializable;

/**
 * The tariff heading describes what product it applies to.
 * It was decided that the tariff heading warranted its own object instead of a
 * simple String as there are validation rules around this heading which
 * requires examining the internal parts.
 * See https://connect.devstream.net/display/Dev/Tariffing for further
 * information.
 */
@Embeddable
public class TariffHeading implements Serializable, Cloneable {

    private static final long serialVersionUID = 1L;

    @Column(length = 4)
    private String part1;

    @Column(length = 2)
    private String part2;

    @Column(length = 2)
    private String part3;

    @Column(length = 2)
    private String part4;

    @Column(length = 2)
    private String part5;

    public static final int PART2_MAX_LENGTH = 2;
    public static final int PART3_MAX_LENGTH = 2;
    public static final int PARTS_MAX_LENGTH = 3;

    private transient boolean failOnInValidaTariff = true;
    private transient boolean validFormat = true;


    public TariffHeading() {
    }

    /**
     * Sets each part of the tariff heading by delegating to
     * {@link #setPart1(String)}, {@link #setPart2(String)}, and
     * {@link #setPart3(String)}.
     *
     * @param part1 the first part of the tariff heading
     * @param part2 the second part of the tariff heading
     * @param part3 the third part of the tariff heading
     */
    public TariffHeading(String part1, String part2, String part3) {
        setPart1(StringUtils.trim(part1));
        setPart2(StringUtils.trim(part2));
        setPart3(StringUtils.trim(part3));
    }

    public TariffHeading(String part1, String part2, String part3, String part4) {
        setPart1(StringUtils.trim(part1));
        setPart2(StringUtils.trim(part2));
        setPart3(StringUtils.trim(part3));
        setPart4(StringUtils.trim(part4));
    }

    public TariffHeading(TariffHeading tariffHeading) {
        setPart1(StringUtils.trim(tariffHeading.part1));
        setPart2(StringUtils.trim(tariffHeading.part2));
        setPart3(StringUtils.trim(tariffHeading.part3));
        setPart4(StringUtils.trim(tariffHeading.part4));
        setPart5(StringUtils.trim(tariffHeading.part5));
    }

    /**
     * Retrieves a string in the format of {@link #part1}.{@link #part2}.
     * {@link #part3}.
     * Examples: If part1 is null then this method will return "null.12.23". If
     * part2 is null then this method will return "1234.null.23". If part3 is
     * null then this method will return "1234.12.null".
     *
     * @return a string made up of each part of the tariff heading.
     */
    public String getTariffHeading() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(getPartValue(part1).trim()).append(".").append(getPartValue(part2).trim()).append(".").
                append(getPartValue(part3).trim());

        if (part4 != null) {
            stringBuilder.append("/").append(getPartValue(part4).trim());
        }

        return stringBuilder.toString();
    }

    public String getPart1() {
        return part1;
    }

    /**
     * Sets part1 if it is less than or equal to {@link #PART1_MAX_LENGTH}.
     *
     * @param part1 the first part of the tariff heading
     * @throws IllegalArgumentException if part1 is longer than {@link #PART1_MAX_LENGTH}
     */
    public void setPart1(String part1) {
        this.part1 = part1;
    }

    public String getPart2() {
        return part2;
    }

    /**
     * Sets part2 if it is less than or equal to {@link #PART2_MAX_LENGTH}.
     *
     * @param part2 the second part of the tariff heading
     * @throws IllegalArgumentException if part2 is longer than {@link #PART2_MAX_LENGTH}
     */
    public void setPart2(String part2) {
        validatePartLength(part2, "part2", PART2_MAX_LENGTH);
        this.part2 = part2;
    }

    public String getPart3() {
        return part3;
    }

    /**
     * Sets part3 if it is less than or equal to {@link #PART3_MAX_LENGTH}.
     *
     * @param part3 the third part of the tariff heading
     * @throws IllegalArgumentException if part3 is longer than {@link #PART3_MAX_LENGTH}
     */
    public void setPart3(String part3) {
        validatePartLength(part3, "part3", PART3_MAX_LENGTH);
        this.part3 = part3;
    }

    public String getPart4() {
        return part4;
    }

    public void setPart4(String part4) {
        this.part4 = part4;
    }

    public String getPart5() {
        return part5;
    }

    public void setPart5(String part5) {
        this.part5 = part5;
    }

    public String getParts() {
        return part1 == null ? null : getTariffHeading();
    }

    public final void setParts(String tariffCode) {
        if (tariffCode != null) {

            setPart1(getSchedulePart(tariffCode.trim(), 1));
            setPart2(getSchedulePart(tariffCode.trim(), 2));
            setPart3(getSchedulePart(tariffCode.trim(), 3));
            setPart4(getSchedulePart(tariffCode.trim(), 4));
            setPart5(getSchedulePart(tariffCode.trim(), 5));
        } else {
            part1 = null;
            part2 = null;
            part3 = null;
            part4 = null;
            part5 = null;
        }
    }

    @Override
    public TariffHeading clone() {
        try {
            TariffHeading clone = (TariffHeading) super.clone();
            clone.setPart1(part1 != null ? part1.trim() : null);
            clone.setPart2(part2 != null ? part2.trim() : null);
            clone.setPart3(part3 != null ? part3.trim() : null);
            clone.setPart4(part4 != null ? part4.trim() : null);
            clone.setPart5(part5 != null ? part5.trim() : null);

            return clone;
        } catch (CloneNotSupportedException ex) {
            return null;
        }
    }

    /**
     * Validates that the part field named partName is less than or equal to the
     * supplied maxLength.
     *
     * @param part      the part field to validate, this will be {@link #part1},
     *                  {@link #part2}, or {@link #part3}
     * @param partName  the name of the part being validated e.g. "part1"
     * @param maxLength the max length the supplied part field is allowed
     * @throws IllegalArgumentException if the part is greater than maxLength
     * @see #PART2_MAX_LENGTH
     * @see #PART3_MAX_LENGTH
     */
    public void validatePartLength(String part, String partName, int maxLength) {
        if (part != null && part.trim().length() > maxLength) {
            if (failOnInValidaTariff) {
                throw new IllegalArgumentException("The maximum length of Tariff Heading " + partName + " is " + maxLength);
            } else {
                validFormat = false;
            }
        }
    }

    public String getTariff() {

        if (part1 != null) {
            StringBuilder stringBuilder = new StringBuilder();

            appendPart(part1, stringBuilder, true, ".");
            appendPart(part2, stringBuilder, false, ".");
            appendPart(part3, stringBuilder, false, ".");
            if (part5 == null) {
                appendPart(part4, stringBuilder, false, "/");
            } else {
                appendPart(part4, stringBuilder, false, ".");
            }
            appendPart(part5, stringBuilder, false, "/");

            return stringBuilder.toString();
        }

        return null;
    }

    public String getPart3Tariff() {

        if (part1 != null) {
            StringBuilder stringBuilder = new StringBuilder();

            appendPart(part1.trim(), stringBuilder, true, ".");
            appendPart(part2.trim(), stringBuilder, false, ".");
            appendPart(part3.trim(), stringBuilder, false, ".");
            return stringBuilder.toString();
        }

        return null;
    }

    private void appendPart(String part, StringBuilder stringBuilder, boolean isFirst, String seperator) {
        if (getPartValue(part) != null && !getPartValue(part).equals("")) {
            if (!isFirst) {
                stringBuilder.append(seperator);
            }
            stringBuilder.append(getPartValue(part));
        }
    }

    public void setTariff(String tariff, boolean failOnInValidaTariff) {
        this.failOnInValidaTariff = failOnInValidaTariff;
        tariff = StringUtils.trim(tariff);
        setPart1(getSchedulePart(tariff, 1));
        setPart2(getSchedulePart(tariff, 2));
        setPart3(getSchedulePart(tariff, 3));
        setPart4(getSchedulePart(tariff, 4));
        setPart5(getSchedulePart(tariff, 5));
        if (!failOnInValidaTariff && !validFormat) {
            clearInvalidTariff();
            failOnInValidaTariff = true;
            validFormat = true;
        }

    }

    public void setTariff(String tariff) {
        this.failOnInValidaTariff = false;
        String tariffHeading = StringUtils.trim(tariff);
        setPart1(getSchedulePart(tariffHeading, 1));
        setPart2(getSchedulePart(tariffHeading, 2));
        setPart3(getSchedulePart(tariffHeading, 3));
        setPart4(getSchedulePart(tariffHeading, 4));
        setPart5(getSchedulePart(tariffHeading, 5));
        if (!failOnInValidaTariff && !validFormat) {
            clearInvalidTariff();
            failOnInValidaTariff = true;
            validFormat = true;
        }
    }

    private void clearInvalidTariff() {
        setPart1(null);
        setPart2(null);
        setPart3(null);
        setPart4(null);
        setPart5(null);
    }

    /**
     * @param tariffHeading Gets the third part of the tariffHeading
     *                      Example, "1234.12.34" returns 34
     *                      "1234.12." returns null
     */
    private String getScheduleThirdPart(String tariffHeading) {
        if (tariffHeading != null && tariffHeading.contains(".")) {
            String[] parts = tariffHeading.split("\\.");
            if (parts.length == PARTS_MAX_LENGTH) {
                String part3 = parts[2];
                if (part3.contains("_")) {
                    part3 = part3.replaceAll("_", " ");
                }
                return part3;
            }
        }
        return null;
    }

    private String getSchedulePart(String tariffHeading, int part) {

        String schedulePart = null;
        String part3 = null;
        if (tariffHeading != null && part == 3 && tariffHeading.contains("/")) {
            tariffHeading = tariffHeading.substring(0, tariffHeading.indexOf("/"));
        }
        if (tariffHeading != null && part == 4 && tariffHeading.contains("/")) {
            tariffHeading = tariffHeading.endsWith("/") ? tariffHeading.substring(0, tariffHeading.length() - 1) : tariffHeading;
            String[] parts = tariffHeading.split("/");
            schedulePart = parts[1];
        }

        if (tariffHeading != null && tariffHeading.contains(".") && !tariffHeading.contains("..")) {
            String[] parts = tariffHeading.split("\\.");

            switch (part) {
                case 1:
                    schedulePart = parts[0].trim();
                    break;
                case 2:
                    schedulePart = parts[1].trim();
                    break;
                case 3:
                    if (parts.length > 2) {
                        schedulePart = parts[2].trim();
                    }
                    break;
                case 4:
                    if (parts.length > 3 && !parts[3].contains("/")) {
                        schedulePart = parts[3].trim();
                    }
                    if (parts.length > 3 && parts[3].contains("/")) {
                        schedulePart = parts[3].split("/")[0];
                    }
                    break;
                case 5:
                    if (parts.length > 3 && !parts[3].contains("/")) {
                        schedulePart = parts[3].trim();
                    }
                    if (parts.length > 3 && parts[3].contains("/")) {
                        schedulePart = parts[3].split("/")[1];
                    }
                    break;
            }

            if (schedulePart != null) {
                if (schedulePart.contains("_")) {
                    schedulePart = schedulePart.replaceAll("_", " ");
                }
            }
        }

        return schedulePart;
    }

    private String getPartValue(String part) {
        return part == null ? "" : part;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof TariffHeading)) {
            return false;
        }

        TariffHeading other = (TariffHeading) obj;
        return new EqualsBuilder().
                append(StringUtils.trim(part1), StringUtils.trim(other.part1)).
                append(StringUtils.trim(part2), StringUtils.trim(other.part2)).
                append(StringUtils.trim(part3), StringUtils.trim(other.part3)).
                append(StringUtils.trim(part4), StringUtils.trim(other.part4)).
                append(StringUtils.trim(part5), StringUtils.trim(other.part5)).
                isEquals();
    }
}