Step.java
/*
* Copyright 2016 the Cook-E development team
*
* This file is part of Cook-E.
*
* Cook-E is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Cook-E is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Cook-E. If not, see <http://www.gnu.org/licenses/>.
*/
package org.cook_e.data;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import org.joda.time.Duration;
import org.joda.time.ReadableDuration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
/*
* Class representing a step in a recipe
*
* Every step has a human-readable description, an action category, list of ingredients, and an
* estimated time the step takes to perform. None of these values may be null.
*
* Objects of this class are immutable.
*/
public final class Step implements Parcelable {
/**
* A human-readable mDescription of the actions involved in this step
*/
@NonNull
private final String mDescription;
/**
* The expected time required to complete this step
*/
@NonNull
private final Duration mTime;
/**
* The ingredients required for this step
*/
@NonNull
private final List<String> mIngredients;
/**
* Whether this step can be done simultaneously
*/
private final boolean mSimultaneous;
/**
* The set of all string patterns in the description
* that indicates this step can be done simultaneously
*/
private static final Set<String> SIMULTANEOUS_PATTERNS = Collections.unmodifiableSet(new HashSet<String>() {{
add("boil");
add("bake");
add("microwave");
}});
/**
* Creates a Step
* @param ingredients the ingredients required for this step
* @param description a human-readable description of this step
* @param duration an estimate of the time required to complete this step
* @param isSimultaneous if this step can be done simultaneously
* @throws NullPointerException if any parameter is null
*/
public Step(@NonNull List<String> ingredients, @NonNull String description,
@NonNull ReadableDuration duration, boolean isSimultaneous) {
Objects.requireNonNull(ingredients, "ingredients must not be null");
Objects.requireNonNull(description, "description must not be null");
Objects.requireNonNull(duration, "duration must not be null");
mDescription = description;
mTime = duration.toDuration();
mIngredients = new ArrayList<>(ingredients);
this.mSimultaneous = isSimultaneous;
}
/**
* Creates a Step without knowing if step can be done simultaneously
* @param ingredients the ingredients required for this step
* @param description a human-readable description of this step
* @param duration an estimate of the time required to complete this step
* @throws NullPointerException if any parameter is null
*/
public Step(@NonNull List<String> ingredients, @NonNull String description,
@NonNull ReadableDuration duration) {
this(ingredients, description, duration, isSimultaneousParser(description));
}
/**
* Identifies if a step can be done simultaneously
* @param description description of the step
* @return true if this step can be done simultaneously, false otherwise
*/
private static boolean isSimultaneousParser(@NonNull String description) {
Objects.requireNonNull(description, "description must not be null");
String[] words = description.split("\\s+");
for (String word : words) {
String lowerCaseWord = word.toLowerCase(Locale.US);
for (String pattern : SIMULTANEOUS_PATTERNS) {
if (lowerCaseWord.contains(pattern)) return true;
}
}
return false;
}
/**
* Returns the description of this step
* @return the description
*/
@NonNull
public String getDescription() {
return mDescription;
}
/**
* Returns the duration of this step
* @return the duration
*/
@NonNull
public ReadableDuration getTime() {
return mTime;
}
/**
* Returns the duration of this step, truncated to minute precision
*
* The behavior of this method is undefined if the number of minutes in the duration of this
* step is greater than Integer.MAX_VALUE.
*
* @return the duration of this step, in minutes
*/
public int getDurationMinutes() {
return (int) mTime.getStandardMinutes();
}
/**
* Returns the ingredients that this step requires
* @return the ingredients
*/
@NonNull
public List<String> getIngredients() {
return new ArrayList<>(mIngredients);
}
/**
* Returns if this step can be done simultaneously
* @return true if this step can be done simultaneously
*/
public boolean isSimultaneous() { return mSimultaneous; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final Step step = (Step) o;
if (mSimultaneous != step.mSimultaneous) return false;
if (!mDescription.equals(step.mDescription)) return false;
if (!mTime.equals(step.mTime)) return false;
return mIngredients.equals(step.mIngredients);
}
@Override
public int hashCode() {
int result = mDescription.hashCode();
result = 31 * result + mTime.hashCode();
result = 31 * result + mIngredients.hashCode();
result = 31 * result + (mSimultaneous ? 1 : 0);
return result;
}
@Override
public String toString() {
return "Step{" +
"mDescription='" + mDescription + '\'' +
", mTime=" + mTime +
", mIngredients=" + mIngredients +
", mSimultaneous=" + mSimultaneous +
'}';
}
// Parceling section
public static final Parcelable.Creator<Step> CREATOR = new Parcelable.Creator<Step>() {
@Override
public Step createFromParcel(Parcel source) {
final String description = source.readString();
final Duration duration = (Duration) source.readSerializable();
final Boolean simultaneous = (Boolean) source.readSerializable();
final List<String> ingredients = new ArrayList<>();
source.readStringList(ingredients);
return new Step(ingredients, description, duration, simultaneous);
}
@Override
public Step[] newArray(int size) {
return new Step[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mDescription);
dest.writeSerializable(mTime);
dest.writeSerializable(mSimultaneous);
dest.writeStringList(mIngredients);
}
}