package net.sf.egonet.web.panel;
import java.util.ArrayList;
import org.apache.wicket.model.Model;
import org.apache.wicket.markup.html.basic.Label;
import com.google.common.collect.Lists;
import net.sf.egonet.model.Alter;
import net.sf.egonet.model.Answer;
import net.sf.egonet.model.Question;
import net.sf.egonet.model.Answer.SkipReason;
import net.sf.egonet.web.component.FocusOnLoadBehavior;
import net.sf.egonet.web.component.TextField;
import net.sf.egonet.web.panel.NumericLimitsPanel.NumericLimitType;
public class NumberAnswerFormFieldPanel extends AnswerFormFieldPanel {
private TextField textField;
private CheckboxesPanel<String> refDKCheck;
private Label lblNumberPrompt;
private int lowBound; // values used for range Validation
private int highBound;
public NumberAnswerFormFieldPanel(String id, Question question, ArrayList<Alter> alters, Long interviewId) {
super(id,question,Answer.SkipReason.NONE,alters,interviewId);
lowBound = Integer.MIN_VALUE;
highBound = Integer.MAX_VALUE;
build("");
}
public NumberAnswerFormFieldPanel(String id,
Question question, String answer, Answer.SkipReason skipReason, ArrayList<Alter> alters, Long interviewId)
{
super(id,question,skipReason,alters,interviewId);
lowBound = Integer.MIN_VALUE;
highBound = Integer.MAX_VALUE;
build(answer);
}
private void build(String previousAnswer) {
textField = new TextField("answer", new Model(previousAnswer), String.class);
add(textField);
lblNumberPrompt = new Label ("numberPrompt", getNumericRangePrompt(alters));
add(lblNumberPrompt);
ArrayList<String> refAndDK = Lists.newArrayList();
if ( !question.getType().equals(Question.QuestionType.EGO_ID)) {
if ( question.getDontKnowButton())
refAndDK.add(dontKnow);
if ( question.getRefuseButton())
refAndDK.add(refuse);
}
ArrayList<String> selectedRefAndDK = Lists.newArrayList();
if(originalSkipReason.equals(SkipReason.DONT_KNOW)) {
selectedRefAndDK.add(dontKnow);
}
if(originalSkipReason.equals(SkipReason.REFUSE)) {
selectedRefAndDK.add(refuse);
}
refDKCheck = new CheckboxesPanel<String>("refDKCheck",
refAndDK,selectedRefAndDK);
add(refDKCheck);
setBounds(alters);
}
public String getAnswer() {
return textField.getText();
}
public void setAutoFocus() {
textField.add(new FocusOnLoadBehavior());
}
public boolean dontKnow() {
return refDKCheck.getSelected().contains(dontKnow);
}
public boolean refused() {
return refDKCheck.getSelected().contains(refuse);
}
// the next two functions are virtual duplicates, differing only in
// that one deals with integers and the other with strings.
// they could be merged, but are separate now
/**
* IF the question requires a numeric answer and a range has been set
* this displays the allowed range.
* @param alterId1 - ID of alter number 1 (may be null)
* @param alterId2 - ID of alter number 2 (may be null)
* @return a string that shows the allowed numeric input range
*/
public String getNumericRangePrompt(ArrayList<Alter> listOfAlters) {
String strAnswer;
String strReturn;
NumericLimitType minLimitType;
NumericLimitType maxLimitType;
boolean hasLowBound;
boolean hasHighBound;
minLimitType = question.getMinLimitType();
maxLimitType = question.getMaxLimitType();
hasLowBound = (minLimitType==NumericLimitType.NLT_NONE)?false:true;
hasHighBound = (maxLimitType==NumericLimitType.NLT_NONE)?false:true;
if ( !hasLowBound && !hasHighBound )
return("(number)");
strReturn = "(number [";
switch ( minLimitType ) {
case NLT_NONE: strReturn += " up"; break;
case NLT_LITERAL: strReturn += question.getMinLiteral(); break;
case NLT_PREVQUES:
strAnswer = question.answerToRangeLimitingQuestion(interviewId, alters, false);
strReturn += strAnswer;
break;
}
switch ( maxLimitType ) {
case NLT_NONE: strReturn += " and up"; break;
case NLT_LITERAL: strReturn += " to " + question.getMaxLiteral(); break;
case NLT_PREVQUES:
strAnswer = question.answerToRangeLimitingQuestion(interviewId, alters, true);
strReturn += " to " + strAnswer;
break;
}
strReturn += "])";
return(strReturn);
}
/**
* grabs values from the question to construct the range validator
* if there are no bounds or the lowbound>highbound the validator
* is not set and false is returned
*/
private boolean setBounds(ArrayList<Alter> listOfAlters) {
String strAnswer;
NumericLimitType minLimitType;
NumericLimitType maxLimitType;
boolean hasLowBound;
boolean hasHighBound;
lowBound = -10000;
highBound = 10000;
minLimitType = question.getMinLimitType();
maxLimitType = question.getMaxLimitType();
hasLowBound = (minLimitType==NumericLimitType.NLT_NONE)?false:true;
hasHighBound = (maxLimitType==NumericLimitType.NLT_NONE)?false:true;
if ( !hasLowBound && !hasHighBound )
return(false);
switch (minLimitType ) {
case NLT_NONE: break;
case NLT_LITERAL: lowBound = question.getMinLiteral(); break;
case NLT_PREVQUES:
strAnswer = question.answerToRangeLimitingQuestion(interviewId, alters, false);
try {
lowBound = Integer.parseInt(strAnswer);
} catch ( NumberFormatException nfe) {
lowBound = Integer.MIN_VALUE;
}
break;
}
switch (maxLimitType ) {
case NLT_NONE: break;
case NLT_LITERAL: highBound = question.getMaxLiteral(); break;
case NLT_PREVQUES:
strAnswer = question.answerToRangeLimitingQuestion(interviewId, alters, true);
try {
highBound = Integer.parseInt(strAnswer);
} catch ( NumberFormatException nfe) {
highBound = Integer.MAX_VALUE;
}
break;
}
// if we somehow got to a state where the lowBound > highBound
// play it safe and don't set any bounds;
if ( lowBound>highBound )
return(false);
// rangeValidator = new NumberValidator.RangeValidator(lowBound,highBound);
// textField.add(rangeValidator);
return(true);
}
/**
* performs simple verification that the number entered into the text field
* was within the limits. Returns an integer so we have the option of
* rather detailed error messages such as "Your value of 14 is too high", but
* for the most part we're concerned whether or not this returns 0
* @return 0 if we are in the range , >0 if answer is too low
* <0 if answer is too high
*/
private int numericAnswerStatus() {
String strAnswer;
int iAnswer;
strAnswer = getAnswer().trim();
try {
iAnswer = Integer.parseInt(strAnswer);
} catch ( NumberFormatException e ) {
iAnswer = 0;
return(Integer.MAX_VALUE);
}
// System.out.println ( "numericAnswerStatus lowBound=" + lowBound + " highBound=" + highBound + " answer=" + iAnswer);
// if we somehow got in a state where lowBound>highBound are
// incompatible, play it safe and do NO error checking
if ( lowBound>highBound )
return(0);
if ( lowBound==Integer.MIN_VALUE && highBound==Integer.MAX_VALUE)
return(0);
if ( iAnswer < lowBound ) {
return(lowBound - iAnswer);
}
if (iAnswer > highBound ) {
return(highBound - iAnswer);
}
return(0);
}
/**
* returns the string used if an incorrect number of checkboxes
* are selected to prompt the user to check more or fewer
*/
public String getRangeCheckNotification() {
int iAnswerStatus;
String strAnswer;
String strNotification = "";
if ( dontKnow() || refused())
return(strNotification);
strAnswer = getAnswer().trim();
try {
int iAnswer = Integer.parseInt(strAnswer);
} catch ( NumberFormatException e ) {
strNotification = "Non-digits in answer field.";
return (strNotification);
}
iAnswerStatus = numericAnswerStatus();
if ( iAnswerStatus<0 ) {
strNotification = "Answer is above upper limit of " + highBound + ".";
} else if ( iAnswerStatus>0 ) {
strNotification = "Answer is below lower limit of " + lowBound + ".";
}
return (strNotification);
}
/**
* if the user selected dontKnow or refused to answer a question
* don't bother counting the responses.
*/
public boolean rangeCheckOkay() {
if ( dontKnow() || refused())
return (true);
return ( (numericAnswerStatus()==0)?true:false);
}
}