/* * RatingData.java * * Created on April 11, 2006, 3:54 PM */ import java.text.MessageFormat; import java.util.Locale; /** *

A convenient demonstration data model for the Rating component. To use * multiple Rating instances in an application, one can create a * Map of RatingData instances. In such a case, the * grade property * of a rating instance could be bound as follows (where "rating1" is the key * used for the map entry): *

*

* grade="#{SessionBean1.ratingMap.rating1.grade}" *

*

* Here are the properties of a Rating component that can bind directly to the * corresponding properties of a RatingData instance: * grade, averageGrade, normalModeText, * averageModeText * gradeReadOnly, inAverageMode, * hoverTexts. *

*

The methods of this class are synchronized to prevent simultaneous access * by multiple threads associated with the same session.

* * @author Matthew Bohm */ public class RatingData { /** *

The message formatting pattern to be used * for the average mode text.

*/ private static String averageModeTextPattern = "Average rating: {0,number,#.#} (from {1,number,integer} votes)"; /** *

The normal mode text used when an appropriate hover text * is not available and a grade has not been saved.

*/ private static String simpleNormalModeText = ""; /** *

The normal mode text used when an appropriate hover text * is not available and a grade has been saved.

*/ private static String simpleNormalModeTextSaved = "Saved"; /** *

The normal mode text message formatting pattern used when an * appropriate hover text is available and a grade has not been saved.

*/ private static String normalModeTextPattern = "My rating: {0}"; /** *

The normal mode text message formatting pattern used when an * appropriate hover text is available and a grade has been saved.

*/ private static String normalModeTextPatternSaved = "Saved: {0}"; /** *

The "not interested" normal mode text when a grade has not * been saved.

*/ private static String notInterestedNormalModeText = "My rating: Not interested"; /** *

The "not interested" normal mode text when a grade has * been saved.

*/ private static String notInterestedNormalModeTextSaved = "Saved: Not interested"; /** *

The "clear" normal mode text when a grade has not * been saved.

*/ private static String clearNormalModeText = "No rating assigned"; /** *

The "clear" normal mode text when a grade has * been saved.

*/ private static String clearNormalModeTextSaved = "No rating assigned"; /** *

The message formatting object.

*/ private MessageFormat mf; /** *

Construct a new RatingData instance and * initialize the normalModeText and * averageModeText properties. *

*/ public RatingData() { this.mf = new MessageFormat(""); initDefaultTexts(false); } /** *

Construct a new RatingData instance and * initialize the normalModeText and * averageModeText properties * taking into account the hover texts. *

* @param hoverTexts The new hover texts. */ public RatingData(String[] hoverTexts) { this.hoverTexts = hoverTexts; this.mf = new MessageFormat(""); initDefaultTexts(false); } /** *

Construct a new RatingData instance and * initialize the normalModeText and * averageModeText properties * taking into account the hover texts, * grade, average grade, vote count, and whether * to show the grade as "saved." *

* @param hoverTexts The new hover texts. * @param grade The new grade. * @param averageGrade The new averageGrade. * @param voteCount The new number of user votes. * @param saved Whether to show the grade as "saved." */ public RatingData(String[] hoverTexts, int grade, double averageGrade, int voteCount, boolean saved) { this.hoverTexts = hoverTexts; this.grade = grade; this.averageGrade = averageGrade; this.voteCount = voteCount; this.mf = new MessageFormat(""); initDefaultTexts(saved); } /** *

Initialize the normalModeText and * averageModeText properties taking into account the * hover texts, grade, average grade, vote count, and whether * to show the grade as "saved." *

* @param saved Whether to show the grade as "saved." */ private void initDefaultTexts(boolean saved) { this.normalModeText = calculateNormalModeText(this.grade, saved); Object[] args = new Object[]{new Double(this.averageGrade), new Integer(this.voteCount)}; mf.applyPattern(this.averageModeTextPattern); this.averageModeText = mf.format(args); } /** *

Get the locale used in message formatting the Rating texts.

*/ public synchronized Locale getLocale() { return this.mf.getLocale(); } /** *

Set the locale used in message formatting the Rating texts.

*/ public synchronized void setLocale(Locale locale) { if (locale == null) { throw new NullPointerException(); } this.mf.setLocale(locale); } private int grade; /** * Get the user's grade. * @return The user's grade. */ public synchronized int getGrade() { return this.grade; } /** *

In addition to setting the grade, the following operations * will occur. The normal mode text * will be updated. Furthermore, if the new grade is * greater than 0 (since "not interested" is denoted by 1 and "clear" * is denoted by 0), the average grade will be updated by counting this * grade assignment as a vote. This is a crude algorithm, since it allows * a single user to vote multiple times. However, that is precisely what * we want for this demo. Along with this, the average mode text will be * updated accordingly, based on the new average grade. Also, if we are * assigning a non-zero grade, we set the gradeReadOnly entry for the * rating instace to True. In this way, by binding the rating instance's * gradeReadOnly property to the * gradeReadOnly property of this RatingData, * you can lock the user's grade once the user assigns it. (If you don't * want this feature, simply don't bind the gradeReadOnly * property to this RatingData.)

*

If the new grade is greater than 0, and the corresponding hover text * is available, this method formats the normal mode text with that * hover text and grade as arguments. Otherwise, it sets the normal mode * text to the * simpleNormalModeText, * simpleNormalModeTextSaved, * notInterestedNormalModeText, * notInterestedNormalModeTextSaved, * clearNormalModeText, or * clearNormalModeTextSaved as appropriate, with no * formatting changes. *

*

* If the new grade is greater than 0, this method formats the average mode * text with the new average grade, vote count, and, if available, the * corresponding hover text. *

* @param grade The new grade. */ public synchronized void setGrade(int grade) { this.grade = grade; //update normalModeText String localNormalModeText = calculateNormalModeText(grade, true); setNormalModeText(localNormalModeText); if (grade > 0) { //update averageGrade with crude algorithm int localVoteCount = getVoteCount(); double localAverageGrade = getAverageGrade(); double totalGrade = localAverageGrade * localVoteCount; totalGrade += grade; localVoteCount++; localAverageGrade = totalGrade / localVoteCount; setVoteCount(localVoteCount); setAverageGrade(localAverageGrade); //update averageModeText Double averageGradeDouble = new Double(localAverageGrade); Integer voteCountInteger = new Integer(localVoteCount); String correspondingHoverText = null; //in case the pattern wants to make use of the corresponding hover text for this average grade if (this.hoverTexts != null) { int averageGradeAsInt = (int)Math.round(localAverageGrade); if (averageGradeAsInt > 0 && averageGradeAsInt <= this.hoverTexts.length) { correspondingHoverText = this.hoverTexts[averageGradeAsInt - 1]; } } Object[] args; if (correspondingHoverText == null) { args = new Object[]{averageGradeDouble, voteCountInteger}; } else { args = new Object[]{averageGradeDouble, voteCountInteger, correspondingHoverText}; } mf.applyPattern(this.averageModeTextPattern); String text = mf.format(args); setAverageModeText(text); } if (grade != 0) { //Set gradeReadOnly true. This will only take effect if the gradeReadOnly property is bound to this RatingData. //So if you want gradeReadOnly to be true once a grade is assigned, bind gradeReadOnly to this RatingData. //This enables you to "lock" the user's grade once the user assigns it. //If you don't want this feature, simply do not bind the gradeReadOnly property to this RatingData. setGradeReadOnly(true); } } private double averageGrade; /** * Get the average grade. * @return The average grade. */ public synchronized double getAverageGrade() { return this.averageGrade; } /** * Set the average grade. * @param averageGrade The new average grade. */ public synchronized void setAverageGrade(double averageGrade) { this.averageGrade = averageGrade; } private String normalModeText; /** * Get the normal mode text. * @return The normal mode text. */ public synchronized String getNormalModeText() { return this.normalModeText; } /** * Set the normal mode text. * @param normalModeText The new normal mode text. */ public synchronized void setNormalModeText(String normalModeText) { this.normalModeText = normalModeText; } private String averageModeText; /** * Get the average mode text. * @return The average mode text. */ public synchronized String getAverageModeText() { return this.averageModeText; } /** * Set the average mode text. * @param averageModeText The new average mode text. */ public synchronized void setAverageModeText(String averageModeText) { this.averageModeText = averageModeText; } private int voteCount; /** * Get the number of user votes. * @return The number of user votes. */ public synchronized int getVoteCount() { return this.voteCount; } /** * Set the number of user votes. * @param voteCount The new number of user votes. */ private void setVoteCount(int voteCount) { this.voteCount = voteCount; } private boolean gradeReadOnly; /** * Get whether the user's grade can be changed. * @return Whether the user's grade can be changed. */ public synchronized boolean isGradeReadOnly() { return this.gradeReadOnly; } /** * Set whether the user's grade can be changed. * @param gradeReadOnly Whether the user's grade can be changed. */ public synchronized void setGradeReadOnly(boolean gradeReadOnly) { this.gradeReadOnly = gradeReadOnly; } private boolean inAverageMode; /** * Get whether this RatingData is in average mode. * @return Whether this RatingData is in average mode. */ public synchronized boolean isInAverageMode() { return this.inAverageMode; } /** * Set whether this RatingData is in average mode. * @param inAverageMode Whether this RatingData is in * average mode. */ public synchronized void setInAverageMode(boolean inAverageMode) { this.inAverageMode = inAverageMode; } private String[] hoverTexts; /** * Get the hover texts (i.e., for the "star" images only). * @return The hover texts. */ public synchronized String[] getHoverTexts() { return this.hoverTexts; } /** * Set the hover texts (i.e., for the "star" images only). * @param hoverTexts The new hover texts. */ public synchronized void setHoverTexts(String[] hoverTexts) { this.hoverTexts = hoverTexts; } /** *

Calculate a new normal mode text.

*@return A new normal mode text. */ private String calculateNormalModeText(int localGrade, boolean saved) { String localNormalModeText = null; if (localGrade > 0) { if (hoverTexts != null && localGrade <= hoverTexts.length && hoverTexts[localGrade - 1] != null){ Object[] args = {hoverTexts[localGrade - 1], new Integer(localGrade)}; String pattern = saved ? this.normalModeTextPatternSaved : this.normalModeTextPattern; mf.applyPattern(pattern); localNormalModeText = mf.format(args); } else { localNormalModeText = saved ? this.simpleNormalModeTextSaved : this.simpleNormalModeText; } } if (localGrade == -1) { localNormalModeText = saved ? this.notInterestedNormalModeTextSaved : this.notInterestedNormalModeText; } else if (localGrade == 0) { localNormalModeText = saved ? this.clearNormalModeTextSaved : this.clearNormalModeText; } return localNormalModeText; } }