/*** * Copyright (c) 2008, Endless Loop Software, Inc. * * This file is part of EgoNet. * * EgoNet is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * EgoNet is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.egonet.model; import java.io.IOException; import java.util.Arrays; import java.util.Collection; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import javax.swing.DefaultListModel; import org.egonet.exceptions.CorruptedInterviewException; import org.egonet.exceptions.MissingPairException; import org.egonet.gui.EgoStore; import org.egonet.model.answer.*; import org.egonet.model.question.AlterPairQuestion; import org.egonet.model.question.AlterPromptQuestion; import org.egonet.model.question.AlterQuestion; import org.egonet.model.question.EgoQuestion; import org.egonet.model.question.Question; import org.egonet.statistics.StatRecord; import org.egonet.statistics.Statistics; import org.egonet.statistics.StatRecord.EgoAnswer; import org.egonet.util.ELSMath; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; public class Interview implements Comparable<Interview> { final private static Logger logger = LoggerFactory.getLogger(Interview.class); private Answer[] _answers; private final Study _study; private int[][] _matrix; private Statistics _stats = null; private String notes = ""; private boolean _complete; private String[] _alterList = new String[0]; // so alter pair is at least stateable /* This matrix will contain the alters of every question prompt. */ private String[][] _alterQuestionPromptList; private int _qIndex = 0; private int _numAlterPairs; private int _numAnswers; public boolean _statisticsAvailable = false; private final String sIntName; private boolean followup = false; public boolean isFollowup() { return followup; } public void setFollowup(boolean followup) { this.followup = followup; } public String getIntName() { return sIntName; } /*************************************************************************** * Create interview from question list * * @param client * parent object for globals * @param numAlters * number of alters to be elicited * @throws CorruptedInterviewException * if unable to read interview */ public Interview(Study study, String sIntName) throws CorruptedInterviewException { this.sIntName = sIntName; _study = study; //Initializes the _alterQuestionPromptList = new String[_study.getQuestionOrder(AlterPromptQuestion.class).size()][0]; //_alterList = new String[]{null}; reinitializeAlterData(); } /** * Helps us find old answers based on question ID * @param haystack old array of answers * @param needle unique id to find * @return the answer that matches in the array */ Answer findUniqueQuestion(Answer[] haystack, long needle) { Answer ret = null; for(Answer possible : haystack) { if(possible.getQuestionId().equals(needle)) ret = possible; } return ret; } /** * This method resets many data structures that are dependent on the number of alters. This used to happen * in a constructor, but now that alter lists have a min and max number of elements, we need to resize these * data on the fly. * @throws CorruptedInterviewException */ public void reinitializeAlterData() throws CorruptedInterviewException { /* Calculate some interview values */ int _numAlters = _alterList.length; _numAlterPairs = ELSMath.summation(_numAlters - 1); set_numAnswers(_study.getQuestionOrder(EgoQuestion.class).size() + _study.getQuestionOrder(AlterPromptQuestion.class) .size() + (_numAlters * _study.getQuestionOrder( AlterQuestion.class).size()) + (_numAlterPairs * _study.getQuestionOrder( AlterPairQuestion.class).size())); // we need to preserve old data, so hold on to any interesting questions (avoid null _answers) Answer [] _oldanswers = new Answer[_answers != null ? _answers.length : 0]; if(_answers != null) System.arraycopy(_answers, 0, _oldanswers, 0, _answers.length); _answers = new Answer[get_numAnswers()]; /* Generate answer instances */ int counter = 0; /* Ego Questions */ Iterator questions = _study.getQuestionOrder(EgoQuestion.class).iterator(); while (questions.hasNext()) { Long questionId = (Long) questions.next(); Question question = _study.getQuestions().getQuestion(questionId); if (question == null) { throw new CorruptedInterviewException(); } int newindex = counter++; Answer oldAnswer = findUniqueQuestion(_oldanswers, question.UniqueId); // if no previous, new, otherwise try to keep if(oldAnswer == null) { Answer a = Answer.newInstance(question.answerType); a.setQuestionId(question.UniqueId); _answers[newindex] = a; } else _answers[newindex] = oldAnswer; } /* Alter Prompt Questions */ questions = _study.getQuestionOrder(AlterPromptQuestion.class).iterator(); while (questions.hasNext()) { Long questionId = (Long) questions.next(); Question question = _study.getQuestions().getQuestion(questionId); if (question == null) { throw new CorruptedInterviewException(); } Answer oldAnswer = findUniqueQuestion(_oldanswers, question.UniqueId); int newindex = counter++; // if no previous, new, otherwise try to keep if(oldAnswer == null) { Answer a = Answer.newInstance(question.answerType); a.setQuestionId(question.UniqueId); _answers[newindex] = a; } else _answers[newindex] = oldAnswer; //_answers[counter++] = new Answer(question.UniqueId); } int j,k; /* Alter Questions */ for (j = 0; j < _numAlters; j++) { questions = _study.getQuestionOrder(AlterQuestion.class).iterator(); int[] alter = { j }; while (questions.hasNext()) { Long questionId = (Long) questions.next(); Question question = _study.getQuestions().getQuestion(questionId); if (question == null) { throw new CorruptedInterviewException(); } Answer answer = Answer.newInstance(question.answerType); answer.setQuestionId(question.UniqueId); answer.setAlters(alter); _answers[counter++] = answer; } } /* Alter Pair Questions */ for (k = 0; k < _numAlters; k++) { for (j = (k + 1); j < _numAlters; j++) { questions = _study.getQuestionOrder(AlterPairQuestion.class).iterator(); int[] alters = { k, j }; while (questions.hasNext()) { Question question = _study.getQuestions().getQuestion((Long) questions.next()); if (question == null) { throw new CorruptedInterviewException(); } Answer answer = Answer.newInstance(question.answerType); answer.setQuestionId(question.UniqueId); answer.setAlters(alters); _answers[counter++] = answer; } } } // really shouldn't make stats available depend on alter answers questions = _study.getQuestionOrder(AlterPairQuestion.class).iterator(); while (questions.hasNext()) { Question question = _study.getQuestions().getQuestion((Long) questions.next()); if (question == null) { throw new CorruptedInterviewException(); } else { if (question.isStatable()) { _statisticsAvailable = true; } } } } /*************************************************************************** * Searches question list for all questions and places them in list * * @param dlm * list model to use in inserting questions */ public void fillList(DefaultListModel<Question> dlm) { dlm.removeAllElements(); for (int i = 0; i < get_numAnswers(); i++) { // Question q = getQuestion(i); Question q = _study.getQuestions().getQuestion(_answers[i].getQuestionId()); if (q instanceof AlterPairQuestion && (_study.getUIType().equals(Shared.PAIR_ELICITATION) || _study .getUIType().equals(Shared.THREE_STEP_ELICITATION))) { /* Skip Alter Pair Questions for Interactive Linking Studies */ } else { dlm.addElement(q); } } } /*************************************************************************** * Returns total number of questions in an interview * * @return i number of questions */ public int getNumQuestions() { return get_numAnswers(); } /*************************************************************************** * Returns current question from an interview * * @return i question index */ public int getQuestionIndex() { return _qIndex; } /*************************************************************************** * Returns current list of alters * * @return s String Array of alters */ public String[] getAlterList() { return _alterList; } /*************************************************************************** * Sets current list of alters * * @param s * String Array of alters * @throws CorruptedInterviewException */ public void setAlterList(String[] s) throws CorruptedInterviewException { _alterList = s; reinitializeAlterData(); } /*************************************************************************** * Gets a set containing all the answers which use a selected question * * @param qId * Unique Identifier of question * @return Set of answers using this question */ public Set<Answer> getAnswerSubset(Long qId) { Set<Answer> s = new HashSet<Answer>(_numAlterPairs); for (int i = 0; i < _answers.length; i++) { if (_answers[i].getQuestionId().equals(qId)) { s.add(_answers[i]); } } return (s); } /*************************************************************************** * Gets a List containing all the answers to ego questions * * @return List of answers using this question */ public List<Answer> getEgoAnswers() { List<Answer> l = new ArrayList<Answer>(); int index = 0; Question q = _study.getQuestions().getQuestion(_answers[index].getQuestionId()); while (q instanceof EgoQuestion) { l.add(_answers[index]); q = _study.getQuestions().getQuestion(_answers[++index].getQuestionId()); } return (l); } /*************************************************************************** * Gets a List containing all the answers to alter questions * * @return List of answers using this question */ public List<Question> getAlterAnswers() { List<Question> l = new ArrayList<Question>(); Collection<Question> questionList = _study.getQuestions().values(); for (Question q : questionList) { if (!(q instanceof AlterQuestion)) continue; l.add(q); } return (l); } /*************************************************************************** * Sets current question Index * * @param i * index of question * @param force * choose the answer even if it's not valid (e.g. may be linked) * @return Choosen question */ public Question setInterviewIndex(int i, boolean force) { Question q = null; if (!force) { i = nextValidAnswer(i, true); } if ((i >= 0) && (i < get_numAnswers())) { _qIndex = i; q = (Question) getQuestion(_qIndex).clone(); q.setAnswer(_answers[_qIndex]); // replaces $$n in String with alter name indexed as alter #n q.text = completeText(q.text, q.getAnswer().getAlters()); } return (q); } /*************************************************************************** * Working forward from beginning, find first unanswered question * * @return index of first unanswered question */ public int getFirstUnansweredQuestion() { int i = 0; int rv = getNumQuestions() - 1; i = nextValidAnswer(0, true); while (i != -1) { if (!_answers[i].isAnswered()) { rv = i; break; } i = nextValidAnswer(i + 1, true); } return (rv); } /*************************************************************************** * Working backwards from the current Answer, find the first answer which * references a specified question. This is generally used for linked * questions * * @param startIndex * starting point from which to search * @param id * Unique Id of question * @return answer Answer of matching question */ private Answer getPriorQuestionInstance(int startIndex, Long id) { Answer a = null; int i; for (i = (startIndex - 1); i >= 0; i--) { if (_answers[i].getQuestionId().equals(id)) break; } if (i >= 0) { a = _answers[i]; } return (a); } /*************************************************************************** * Checks question link info of current answer to see if the question should * be included in interview. * * @param index * index of answer to check. * @return bool true iff question passes link check */ private boolean checkQuestionLink(int index) { boolean b = false; Question q = getQuestion(index); if (q.link.isActive()) { //logger.info("Link active!"); Answer a = getPriorQuestionInstance(index, q.link.getAnswer().getQuestionId()); if (a != null) { //logger.info("\tLink is tied to an answer -- "); if (q.link.getAnswer().getValue() == Answer.ALL_ADJACENT) { //logger.info("\tAnswer.ALL_ADJACENT"); if (a.adjacent) { //logger.info("\tif (a.adjacent) {b = true"); b = true; } else { //logger.info("\tif (a.adjacent) {b = false"); } } else { //logger.info("\tNOT NOT Answer.ALL_ADJACENT"); if (a.getValue() == q.link.getAnswer().getValue()) { //logger.info("\tif (a.getValue() "+a.getValue()+" == q.link.answer.getValue() "+q.link.answer.getValue()+") {b = true"); b = true; } else { //logger.info("\tif (a.getValue() "+a.getValue()+" == q.link.answer.getValue() "+q.link.answer.getValue()+") {b = false"); } } } else { /* * This case means one of 2 things happened, the user linked to * a non-existent question or the linked question comes after * this question. Neither case is proper but the less evil * appears to be to always ask this question in this case */ b = true; //logger.info("\tNO answer tied to link! BAD BAD"); } } else { //logger.info("Link inactive."); /* No link to check */ b = true; } return (b); } /*************************************************************************** * Returns current question from an interview * * @return b true iff there are more questions */ public boolean hasNext() { int checkIndex = nextValidAnswer(_qIndex + 1, true); return (checkIndex != -1); } /*************************************************************************** * Returns next question from an interview which passes all link checks. Cannot be used while reading an interview, but can only be used while conducting one. * * @param checkIndex * answer at which to start * @param forward * true iff search forward, else search backwards * @return answerIndex index of next or previous valid answer, -1 if none * found */ private int nextValidAnswer(int checkIndex, boolean forward) { boolean b = false; while (!b && (checkIndex < get_numAnswers()) && (checkIndex >= 0)) { // validate follow up and link if (getQuestion(checkIndex).isFollowupOnly() && isFollowup() && checkQuestionLink(checkIndex)) { b = true; } // validate non-followup and link else if (!getQuestion(checkIndex).isFollowupOnly() && checkQuestionLink(checkIndex)) { b = true; } else { checkIndex = forward ? (checkIndex + 1) : (checkIndex - 1); } } if (!b) { checkIndex = -1; } return (checkIndex); } /*************************************************************************** * Returns current question from an interview * * @return i question index */ public boolean hasPrevious() { int checkIndex = nextValidAnswer(_qIndex - 1, false); return (checkIndex != -1); } /*************************************************************************** * Returns current question from an interview * * @return i question index */ public Question next() { Question q; _qIndex = nextValidAnswer(_qIndex + 1, true); q = (Question) getQuestion(_qIndex).clone(); q.setAnswer(_answers[_qIndex]); q.text = completeText(q.text, q.getAnswer().getAlters()); return (q); } /*************************************************************************** * Returns current question from an interview * * @return i question index */ public Question previous() { Question q; _qIndex = nextValidAnswer(_qIndex - 1, false); // assert (qIndex != -1); q = (Question) getQuestion(_qIndex).clone(); q.setAnswer(_answers[_qIndex]); q.text = completeText(q.text, q.getAnswer().getAlters()); return (q); } /*************************************************************************** * Is this question last alter prompt * * @return b true iff current question is last alter prompt */ public boolean isLastAlterPrompt() { boolean b = false; logger.info("Current question type: " + getQuestion(_qIndex).getClass().getSimpleName()); logger.info("hasNext: " + hasNext()); if(_qIndex+1 >= _answers.length) // if there are no more questions, this is *definitely* the last alter prompt :) return true; logger.info("Next question type: " + getQuestion(_qIndex + 1).getClass().getSimpleName()); b = (getQuestion(_qIndex) instanceof AlterPromptQuestion) // current question is alter prompt && hasNext() // there IS a next question && (!(getQuestion(_qIndex + 1) instanceof AlterPromptQuestion)); // next question is alter prompt logger.info("-> isLastAlterPrompt = " + b); return (b); } /**** */ public String[] getAlterStrings(Question q) { String[] s = new String[2]; try { if ((q.getAnswer().hasAtLeastOneAlter()) && (q.getAnswer().firstAlter() != -1)) { s[0] = _alterList[q.getAnswer().firstAlter()]; } if ((q.getAnswer().hasTwoAlters()) && (q.getAnswer().secondAlter() != -1)) { s[1] = _alterList[q.getAnswer().secondAlter()]; } } catch (Exception ex) { s[0] = ""; s[1] = ""; } return s; } /** * Get a question from the study using the answer's question ID * @param index of an answer * @return */ public Question getQuestion(int index) { int length = _answers.length; if(index > -1 && index < length) { Answer result = _answers[index]; return _study.getQuestion(result.getQuestionId()); } throw new RuntimeException("Requested a question at index " + index + " but there are only " + length + " questions!"); } public List<Answer> getAnswersByUniqueId(long id) { List<Answer> list = new ArrayList<Answer>(); for(Answer answer : _answers) { if(answer.getQuestionId().equals(id)) list.add(answer); } return list; } private String completeText(String s, List<Integer> alters) { int [] aa = new int[alters.size()]; int i = 0; for(Integer alt : alters) aa[i++] = alt; return completeText(s, aa); } /*************************************************************************** * Replaces alter name placeholders with alter names * * @param s * Question string to parse * @param alters * names of alters to put in placeholders * @return modified string */ private String completeText(String s, int[] alters) { int parsePtr; String oldS = s; try { for (parsePtr = s.indexOf("$$1"); (parsePtr != -1); parsePtr = s .indexOf("$$1")) { s = s.substring(0, parsePtr) + _alterList[alters[0]] + s.substring(parsePtr + 3); } for (parsePtr = s.indexOf("$$2"); (parsePtr != -1); parsePtr = s .indexOf("$$2")) { s = s.substring(0, parsePtr) + _alterList[alters[1]] + s.substring(parsePtr + 3); } for (parsePtr = s.indexOf("$$-1"); (parsePtr != -1); parsePtr = s .indexOf("$$-1")) { s = s.substring(0, parsePtr) + _alterList[alters[0] - 1] + s.substring(parsePtr + 4); } for (parsePtr = s.indexOf("$$"); (parsePtr != -1); parsePtr = s .indexOf("$$")) { s = s.substring(0, parsePtr) + _alterList[alters[0]] + s.substring(parsePtr + 2); } } catch (Exception ex) { s = oldS; logger.error("Could not resolve dollar-dollars", ex.toString()); } return s; } /*************************************************************************** * Returns complete attribut for interview * * @returns complete */ public boolean isComplete() { return _complete; } public void setComplete(boolean complete) { _complete = complete; } /*************************************************************************** * Ego has answered all questions. Write file and generate stats * * @throws IOException * @throws IOException */ public void completeInterview(EgoStore storage) throws IOException { logger.info("Interview completion requested!"); /*********************************************************************** * Generate statistics for the first statable question */ Question q = _study.getFirstStatableQuestion(); _complete = true; storage.writeCurrentInterview(); if (q != null) { Statistics stats = storage.getInterview().generateStatistics(q); if(stats == null) logger.error("generateStatistics produced null output. This is likely to cause problems later."); else if(stats.alterList == null || stats.alterList.length==0) logger.error("generateStatistics produced an empty alter list. This is likely to cause problems later."); else logger.trace(Arrays.asList(stats.alterList).toString()); storage.writeStatisticsFiles(stats); } else { logger.info("Interview completion DID NOT generate statistics!"); } } /*************************************************************************** * Add interview information to an xml structure for output to a file * * @param e * XML Element, parent of interview tree */ public EgoAnswer[] getEgoAnswerArray(StatRecord record) { Iterator egoAnswerIter = getEgoAnswers().iterator(); EgoAnswer[] egoAnswers = new EgoAnswer[getEgoAnswers().size()]; int index = 0; while (egoAnswerIter.hasNext()) { Answer answer = (Answer) egoAnswerIter.next(); if (answer.isAnswered()) { egoAnswers[index++] = new EgoAnswer(_study .getQuestions().getQuestion(answer.getQuestionId()).title, answer.string, answer.getValue()); } else { egoAnswers[index++] = new EgoAnswer(_study .getQuestions().getQuestion(answer.getQuestionId()).title, "N/A", Answer.NO_ANSWER); } } return egoAnswers; } /*************************************************************************** * How many alters were used to generate this interview * * @return _numAlters */ public int getNumberAlters() { return _alterList.length; } /*************************************************************************** * What study is used for this interview * * @return _numAlters */ public Study getStudy() { return (_study); } /** * Returns statistics * * @return the statistics */ public Statistics getStats() { return _stats; } /** * @param q * The stats to set. */ public Statistics generateStatistics(Question q) { _stats = Statistics.generateInterviewStatistics(this, q); return _stats; } /*************************************************************************** * Goes through Set of answers creating a matrix representing the adjacency * graph for a set of alters. For each pair of alters if answer value > 0 an * edge is placed between the alters * * @param answers * Set of alter pair answers * @param numAlters * Total alters in interview * @param weighted * whether to use actual answer values for matrix or 1:0 * @return Matrix representing non-directed graph of alters * @throws MissingPairException */ public int[][] generateAdjacencyMatrix(Question q, boolean weighted) throws MissingPairException { //logger.info("Adjacency matrix ("+(weighted ? "" : "non-")+"weighted) : "); int _numAlters = _alterList.length; if (_study.getUIType().equals(Shared.TRADITIONAL_QUESTIONS)) { int[][] m = new int[_numAlters][_numAlters]; /* * init to make sure we get all pairs, in the case of linked * questions not all will be answered */ for (int i = 0; i < _numAlters; i++) { for (int j = 0; j < _numAlters; j++) { m[i][j] = 0; } m[i][i] = 1; } for (Iterator<Answer> it = getAnswerSubset(q.UniqueId).iterator(); it.hasNext();) { Answer a = it.next(); // in a weighted one, print all values int weightedValue = a.getValue(); // in unweighted, just print 0 or 1, ignore answer value int nonweightedValue = a.adjacent ? 1 : 0; int value = weighted ? weightedValue : nonweightedValue; //logger.info("Working on answer (adj="+a.adjacent+",v="+value+",nw="+nonweightedValue+",w="+weightedValue+") for adj: " + a.getString()); m[a.firstAlter()][a.secondAlter()] = value; m[a.secondAlter()][a.firstAlter()] = value; } _matrix = m; } for(int x = 0; x < _matrix.length; x++) { for(int y = 0; y < _matrix[x].length; y++) { //System.out.print(_matrix[x][y] + "\t"); } //logger.info(); } return (_matrix); } public void rewind() { while (hasPrevious()) previous(); } public Answer[] get_answers() { return _answers; } public Answer get_answerElement(int index){ return _answers[index]; } public void set_answerElement(int index, Answer value){ _answers[index] = value; } public void set_numAnswers(int _numAnswers) { this._numAnswers = _numAnswers; } public int get_numAnswers() { return _numAnswers; } public int compareTo(Interview o) { return sIntName.hashCode() - o.sIntName.hashCode(); } public void setNotes(String notes) { this.notes = notes; } public String getNotes() { return notes; } public String toString() { return sIntName; } /*Returns a matrix with all the alters of every alter question prompt.*/ public String[][] getAlterQuestionPromptAnswers(){ return _alterQuestionPromptList; } public void setAlterQuestionPromptAnswers(String[] s, int questionIndex){ _alterQuestionPromptList[questionIndex] = s; } public void setAlterQuestionPromptAnswers(String[][] list) { _alterQuestionPromptList = list; } /* Returns the number of the current alter question prompt. If the current * question is not an alter question prompt, returns -1. */ public int getCurrentAlterQuestionPrompt(){ int egoQuestions = _study.getQuestionOrder(EgoQuestion.class).size(); int currentQuestion = getQuestionIndex()-egoQuestions; if (currentQuestion < 0) { return -1; } else { return currentQuestion; } } public String dump() { StringBuilder sb = new StringBuilder(); /* private Answer[] ; private String[] = new String[0]; // so alter pair is at least stateable */ sb.append("Name: " + sIntName + "\n"); sb.append("_statisticsAvailable: " + _statisticsAvailable + "\n"); sb.append("_complete: " + _complete + "\n"); sb.append("_qIndex: " + _qIndex + "\n"); sb.append("_numAlterPairs: " + _numAlterPairs + "\n"); sb.append("followup: " + followup + "\n"); sb.append("_numAnswers: " + _numAnswers + "\n"); sb.append("notes: " + notes + "\n"); sb.append("_study: " + _study + "\n"); sb.append("_alterList: " + Arrays.toString(_alterList) + "\n"); for(int i = 0; i < _answers.length; i++) { Answer a = _answers[i]; sb.append("Answer "+i+", instance "+a.hashCode()+": " + a.getString() + "\n"); } return sb.toString(); } /* * Returns an array containing the union of the alters of all alter question prompt. * Alters will appear just once in this array. */ public String[] getUnifiedAlterList() { ArrayList <String> tempList = new ArrayList <String>(); for (int i = 0; i < _alterQuestionPromptList.length; i++) { for ( int j = 0; j < _alterQuestionPromptList[i].length; j++) { if(!tempList.contains(_alterQuestionPromptList[i][j])) { tempList.add(_alterQuestionPromptList[i][j]); } } } String[] unifiedList = new String[tempList.size()]; unifiedList = tempList.toArray(unifiedList); return unifiedList; } /* * Passed an array of strings, returns an array containing the union of * the alters in the given array, and the global alter list. */ public String[] getUnifiedAlterList(String[] s) { ArrayList <String> tempList = new ArrayList <String>(); List <String> knownAlters = Arrays.asList(_alterList); tempList.addAll(knownAlters); for (int i = 0; i < s.length; i++) { if(!knownAlters.contains(s[i])) { tempList.add(s[i]); } } String[] unknownAlterList = new String[tempList.size()]; unknownAlterList = tempList.toArray(unknownAlterList); return unknownAlterList; } //Remove an alter or a collection of alters from the globl alter list. public void removeAlters(ArrayList <String> list) { ArrayList <String> alterList = new ArrayList<String>(Arrays.asList(_alterList)); alterList.removeAll(list); String[] newAlterList = new String[alterList.size()]; newAlterList = alterList.toArray(newAlterList); setAlterList(newAlterList); } //Returns a hashmap containing for every alter, how many times appears. public HashMap <String, Integer> getAlterHashmap() { HashMap<String, Integer> alterCounter = new HashMap <String, Integer>(); ArrayList <String> alterList = new ArrayList<String>(Arrays.asList(_alterList)); for(int i = 0; i < _alterQuestionPromptList.length; i++) { for(int j = 0; j <_alterQuestionPromptList[i].length; j++) { if(alterList.contains(_alterQuestionPromptList[i][j])) { int counter; if(alterCounter.get(_alterQuestionPromptList[i][j]) == null) { counter = 1; }else { counter = alterCounter.get(_alterQuestionPromptList[i][j])+1; } alterCounter.put(_alterQuestionPromptList[i][j], counter); } } } return alterCounter; } //Generates a matrix containing the relation/appearence between every alter prompt question //and all the alters. public int[][] generateAlterByAlterPromptMatrix() { int numPrompts = _study.getQuestionOrder(AlterPromptQuestion.class).size(); int matrix [][] = new int[_alterList.length][numPrompts]; for (int i = 0; i < _alterList.length; i++) { for (int j = 0; j < numPrompts; j++){ if (Arrays.asList(_alterQuestionPromptList[j]).contains(_alterList[i]) ){ matrix[i][j] = 1; } else { matrix[i][j] = 0; } } } return matrix; } }