Version.java

package org.europa.together.domain;

import java.util.Objects;
import org.europa.together.application.LogbackLogger;
import org.europa.together.business.Logger;
import org.europa.together.exceptions.MisconfigurationException;
import org.europa.together.utils.StringUtils;
import org.europa.together.utils.Validator;

/**
 * Data class for program version numbers, based on semantic versioning. This
 * implementation allwos to compare and sort version numbers.<br>
 * Pattern: Major.Minor.Patch-Label (Patch and Label are optional)<br>
 * <code>
 *  true:  equals(1.2.3-SNAPSHOT : 1.2.3-SNAPSHOT);
 *  true:  equals(1.2.3-LABLE : 1.2.3-SNAPSHOT);
 *  true:  equals(1.0 : 1.0.0);
 *  false: equals(1.0 : 1.0.1);
 *  false: equals(1.0-LABEL : 1.0);
 *  A greater B: compareTo(2.0 : 1.0);
 *  A greater B: compareTo(1.1 : 1.0);
 *  A greater B: compareTo(1.0.1 : 1.0.0);
 *  A greater B: compareTo(2.1 : 1.1);
 *  A equal B:   compareTo(2.1.1 : 2.1.1-SNAPSHOT);
 * </code> The only recomennded label is SNAPSHOT, because labels are not part
 * of comparing.
 *
 * <b>Definition:</b><br>
 * A version of a software artifact is always uniqe. Artifacts can have
 * different versions (1:n relation). That means when an artifact a exist two
 * times and both are not identical, the first artifact has a different version
 * than the second artifact. Both artifacts can not have the same version. If
 * both artifacts with the same name have the same version it is impossible to
 * distinguish them.
 */
public class Version implements Comparable<Version> {

    private static final Logger LOGGER = new LogbackLogger(Version.class);

    private int major = -1;
    private int minor = -1;
    private int patch = -1;
    private String label = "";

    /**
     * Constructor.
     *
     * @param version as String
     * @throws org.europa.together.exceptions.MisconfigurationException
     */
    public Version(final String version)
            throws MisconfigurationException {
        if (!Validator.validate(version, Validator.SEMANTIC_VERSION_NUMBER)) {
            String msg = "The version number " + version
                    + " do not match the Pattern: [1].[2].[3]-[LABEL].";
            throw new MisconfigurationException(msg);
        }
        String[] fragments = version.split("\\.");
        LOGGER.log("Fragments: " + fragments.length, LogLevel.DEBUG);

        major = Integer.parseInt(fragments[0]);
        if (fragments.length > 1) {
            if (!fragments[1].contains("-")) {
                minor = Integer.parseInt(fragments[1]);
            } else {
                LOGGER.log("Lable after minor.", LogLevel.DEBUG);
                String[] optional = fragments[1].split("-");
                minor = Integer.parseInt(optional[0]);
                label = optional[1];
            }
        }
        if (fragments.length > 2) {
            String[] optional = fragments[2].split("-");

            patch = Integer.parseInt(optional[0]);
            if (optional.length == 2) { //prevent index out of bound exception
                label = optional[1];
            }
        }
        LOGGER.log("Version: " + toString(), LogLevel.DEBUG);
    }

    //<editor-fold defaultstate="collapsed" desc="Getter / Setter">
    /**
     * Return the Major section of a version number as int. MANDANTORY.
     *
     * @return Major as int
     */
    public int getMajor() {
        return major;
    }

    /**
     * Return the Minor section of a version number as int. MANDANTORY.
     *
     * @return Minor as int
     */
    public int getMinor() {
        return minor;
    }

    /**
     * Return the Patch level section of a version number as int. OPTIONAL. If
     * the patch level not exist the method return -1.
     *
     * @return Patch as int
     */
    public int getPatch() {
        return patch;
    }

    /**
     * Return the Label section of a version number as String. OPTIONAL. If the
     * label not exist the method return an empty String.
     *
     * @return Label as String
     */
    public String getLabel() {
        return label;
    }

    /**
     * Return the whole version number as String.
     *
     * @return version as String
     */
    public String getVersion() {
        String version = Integer.toString(major);
        if (minor != -1) {
            version = version + "." + minor;
        }
        if (patch != -1) {
            version = version + "." + patch;
        }
        if (!StringUtils.isEmpty(label)) {
            version = version + "-" + label;
        }
        return version;
    }
    //</editor-fold>

    @Override
    public int compareTo(final Version o) {
        // -1:smaller | 0:equal | 1:greater
        int compare;
        if (this.major > o.major) {
            compare = 1;
        } else if (this.major < o.major) {
            compare = -1;
        } else {
            if (this.minor > o.minor) {
                compare = 1;
            } else if (this.minor < o.minor) {
                compare = -1;
            } else {

                if (this.patch > o.patch) {
                    compare = 1;
                } else if (this.patch < o.patch) {
                    compare = -1;
                } else {
                    compare = 0;
                }
            }
        }
        return compare;
    }

    @Override
    public boolean equals(final Object object) {
        boolean success = false;
        if (object != null && object instanceof Version) {
            if (this == object) {
                success = true;
            } else {
                final Version other = (Version) object;
                if (Objects.equals(this.major, other.major)
                        && Objects.equals(this.minor, other.minor)
                        && ((this.patch == -1 || this.patch == 0)
                        && (other.patch == -1 || other.patch == 0)
                        || Objects.equals(this.patch, other.patch))) {
                    success = true;
                }
            }
        }
        return success;
    }

    @Override
    public int hashCode() {
        int hash = Objects.hashCode(this.major);
        hash += Objects.hashCode(this.minor);
        hash += Objects.hashCode(this.patch);
        return hash;
    }

    @Override
    public String toString() {
        return "Version{" + getVersion() + "}";
    }
}