Recipe.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.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.joda.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/*
* Represents a recipe
*
* Has an ordered list of steps, a title, and an author.
* No field may be null.
*/
public final class Recipe extends DatabaseObject implements Parcelable {
/**
* The steps that this recipe contains
*/
@NonNull
private List<Step> mSteps;
/**
* The title of this recipe
*/
@NonNull
private String mTitle;
/**
* The author of this recipe
*/
@NonNull
private String mAuthor;
/**
* The image associated with this recipe, or null if the recipe has no image
*/
@Nullable
private Bitmap mImage;
/**
* Constructor
*@param title the title of the recipe, must not be null
*@param author the author of the recipe, must not be null
*@param steps the list of steps for the recipe, must not be null
*@throws NullPointerException if any of the parameters passed to it are null
*/
public Recipe(@NonNull String title, @NonNull String author, @NonNull List<Step> steps) {
super();
Objects.requireNonNull(title, "title must not be null");
Objects.requireNonNull(author, "author must not be null");
Objects.requireNonNull(steps, "steps must not be null");
mSteps = new ArrayList<>(steps);
mTitle = title;
mAuthor = author;
mImage = null;
}
/**
* Creates a deep copy of another recipe. No part of the new recipe will be modifiable from the
* old one.
* @param other the recipe to copy from
*/
public Recipe(Recipe other) {
// Step, and String are immutable, so they do not need to be copied.
// The delegated constructor copies the list of steps.
this(other.getTitle(), other.getAuthor(), other.getSteps());
setImage(other.getImage());
setObjectId(other.getObjectId());
}
/**
* Sets the steps in this recipe
* @param steps the steps to set
* @throws NullPointerException if steps is null
*/
public void setSteps(@NonNull List<Step> steps) {
Objects.requireNonNull(steps, "steps must not be null");
mSteps = new ArrayList<>(steps);
}
/**
* Set the ith step in this recipe
* The original step will be replaced by the new one
* Do nothing if index is less than 0 or greeter than the max index
* @param step the step to set
* @param i the target index of new step, index start from 0
* @throws NullPointerException if step is null
*/
public void setStep(@NonNull Step step, int i) {
Objects.requireNonNull(step, "step must not be null");
if (i >= 0 && i < mSteps.size()) {
mSteps.set(i, step);
}
}
/**
* Add step to end of the list of steps
* @param step the step to add
* @throws NullPointerException if step is null
*/
public void addStep(@NonNull Step step) {
Objects.requireNonNull(step, "step must not be null");
mSteps.add(step);
}
/**
* Returns the steps in this recipe
* @return the steps
*/
@NonNull
public List<Step> getSteps() {
return new ArrayList<>(mSteps);
}
/**
* Returns a List of all the ingredients required by all the steps of the recipe
*/
@NonNull
public List<String> getIngredients() {
List<String> ings = new ArrayList<>();
for (Step s: mSteps) {
for (String ingredient: s.getIngredients()) {
ings.add(ingredient);
}
}
return ings;
}
/**
* Returns the total estimated time of all the recipe's mSteps
*/
@NonNull
public Duration getTotalTime() {
Duration time = Duration.ZERO;
for (Step s: mSteps) {
time = time.withDurationAdded(s.getTime(), 1);
}
return time;
}
@NonNull
public String getTitle() {
return mTitle;
}
@NonNull
public String getAuthor() {
return mAuthor;
}
/**
* Remove the ith step in this recipe.
* All steps after it will be moved one step forward.
* Doesn't modify recipe if index is less than 0 or greater than max index.
* @param i the index of step to remove, index starts from 0.
* @return The removed step if succeeded, null if failed.
*/
public Step removeStep(int i) {
if (i >= 0 && i < mSteps.size()) return mSteps.remove(i);
else return null;
}
/**
* Returns the image associated with this recipe
* @return an immutable image, or null if this recipe has no image
*/
@Nullable
public Bitmap getImage() {
if (mImage != null) {
return Bitmap.createBitmap(mImage);
}
else {
return null;
}
}
/**
* Sets the image to associate with this recipe
* @param image the image to set, or null to set no image
*/
public void setImage(@Nullable Bitmap image) {
if (image != null) {
mImage = Bitmap.createBitmap(image);
}
else {
mImage = null;
}
}
public void setTitle(@NonNull String title) {
Objects.requireNonNull(title, "title must not be null");
mTitle = title;
}
public void setAuthor(@NonNull String author) {
Objects.requireNonNull(author, "author must not be null");
mAuthor = author;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final Recipe recipe = (Recipe) o;
if (!mSteps.equals(recipe.mSteps)) return false;
if (!mTitle.equals(recipe.mTitle)) return false;
if (!mAuthor.equals(recipe.mAuthor)) return false;
return !(mImage != null ? !mImage.equals(recipe.mImage) : recipe.mImage != null);
}
@Override
public int hashCode() {
int result = mSteps.hashCode();
result = 31 * result + mTitle.hashCode();
result = 31 * result + mAuthor.hashCode();
result = 31 * result + (mImage != null ? mImage.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Recipe{" +
"mSteps=" + mSteps +
", mTitle='" + mTitle + '\'' +
", mAuthor='" + mAuthor + '\'' +
", mImage=" + mImage +
'}';
}
// Parceling section
public static final Parcelable.Creator<Recipe> CREATOR = new Parcelable.Creator<Recipe>() {
@Override
public Recipe createFromParcel(Parcel source) {
final long id = source.readLong();
final Step[] steps = Objects.castArray(
source.readParcelableArray(Step.class.getClassLoader()), Step[].class);
final String title = source.readString();
final String author = source.readString();
final byte hasImage = source.readByte();
Bitmap image = null;
if (hasImage == 1) {
image = Bitmap.CREATOR.createFromParcel(source);
}
final Recipe recipe = new Recipe(title, author, Arrays.asList(steps));
recipe.setImage(image);
recipe.setObjectId(id);
return recipe;
}
@Override
public Recipe[] newArray(int size) {
return new Recipe[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(getObjectId());
dest.writeParcelableArray(mSteps.toArray(new Step[mSteps.size()]), flags);
dest.writeString(mTitle);
dest.writeString(mAuthor);
// Write has image: 0 (false) or 1 (true)
dest.writeByte(mImage != null ? (byte) 1 : (byte) 0);
if (mImage != null) {
mImage.writeToParcel(dest, 0);
}
}
}