com.intuit.wasabi.tests.model.Bucket.java Source code

Java tutorial

Introduction

Here is the source code for com.intuit.wasabi.tests.model.Bucket.java

Source

/*******************************************************************************
 * Copyright 2016 Intuit
 *
 * 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 com.intuit.wasabi.tests.model;

import com.google.gson.FieldAttributes;
import com.intuit.wasabi.tests.library.util.serialstrategies.DefaultNameExclusionStrategy;
import com.intuit.wasabi.tests.library.util.serialstrategies.SerializationStrategy;

import java.lang.reflect.Field;
import java.util.Objects;

/**
 * A simplified Bucket class which can be instantiated in various ways and
 * modified directly via methods or public access to make it easy to test around with it.
 */
public class Bucket extends ModelItem {

    /**
     * The serialization strategy for comparisons and JSON serialization.
     */
    private static SerializationStrategy serializationStrategy = new DefaultNameExclusionStrategy();
    /** The experiment Label. Required, unique among experiment. */
    public String label;
    /** The experiment ID. Required. */
    public String experimentID;
    /** The bucket's allocation percentage. Required. */
    public double allocationPercent;
    /** The bucket's control group status. Required. Only one bucket per experiment must be the control group. */
    public boolean isControl;
    /** The bucket's description. Optional. */
    public String description;
    /** The bucket's payload. Optional. */
    public String payload;
    /** The bucket's state. Optional. */
    public String state;

    /**
     * Creates a bucket without any members set.
     */
    public Bucket() {
    }

    /**
     * Creates a deep copy of the {@code other} Bucket.
     *
     * @param other a Bucket to copy.
     */
    public Bucket(Bucket other) {
        update(other);
    }

    /**
     * Creates a bucket with default values for all required arguments.
     * By default all buckets created are no control-buckets, so consider
     * using an appropriate constructor or setting the control-option for one
     * bucket.
     *
     * @param label the bucket label (will be prefixed)
     * @param experiment the experiment
     * @param allocationPercent the percentage that should go into this bucket
     */
    public Bucket(String label, Experiment experiment, double allocationPercent) {
        this(label, experiment.id, allocationPercent);
    }

    /**
     * Creates a bucket with default values for all required arguments.
     * By default all buckets created are no control-buckets, so consider
     * using an appropriate constructor or setting the control-option for one
     * bucket.
     *
     * @param label the bucket label (will be prefixed)
     * @param experimentId the experiment id
     * @param allocationPercent the percentage that should go into this bucket
     */
    public Bucket(String label, String experimentId, double allocationPercent) {
        this(label, experimentId, allocationPercent, false);
    }

    /**
     * Creates a bucket with default values for all required arguments.
     *
     * @param label the bucket label (will be prefixed)
     * @param experiment the experiment
     * @param allocationPercent the percentage that should go into this bucket
     * @param isControl true if this bucket is the control group, else false
     */
    public Bucket(String label, Experiment experiment, double allocationPercent, boolean isControl) {
        this(label, experiment.id, allocationPercent, isControl, null);
    }

    /**
     * Creates a bucket with default values for all required arguments.
     *
     * @param label the bucket label (will be prefixed)
     * @param experimentId the experiment id
     * @param allocationPercent the percentage that should go into this bucket
     * @param isControl true if this bucket is the control group, else false
     */
    public Bucket(String label, String experimentId, double allocationPercent, boolean isControl) {
        this(label, experimentId, allocationPercent, isControl, null);
    }

    /**
     * Creates a bucket with the supplied values.
     *
     * @param label the bucket label (will be prefixed)
     * @param experiment the experiment
     * @param allocationPercent the percentage that should go into this bucket
     * @param isControl true if this bucket is the control group, else false
     * @param description the bucket description
     */
    public Bucket(String label, Experiment experiment, double allocationPercent, boolean isControl,
            String description) {
        this(label, experiment.id, allocationPercent, isControl, description, null);
    }

    /**
     * Creates a bucket with the supplied values.
     *
     * @param label the bucket label (will be prefixed)
     * @param experimentId the experiment id
     * @param allocationPercent the percentage that should go into this bucket
     * @param isControl true if this bucket is the control group, else false
     * @param description the bucket description
     */
    public Bucket(String label, String experimentId, double allocationPercent, boolean isControl,
            String description) {
        this(label, experimentId, allocationPercent, isControl, description, null);
    }

    /**
     * Creates a bucket with the supplied values.
     *
     * @param label the bucket label (will be prefixed)
     * @param experiment the experiment
     * @param allocationPercent the percentage that should go into this bucket
     * @param isControl true if this bucket is the control group, else false
     * @param description the bucket description
     * @param payload the payload for this bucket
     */
    public Bucket(String label, Experiment experiment, double allocationPercent, boolean isControl,
            String description, String payload) {
        this(label, experiment.id, allocationPercent, isControl, description, payload, null);
    }

    /**
     * Creates a bucket with the supplied values.
     *
     * @param label the bucket label (will be prefixed)
     * @param experimentId the experiment id
     * @param allocationPercent the percentage that should go into this bucket
     * @param isControl true if this bucket is the control group, else false
     * @param description the bucket description
     * @param payload the payload for this bucket
     */
    public Bucket(String label, String experimentId, double allocationPercent, boolean isControl,
            String description, String payload) {
        this(label, experimentId, allocationPercent, isControl, description, payload, null);
    }

