gt: 		return( maxCheckableBoxes );
context: package net.sf.egonet.model;
  
  import java.util.ArrayList;
  import java.util.regex.Matcher;
  
  import net.sf.egonet.model.Answer.AnswerType;
  import net.sf.egonet.persistence.TextInsertionUtil;
  import net.sf.egonet.web.panel.NumericLimitsPanel.NumericLimitType;
  
  public class Question extends OrderedEntity
  {
  	// July 2010 - D. Kennedy requested the option for multiple questions
  	// to appear on one page, but did not want it actually incorporated
  	// this variable will control whether the option is available
  	public static final boolean ALLOW_MULTIPLE_QUESTIONS_PER_PAGE = false;
  	
  	public static enum QuestionType { EGO_ID, EGO, ALTER, ALTER_PAIR, NETWORK };
  
  	private Long studyId;
  	private String title;    // Gender
  	protected String citation; // This question originally written by Dr. X from the Institute of Advanced Research.
  	protected String citationOld;
  	protected String prompt;   // Are you male or female?
  	protected String promptOld;
  	protected String preface;   // The following questions relate to...
  	protected String prefaceOld;
  	private AnswerType answerType;
  	private QuestionType type;
  	private Boolean askingStyleList;
  	private Long answerReasonExpressionId; // Answer the question if this expression is null or true
  	private Long networkRelationshipExprId;
  	private Long networkNShapeQId;
  	private Long networkNColorQId;
  	private Long networkNSizeQId;
  	private Long networkEColorQId;
  	private Long networkESizeQId;
  
  	// an 'additional logic' expression in string form
  	// to be evaluated if answerReasonExpressionId is null
  	private String useIfExpression;
  	// variables that deal with Selection / Multiple Selection questions
  	// using the 'other-specify' option
  	private Boolean otherSpecify;  // if true, selection of 'Other' brings up text box
  	// each question has SIX optional buttons associated with it
  	// four of these are only active if the question is asked in a 
  	// list format, two always apply
  	private Boolean noneButton; // list format only
  	private Boolean allButton;  // list format only
  	private Boolean pageLevelDontKnowButton; // list format only
  	private Boolean pageLevelRefuseButton;   // list format only
  	private Boolean dontKnowButton; // single question format only
  	private Boolean refuseButton;   // single question format only
  	private String allOptionString; // if All button is clicked, use this
  	// variables that will be specific to numeric answers, 
  	// and (optionally) limiting them to a range
  	private NumericLimitType minLimitType;
  	private Integer minLiteral;
  	private String minPrevQues;
  	private NumericLimitType maxLimitType;
  	private Integer maxLiteral;
  	private String maxPrevQues;	
  	// similar variables to be used with MULTIPLE_SELECTION questions
  	private Integer minCheckableBoxes;
  	private Integer maxCheckableBoxes;
  	// these variables are (optionally) used with
  	// list-of-alters format, when we want a limited number
  	// of one item selected
  	private Boolean withListRange; 
  	private String  listRangeString;
  	private Integer minListRange;
  	private Integer maxListRange;
  	// Date & Time_Span questions will let authors select which
  	// time units are available for answering.
  	// they will be stored as boolean bit values
  	private Integer timeUnits;
  	// symmetric indicates if a question should be asked a->b and b->a or just a->b
  	private Boolean symmetric;
  	// groupID will be used to group questions together on one page
  	private Boolean keepOnSamePage;
  	
  	public Question() {
  		prompt = "";
  		answerType = AnswerType.TEXTUAL;
  		type = QuestionType.EGO;
  		askingStyleList = new Boolean(false);
  		otherSpecify = new Boolean(false);
  		noneButton = new Boolean(false);
  		allButton = new Boolean(false);
  		pageLevelDontKnowButton = new Boolean(true);
  		pageLevelRefuseButton = new Boolean(true);
  		dontKnowButton = new Boolean(true);
  		refuseButton = new Boolean(true);
  		allOptionString = "";
  		useIfExpression = "";
  		minLimitType = NumericLimitType.NLT_NONE;
  		minLiteral = 0;
  		minPrevQues = "";
  		maxLimitType = NumericLimitType.NLT_NONE;
  		maxLiteral = 10000;
  		maxPrevQues = "";
  		
  		minCheckableBoxes = 0;
  		maxCheckableBoxes = 100;
  		withListRange = new Boolean(false); 
  		listRangeString = new String("");
  		minListRange = new Integer(0);
  		maxListRange = new Integer(100);
  		timeUnits = 0xff;
  		symmetric = true;
  		keepOnSamePage = false;
  	}
  
  	public String toString() {
  		return (title == null ? "Untitled question" : title)+" ("+type+")";
  	}
  	
  	// Copy all attributes except identifiers: id and key
  	// Change name just slightly to not conflict with existing names
  	public Question copy() {
  		Question c = new Question();
  		c.setAnswerReasonExpressionId(getAnswerReasonExpressionId());
  		c.setAnswerType(getAnswerType());
  		c.setAskingStyleList(getAskingStyleList());
  		c.setCitation(getCitation());
  		c.setMaxCheckableBoxes(getMaxCheckableBoxes());
  		c.setMaxLimitTypeDB(getMaxLimitTypeDB());
  		c.setMaxLiteral(getMaxLiteral());
  		c.setMaxPrevQues(getMaxPrevQues());
  		c.setMinCheckableBoxes(getMinCheckableBoxes());
  		c.setMinLimitTypeDB(getMinLimitTypeDB());
  		c.setMinLiteral(getMinLiteral());
  		c.setMinPrevQues(getMinPrevQues());
  		c.setOrdering(getOrdering());
  		c.setOtherSpecify(getOtherSpecify());
  		c.setPreface(getPreface());
  		c.setPrompt(getPrompt());
  		c.setStudyId(getStudyId());
  		c.setTitle(getTitle().replaceAll("\\s+c\\d+", "")+" c"+(Math.abs(c.getRandomKey())%1000));
  		c.setTypeDB(getTypeDB());
  		c.setUseIfExpression(getUseIfExpression());
  		c.setNoneButton(getNoneButton());
  		c.setAllButton(getAllButton());
  		c.setPageLevelDontKnowButton(getPageLevelDontKnowButton());
  		c.setPageLevelRefuseButton(getPageLevelRefuseButton());
  		c.setDontKnowButton(getDontKnowButton());
  		c.setRefuseButton(getRefuseButton());
  		c.setAllOptionString(getAllOptionString());		
  		c.setWithListRange(getWithListRange());
  		c.setListRangeString(getListRangeString());
  		c.setMinListRange(getMinListRange());
  		c.setMaxListRange(getMaxListRange());
  		c.setTimeUnits(getTimeUnits());
  		c.setSymmetric(getSymmetric());
  		c.setKeepOnSamePage(getKeepOnSamePage());
  		return c;
  	}
  	
  	@Override
  	public int compareTo(OrderedEntity entity) {
  		if(entity instanceof Question) {
  			Question question = (Question) entity;
  			if(! getType().equals(question.getType())) {
  				if(getType().ordinal() < question.getType().ordinal()) {
  					return -1;
  				}
  				if(getType().ordinal() > question.getType().ordinal()) {
  					return 1;
  				}
  			}
  		}
  		return super.compareTo(entity);
  	}
  
  	public String individualizePrompt(ArrayList<Alter> alters) {
  		String questionText = getPrompt();
  		String dd = "\\$\\$";
  		String dd1 = dd+"1";
  		String dd2 = dd+"2";
  		String blank = "_____";
  		String alter1Name = alters.size() > 0 ? alters.get(0).getName() : blank;
  		String alter2Name = alters.size() > 1 ? alters.get(1).getName() : blank;
  		
  		// Prevent StringIndexOutOfBoundsException when alter name contains ? or \
  		alter1Name = Matcher.quoteReplacement(alter1Name);
  		alter2Name = Matcher.quoteReplacement(alter2Name);
  		
  		if(getType().equals(QuestionType.ALTER)) {
  			questionText = questionText.replaceAll(dd1, alter1Name);
  			questionText = questionText.replaceAll(dd, alter1Name);
  		}
  		if(getType().equals(QuestionType.ALTER_PAIR)) {
  			questionText = questionText.replaceAll(dd1, alter1Name);
  			questionText = questionText.replaceAll(dd2, alter2Name);
  			questionText = questionText.replaceFirst(dd, alter1Name);
  			questionText = questionText.replaceFirst(dd, alter2Name);
  		}
  		return questionText;
  	}
  	
  	public AnswerType   getAnswerType()   { return this.answerType;   }
  	
  	public String       getTitle()        { return this.title;        }
  	public QuestionType getType()         { return this.type;         }
  	public Long         getStudyId()      { return this.studyId;        }
  	public String       getAnswerTypeDB() { return getAnswerType().name(); }
  	public String       getTypeDB()       { return typeDB(getType());       }
  
  	public static String typeDB(QuestionType type) { return type == null ? null : type.name(); }
  	/**
  	 * @return
  	 *  if needsSelectionResponse returns a list of one Option,
  	 *  if needsMultiSelectionResponse returns a list of Option
  	 *  otherwise null
  	 */
  	public void setAnswerType(AnswerType val) { this.answerType = val; }
  	public void setTitle(String          val) { this.title      = val; }
  	public void setType(QuestionType     val) { this.type       = val; }
  	public void setStudyId(Long          val) { this.studyId      = val; }
  
  	public void setAnswerTypeDB(String val) { this.setAnswerType(AnswerType.valueOf(val)); }
  	public void setTypeDB(String val) { this.setType(QuestionType.valueOf(val)); }
  
  	/** Whether the question identifies the interviewee
  	  */
  	public boolean identifiesEgo() { return getType() == QuestionType.EGO_ID; }
  
  	/** Whether the question concerns the interviewee
  	  */
  	public boolean isAboutEgo() { return getType() == QuestionType.EGO; }
  
  	/** Whether the question concerns the interviewee's alter
  	  */
  	public boolean isAboutAlter() { return getType() == QuestionType.ALTER; }
  
  	/** Whether the question describes the relationship between two of the interviewee's alters
  	  */
  	public boolean isAboutRelationship() { return getType() == QuestionType.ALTER_PAIR; }
  
  	/** Whether the question describes the network of the interviewee's alters
  	  */
  	public boolean isAboutNetwork() { return getType() == QuestionType.NETWORK; }
  
  	public boolean needsTextualResponse()        { return getAnswerType() == AnswerType.TEXTUAL;            }
  	public boolean needsNumericalResponse()      { return getAnswerType() == AnswerType.NUMERICAL;          }
  	public boolean needsSelectionResponse()      { return getAnswerType() == AnswerType.SELECTION;          }
  	public boolean needsMultiSelectionResponse() { return getAnswerType() == AnswerType.MULTIPLE_SELECTION; }
  
  	public void setAnswerReasonExpressionId(Long answerReasonExpressionId) {
  		this.answerReasonExpressionId = answerReasonExpressionId;
  	}
  
  	public Long getAnswerReasonExpressionId() {
  		return answerReasonExpressionId;
  	}
  
  	public void setNetworkRelationshipExprId(Long expId)
  	{
  		this.networkRelationshipExprId = expId;
  	}
  
  	public Long getNetworkRelationshipExprId()
  	{
  		return networkRelationshipExprId;
  	}
  
  	public void setNetworkNShapeQId(Long qid)
  	{
  		this.networkNShapeQId = qid;
  	}
  
  	public Long getNetworkNShapeQId()
  	{
  		return networkNShapeQId;
  	}
  
  	public void setNetworkNColorQId(Long qid)
  	{
  		this.networkNColorQId = qid;
  	}
  
  	public Long getNetworkNColorQId()
  	{
  		return networkNColorQId;
  	}
  
  	public void setNetworkNSizeQId(Long qid)
  	{
  		this.networkNSizeQId = qid;
  	}
  
  	public Long getNetworkNSizeQId()
  	{
  		return networkNSizeQId;
  	}
  
  	public void setNetworkEColorQId(Long qid)
  	{
  		this.networkEColorQId = qid;
  	}
  
  	public Long getNetworkEColorQId()
  	{
  		return networkEColorQId;
  	}
  
  	public void setNetworkESizeQId(Long qid)
  	{
  		this.networkESizeQId = qid;
  	}
  
  	public Long getNetworkESizeQId()
  	{
  		return networkESizeQId;
  	}
  
  
  	
  	// ---------------------------------
  
  	protected String getPromptOld() {
  		return null;
  	}
  	public String getPrompt() {
  		return migrateToText(this,"prompt");
  	}
  	public void setPrompt(String val) {
  		this.prompt = val; 
  	}
  	protected void setPromptOld(String val) {
  		this.promptOld  = val;
  	}
  	
  
  	public void setPreface(String preface) {
  		this.preface = preface;
  	}
  	public String getPreface() {
  		return migrateToText(this,"preface");
  	}
  	public boolean hasPreface() {
  		String preface = getPreface();
  		return ! (preface == null || preface.isEmpty() || preface.matches("\\s*"));
  	}
  	protected void setPrefaceOld(String preface) {
  		this.prefaceOld = preface;
  	}
  	protected String getPrefaceOld() {
  		return null;
  	}
  	
  	public String getCitation() { 
  		return migrateToText(this,"citation");
  	}
  	public void setCitation(String val) {
  		this.citation   = val;
  	}
  	protected String getCitationOld() { 
  		return null;
  	}
  	protected void setCitationOld(String val) {
  		this.citationOld = val;
  	}
  
  	public void setAskingStyleList(Boolean askingStyleList) {
  		if(askingStyleList != null) {
  			this.askingStyleList = askingStyleList;
  		}
  	}
  
  	public Boolean getAskingStyleList() {
  		if(getType().equals(QuestionType.EGO)) {
  			return false;
  		}
  		return askingStyleList == null ? false : askingStyleList;
  	}
  	
  	/**
  	 * get/set the 'otherSpecify' flag which will deal with
  	 * a text entry box appearing when 'Other' is selected
  	 * @param otherSpecify
  	 */
  	public void setOtherSpecify (Boolean otherSpecify) {
  		this.otherSpecify = (otherSpecify==null)? false:otherSpecify;
  	}
  	public Boolean getOtherSpecify() {
  		if ( otherSpecify==null )
  			otherSpecify = false;
  		return(otherSpecify);
  	}
  	
  	public void setNoneButton ( Boolean noneButton) {
  		this.noneButton = (noneButton==null) ? new Boolean(false) : noneButton;
  	}
  	public Boolean getNoneButton() {
  		if ( noneButton==null)
  			noneButton = new Boolean(false);
  		return(noneButton);
  	}
  	
  	public void setAllButton ( Boolean allButton) {
  		this.allButton = (allButton==null) ? new Boolean(false) : allButton;
  	}
  	public Boolean getAllButton() {
  		if ( allButton==null)
  			allButton =new Boolean(false);
  		return(allButton);
  	}
  	
  	public void setPageLevelDontKnowButton ( Boolean pageLevelDontKnowButton ) {
  		if ( pageLevelDontKnowButton==null)
  			System.out.println ( "question " + title + "has null pageLevelDontKnowButton");
  		this.pageLevelDontKnowButton  = (pageLevelDontKnowButton==null) ? new Boolean(true) : pageLevelDontKnowButton;
  	}
  	public Boolean getPageLevelDontKnowButton() {
  		if ( pageLevelDontKnowButton==null)
  			pageLevelDontKnowButton  = new Boolean(true);
  		return(pageLevelDontKnowButton );
  	}
  	
  	public void setPageLevelRefuseButton( Boolean pageLevelRefuseButton ) {
  		this.pageLevelRefuseButton = (pageLevelRefuseButton==null) ? new Boolean(true) : pageLevelRefuseButton; 
  	}
  	public Boolean getPageLevelRefuseButton() {
  		if ( pageLevelRefuseButton==null)
  			pageLevelRefuseButton = new Boolean(true);
  		return(pageLevelRefuseButton);
  	}
  	
  	public void setDontKnowButton ( Boolean dontKnowButton ) {
  		this.dontKnowButton = (dontKnowButton==null) ? new Boolean(true): dontKnowButton;
  	}
  	public Boolean getDontKnowButton() {
  		if ( dontKnowButton==null)
  			dontKnowButton = new Boolean(true);
  		return(dontKnowButton);
  	}
  	
  	public void setRefuseButton ( Boolean refuseButton) {
  		this.refuseButton = (refuseButton==null) ? new Boolean(true) : refuseButton;
  	}
  	public Boolean getRefuseButton() {
  		if ( refuseButton==null)
  			refuseButton = new Boolean(true);
  		return(refuseButton);
  	}	
  	
  	public void setAllOptionString ( String allOptionString) {
  		this.allOptionString = (allOptionString==null) ? "" : allOptionString;
  	}
  	public String getAllOptionString() {
  		if ( allOptionString==null)
  			allOptionString = "";
  		return(allOptionString);
  	}
  	
  	public void setUseIfExpression ( String useIfExpression ) {
  		this.useIfExpression = (useIfExpression==null) ? "" : useIfExpression;
  	}
  	public String getUseIfExpression() {
  		if ( useIfExpression==null )
  			useIfExpression = "";
  		return (useIfExpression);
  	}
  	
  	/* ****************************************************************** */
  	/* next set of functions deal with setting limits on NUMERICAL        */
  	/* questions                                                          */
  	/* ****************************************************************** */
  	
  	/**
  	 * getters & setters for the numeric bounds
  	 */
  	
  	public void setMinLimitType(NumericLimitType minLimitType) {
  		this.minLimitType = (minLimitType==null)?NumericLimitType.NLT_NONE:minLimitType;
  	}
  	public NumericLimitType getMinLimitType() { return(minLimitType);}
  	
  	public String getMinLimitTypeDB() {
  		NumericLimitType nlType = getMinLimitType();
  		return ( nlType==null ? NumericLimitType.NLT_NONE.name() : nlType.name() );
  	}
  
  	public void setMinLimitTypeDB ( String val ) {
  	    NumericLimitType nlType;
  	   
  	    if ( val==null || val.length()==0 ) 
  	        nlType = NumericLimitType.NLT_NONE;
  	    else
  		    nlType = NumericLimitType.valueOf(val);
  	    setMinLimitType(nlType);
  	}
  	  
  	public void setMinLiteral ( Integer minLiteral ) {
  		this.minLiteral = (minLiteral==null)?0:minLiteral;
  	}
  	public Integer getMinLiteral () { return(minLiteral);}
  	
  	public void setMinPrevQues ( String minPrevQues ) {
  		this.minPrevQues = (minPrevQues==null)?"":minPrevQues;
  	}
  	public String getMinPrevQues() {return(minPrevQues);};
  	
  	public void setMaxLimitType(NumericLimitType maxLimitType) {
  		this.maxLimitType = (maxLimitType==null)?NumericLimitType.NLT_NONE:maxLimitType;
  	}
  	public NumericLimitType getMaxLimitType() { return(maxLimitType);}
  	
  	
  	public String getMaxLimitTypeDB() {
  		NumericLimitType nlType = getMaxLimitType();
  		return ( nlType==null ? NumericLimitType.NLT_NONE.name() : nlType.name() );
  	}
  
  	public void setMaxLimitTypeDB ( String val ) {
  	    NumericLimitType nlType;
  	  
  	    if ( val==null || val.length()==0 ) 
  	        nlType = NumericLimitType.NLT_NONE;
  	    else
  		    nlType = NumericLimitType.valueOf(val);
  	    setMaxLimitType(nlType);
  	}
  	
  	public void setMaxLiteral ( Integer maxLiteral ) {
  		this.maxLiteral = (maxLiteral==null)?10000:maxLiteral;
  	}
  	public Integer getMaxLiteral () { return(maxLiteral);}
  	
  	public void setMaxPrevQues ( String maxPrevQues ) {
  		this.maxPrevQues = (maxPrevQues==null)?"":maxPrevQues;
  	}
  	public String getMaxPrevQues() {return(maxPrevQues);};
  	
  	/**
  	 * getters & setters for min/max checkable checkboxes
  	 * for MULTIPLE_SELECTION questions
  	 */
  	
  	public void setMinCheckableBoxes (Integer minCheckableBoxes) {
  		this.minCheckableBoxes = (minCheckableBoxes==null)?new Integer(0):minCheckableBoxes;
  	}
  	public Integer getMinCheckableBoxes() { 
  		return( minCheckableBoxes );
  	}
  	public void setMaxCheckableBoxes (Integer maxCheckableBoxes) {
  		this.maxCheckableBoxes = (maxCheckableBoxes==null)?new Integer(100):maxCheckableBoxes;
  	}
  	public Integer getMaxCheckableBoxes() { 
  