parent
851658a356
commit
4bcf8b911f
9 changed files with 1289 additions and 0 deletions
@ -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(); |
||||
} |
||||
} |
@ -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.
|
||||
} |
||||
} |
@ -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; |
||||
} |
||||
} |
@ -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(); |
||||
} |
||||
} |
@ -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()); |
||||
} |
||||
} |
@ -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();
|
||||
// }
|
||||
} |
@ -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> |
@ -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