    /**
     * Creates a bucket with the supplied values.
     *
     * @param label the bucket label (will be prefixed)
     * @param experimentID the experimentID
     * @param allocationPercent the percentage that should go into this bucket
     * @param isControl true if this bucket is the control group, else false
     * @param description the bucket description
     * @param payload the payload for this bucket
     * @param state   the desired state
     */
    public Bucket(String label, String experimentID, double allocationPercent, boolean isControl,
            String description, String payload, String state) {
        this.setLabel(label).setExperimentID(experimentID).setAllocationPercent(allocationPercent)
                .setControl(isControl).setDescription(description).setPayload(payload).setState(state);
    }

    /**
     * Sets the label to {@code label} and returns this instance.
     * Allows for builder patterns.
     *
     * @param label the label to set
     * @return this
     */
    public Bucket setLabel(String label) {
        this.label = label;
        return this;
    }

    /**
     * Sets the experiment id and returns this instance.
     * Allows for builder patterns.
     *
     * @param experimentID the experiment id to be set
     * @return this
     */
    public Bucket setExperimentID(String experimentID) {
        this.experimentID = experimentID;
        return this;
    }

    /**
     * Sets the experiment id and returns this instance.
     * Allows for builder patterns.
     *
     * @param experiment the experiment to be set
     * @return this
     */
    public Bucket setExperimentID(Experiment experiment) {
        return this.setExperimentID(experiment.id);
    }

    /**
     * Alias for setExperimentId.
     * Sets the experiment id and returns this instance.
     * Allows for builder patterns.
     *
     * @param experiment the experiment to be set
     * @return this
     */
    public Bucket setExperiment(Experiment experiment) {
        return this.setExperimentID(experiment);
    }

    /**
     * Alias for setExperimentId.
     * Sets the experiment id and returns this instance.
     * Allows for builder patterns.
     *
     * @param experimentID the experiment id to be set
     * @return this
     */
    public Bucket setExperiment(String experimentID) {
        return this.setExperimentID(experimentID);
    }

    /**
     * Sets the allocation percentage {@code (0, 1]} and returns this instance.
     * Allows for builder patterns.
     *
     * @param allocationPercent the percentage to be set
     * @return this
     */
    public Bucket setAllocationPercent(double allocationPercent) {
        this.allocationPercent = allocationPercent;
        return this;
    }

    /**
     * Sets the control group status and returns this instance.
     * Allows for builder patterns.
     *
     * @param isControl true if this shall be the control group
     * @return this
     */
    public Bucket setControl(boolean isControl) {
        this.isControl = isControl;
        return this;
    }

    /**
     * Sets the description and returns this instance.
     * Allows for builder patterns.
     *
     * @param description the description to be set
     * @return this
     */
    public Bucket setDescription(String description) {
        this.description = description;
        return this;
    }

    /**
     * Sets the payload and returns this instance.
     * Allows for builder patterns.
     *
     * @param payload the payload to be set
     * @return this
     */
    public Bucket setPayload(String payload) {
        this.payload = payload;
        return this;
    }

    /**
     * Sets the state and returns this instance.
     * Allows for builder patterns.
     *
     * @param state the state to be set
     * @return this
     */
    public Bucket setState(String state) {
        this.state = state;
        return this;
    }

    @Override
    public SerializationStrategy getSerializationStrategy() {
        return Bucket.serializationStrategy;
    }

    @Override
    public void setSerializationStrategy(SerializationStrategy serializationStrategy) {
        Bucket.serializationStrategy = serializationStrategy;
    }

    /**
     * This is a workaround for the allocation percentage! If they are not equal, they are compared again
     * only taking the first 5 decimal digits into account, which should usually be more than enough.
     *
     * @param other another object
     * @return true if both are equal, with the exception of the allocation percentage as described above.
     */
    @Override
    public boolean equals(Object other) {
        if (super.equals(other))
            return true;
        LOGGER.info("Retrying bucket comparison, maybe allocation percentages have floating point inaccuracies.");
        if (!this.getClass().isInstance(other)) {
            return false;
        }

        for (Field field : this.getClass().getFields()) {
            if (!this.getSerializationStrategy().shouldSkipField(new FieldAttributes(field))) {
                try {
                    boolean thisFieldEquals = Objects.equals(field.get(this), field.get(other));
                    if (!thisFieldEquals && field.getName().equals("allocationPercent")) {
                        LOGGER.debug("Retrying comparison of allocation percentages: " + this.allocationPercent
                                + " and " + ((Bucket) other).allocationPercent);
                        String allocThis = String.valueOf(this.allocationPercent);
                        String allocOther = String.valueOf(((Bucket) other).allocationPercent);
                        allocThis = allocThis.substring(0,
                                Math.min(allocThis.indexOf(".") + 6, allocThis.length()));
                        allocOther = allocOther.substring(0,
                                Math.min(allocOther.indexOf(".") + 6, allocOther.length()));
                        LOGGER.debug("\tComparing only " + allocThis + " and " + allocOther);
                        thisFieldEquals = Objects.equals(allocThis, allocOther);
                        LOGGER.debug("\tResult: " + (thisFieldEquals ? "" : "not ") + "equal.");
                    }
                    if (!thisFieldEquals) {
                        return false;
                    }
                } catch (IllegalAccessException ignored) {
                    // do nothing, just skip this field
                }
            }
        }
        return true;
    }

}