Trauma averted - beans migrated
This commit is contained in:
parent
851658a356
commit
4bcf8b911f
208
src/main/java/com/mousetech/gourmetj/AdminMainBean.java
Normal file
208
src/main/java/com/mousetech/gourmetj/AdminMainBean.java
Normal file
|
@ -0,0 +1,208 @@
|
|||
package com.mousetech.gourmetj;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import javax.faces.model.DataModel;
|
||||
import javax.faces.model.ListDataModel;
|
||||
import javax.faces.view.ViewScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.mousetech.gourmetj.UserSession;
|
||||
import com.mousetech.gourmetj.persistence.model.Category;
|
||||
import com.mousetech.gourmetj.persistence.model.Recipe;
|
||||
import com.mousetech.gourmetj.persistence.service.RecipeService;
|
||||
|
||||
/**
|
||||
* Main control panel backing bean.
|
||||
*
|
||||
* @author timh
|
||||
* @since Jun 28, 2012
|
||||
*/
|
||||
|
||||
@Named
|
||||
@ViewScoped
|
||||
public class AdminMainBean implements Serializable {
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 2L;
|
||||
|
||||
public AdminMainBean() {
|
||||
|
||||
}
|
||||
|
||||
/* Logger */
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AdminMainBean.class);
|
||||
|
||||
|
||||
/**
|
||||
* Persistency service for Recipes
|
||||
*/
|
||||
|
||||
@Inject
|
||||
transient RecipeService recipeService;
|
||||
|
||||
public void setRecipeService(RecipeService service) {
|
||||
log.debug("INJECT RECIPESERVICE===" + service);
|
||||
this.recipeService = service;
|
||||
}
|
||||
|
||||
// **
|
||||
@Inject
|
||||
private UserSession userSession;
|
||||
|
||||
/**
|
||||
* @return the userSession
|
||||
*/
|
||||
public UserSession getUserSession() {
|
||||
return userSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param userSession the userSession to set
|
||||
*/
|
||||
public void setUserSession(UserSession userSession) {
|
||||
this.userSession = userSession;
|
||||
}
|
||||
|
||||
// **
|
||||
private String searchText;
|
||||
|
||||
/**
|
||||
* @return the searchText
|
||||
*/
|
||||
public String getSearchText() {
|
||||
if (this.searchResults == null) {
|
||||
// Fake around broken @PostConstruct
|
||||
this.setSearchText(userSession.getLastSearch());
|
||||
}
|
||||
return searchText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param searchText the searchText to set
|
||||
*/
|
||||
public void setSearchText(String searchText) {
|
||||
this.searchText = searchText;
|
||||
}
|
||||
|
||||
/**/
|
||||
transient DataModel<Recipe> searchResults;
|
||||
|
||||
/**
|
||||
* @return the searchResults
|
||||
*/
|
||||
public DataModel<Recipe> getSearchResults() {
|
||||
if (searchResults == null) {
|
||||
searchResults = new ListDataModel<Recipe>();
|
||||
init(); // @PostConstruct is broken
|
||||
}
|
||||
return searchResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param searchResults the searchResults to set
|
||||
*/
|
||||
public void setSearchResults(
|
||||
DataModel<Recipe> searchResults) {
|
||||
this.searchResults = searchResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return to last search, if any
|
||||
*/
|
||||
@PostConstruct
|
||||
void init() {
|
||||
this.setSearchText(userSession.getLastSearch());
|
||||
// Clean up from any previous operations.
|
||||
this.userSession.setRecipe(null);
|
||||
doFind();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finder
|
||||
*
|
||||
* @return Navigation string
|
||||
*/
|
||||
public String doFind() {
|
||||
List<Recipe> recipes =
|
||||
recipeService.findByTitle(searchText);
|
||||
|
||||
getSearchResults().setWrappedData(recipes);
|
||||
this.userSession.setLastSearch(this.getSearchText());
|
||||
return null; // Stay on page
|
||||
}
|
||||
|
||||
/**
|
||||
* Prep to create a new recipe
|
||||
*
|
||||
* @return navigation to detailEdit page
|
||||
*/
|
||||
public String doNewRecipe() {
|
||||
// Clear for new recipe
|
||||
this.userSession.setLastEdit(null);
|
||||
// Construct a blank recipe to be created.
|
||||
this.userSession.setRecipe(new Recipe());
|
||||
return "detailEdit?faces-redirect=true";
|
||||
}
|
||||
|
||||
/**
|
||||
* Show selected recipe
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String showRecipe() {
|
||||
long recipeId = getSearchResults().getRowData().getId();
|
||||
JSFUtils.flashScope().put("recipeID",
|
||||
Long.valueOf(recipeId));
|
||||
userSession.setLastEdit(recipeId);
|
||||
userSession.setRecipe(null); // forces loading of line
|
||||
// items.
|
||||
return "recipeDetails?faces-redirect=true";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get printable preptime. Database version is in seconds.
|
||||
*
|
||||
* @return Formatted time. Called from EL on main page.
|
||||
*/
|
||||
public String formatPreptime(int timesec) {
|
||||
StringBuffer sb = new StringBuffer(20);
|
||||
int preptime = timesec / 60;
|
||||
if (preptime > 60) {
|
||||
int hours = preptime / 60;
|
||||
sb.append(hours);
|
||||
sb.append(" h. ");
|
||||
preptime %= 60;
|
||||
}
|
||||
if (preptime > 0) {
|
||||
sb.append(preptime);
|
||||
sb.append(" min.");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String formatCategories(Recipe r) {
|
||||
StringBuffer sb = new StringBuffer(30);
|
||||
boolean first = true;
|
||||
for (Category cat : r.getCategories()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(cat.getCategory());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
49
src/main/java/com/mousetech/gourmetj/CuisineBean.java
Normal file
49
src/main/java/com/mousetech/gourmetj/CuisineBean.java
Normal file
|
@ -0,0 +1,49 @@
|
|||
/**
|
||||
* Copyright (C) 2021, Tim Holloway
|
||||
*
|
||||
* Date written: Dec 7, 2021
|
||||
* Author: Tim Holloway <timh@mousetech.com>
|
||||
*/
|
||||
package com.mousetech.gourmetj;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import com.mousetech.gourmetj.persistence.service.RecipeService;
|
||||
|
||||
/**
|
||||
* @author timh
|
||||
* @since Dec 7, 2021
|
||||
*/
|
||||
@Named
|
||||
@ApplicationScoped
|
||||
public class CuisineBean {
|
||||
|
||||
@Inject
|
||||
private RecipeService recipeService;
|
||||
|
||||
private List<String> cuisineList;
|
||||
|
||||
/**
|
||||
* Return list of cuisines currently on file. Create it if
|
||||
* needed.
|
||||
*/
|
||||
public List<String> getCuisineList() {
|
||||
if (this.cuisineList == null ) {
|
||||
this.cuisineList = loadCuisineList();
|
||||
}
|
||||
return this.cuisineList;
|
||||
}
|
||||
|
||||
private synchronized List<String> loadCuisineList() {
|
||||
List<String> list = this.recipeService.findCuisines();
|
||||
return list;
|
||||
}
|
||||
|
||||
public synchronized void registerCuisine(String cuisineString) {
|
||||
// search in-memory list. Add if needed.
|
||||
}
|
||||
}
|
332
src/main/java/com/mousetech/gourmetj/IngredientDigester.java
Normal file
332
src/main/java/com/mousetech/gourmetj/IngredientDigester.java
Normal file
|
@ -0,0 +1,332 @@
|
|||
package com.mousetech.gourmetj;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.mousetech.gourmetj.persistence.model.Ingredient;
|
||||
|
||||
/**
|
||||
* Service for taking an ingredients line, parsing it and
|
||||
* returning a populated Ingredients object. Understands fraction
|
||||
* characters in the amounts fields.
|
||||
*
|
||||
* @author timh
|
||||
* @since Dec 1, 2021 TestedBy @see IngredientDigesterTest
|
||||
*/
|
||||
public class IngredientDigester {
|
||||
|
||||
/**
|
||||
* @author timh
|
||||
* @since Dec 5, 2021
|
||||
*/
|
||||
public enum IngredientAmountFormat {
|
||||
IA_DECIMAL, // 5.75
|
||||
IA_TEXT, // 5 3/4
|
||||
IA_SYMBOLS, // 5¾
|
||||
}
|
||||
|
||||
final static String amountPatternTxt =
|
||||
"([0-9¼⅓½⅔¾⅛⅜⅝⅞][\\s\\-\\.]*[/0-9¼⅓½⅔¾⅛⅜⅝⅞]*)";
|
||||
final static Pattern amountPattern =
|
||||
Pattern.compile(amountPatternTxt);
|
||||
|
||||
final static Pattern fractionPattern =
|
||||
Pattern.compile("(\\d*)[\\s\\-]*(\\d+)/(\\d+)");
|
||||
|
||||
/**
|
||||
* Digest an ingredient text line and construct an Ingredient
|
||||
* object
|
||||
*
|
||||
* @param inputText Input text line
|
||||
* @return Ingredient
|
||||
*/
|
||||
public static Ingredient digest(String inputText) {
|
||||
inputText = inputText.trim();
|
||||
final boolean optional =
|
||||
inputText.toLowerCase().contains("optional");
|
||||
|
||||
Ingredient ing = new Ingredient();
|
||||
ing.setAmount(0.0);
|
||||
|
||||
// Split for amount, unit and rest of string.
|
||||
String[] pamt = parseFancyNumber(inputText);
|
||||
if (pamt == null) {
|
||||
// Can't parse
|
||||
ing.setAmount(null);
|
||||
ing.setUnit(null);
|
||||
ing.setItem(inputText);
|
||||
} else {
|
||||
final Double[] amount = digestAmount(pamt[0]);
|
||||
ing.setAmount(amount[0]);
|
||||
ing.setRangeamount(amount[1]);
|
||||
|
||||
String[] unext = pamt[1].split("\\s", 2);
|
||||
switch (unext.length) {
|
||||
case 1:
|
||||
ing.setUnit(null);
|
||||
ing.setItem(unext[0]);
|
||||
break;
|
||||
case 2:
|
||||
ing.setUnit(unext[0]);
|
||||
ing.setItem(unext[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ing.setPosition(null);
|
||||
ing.setOptional(optional ? 1 : 0);
|
||||
// Extract the item type from the item name
|
||||
// Assume possible prep details ("parsley, chopped")
|
||||
String istring = ing.getItem();
|
||||
String iclass = istring.split("\\s*,",2)[0];
|
||||
ing.setIngkey(iclass);
|
||||
return ing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Break down ingredients line into 2 parts. First part is
|
||||
* numeric text representing amount and optional range,
|
||||
* second part is text remainder, including units.
|
||||
*
|
||||
* @param instring String to parse
|
||||
* @return 2-dimensional String array with amount and
|
||||
* remainder or <code>null</code> if instring does
|
||||
* not parse.
|
||||
*
|
||||
*/
|
||||
static String[] parseFancyNumber(String instring) {
|
||||
Matcher m = amountPattern.matcher(instring);
|
||||
if (m.find()) {
|
||||
// System.out.println("GROUPS=" + m.groupCount());
|
||||
String rs = m.group(1);
|
||||
return new String[] { rs.trim(),
|
||||
instring.substring(m.end()).trim() };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: Remove "optional", if present, including
|
||||
// brackets/parentheses
|
||||
|
||||
/**
|
||||
* Convert instring into a number with optional fractions.
|
||||
* Can take decimals, unicode ratio characters, and n/m form
|
||||
* with embedded spaces and dashes.
|
||||
*
|
||||
* @param instring String to analyze
|
||||
* @return number equivalent pair. Note that thirds are not
|
||||
* precise in binary floating-point! First element is
|
||||
* low range amount. Second element is null unless a
|
||||
* high range was given as well.
|
||||
*/
|
||||
public static Double[] digestAmount(String instring) {
|
||||
String[] amtParts = instring.split("\\s*-\\s*");
|
||||
Double[] result = new Double[2];
|
||||
result[0] = parseSingleAmount(amtParts[0]);
|
||||
if (amtParts.length == 1) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// dash separates low, high values OR integer and a
|
||||
// fraction.
|
||||
result[1] = parseSingleAmount(amtParts[1]);
|
||||
if (result[1] < result[0]) {
|
||||
result[0] += result[1];
|
||||
result[1] = 0.0d;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single amount. May be integer, decimal number,
|
||||
* integer + fraction character or [integer] integer /
|
||||
* integer.
|
||||
*
|
||||
* @param instring amount string to parse
|
||||
* @return parsed value.
|
||||
* @throws NumberFormatException if bad decimal value given.
|
||||
*/
|
||||
static Double parseSingleAmount(String instring) {
|
||||
double value = 0;
|
||||
double frac = 0;
|
||||
|
||||
if (instring.contains("/")) {
|
||||
// Break down a/b fraction
|
||||
frac = digestFraction(instring);
|
||||
return frac;
|
||||
}
|
||||
|
||||
if (instring.contains(".")) {
|
||||
frac = Double.valueOf(instring);
|
||||
return frac;
|
||||
}
|
||||
|
||||
// Look for integer and possibly a symbol;
|
||||
for (int i = 0; i < instring.length(); i++) {
|
||||
final Character ch = instring.charAt(i);
|
||||
switch (ch) {
|
||||
case '⅛':
|
||||
frac = 0.125d;
|
||||
break;
|
||||
case '⅓':
|
||||
frac = 1.0 / 3.0;
|
||||
break;
|
||||
case '¼':
|
||||
frac = 0.25d;
|
||||
break;
|
||||
case '⅜':
|
||||
frac = 3.0 * 0.125d;
|
||||
break;
|
||||
case '½':
|
||||
frac = 0.5d;
|
||||
break;
|
||||
case '⅝':
|
||||
frac = 5.0 * 0.125d;
|
||||
break;
|
||||
case '⅔':
|
||||
frac = 2.0 / 3.0;
|
||||
break;
|
||||
case '⅞':
|
||||
frac = 7.0 * 0.125d;
|
||||
break;
|
||||
default:
|
||||
if (Character.isDigit(ch)) {
|
||||
value = value * 10.0 + (ch - '0');
|
||||
}
|
||||
// Ignore spaces, dashes, etc.
|
||||
break;
|
||||
}
|
||||
}
|
||||
value += frac;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull fraction from tail-end of string and evaluate it.
|
||||
*
|
||||
* @param instring String in the form dddxxxddd/ddd, where d
|
||||
* is digit, x is dash or space (optional)
|
||||
* @return results of evaluation. May need to throw
|
||||
* Exception???
|
||||
*/
|
||||
static double digestFraction(String instring) {
|
||||
Matcher m = fractionPattern.matcher(instring);
|
||||
if (!m.find()) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
String lead = m.group(1);
|
||||
String snumerator = m.group(2);
|
||||
String sdenominator = m.group(3);
|
||||
|
||||
double numerator;
|
||||
double denominator;
|
||||
try {
|
||||
numerator = Double.parseDouble(snumerator);
|
||||
denominator = Double.parseDouble(sdenominator);
|
||||
} catch (NumberFormatException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if (denominator == 0.0d) {
|
||||
return 0.0d;
|
||||
}
|
||||
double result = numerator / denominator;
|
||||
if (!lead.isEmpty()) {
|
||||
result += Double.valueOf(lead); // integral part
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert floating-point amounts to display-friendly form.
|
||||
*
|
||||
* @param format Amount formatting scheme
|
||||
* @param amt Amount to display
|
||||
* @param ext amount range high value (can be null)
|
||||
* @return Displayable String. Zero returns an empty string.
|
||||
*
|
||||
*/
|
||||
public static String displayAmount(
|
||||
IngredientAmountFormat format, Double amt,
|
||||
Double ext) {
|
||||
if (amt == 0.0d) {
|
||||
return "";
|
||||
}
|
||||
String amountRange = displayAmountPart(format, amt);
|
||||
if (ext != null) {
|
||||
amountRange += "-" + displayAmountPart(format, ext);
|
||||
}
|
||||
return amountRange;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param format
|
||||
* @param amt Amount to format
|
||||
* @return Amount formatted in decimal, text, or text/symbol.
|
||||
* Values that don't conform to the common fractions
|
||||
* report as decimal values. Returns empty string for
|
||||
* 0.
|
||||
*/
|
||||
static String displayAmountPart(
|
||||
IngredientAmountFormat format, Double amt) {
|
||||
if (amt == 0.0d) {
|
||||
return "";
|
||||
}
|
||||
DecimalFormat df = new DecimalFormat("#.###");
|
||||
String fnum = df.format(amt);
|
||||
if (format == IngredientAmountFormat.IA_DECIMAL) {
|
||||
return fnum;
|
||||
}
|
||||
|
||||
String part[] = fnum.split("\\.");
|
||||
if (part.length == 1) {
|
||||
return part[0]; // Integer
|
||||
}
|
||||
String p2 = part[1];
|
||||
String fs = "";
|
||||
if (format == IngredientAmountFormat.IA_TEXT) {
|
||||
if (p2.equals("125")) {
|
||||
fs = "1/8";
|
||||
} else if (p2.equals("333")) {
|
||||
fs = "1/3";
|
||||
} else if (p2.equals("25")) {
|
||||
fs = "1/4";
|
||||
} else if (p2.equals("5")) {
|
||||
fs = "1/2";
|
||||
} else if (p2.equals("667")) {
|
||||
fs = "2/3";
|
||||
} else if (p2.equals("75")) {
|
||||
fs = "3/4";
|
||||
} else { // 3, 5, 7/8
|
||||
return fnum;
|
||||
}
|
||||
if (!"0".equals(part[0])) {
|
||||
fs = ' ' + fs;
|
||||
}
|
||||
} else {
|
||||
if (p2.equals("125")) {
|
||||
fs = "⅛";
|
||||
} else if (p2.equals("333")) {
|
||||
fs = "⅓";
|
||||
} else if (p2.equals("25")) {
|
||||
fs = "¼";
|
||||
} else if (p2.equals("5")) {
|
||||
fs = "½";
|
||||
} else if (p2.equals("667")) {
|
||||
fs = "⅔";
|
||||
} else if (p2.equals("75")) {
|
||||
fs = "¾";
|
||||
} else { // 3, 5, 7/8
|
||||
return fnum;
|
||||
}
|
||||
}
|
||||
if ("0".equals(part[0])) {
|
||||
part[0] = "";
|
||||
}
|
||||
return part[0] + fs;
|
||||
}
|
||||
}
|
375
src/main/java/com/mousetech/gourmetj/IngredientUI.java
Normal file
375
src/main/java/com/mousetech/gourmetj/IngredientUI.java
Normal file
|
@ -0,0 +1,375 @@
|
|||
package com.mousetech.gourmetj;
|
||||
|
||||
|
||||
import com.mousetech.gourmetj.persistence.model.Ingredient;
|
||||
import com.mousetech.gourmetj.persistence.model.IngredientIF;
|
||||
import com.mousetech.gourmetj.persistence.model.Recipe;
|
||||
import com.mousetech.gourmetj.IngredientDigester;
|
||||
import com.mousetech.gourmetj.IngredientDigester.IngredientAmountFormat;
|
||||
|
||||
/**
|
||||
* JSF-friendly decorator for @see Ingredient. Formats amount
|
||||
* with fractions and supports checkboxes. Primary use
|
||||
* is as a JSF TableModel wrapped content in @see RecipeDetailBean.
|
||||
*
|
||||
* TestedBy @see IngredientUITest
|
||||
*
|
||||
* @author timh
|
||||
* @since Dec 2, 2021
|
||||
*/
|
||||
public class IngredientUI implements IngredientIF {
|
||||
private Ingredient ingredient;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param ingredient
|
||||
*/
|
||||
public IngredientUI(Ingredient ingredient) {
|
||||
this.ingredient = ingredient;
|
||||
}
|
||||
|
||||
public Ingredient getIngredient() {
|
||||
return this.ingredient;
|
||||
}
|
||||
|
||||
private boolean ingGroup;
|
||||
/**
|
||||
* @param ingGroup the ingGroup to set
|
||||
*/
|
||||
public void setIngGroup(boolean ingGroup) {
|
||||
this.ingGroup = ingGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ingredient groups are rendered visually as synthetic
|
||||
* rows. Actual group IDs are part of line-item ingredients,
|
||||
* so when building the model, we create a group row when
|
||||
* the line item inggroup value changes.
|
||||
*
|
||||
* @return <code>true</code> for an Ingredient Group
|
||||
* header row.
|
||||
*/
|
||||
public boolean isIngGroup() {
|
||||
return this.ingGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Row selection checkbox (UI only)
|
||||
*/
|
||||
|
||||
private boolean selected;
|
||||
|
||||
|
||||
/**
|
||||
* @return the selected status
|
||||
*/
|
||||
public boolean isSelected() {
|
||||
return selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selected the selected to set
|
||||
*/
|
||||
public void setSelected(boolean selected) {
|
||||
this.selected = selected;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getAmount()
|
||||
*/
|
||||
public Double getAmount() {
|
||||
return ingredient.getAmount();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param amount
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setAmount(java.lang.Double)
|
||||
*/
|
||||
public void setAmount(Double amount) {
|
||||
ingredient.setAmount(amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get amount display-friendly
|
||||
* @see #getAmount
|
||||
*/
|
||||
public String getDisplayAmount() {
|
||||
// TODO
|
||||
Double amt = ingredient.getAmount();
|
||||
if ( amt == null ) {
|
||||
return "";
|
||||
}
|
||||
String amt1 = IngredientDigester.displayAmount(
|
||||
IngredientAmountFormat.IA_SYMBOLS, amt, ingredient.getRangeamount());
|
||||
return amt1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set amount display-friendly
|
||||
* @see #setAmount
|
||||
*/
|
||||
public void setDisplayAmount(String amount) {
|
||||
if ( amount.isBlank() ) {
|
||||
ingredient.setAmount(null);
|
||||
}
|
||||
IngredientDigester digester = new IngredientDigester();
|
||||
Double[] amt = digester.digestAmount(amount);
|
||||
ingredient.setAmount(amt[0]);
|
||||
ingredient.setRangeamount(amt[1]);
|
||||
}
|
||||
/**
|
||||
* @return
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getDeleted()
|
||||
*/
|
||||
public Integer getDeleted() {
|
||||
return ingredient.getDeleted();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param deleted
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setDeleted(java.lang.Integer)
|
||||
*/
|
||||
public void setDeleted(Integer deleted) {
|
||||
ingredient.setDeleted(deleted);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getId()
|
||||
*/
|
||||
public Long getId() {
|
||||
return ingredient.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setId(long)
|
||||
*/
|
||||
public void setId(Long id) {
|
||||
ingredient.setId(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
public int hashCode() {
|
||||
return ingredient.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getInggroup()
|
||||
*/
|
||||
public String getInggroup() {
|
||||
return ingredient.getInggroup();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param inggroup
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setInggroup(java.lang.String)
|
||||
*/
|
||||
public void setInggroup(String inggroup) {
|
||||
ingredient.setInggroup(inggroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getIngkey()
|
||||
*/
|
||||
public String getIngkey() {
|
||||
return ingredient.getIngkey();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ingkey
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setIngkey(java.lang.String)
|
||||
*/
|
||||
public void setIngkey(String ingkey) {
|
||||
ingredient.setIngkey(ingkey);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getItem()
|
||||
*/
|
||||
public String getItem() {
|
||||
return ingredient.getItem();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param item
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setItem(java.lang.String)
|
||||
*/
|
||||
public void setItem(String item) {
|
||||
ingredient.setItem(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getOptional()
|
||||
*/
|
||||
public Integer getOptional() {
|
||||
return ingredient.getOptional();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param optional
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setOptional(java.lang.Integer)
|
||||
*/
|
||||
public void setOptional(Integer optional) {
|
||||
ingredient.setOptional(optional);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get optional value in boolean Checkbox friendly form
|
||||
* @return
|
||||
*/
|
||||
public boolean getOptionalCB() {
|
||||
return ingredient.getOptional() != 0;
|
||||
}
|
||||
|
||||
public void setOptionalCB(boolean value) {
|
||||
ingredient.setOptional(value ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getPosition()
|
||||
*/
|
||||
public Integer getPosition() {
|
||||
return ingredient.getPosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param position
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setPosition(java.lang.Integer)
|
||||
*/
|
||||
public void setPosition(Integer position) {
|
||||
ingredient.setPosition(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getRangeamount()
|
||||
*/
|
||||
public Double getRangeamount() {
|
||||
return ingredient.getRangeamount();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rangeamount
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setRangeamount(java.lang.Double)
|
||||
*/
|
||||
public void setRangeamount(Double rangeamount) {
|
||||
ingredient.setRangeamount(rangeamount);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getRecipe()
|
||||
*/
|
||||
public Recipe getRecipe() {
|
||||
return ingredient.getRecipe();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param recipe
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setRecipe(com.mousetech.gourmetj.persistence.model.Recipe)
|
||||
*/
|
||||
public void setRecipe(Recipe recipe) {
|
||||
ingredient.setRecipe(recipe);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getRefid()
|
||||
*/
|
||||
public Long getRefid() {
|
||||
return ingredient.getRefid();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param refid
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setRefid(java.lang.Long)
|
||||
*/
|
||||
public void setRefid(Long refid) {
|
||||
ingredient.setRefid(refid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getShopoptional()
|
||||
*/
|
||||
public Integer getShopoptional() {
|
||||
return ingredient.getShopoptional();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param shopoptional
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setShopoptional(java.lang.Integer)
|
||||
*/
|
||||
public void setShopoptional(Integer shopoptional) {
|
||||
ingredient.setShopoptional(shopoptional);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getShopoptional()
|
||||
*/
|
||||
public boolean getShopoptionalCB() {
|
||||
return ingredient.getShopoptional() != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param shopoptional
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setShopoptional(java.lang.Integer)
|
||||
*/
|
||||
public void setShopoptionalCB(boolean shopoptional) {
|
||||
ingredient.setShopoptional(shopoptional? 1:0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#getUnit()
|
||||
*/
|
||||
public String getUnit() {
|
||||
return ingredient.getUnit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param unit
|
||||
* @see com.mousetech.gourmetj.persistence.model.Ingredient#setUnit(java.lang.String)
|
||||
*/
|
||||
public void setUnit(String unit) {
|
||||
ingredient.setUnit(unit);
|
||||
}
|
||||
|
||||
|
||||
// This goes to the shopCats table via ManyToOne at save time.
|
||||
private String shopCat;
|
||||
|
||||
/**
|
||||
* @return the shopCat
|
||||
*/
|
||||
public String getShopCat() {
|
||||
return shopCat;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param shopCat the shopCat to set
|
||||
*/
|
||||
public void setShopCat(String shopCat) {
|
||||
this.shopCat = shopCat;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return ingredient.toString();
|
||||
}
|
||||
}
|
86
src/main/java/com/mousetech/gourmetj/JSFUtils.java
Normal file
86
src/main/java/com/mousetech/gourmetj/JSFUtils.java
Normal file
|
@ -0,0 +1,86 @@
|
|||
package com.mousetech.gourmetj;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import javax.faces.application.FacesMessage;
|
||||
import javax.faces.context.ExternalContext;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.Flash;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class JSFUtils {
|
||||
|
||||
/* Logger */
|
||||
|
||||
private static final Logger log =
|
||||
LoggerFactory.getLogger(JSFUtils.class);
|
||||
|
||||
private static ExternalContext getExternalContext() {
|
||||
FacesContext facesContext =
|
||||
FacesContext.getCurrentInstance();
|
||||
ExternalContext externalContext =
|
||||
facesContext.getExternalContext();
|
||||
return externalContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain resource as Stream. The caller must close this
|
||||
* stream after use.
|
||||
*
|
||||
* @param pathName Path of the resource. For example,
|
||||
* "/WEB-INF/classes/xys.properties".
|
||||
* @return InputStream or <code>null</code>, if no such
|
||||
* resource exists.
|
||||
*/
|
||||
public static InputStream getResourceStream(
|
||||
String pathName) {
|
||||
InputStream response = getExternalContext()
|
||||
.getResourceAsStream(pathName);
|
||||
return response;
|
||||
}
|
||||
|
||||
// ===
|
||||
/**
|
||||
* Post an info-level message to the FacesContext where the
|
||||
* <h:messages> tag can display it.
|
||||
*
|
||||
* @param string Message text
|
||||
*/
|
||||
public static void addInfoMessage(String string) {
|
||||
FacesMessage message = new FacesMessage(
|
||||
FacesMessage.SEVERITY_INFO, string, string);
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Post an error-level message to the FacesContext where the
|
||||
* <h:messages> tag can display it.
|
||||
*
|
||||
* @param string Message text
|
||||
*/
|
||||
public static void addErrorMessage(String string) {
|
||||
FacesMessage message = new FacesMessage(
|
||||
FacesMessage.SEVERITY_ERROR, string, string);
|
||||
FacesContext.getCurrentInstance().addMessage(null,
|
||||
message);
|
||||
}
|
||||
|
||||
public static void addErrorMessage(String string,
|
||||
Exception e) {
|
||||
if (e instanceof NullPointerException) {
|
||||
addErrorMessage(
|
||||
"Internal logic error (NullPointerException)");
|
||||
} else {
|
||||
addErrorMessage(string);
|
||||
}
|
||||
log.error(string, e);
|
||||
}
|
||||
|
||||
public static Flash flashScope() {
|
||||
return (FacesContext.getCurrentInstance()
|
||||
.getExternalContext().getFlash());
|
||||
}
|
||||
}
|
97
src/main/java/com/mousetech/gourmetj/UserSession.java
Normal file
97
src/main/java/com/mousetech/gourmetj/UserSession.java
Normal file
|
@ -0,0 +1,97 @@
|
|||
package com.mousetech.gourmetj;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import javax.enterprise.context.SessionScoped;
|
||||
import javax.inject.Named;
|
||||
|
||||
import com.mousetech.gourmetj.persistence.model.Recipe;
|
||||
|
||||
@Named
|
||||
@SessionScoped
|
||||
public class UserSession implements Serializable {
|
||||
|
||||
/**
|
||||
* Serial version for session save/restore
|
||||
*/
|
||||
private static final long serialVersionUID =
|
||||
7449440266704831598L;
|
||||
|
||||
private String lastSearch = "";
|
||||
|
||||
/**
|
||||
* @return the lastSearch
|
||||
*/
|
||||
public String getLastSearch() {
|
||||
return lastSearch;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lastSearch the lastSearch to set
|
||||
*/
|
||||
public void setLastSearch(String lastSearch) {
|
||||
this.lastSearch = lastSearch;
|
||||
}
|
||||
|
||||
private Long lastEdit;
|
||||
|
||||
/**
|
||||
* @return the lastEdit
|
||||
*/
|
||||
public Long getLastEdit() {
|
||||
return lastEdit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lastEdit the lastEdit to set
|
||||
*/
|
||||
public void setLastEdit(Long lastEdit) {
|
||||
this.lastEdit = lastEdit;
|
||||
}
|
||||
|
||||
//***
|
||||
/**
|
||||
* Tab index to select when presenting editDetails.
|
||||
* First tab is 0.
|
||||
*/
|
||||
private int detailTab;
|
||||
|
||||
/**
|
||||
* @return the detailTab
|
||||
*/
|
||||
public int getDetailTab() {
|
||||
return detailTab;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param detailTab the detailTab to set
|
||||
*/
|
||||
public void setDetailTab(int detailTab) {
|
||||
this.detailTab = detailTab;
|
||||
}
|
||||
|
||||
//***
|
||||
private Recipe recipe;
|
||||
|
||||
/**
|
||||
* Recipe is set by the mainpage bean to a blank recipe
|
||||
* before dispatching to the detailEdit page (new recipe).
|
||||
* It is also set by the detail view page so that the
|
||||
* detail view can be edited.
|
||||
*
|
||||
* In addition to detail editing, it's also used by the
|
||||
* @see PictureController.
|
||||
*
|
||||
* @return Recipe selected.
|
||||
*/
|
||||
public Recipe getRecipe() {
|
||||
return recipe;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param recipe the recipe to set
|
||||
*/
|
||||
public void setRecipe(Recipe recipe) {
|
||||
this.recipe = recipe;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package com.mousetech.gourmetj.persistence.service;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import javax.enterprise.context.ApplicationScoped;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.mousetech.gourmetj.persistence.dao.CategoryRepository;
|
||||
import com.mousetech.gourmetj.persistence.dao.RecipeRepository;
|
||||
import com.mousetech.gourmetj.persistence.model.Category;
|
||||
import com.mousetech.gourmetj.persistence.model.Recipe;
|
||||
|
||||
@Named
|
||||
@ApplicationScoped
|
||||
@Transactional
|
||||
public class RecipeService implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Inject
|
||||
private RecipeRepository recipeRepository;
|
||||
|
||||
public List<Recipe> findAll() {
|
||||
return recipeRepository.findAll();
|
||||
}
|
||||
|
||||
public List<String> findCuisines() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<Recipe> findByTitle(String searchText) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
// /**
|
||||
// *
|
||||
// * @return All Category names that are not null/blank, sorted.
|
||||
// */
|
||||
// public List<String> findCategoryNames() {
|
||||
// return categoryRepository.findDistinctCategoryNative();
|
||||
// }
|
||||
}
|
67
src/main/resources/META-INF/resources/main.xhtml
Normal file
67
src/main/resources/META-INF/resources/main.xhtml
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui:composition template="/WEB-INF/layout/layout.xhtml"
|
||||
xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:tc="http://myfaces.apache.org/tobago/component"
|
||||
>
|
||||
<ui:define name="title">Gourmet Recipe Manager</ui:define>
|
||||
<ui:define name="content">
|
||||
<tc:messages />
|
||||
<tc:form id="form1">
|
||||
<tc:section label="Find a Recipe" level="2">
|
||||
<tc:gridLayout id="gfinder"
|
||||
columns="60em auto auto"
|
||||
>
|
||||
<tc:in id="searchFor" focus="true"
|
||||
placeholder="Recipe title (todo) cuisine, etc."
|
||||
value="#{adminMainBean.searchText}"
|
||||
/>
|
||||
<tc:button id="find" label="Find"
|
||||
defaultCommand="true"
|
||||
action="#{adminMainBean.doFind}"
|
||||
>
|
||||
<tc:style minWidth="58px" height="40px" />
|
||||
</tc:button>
|
||||
<tc:button label="New Recipe"
|
||||
action="#{adminMainBean.doNewRecipe}"
|
||||
>
|
||||
<tc:style minWidth="58px" height="40px" />
|
||||
</tc:button>
|
||||
</tc:gridLayout>
|
||||
</tc:section>
|
||||
</tc:form>
|
||||
<tc:sheet id="table1" rows="30"
|
||||
value="#{adminMainBean.searchResults}" var="row"
|
||||
>
|
||||
<tc:column label="Icon">
|
||||
<tc:image width="64" height="64"
|
||||
value="/gourmetj/img/thumb/#{row.id}"
|
||||
/>
|
||||
</tc:column>
|
||||
<tc:column label="Recipe">
|
||||
<tc:link action="#{adminMainBean.showRecipe}"
|
||||
label="#{row.title}"
|
||||
/>
|
||||
</tc:column>
|
||||
<tc:column label="Category">
|
||||
<tc:out
|
||||
value="#{adminMainBean.formatCategories(row)}"
|
||||
/>
|
||||
</tc:column>
|
||||
<tc:column label="Cuisine">
|
||||
<tc:out value="#{row.cuisine}" />
|
||||
</tc:column>
|
||||
<tc:column label="Rating">
|
||||
<tc:stars value="#{row.rating}" readonly="true" />
|
||||
</tc:column>
|
||||
<tc:column label="Source">
|
||||
<tc:out value="#{row.source}" />
|
||||
</tc:column>
|
||||
<tc:column label="Prep Time">
|
||||
<tc:out
|
||||
value="#{adminMainBean.formatPreptime(row.preptime)}"
|
||||
/>
|
||||
</tc:column>
|
||||
</tc:sheet>
|
||||
</ui:define>
|
||||
</ui:composition>
|
28
src/main/resources/WEB-INF/layout/layout.xhtml
Normal file
28
src/main/resources/WEB-INF/layout/layout.xhtml
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui:composition xmlns:f="http://xmlns.jcp.org/jsf/core"
|
||||
xmlns:ui="http://java.sun.com/jsf/facelets"
|
||||
xmlns:h="http://java.sun.com/jsf/html"
|
||||
xmlns:tc="http://myfaces.apache.org/tobago/component"
|
||||
>
|
||||
<f:view>
|
||||
<tc:page id="page">
|
||||
<title><ui:insert name="title">Gourmet Recipe Manager (web version)</ui:insert></title>
|
||||
<link rel="icon" type="image/vnd.microsoft.icon"
|
||||
href="#{pageContext.contextPath}/favicon.ico"
|
||||
/>
|
||||
<tc:style file="css/style.css" />
|
||||
<h1>
|
||||
<ui:insert name="title">Gourmet Recipe Manager (web version)</ui:insert>
|
||||
</h1>
|
||||
<ui:insert name="content">
|
||||
<ui:include src="content.xhtml" />
|
||||
</ui:insert>
|
||||
<tc:footer fixed="true">
|
||||
© 2021 Tim Holloway, Licensed under the <a
|
||||
href="http://www.apache.org/licenses/LICENSE-2.0"
|
||||
>Apache License, Version 2.0</a>.
|
||||
<p>Based on Gourmet Recipe Manager by T. Hinkle</p>
|
||||
</tc:footer>
|
||||
</tc:page>
|
||||
</f:view>
|
||||
</ui:composition>
|
Loading…
Reference in New Issue
Block a user