text:
package org.unitime.timetable.onlinesectioning.status.db;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.cpsolver.coursett.Constants;
import org.cpsolver.coursett.model.Placement;
import org.cpsolver.coursett.model.TimeLocation;
import org.cpsolver.ifs.util.DistanceMetric;
import org.cpsolver.studentsct.model.Student.BackToBackPreference;
import org.cpsolver.studentsct.model.Student.ModalityPreference;
import org.unitime.timetable.defaults.ApplicationProperty;
import org.unitime.timetable.gwt.shared.ClassAssignmentInterface;
import org.unitime.timetable.gwt.shared.ClassAssignmentInterface.AdvisedInfoInterface;
import org.unitime.timetable.gwt.shared.ClassAssignmentInterface.StudentInfo;
import org.unitime.timetable.gwt.shared.OnlineSectioningInterface.WaitListMode;
import org.unitime.timetable.model.Advisor;
import org.unitime.timetable.model.AdvisorCourseRequest;
import org.unitime.timetable.model.Assignment;
import org.unitime.timetable.model.CourseCreditUnitConfig;
import org.unitime.timetable.model.CourseDemand;
import org.unitime.timetable.model.CourseDemand.Critical;
import org.unitime.timetable.model.CourseOffering;
import org.unitime.timetable.model.CourseRequest;
import org.unitime.timetable.model.InstructionalMethod;
import org.unitime.timetable.model.Student;
import org.unitime.timetable.model.StudentAccomodation;
import org.unitime.timetable.model.StudentAreaClassificationMajor;
import org.unitime.timetable.model.StudentAreaClassificationMinor;
import org.unitime.timetable.model.StudentClassEnrollment;
import org.unitime.timetable.model.StudentClassPref;
import org.unitime.timetable.model.StudentGroup;
import org.unitime.timetable.model.StudentInstrMthPref;
import org.unitime.timetable.model.StudentNote;
import org.unitime.timetable.model.StudentSectioningPref;
import org.unitime.timetable.model.StudentSectioningStatus;
import org.unitime.timetable.model.StudentSectioningStatus.Option;
import org.unitime.timetable.onlinesectioning.AcademicSessionInfo;
import org.unitime.timetable.onlinesectioning.OnlineSectioningHelper;
import org.unitime.timetable.onlinesectioning.OnlineSectioningServer;
import org.unitime.timetable.onlinesectioning.status.FindStudentInfoAction;
import org.unitime.timetable.onlinesectioning.status.SectioningStatusFilterAction;
import org.unitime.timetable.onlinesectioning.status.StatusPageSuggestionsAction.CourseLookup;
import org.unitime.timetable.onlinesectioning.status.db.DbFindEnrollmentInfoAction.DbCourseRequestMatcher;
import org.unitime.timetable.onlinesectioning.status.db.DbFindEnrollmentInfoAction.DbFindStudentInfoMatcher;
import org.unitime.timetable.util.Formats;

/**
 * @author Tomas Muller
 */
public class DbFindStudentInfoAction extends FindStudentInfoAction {
	private static final long serialVersionUID = 1L;

	public boolean isMyStudent(Student student) {
		return iMyStudents != null && iMyStudents.contains(student.getUniqueId());
	}

	public boolean isCanSelect(Student student) {
		if (iIsAdmin) return true;
		if (iIsAdvisor) {
			if (iCanEditOtherStudents || (iCanEditMyStudents && isMyStudent(student))) return true;
		} else {
			if (iCanSelect) return true;
		}
		return false;
	}

	@Override
	public List<StudentInfo> execute(final OnlineSectioningServer server, final OnlineSectioningHelper helper) {
		if (iFilter == null) return super.execute(server, helper);

		Map<Long, StudentInfo> students = new HashMap<Long, StudentInfo>();

		int gEnrl = 0, gWait = 0, gRes = 0, gUnasg = 0, gNoSub = 0, gSwap = 0;
		int gtEnrl = 0, gtWait = 0, gtRes = 0, gtUnasg = 0, gtNoSub = 0, gtSwap = 0;
		int gConNeed = 0, gtConNeed = 0, gOvrNeed = 0, gtOvrNeed = 0;
		int gDist = 0, gtDist = 0, gNrDC = 0, gtNrDC = 0, gShr = 0, gtShr = 0;
		int gFre = 0, gtFre = 0, gPIM = 0, gtPIM = 0, gPSec = 0, gtPSec = 0;
		Set<Long> unassigned = new HashSet<Long>();
		Set<Long> assignedRequests = new HashSet<Long>();
		AcademicSessionInfo session = server.getAcademicSession();
		CourseLookup lookup = new CourseLookup(session);
		DistanceMetric dm = server.getDistanceMetric();

		boolean useAdvisorWaitLists = server.getConfig().getPropertyBoolean("Load.UseAdvisorWaitLists", false);
		boolean useAdvisorNoSubs = server.getConfig().getPropertyBoolean("Load.UseAdvisorNoSubs", false);

		DbFindStudentInfoMatcher sm = new DbFindStudentInfoMatcher(session, iQuery, helper.getStudentNameFormat(), iMyStudents); sm.setServer(server);

		Map<CourseOffering, List<CourseRequest>> requests = new HashMap<CourseOffering, List<CourseRequest>>();
		cr: for (CourseRequest cr: (List<CourseRequest>)SectioningStatusFilterAction.getCourseQuery(iFilter, server, helper).select("distinct cr").query(helper.getHibSession()).list()) {
			if (!hasMatchingSubjectArea(cr.getCourseOffering().getSubjectAreaAbbv())) continue;
			if (!isCourseVisible(cr.getCourseOffering().getUniqueId())) continue;
			if (!query().match(new DbCourseRequestMatcher(session, cr, isConsentToDoCourse(cr.getCourseOffering()), isMyStudent(cr.getCourseDemand().getStudent()), helper.getStudentNameFormat(), lookup))) continue;
			if (cr.getClassEnrollments().isEmpty()) { // skip course requests where course demand is enrolled to some other course
				for (CourseRequest x: cr.getCourseDemand().getCourseRequests())
					if (!x.equals(cr) && !x.getClassEnrollments().isEmpty()) continue cr;
			}
			List<CourseRequest> list = requests.get(cr.getCourseOffering());
			if (list == null) {
				list = new ArrayList<CourseRequest>();
				requests.put(cr.getCourseOffering(), list);
			}
			list.add(cr);
		}

		for (Map.Entry<CourseOffering, List<CourseRequest>> entry: requests.entrySet()) {
			CourseOffering course = entry.getKey();
			boolean isConsentToDoCourse = isConsentToDoCourse(course);

			for (CourseRequest request: entry.getValue()) {
				Student student = request.getCourseDemand().getStudent();
				StudentInfo s = students.get(student.getUniqueId());
				if (s == null) {
					s = new StudentInfo();
					students.put(student.getUniqueId(), s);
					ClassAssignmentInterface.Student st = new ClassAssignmentInterface.Student(); s.setStudent(st);
					st.setId(student.getUniqueId());
					st.setSessionId(session.getUniqueId());
					st.setExternalId(student.getExternalUniqueId());
					st.setCanShowExternalId(iCanShowExtIds);
					StudentSectioningStatus status = student.getEffectiveStatus();
					if (status == null || status.hasOption(Option.waitlist)) {
						st.setWaitListMode(WaitListMode.WaitList);
					} else if (status != null && status.hasOption(Option.nosubs)) {
						st.setWaitListMode(WaitListMode.NoSubs);
					} else {
						st.setWaitListMode(WaitListMode.None);
					}
					st.setCanRegister(iCanRegister && (status == null
							|| status.hasOption(StudentSectioningStatus.Option.regenabled)
							|| (iIsAdmin && status.hasOption(StudentSectioningStatus.Option.regadmin))
							|| (iIsAdvisor && status.hasOption(StudentSectioningStatus.Option.regadvisor))
							));
					st.setCanUseAssistant(iCanUseAssistant && (status == null
							|| status.hasOption(StudentSectioningStatus.Option.enabled)
							|| (iIsAdmin && status.hasOption(StudentSectioningStatus.Option.admin))
							|| (iIsAdvisor && status.hasOption(StudentSectioningStatus.Option.advisor))
							));
					st.setCanSelect(isCanSelect(student));
					st.setName(helper.getStudentNameFormat().format(student));
					for (StudentAreaClassificationMajor acm: new TreeSet<StudentAreaClassificationMajor>(student.getAreaClasfMajors())) {
						st.addArea(acm.getAcademicArea().getAcademicAreaAbbreviation(), acm.getAcademicArea().getTitle());
						st.addClassification(acm.getAcademicClassification().getCode(), acm.getAcademicClassification().getName());
						st.addMajor(acm.getMajor().getCode(), acm.getMajor().getName());
						st.addConcentration(acm.getConcentration() == null ? null : acm.getConcentration().getCode(), acm.getConcentration() == null ? null : acm.getConcentration().getName());
						st.addDegree(acm.getDegree() == null ? null : acm.getDegree().getReference(), acm.getDegree() == null ? null : acm.getDegree().getLabel());
						st.addProgram(acm.getProgram() == null ? null : acm.getProgram().getReference(), acm.getProgram() == null ? null : acm.getProgram().getLabel());
						st.addCampus(acm.getCampus() == null ? null : acm.getCampus().getReference(), acm.getCampus() == null ? null : acm.getCampus().getLabel());
					}
					st.setDefaultCampus(server.getAcademicSession().getCampus());
					for (StudentAreaClassificationMinor acm: new TreeSet<StudentAreaClassificationMinor>(student.getAreaClasfMinors())) {
						st.addMinor(acm.getMinor().getCode(), acm.getMinor().getName());
					}
					for (StudentAccomodation acc: student.getAccomodations()) {
						st.addAccommodation(acc.getAbbreviation(), acc.getName());
					}
					for (StudentGroup gr: student.getGroups()) {
						if (gr.getType() == null)
							st.addGroup(gr.getGroupAbbreviation(), gr.getGroupName());
						else
							st.addGroup(gr.getType().getReference(), gr.getGroupAbbreviation(), gr.getGroupName());
					}
	    			for (Advisor a: student.getAdvisors()) {
	    				if (a.getLastName() != null)
	    					st.addAdvisor(helper.getInstructorNameFormat().format(a));
	    			}

					int tEnrl = 0, tWait = 0, tRes = 0, tConNeed = 0, tReq = 0, tUnasg = 0, tOvrNeed = 0, ovrNeed = 0, tNoSub = 0, tSwap = 0;
					float tCred = 0f;
					int nrDisCnf = 0, maxDist = 0, share = 0;
					int ftShare = 0;
					List<Float> minsTot = new ArrayList<Float>();
					List<Float> maxsTot = new ArrayList<Float>();
					List<Float> mins = new ArrayList<Float>();
					List<Float> maxs = new ArrayList<Float>();
					int nrCoursesTot = 0, nrCourses = 0;
					float studentMin = 0f, studentMax = 0f;
					float studentMinTot = 0f, studentMaxTot = 0f;
					Set<Long> advisorWaitListedCourseIds = student.getAdvisorWaitListedCourseIds(useAdvisorWaitLists, useAdvisorNoSubs);
					for (CourseDemand demand: student.getCourseDemands()) {
						if (!demand.getCourseRequests().isEmpty()) {
							Float minTot = null, maxTot = null;
							Float min = null, max = null;
							for (CourseRequest r: demand.getCourseRequests()) {
								CourseCreditUnitConfig c = r.getCourseOffering().getCredit();
								if (c != null) {
									if (minTot == null || minTot > c.getMinCredit()) minTot = c.getMinCredit();
									if (maxTot == null || maxTot < c.getMaxCredit()) maxTot = c.getMaxCredit();
								}
								if (r.isRequestPending()) { tOvrNeed ++; gtOvrNeed ++ ; }
								if (query().match(new DbCourseRequestMatcher(session, r, isConsentToDoCourse(r.getCourseOffering()), isMyStudent(student), helper.getStudentNameFormat(), lookup))) {
									if (c != null) {
										if (min == null || min > c.getMinCredit()) min = c.getMinCredit();
										if (max == null || max < c.getMaxCredit()) max = c.getMaxCredit();
									}
									if (r.isRequestPending()) { ovrNeed ++; gOvrNeed ++ ; }
								}
							}
							boolean isWaitList = false;
							if (!demand.isAlternative()) {
								if (demand.isWaitListOrNoSub(st.getWaitListMode())) {
									isWaitList = true;
								} else if (advisorWaitListedCourseIds != null && !advisorWaitListedCourseIds.isEmpty()) {
									for (CourseRequest r: demand.getCourseRequests())
										if (advisorWaitListedCourseIds.contains(r.getCourseOffering().getUniqueId())) {
											isWaitList = true; break;
										}
								}
							}
							if (isWaitList) {
								if (minTot != null) {
									studentMinTot += minTot; studentMaxTot += maxTot;
								}
								if (min != null) {
									studentMin += min; studentMax += max;
								}
							} else {
								if (minTot != null) {
									minsTot.add(minTot); maxsTot.add(maxTot);
									if (!demand.isAlternative()) nrCoursesTot ++;
								}
								if (min != null) {
									mins.add(min); maxs.add(max);
									if (!demand.isAlternative()) nrCourses ++;
								}
							}

							if (!demand.isAlternative()) tReq ++;
							List<StudentClassEnrollment> enrollment = null;
							CourseRequest assigned = null;
							for (CourseRequest r: demand.getCourseRequests()) {
								enrollment = r.getClassEnrollments();
								if (!enrollment.isEmpty()) {
									assigned = r; break;
								}
							}
							if (enrollment.isEmpty()) {
								CourseRequest first = null;
								for (CourseRequest r: demand.getCourseRequests()) {
									if (first == null || r.getOrder() < first.getOrder()) first = r;
								}
								DbCourseRequestMatcher crm = new DbCourseRequestMatcher(session, first, isConsentToDoCourse(first.getCourseOffering()), isMyStudent(student), helper.getStudentNameFormat(), lookup);
								if (crm.canAssign()) {
									tUnasg ++; gtUnasg ++;
									if (demand.effectiveWaitList()) {
										tWait ++; gtWait ++;
									}
									if (demand.effectiveNoSub()) {
										tNoSub ++; gtNoSub ++;
									}
								}
							} else {
								tEnrl ++; gtEnrl ++;
								DbCourseRequestMatcher crm = new DbCourseRequestMatcher(session, assigned, isConsentToDoCourse(assigned.getCourseOffering()), isMyStudent(student), helper.getStudentNameFormat(), lookup);
								if (crm.reservation() != null) {
									tRes ++; gtRes ++;
								}
								if (assigned.getCourseOffering().getConsentType() != null && crm.approval() == null) {
									tConNeed ++; gtConNeed ++;
								}
								if (crm.request().getCourseDemand().effectiveWaitList() && !crm.request().getCourseDemand().isEnrolledExceptForWaitListSwap()) {
									tSwap ++; gtSwap ++;
								}
								if (assigned.getCourseOffering().getCredit() != null) {
									tCred += assigned.getCourseOffering().getCredit().getMinCredit();
									for (StudentClassEnrollment e: enrollment) {
										InstructionalMethod im = e.getClazz().getSchedulingSubpart().getInstrOfferingConfig().getInstructionalMethod();
										if (im != null)
											s.addIMTotalCredit(im.getReference(), assigned.getCourseOffering().getCredit().getMinCredit());
										break;
									}
								} else {
									for (StudentClassEnrollment e: enrollment) {
										if (e.getClazz().getSchedulingSubpart().getCredit() != null) {
											tCred += e.getClazz().getSchedulingSubpart().getCredit().getMinCredit();
											InstructionalMethod im = e.getClazz().getSchedulingSubpart().getInstrOfferingConfig().getInstructionalMethod();
											if (im != null)
												s.addIMTotalCredit(im.getReference(), e.getClazz().getSchedulingSubpart().getCredit().getMinCredit());
										}
									}
								}

								for (StudentClassEnrollment section: enrollment) {
									Assignment assignment = section.getClazz().getCommittedAssignment();
									if (assignment == null) continue;
									for (StudentClassEnrollment otherSection: student.getClassEnrollments()) {
										if (section.equals(otherSection)) continue;
										Assignment otherAssignment = otherSection.getClazz().getCommittedAssignment();
										if (otherAssignment == null) continue;
										if (isDistanceConflict(student, assignment, otherAssignment, dm)) {
											nrDisCnf ++; gtNrDC ++;
											int d = getDistanceInMinutes(assignment, otherAssignment, dm);
											if (d > maxDist) maxDist = d;
											if (d > gtDist) gtDist = d;
										}
										if (assignment.getTimeLocation().hasIntersection(otherAssignment.getTimeLocation()) && !section.getClazz().isToIgnoreStudentConflictsWith(otherSection.getClazz()) && section.getClazz().getUniqueId() < otherSection.getClazz().getUniqueId()) {
											int sh = assignment.getTimeLocation().nrSharedDays(otherAssignment.getTimeLocation()) * assignment.getTimeLocation().nrSharedHours(otherAssignment.getTimeLocation()) * Constants.SLOT_LENGTH_MIN;
											share += sh;
											gtShr += sh;
										}
									}
									for (CourseDemand cd: student.getCourseDemands()) {
										if (cd.getFreeTime() != null) {
											TimeLocation ft = new TimeLocation(cd.getFreeTime().getDayCode(), cd.getFreeTime().getStartSlot(), cd.getFreeTime().getLength(),
													0, 0.0, 0, null, null, session.getFreeTimePattern(), 0);
											if (ft.hasIntersection(assignment.getTimeLocation())) {
												int sh = assignment.getTimeLocation().nrSharedDays(ft) * assignment.getTimeLocation().nrSharedHours(ft) * Constants.SLOT_LENGTH_MIN;
												ftShare += sh;
												gtFre += sh;
											}
										}
									}
								}
							}
						}
					}

					Collections.sort(mins);
					Collections.sort(maxs);
					for (int i = 0; i < nrCourses; i++) {
						studentMin += mins.get(i);
						studentMax += maxs.get(maxs.size() - i - 1);
					}
					Collections.sort(minsTot);
					Collections.sort(maxsTot);
					for (int i = 0; i < nrCoursesTot; i++) {
						studentMinTot += minsTot.get(i);
						studentMaxTot += maxsTot.get(maxsTot.size() - i - 1);
					}
					if (student.isRequestPending()) {
						if (nrCourses == nrCoursesTot) { gOvrNeed ++; ovrNeed ++; }
						gtOvrNeed ++; tOvrNeed ++;
					}
					s.setRequestCredit(studentMin, studentMax);
					s.setTotalRequestCredit(studentMinTot, studentMaxTot);
					s.setTotalEnrollment(tEnrl);
					s.setTotalReservation(tRes);
					s.setTotalWaitlist(tWait);
					s.setTotalNoSub(tNoSub);
					s.setTotalSwap(tSwap);
					s.setTotalUnassigned(tUnasg);
					s.setTotalConsentNeeded(tConNeed);
					s.setTotalOverrideNeeded(tOvrNeed);
					s.setEnrollment(0);
					s.setReservation(0);
					s.setWaitlist(0);
					s.setNoSub(0);
					s.setSwap(0);
					s.setUnassigned(0);
					s.setConsentNeeded(0);
					s.setOverrideNeeded(ovrNeed);
					s.setRequested(tReq);
					s.setStatus(student.getSectioningStatus() == null ? session.getDefaultSectioningStatus() : student.getSectioningStatus().getReference());
					s.setEmailDate(student.getScheduleEmailedDate() == null ? null : student.getScheduleEmailedDate());
					s.setCredit(0f);
					s.setTotalCredit(tCred);
					s.setNrDistanceConflicts(0);
					s.setLongestDistanceMinutes(0);
					s.setOverlappingMinutes(0);
					s.setTotalNrDistanceConflicts(nrDisCnf);
					s.setTotalLongestDistanceMinutes(maxDist);
					s.setTotalOverlappingMinutes(share);
					s.setFreeTimeOverlappingMins(0);
					s.setTotalFreeTimeOverlappingMins(ftShare);
					s.setPrefInstrMethConflict(0);
					s.setTotalPrefInstrMethConflict(0);
					s.setPrefSectionConflict(0);
					s.setTotalPrefSectionConflict(0);

					StudentNote note = null;
					for (StudentNote n: student.getNotes())
						if (note == null || note.compareTo(n) < 0) note = n;
					if (note != null) s.setNote(note.getTextNote());
					s.setMyStudent(isMyStudent(student));
					s.setAdvisedInfo(getAdvisedInfo(student, server, helper));
					s.setPreference(getStudentSchedulingPreference(student, server, helper));
				}
				DbCourseRequestMatcher crm = new DbCourseRequestMatcher(session, request, isConsentToDoCourse, isMyStudent(student), helper.getStudentNameFormat(), lookup);
				if (!crm.enrollment().isEmpty()) {
					if (assignedRequests.add(crm.request().getCourseDemand().getUniqueId())) {
						s.setEnrollment(s.getEnrollment() + 1); gEnrl ++;
						if (crm.reservation() != null) { s.setReservation(s.getReservation() + 1); gRes ++; }
						if (course.getConsentType() != null && crm.approval() == null) {
							s.setConsentNeeded(s.getConsentNeeded() + 1); gConNeed ++;
						}
						if (crm.request().getCourseDemand().effectiveWaitList() && !crm.request().getCourseDemand().isEnrolledExceptForWaitListSwap()) {
							s.setSwap(s.getSwap() + 1); gSwap ++;
						}
						for (StudentClassEnrollment e: crm.enrollment()) {
							if (e.getTimestamp() != null) {
								if (s.getEnrolledDate() == null)
									s.setEnrolledDate(e.getTimestamp());
								else if (e.getTimestamp().after(s.getEnrolledDate()))
									s.setEnrolledDate(e.getTimestamp());
							}
						}
						if (crm.approval() != null) {
							for (StudentClassEnrollment e: crm.enrollment()) {
								if (e.getApprovedDate() != null) {
									if (s.getApprovedDate() == null)
										s.setApprovedDate(e.getApprovedDate());
									else if (e.getApprovedDate().after(s.getApprovedDate()))
										s.setApprovedDate(e.getApprovedDate());
								}
							}
						}
						if (course.getCredit() != null) {
							s.setCredit(s.getCredit() + course.getCredit().getMinCredit());
							for (StudentClassEnrollment e: crm.enrollment()) {
								InstructionalMethod im = e.getClazz().getSchedulingSubpart().getInstrOfferingConfig().getInstructionalMethod();
								if (im != null)
									s.addIMCredit(im.getReference(), course.getCredit().getMinCredit());
								break;
							}
						} else {
							for (StudentClassEnrollment e: crm.enrollment()) {
								if (e.getClazz().getSchedulingSubpart().getCredit() != null) {
									s.setCredit(s.getCredit() + e.getClazz().getSchedulingSubpart().getCredit().getMinCredit());
									InstructionalMethod im = e.getClazz().getSchedulingSubpart().getInstrOfferingConfig().getInstructionalMethod();
									if (im != null)
										s.addIMCredit(im.getReference(), e.getClazz().getSchedulingSubpart().getCredit().getMinCredit());
								}
							}
						}
						boolean hasIm = false;
						boolean im = false;
						Set<String> allSubpart = new HashSet<String>();
						Set<String> selectedSubparts = new HashSet<String>();
						for (StudentSectioningPref pref: request.getPreferences()) {
							if (pref instanceof StudentInstrMthPref) {
								StudentInstrMthPref imp = (StudentInstrMthPref)pref;
								InstructionalMethod method = crm.enrollment().get(0).getClazz().getSchedulingSubpart().getInstrOfferingConfig().getEffectiveInstructionalMethod();
								hasIm = true;
								if (!im && method != null && method.equals(imp.getInstructionalMethod())) { im = true; }
							}
							if (pref instanceof StudentClassPref) {
								StudentClassPref scp = (StudentClassPref)pref;
								allSubpart.add(scp.getClazz().getSchedulingSubpart().getItypeDesc());
								for (StudentClassEnrollment section: crm.enrollment()) {
									if (scp.getClazz().equals(section.getClazz()))
										selectedSubparts.add(scp.getClazz().getSchedulingSubpart().getItypeDesc());
								}
							}
						}
						if (hasIm) {
							s.setTotalPrefInstrMethConflict(s.getTotalPrefInstrMethConflict() + 1);
							gtPIM++;
							if (im) {
								s.setPrefInstrMethConflict(s.getPrefInstrMethConflict() + 1);
								gPIM++;
							}
						}
						if (!allSubpart.isEmpty()) {
							s.setTotalPrefSectionConflict(s.getTotalPrefSectionConflict() + allSubpart.size());
							gtPSec += allSubpart.size();
							s.setPrefSectionConflict(s.getPrefSectionConflict() + selectedSubparts.size());
							gPSec += selectedSubparts.size();
						}
					}
					for (StudentClassEnrollment section: crm.enrollment()) {
						Assignment assignment = section.getClazz().getCommittedAssignment();
						if (assignment == null) continue;
						for (StudentClassEnrollment otherSection: student.getClassEnrollments()) {
							if (section.equals(otherSection)) continue;
							Assignment otherAssignment = otherSection.getClazz().getCommittedAssignment();
							if (otherAssignment == null) continue;
							if (isDistanceConflict(student, assignment, otherAssignment, dm)) {
								s.setNrDistanceConflicts(s.getNrDistanceConflicts() + 1); gNrDC ++;
								int d = getDistanceInMinutes(assignment, otherAssignment, dm);
								if (d > s.getLongestDistanceMinutes()) s.setLongestDistanceMinutes(d);
								if (d > gDist) gDist = d;
							}
							if (assignment.getTimeLocation().hasIntersection(otherAssignment.getTimeLocation()) && !section.getClazz().isToIgnoreStudentConflictsWith(otherSection.getClazz())) {
								if (section.getClazz().getUniqueId() < otherSection.getClazz().getUniqueId() ||
									!query().match(new DbCourseRequestMatcher(session, otherSection.getCourseRequest(), isConsentToDoCourse(otherSection.getCourseOffering()), isMyStudent(student), helper.getStudentNameFormat(), lookup))) {
									int sh = assignment.getTimeLocation().nrSharedDays(otherAssignment.getTimeLocation()) * assignment.getTimeLocation().nrSharedHours(otherAssignment.getTimeLocation()) * Constants.SLOT_LENGTH_MIN;
									s.setOverlappingMinutes(s.getOverlappingMinutes() + sh);
									gShr += sh;
								}
							}
						}
						for (CourseDemand cd: student.getCourseDemands()) {
							if (cd.getFreeTime() != null) {
								TimeLocation ft = new TimeLocation(cd.getFreeTime().getDayCode(), cd.getFreeTime().getStartSlot(), cd.getFreeTime().getLength(),
										0, 0.0, 0, null, null, session.getFreeTimePattern(), 0);
								if (ft.hasIntersection(assignment.getTimeLocation())) {
									int sh = assignment.getTimeLocation().nrSharedDays(ft) * assignment.getTimeLocation().nrSharedHours(ft) * Constants.SLOT_LENGTH_MIN;
									s.setFreeTimeOverlappingMins(s.getFreeTimeOverlappingMins() + sh);
									gFre += sh;
								}
							}
						}
					}
				} else if (unassigned.add(crm.request().getCourseDemand().getUniqueId())) {
					if (crm.canAssign()) {
						if (crm.request().getCourseDemand().effectiveWaitList()) {
							s.setWaitlist(s.getWaitlist() + 1); gWait ++;
							if (s.getTopWaitingPriority() == null)
								s.setTopWaitingPriority(1 + crm.request().getCourseDemand().getPriority());
							else
								s.setTopWaitingPriority(Math.min(1 + crm.request().getCourseDemand().getPriority(), s.getTopWaitingPriority()));
						}
						if (crm.request().getCourseDemand().effectiveNoSub()) {
							s.setNoSub(s.getNoSub() + 1); gNoSub ++;
						}
						s.setUnassigned(s.getUnassigned() + 1); gUnasg ++;
					}
					for (CourseRequest c: crm.request().getCourseDemand().getCourseRequests()) {
						boolean reqIm = false;
						Set<String> reqSubparts = new HashSet<String>();
						for (StudentSectioningPref pref: c.getPreferences()) {
							if (!pref.isRequired()) continue;
							if (pref instanceof StudentInstrMthPref) {
								reqIm = true;
							}
							if (pref instanceof StudentClassPref) {
								StudentClassPref scp = (StudentClassPref)pref;
								reqSubparts.add(scp.getClazz().getSchedulingSubpart().getItypeDesc());
							}
						}
						if (reqIm) {
							s.setTotalPrefInstrMethConflict(s.getTotalPrefInstrMethConflict() + 1);
							gtPIM++;
						}
						if (!reqSubparts.isEmpty()) {
							s.setTotalPrefSectionConflict(s.getTotalPrefSectionConflict() + reqSubparts.size());
							gtPSec += reqSubparts.size();
						}
					}
				}
				if (crm.request().getCourseDemand().getTimestamp() != null) {
					if (s.getRequestedDate() == null)
						s.setRequestedDate(crm.request().getCourseDemand().getTimestamp());
					else if (crm.request().getCourseDemand().getTimestamp().after(s.getRequestedDate()))
						s.setRequestedDate(crm.request().getCourseDemand().getTimestamp());
				}
				if (crm.request().getCourseDemand().getWaitlistedTimeStamp() != null) {
					if (s.getWaitListedDate() == null)
						s.setWaitListedDate(crm.request().getCourseDemand().getWaitlistedTimeStamp());
					else if (crm.request().getCourseDemand().getWaitlistedTimeStamp().after(s.getWaitListedDate()))
						s.setWaitListedDate(crm.request().getCourseDemand().getWaitlistedTimeStamp());
				}
			}
		}

		List<StudentInfo> ret = new ArrayList<StudentInfo>(students.values());

		if (iSubjectAreas == null && iCoursesIcoordinate == null) {
			for (Student student: (List<Student>)SectioningStatusFilterAction.getQuery(iFilter, server, helper).select("distinct s").query(helper.getHibSession()).list()) {
				if (students.containsKey(student.getUniqueId())) continue;
				if (!sm.match(student)) continue;
				StudentInfo s = new StudentInfo();
				ClassAssignmentInterface.Student st = new ClassAssignmentInterface.Student(); s.setStudent(st);
				st.setId(student.getUniqueId());
				st.setSessionId(session.getUniqueId());
				st.setExternalId(student.getExternalUniqueId());
				st.setCanShowExternalId(iCanShowExtIds);
				StudentSectioningStatus status = student.getEffectiveStatus();
				if (status == null || status.hasOption(Option.waitlist)) {
					st.setWaitListMode(WaitListMode.WaitList);
				} else if (status != null && status.hasOption(Option.nosubs)) {
					st.setWaitListMode(WaitListMode.NoSubs);
				} else {
					st.setWaitListMode(WaitListMode.None);
				}
				st.setCanRegister(iCanRegister && (status == null
						|| status.hasOption(StudentSectioningStatus.Option.regenabled)
						|| (iIsAdmin && status.hasOption(StudentSectioningStatus.Option.regadmin))
						|| (iIsAdvisor && status.hasOption(StudentSectioningStatus.Option.regadvisor))
						));
				st.setCanUseAssistant(iCanUseAssistant && (status == null
						|| status.hasOption(StudentSectioningStatus.Option.enabled)
						|| (iIsAdmin && status.hasOption(StudentSectioningStatus.Option.admin))
						|| (iIsAdvisor && status.hasOption(StudentSectioningStatus.Option.advisor))
						));
				st.setCanSelect(isCanSelect(student));
				st.setName(helper.getStudentNameFormat().format(student));
				for (StudentAreaClassificationMajor acm: new TreeSet<StudentAreaClassificationMajor>(student.getAreaClasfMajors())) {
					st.addArea(acm.getAcademicArea().getAcademicAreaAbbreviation(), acm.getAcademicArea().getTitle());
					st.addClassification(acm.getAcademicClassification().getCode(), acm.getAcademicClassification().getName());
					st.addMajor(acm.getMajor().getCode(), acm.getMajor().getName());
					st.addConcentration(acm.getConcentration() == null ? null : acm.getConcentration().getCode(), acm.getConcentration() == null ? null : acm.getConcentration().getName());
					st.addDegree(acm.getDegree() == null ? null : acm.getDegree().getReference(), acm.getDegree() == null ? null : acm.getDegree().getLabel());
					st.addProgram(acm.getProgram() == null ? null : acm.getProgram().getReference(), acm.getProgram() == null ? null : acm.getProgram().getLabel());
					st.addCampus(acm.getCampus() == null ? null : acm.getCampus().getReference(), acm.getCampus() == null ? null : acm.getCampus().getLabel());
				}
				st.setDefaultCampus(server.getAcademicSession().getCampus());
				for (StudentAreaClassificationMinor acm: new TreeSet<StudentAreaClassificationMinor>(student.getAreaClasfMinors())) {
					st.addMinor(acm.getMinor().getCode(), acm.getMinor().getName());
				}
				for (StudentAccomodation acc: student.getAccomodations()) {
					st.addAccommodation(acc.getAbbreviation(), acc.getName());
				}
				for (StudentGroup gr: student.getGroups()) {
					if (gr.getType() == null)
						st.addGroup(gr.getGroupAbbreviation(), gr.getGroupName());
					else
						st.addGroup(gr.getType().getReference(), gr.getGroupAbbreviation(), gr.getGroupName());
				}
    			for (Advisor a: student.getAdvisors()) {
    				if (a.getLastName() != null)
    					st.addAdvisor(helper.getInstructorNameFormat().format(a));
    			}
				s.setStatus(student.getSectioningStatus() == null ? session.getDefaultSectioningStatus() : student.getSectioningStatus().getReference());
				s.setEmailDate(student.getScheduleEmailedDate() == null ? null : student.getScheduleEmailedDate());

				StudentNote note = null;
				for (StudentNote n: student.getNotes())
					if (note == null || note.compareTo(n) < 0) note = n;
				if (note != null) s.setNote(note.getTextNote());
				s.setMyStudent(isMyStudent(student));
				s.setAdvisedInfo(getAdvisedInfo(student, server, helper));
				s.setPreference(getStudentSchedulingPreference(student, server, helper));

				ret.add(s);
			}
		}

		Collections.sort(ret, new Comparator<StudentInfo>() {
			@Override
			public int compare(StudentInfo s1, StudentInfo s2) {
				int cmp = s1.getStudent().getName().compareTo(s2.getStudent().getName());
				if (cmp != 0) return cmp;
				return Long.valueOf(s1.getStudent().getId()).compareTo(s2.getStudent().getId());
			}
		});

		if (limit() != null && ret.size() >= limit()) {
			List<StudentInfo>  r = new ArrayList<StudentInfo>(limit());
			for (StudentInfo i: ret) {
				r.add(i);
				if (r.size() == limit()) break;
			}
			ret = r;
		}

		// if (students.size() > 0) {
		StudentInfo t = new StudentInfo();

		t.setEnrollment(gEnrl);
		t.setReservation(gRes);
		t.setWaitlist(gWait);
		t.setNoSub(gNoSub);
		t.setSwap(gSwap);
		t.setUnassigned(gUnasg);

		t.setTotalEnrollment(gtEnrl);
		t.setTotalReservation(gtRes);
		t.setTotalWaitlist(gtWait);
		t.setTotalNoSub(gtNoSub);
		t.setTotalSwap(gtSwap);
		t.setTotalUnassigned(gtUnasg);

		t.setConsentNeeded(gConNeed);
		t.setTotalConsentNeeded(gtConNeed);
		t.setOverrideNeeded(gOvrNeed);
		t.setTotalOverrideNeeded(gtOvrNeed);

		t.setNrDistanceConflicts(gNrDC);
		t.setTotalNrDistanceConflicts(gtNrDC);
		t.setLongestDistanceMinutes(gDist);
		t.setTotalLongestDistanceMinutes(gtDist);
		t.setOverlappingMinutes(gShr);
		t.setTotalOverlappingMinutes(gtShr);
		t.setFreeTimeOverlappingMins(gFre);
		t.setTotalFreeTimeOverlappingMins(gtFre);
		t.setPrefInstrMethConflict(gPIM);
		t.setTotalPrefInstrMethConflict(gtPIM);
		t.setPrefSectionConflict(gPSec);
		t.setTotalPrefSectionConflict(gtPSec);

		ret.add(t);

		return ret;
	}

	public int getDistanceInMinutes(Assignment as1, Assignment as2, DistanceMetric m) {
		Placement p1 = as1.getPlacement(), p2 = as2.getPlacement();
		if (p1.getNrRooms() == 0 || p2.getNrRooms() == 0) return 0;
		TimeLocation t1 = p1.getTimeLocation(), t2 = p2.getTimeLocation();
		if (t1 == null || t2 == null || !t1.shareDays(t2) || !t1.shareWeeks(t2)) return 0;
        int a1 = t1.getStartSlot(), a2 = t2.getStartSlot();
        if (m.doComputeDistanceConflictsBetweenNonBTBClasses()) {
        	if (a1 + t1.getLength() <= a2) {
        		int dist = Placement.getDistanceInMinutes(m, p1, p2);
        		if (dist > t1.getBreakTime() + Constants.SLOT_LENGTH_MIN * (a2 - a1 - t1.getLength()))
        			return dist;
        	}
        } else {
        	if (a1 + t1.getLength() == a2)
        		return Placement.getDistanceInMinutes(m, p1, p2);
        }
        return 0;
    }

	public boolean isDistanceConflict(Student student, Assignment as1, Assignment as2, DistanceMetric m) {
		Placement p1 = as1.getPlacement(), p2 = as2.getPlacement();
		if (p1.getNrRooms() == 0 || p2.getNrRooms() == 0) return false;
		TimeLocation t1 = p1.getTimeLocation(), t2 = p2.getTimeLocation();
		if (t1 == null || t2 == null || !t1.shareDays(t2) || !t1.shareWeeks(t2)) return false;
		int a1 = t1.getStartSlot(), a2 = t2.getStartSlot();
		boolean sd = false;
		for (StudentAccomodation a: student.getAccomodations()) {
			if (m.getShortDistanceAccommodationReference().equals(a.getAbbreviation())) { sd = true; break; }
		}
        if (sd) {
        	if (m.doComputeDistanceConflictsBetweenNonBTBClasses()) {
	        	if (a1 + t1.getLength() <= a2) {
	        		int dist = Placement.getDistanceInMinutes(m, p1, p2);
	        		return (dist > Constants.SLOT_LENGTH_MIN * (a2 - a1 - t1.getLength()));
	        	}
	        } else {
	        	if (a1 + t1.getLength() == a2)
	        		return Placement.getDistanceInMinutes(m, p1, p2) > 0;
	        }
		} else {
	        if (m.doComputeDistanceConflictsBetweenNonBTBClasses()) {
	        	if (a1 + t1.getLength() <= a2) {
	        		int dist = Placement.getDistanceInMinutes(m, p1, p2);
	        		return (dist > t1.getBreakTime() + Constants.SLOT_LENGTH_MIN * (a2 - a1 - t1.getLength()));
	        	}
	        } else {
	        	if (a1 + t1.getLength() == a2)
	        		return Placement.getDistanceInMinutes(m, p1, p2) > t1.getBreakTime();
	        }
		}
        return false;
	}

	public boolean isConsentToDoCourse(CourseOffering course) {
		return iCoursesIcanApprove != null && course.getConsentType() != null && iCoursesIcanApprove.contains(course.getUniqueId());
	}

	public static String getStudentSchedulingPreference(Student student, OnlineSectioningServer server, OnlineSectioningHelper helper) {
		String pref = null;
		if (student.getModalityPreference() != null && student.getModalityPreference() != ModalityPreference.NO_PREFERENCE) {
			switch(student.getModalityPreference()) {
			case ONILNE_DISCOURAGED:
				pref = (pref == null ? "" : pref + "\n") + MSG.itemSchedulingModalityPreferFaceToFace();
				break;
			case ONLINE_PREFERRED:
				pref = (pref == null ? "" : pref + "\n") + MSG.itemSchedulingModalityPreferOnline();
				break;
			case ONLINE_REQUIRED:
				pref = (pref == null ? "" : pref + "\n") + MSG.itemSchedulingModalityRequireOnline();
				break;
			}
		}
		if (student.getBackToBackPreference() != null && student.getBackToBackPreference() != BackToBackPreference.NO_PREFERENCE) {
			switch(student.getBackToBackPreference()) {
			case BTB_DISCOURAGED:
				pref = (pref == null ? "" : pref + "\n") + MSG.itemSchedulingBackToBackDiscourage();
				break;
			case BTB_PREFERRED:
				pref = (pref == null ? "" : pref + "\n") + MSG.itemSchedulingBackToBackPrefer();
				break;
			}
		}
		if (student.getClassStartDate() != null || student.getClassEndDate() != null) {
			if (student.getClassStartDate() == null) {
				pref = (pref == null ? "" : pref + "\n") +
						MSG.schedulingPrefClassesTo(Formats.getDateFormat(CONST.patternDateFormat()).format(student.getClassEndDate()));
			} else if (student.getClassEndDate() == null) {
				pref = (pref == null ? "" : pref + "\n") +
						MSG.schedulingPrefClassesFrom(Formats.getDateFormat(CONST.patternDateFormat()).format(student.getClassStartDate()));
			} else {
				pref = (pref == null ? "" : pref + "\n") +
						MSG.schedulingPrefClassesBetween(
						Formats.getDateFormat(CONST.patternDateFormat()).format(student.getClassStartDate()),
						Formats.getDateFormat(CONST.patternDateFormat()).format(student.getClassEndDate())
						);
			}
		}
		return pref;
	}

	public static AdvisedInfoInterface getAdvisedInfo(Student student, OnlineSectioningServer server, OnlineSectioningHelper helper) {
		if (student == null || student.getAdvisorCourseRequests() == null || student.getAdvisorCourseRequests().isEmpty()) return null;
		List<AdvisorCourseRequest> acrs = new ArrayList<AdvisorCourseRequest>(student.getAdvisorCourseRequests());
		Collections.sort(acrs);

		Set<Long> enrolledCourseIds = new HashSet<Long>();
		for (StudentClassEnrollment e: student.getClassEnrollments())
			enrolledCourseIds.add(e.getCourseOffering().getUniqueId());

		AdvisedInfoInterface info = new AdvisedInfoInterface();
		AdvisorCourseRequest last = null;
		CourseDemand.Critical advCritical = CourseDemand.Critical.fromText(ApplicationProperty.AdvisorCourseRequestsAllowCritical.valueOfSession(server.getAcademicSession().getUniqueId()));
		CourseOffering advFirstChoice = null;
		CourseRequest firstChoice = null;
		CourseOffering firstEnrolled = null;
		boolean firstChoiceCritical = false;
		float minCred = 0f, maxCred = 0f, cm = 0f, cx = 0f;
		int nrCourses = 0, nrCriticalCourses = 0, nrCoursesFound = 0, nrCriticalCoursesFound = 0, nrSubstMisMatch = 0, nrCoursesAssigned = 0;
		int foundPrioMin = 0, foundPrioMax = 0;
		int points = 0, maxPoints = 0;
		int missingCrit = 0, missingPrim = 0;
		int notAssignedCrit = 0, notAssignedPrim = 0;
		for (AdvisorCourseRequest acr: acrs) {
			if (acr.getPriority() == -1) continue;
			if (last != null && last.getPriority() != acr.getPriority()) {
				minCred += cm; maxCred += cx; cm = 0; cx = 0;
				if (advFirstChoice != null) { // ignore requests without any courses
					if (firstChoiceCritical && nrCoursesFound == 0) {
						missingCrit ++; missingPrim ++;
						if (nrCourses > 1)
							info.addMessage(
									advCritical == Critical.IMPORTANT ? MSG.advMessageMissingImportantCourseWithAlts(advFirstChoice.getCourseName()) :
									advCritical == Critical.VITAL ? MSG.advMessageMissingVitalCourseWithAlts(advFirstChoice.getCourseName()) :
									MSG.advMessageMissingCriticalCourseWithAlts(advFirstChoice.getCourseName()));
						else
							info.addMessage(advCritical == Critical.IMPORTANT ? MSG.advMessageMissingImportantCourse(advFirstChoice.getCourseName()) :
								advCritical == Critical.VITAL ? MSG.advMessageMissingVitalCourse(advFirstChoice.getCourseName()) :
								MSG.advMessageMissingCriticalCourse(advFirstChoice.getCourseName()));
					} else if (!last.isSubstitute() && nrCoursesFound - nrSubstMisMatch == 0) {
						missingPrim ++;
						if (nrCourses > 1)
							info.addMessage(MSG.advMessageMissingCourseWithAlts(advFirstChoice.getCourseName()));
						else
							info.addMessage(MSG.advMessageMissingCourse(advFirstChoice.getCourseName()));
					} else if (last.isSubstitute() && nrCoursesFound == 0) {
						if (nrCourses > 1)
							info.addMessage(MSG.advMessageMissingSubstituteCourseWithAlts(advFirstChoice.getCourseName()));
						else
							info.addMessage(MSG.advMessageMissingSubstituteCourse(advFirstChoice.getCourseName()));
					} else if (firstChoice == null && firstChoiceCritical) {
						info.addMessage(advCritical == Critical.IMPORTANT ? MSG.advMessageMissingImportantCourseHasAlts(advFirstChoice.getCourseName()) :
							advCritical == Critical.VITAL ? MSG.advMessageMissingVitalCourseHasAlts(advFirstChoice.getCourseName()) :
							MSG.advMessageMissingCriticalCourseHasAlts(advFirstChoice.getCourseName()));
					} else if (firstChoice == null) {
						info.addMessage(MSG.advMessageMissingCourseHasAlts(advFirstChoice.getCourseName()));
					} else if (nrCoursesFound < nrCourses) {
						info.addMessage(MSG.advMessageMissingAlternatives(advFirstChoice.getCourseName()));
					}

					if (nrCoursesFound > 0 && nrCoursesAssigned == 0 && !last.isSubstitute()) {
						notAssignedPrim ++;
						if (firstChoiceCritical) {
							notAssignedCrit ++;
							if (nrCourses > 1)
								info.addNotAssignedMessage(advCritical == Critical.IMPORTANT ? MSG.advMessageNotEnrolledImportantCourseWithAlts(advFirstChoice.getCourseName()) :
									advCritical == Critical.VITAL ? MSG.advMessageNotEnrolledVitalCourseWithAlts(advFirstChoice.getCourseName()) :
									MSG.advMessageNotEnrolledCriticalCourseWithAlts(advFirstChoice.getCourseName()));
							else
								info.addNotAssignedMessage(advCritical == Critical.IMPORTANT ? MSG.advMessageNotEnrolledImportantCourse(advFirstChoice.getCourseName()) :
									advCritical == Critical.VITAL ? MSG.advMessageNotEnrolledVitalCourse(advFirstChoice.getCourseName()) :
									MSG.advMessageNotEnrolledCriticalCourse(advFirstChoice.getCourseName()));
						} else {
							if (nrCourses > 1)
								info.addNotAssignedMessage(MSG.advMessageNotEnrolledCourseWithAlts(advFirstChoice.getCourseName()));
							else
								info.addNotAssignedMessage(MSG.advMessageNotEnrolledCourse(advFirstChoice.getCourseName()));
						}
					} else if (last.isSubstitute() && nrCoursesAssigned > 0) {
						notAssignedPrim --;
						info.addNotAssignedMessage(MSG.advMessageHasEnrolledSubstituteCourse(firstEnrolled.getCourseName()));
					}

					// 1st choice course match
					maxPoints += 4 + (firstChoiceCritical ? 2 : 0);
					if (firstChoice != null) {
						points += 2; // first choice course is present
						if (firstChoiceCritical && firstChoice.getCourseDemand().isCriticalOrImportant()) points +=2; // also is critical
						if (firstChoice.getCourseDemand().isAlternative() == last.isSubstitute()) points ++; // in the same table
						if (firstChoice.getCourseDemand().getFirstChoiceCourseOffering().equals(advFirstChoice)) points ++; // also first as choice (not alt to something else)
					}

					// number of courses match
					maxPoints += 2;
					if (nrCourses == nrCoursesFound) points += 2; // all courses exist

					if (nrCourses > 1) maxPoints ++;
					if (nrCourses > 1 && nrCoursesFound >= 1) points ++; // at least one

					if (nrCourses > 0 && !last.isSubstitute()) maxPoints += 2;
					if (nrCourses > 0 && nrCoursesFound >= 1 && !last.isSubstitute() && nrCoursesFound - nrSubstMisMatch > 0) points += 2; // and are proprity

					if (nrCourses > 1) maxPoints ++;
					if (nrCourses > 1 && nrCoursesFound > 1) points++; // at least two

					if (nrCourses > 2) maxPoints ++;
					if (nrCourses > 2 && nrCoursesFound > 2) points++; // at least three

					if (nrCoursesFound > 1) maxPoints ++;
					if (nrCoursesFound > 1 && foundPrioMin == foundPrioMax) points++; // and have same priority

					maxPoints ++;
					if (nrSubstMisMatch == 0 && nrCoursesFound > 0) points ++; // not priority mismatch

					if (nrCriticalCourses > 1) maxPoints += 3;
					if (nrCriticalCourses > 1 && nrCriticalCoursesFound > 1) points += 3; // critical
				}
				nrCourses = 0; nrCriticalCourses = 0; nrCoursesFound = 0; nrCriticalCoursesFound = 0; nrSubstMisMatch = 0;
				advFirstChoice = null; firstChoice = null; firstChoiceCritical = false;
				foundPrioMin = 0; foundPrioMax = 0;
				firstEnrolled = null; nrCoursesAssigned = 0;
			}
			if (!acr.isSubstitute()) {
				CourseCreditUnitConfig credit = (acr.getCourseOffering() == null ? null: acr.getCourseOffering().getCredit());
				if (credit != null) {
					if (acr.getAlternative() == 0 || credit.getMinCredit() < cm) cm = credit.getMinCredit();
					if (acr.getAlternative() == 0 || credit.getMaxCredit() > cx) cx = credit.getMaxCredit();
				} else if (acr.getAlternative() == 0) {
					cm = acr.getCreditMin(); cx = acr.getCreditMax();
				}
			}
			if (acr.getCourseOffering() != null) {
				nrCourses ++;
				if (acr.isCriticalOrImportant() && !acr.isSubstitute()) nrCriticalCourses ++;
				CourseRequest request = null;
				cd: for (CourseDemand cd: student.getCourseDemands()) {
					for (CourseRequest cr: cd.getCourseRequests()) {
						if (cr.getCourseOffering().equals(acr.getCourseOffering())) {
							request = cr; break cd;
						}
					}
				}
				if (advFirstChoice == null) {
					advFirstChoice = acr.getCourseOffering();
					firstChoiceCritical = acr.isCriticalOrImportant();
					firstChoice = request;
				}
				if (enrolledCourseIds.contains(acr.getCourseOffering().getUniqueId())) {
					nrCoursesAssigned ++;
					if (firstEnrolled == null) firstEnrolled = acr.getCourseOffering();
				}
				if (request != null) {
					nrCoursesFound ++;
					if (nrCoursesFound == 1) {
						foundPrioMin = request.getCourseDemand().getPriority();
						foundPrioMax = request.getCourseDemand().getPriority();
					} else {
						if (request.getCourseDemand().getPriority() < foundPrioMin) foundPrioMin = request.getCourseDemand().getPriority();
						if (request.getCourseDemand().getPriority() > foundPrioMax) foundPrioMax = request.getCourseDemand().getPriority();
					}
					if (acr.isSubstitute() != request.getCourseDemand().isAlternative()) nrSubstMisMatch ++;
					if (acr.isCriticalOrImportant() && request.getCourseDemand().isCriticalOrImportant() && !request.getCourseDemand().isAlternative()) nrCriticalCoursesFound ++;
				}
			}
			last = acr;
		}
		minCred += cm; maxCred += cx;
		info.setMinCredit(minCred); info.setMaxCredit(maxCred);
		if (advFirstChoice != null) { // ignore requests without any courses
			if (firstChoiceCritical && nrCoursesFound == 0) {
				missingCrit ++; missingPrim ++;
				if (nrCourses > 1)
					info.addMessage(advCritical == Critical.IMPORTANT ? MSG.advMessageMissingImportantCourseWithAlts(advFirstChoice.getCourseName()) :
						advCritical == Critical.VITAL ? MSG.advMessageMissingVitalCourseWithAlts(advFirstChoice.getCourseName()) :
						MSG.advMessageMissingCriticalCourseWithAlts(advFirstChoice.getCourseName()));
				else
					info.addMessage(advCritical == Critical.IMPORTANT ? MSG.advMessageMissingImportantCourse(advFirstChoice.getCourseName()) :
						advCritical == Critical.VITAL ? MSG.advMessageMissingVitalCourse(advFirstChoice.getCourseName()) :
						MSG.advMessageMissingCriticalCourse(advFirstChoice.getCourseName()));
			} else if (!last.isSubstitute() && nrCoursesFound - nrSubstMisMatch == 0) {
				missingPrim ++;
				if (nrCourses > 1)
					info.addMessage(MSG.advMessageMissingCourseWithAlts(advFirstChoice.getCourseName()));
				else
					info.addMessage(MSG.advMessageMissingCourse(advFirstChoice.getCourseName()));
			} else if (last.isSubstitute() && nrCoursesFound == 0) {
				if (nrCourses > 1)
					info.addMessage(MSG.advMessageMissingSubstituteCourseWithAlts(advFirstChoice.getCourseName()));
				else
					info.addMessage(MSG.advMessageMissingSubstituteCourse(advFirstChoice.getCourseName()));
			} else if (firstChoice == null && firstChoiceCritical) {
				info.addMessage(advCritical == Critical.IMPORTANT ? MSG.advMessageMissingImportantCourseHasAlts(advFirstChoice.getCourseName()) :
					advCritical == Critical.VITAL ? MSG.advMessageMissingVitalCourseHasAlts(advFirstChoice.getCourseName()) :
					MSG.advMessageMissingCriticalCourseHasAlts(advFirstChoice.getCourseName()));
			} else if (firstChoice == null) {
				info.addMessage(MSG.advMessageMissingCourseHasAlts(advFirstChoice.getCourseName()));
			} else if (nrCoursesFound < nrCourses) {
				info.addMessage(MSG.advMessageMissingAlternatives(advFirstChoice.getCourseName()));
			}

			if (nrCoursesFound > 0 && nrCoursesAssigned == 0 && !last.isSubstitute()) {
				notAssignedPrim ++;
				if (firstChoiceCritical) {
					notAssignedCrit ++;
					if (nrCourses > 1)
						info.addNotAssignedMessage(advCritical == Critical.IMPORTANT ? MSG.advMessageNotEnrolledImportantCourseWithAlts(advFirstChoice.getCourseName()) :
							advCritical == Critical.VITAL ? MSG.advMessageNotEnrolledVitalCourseWithAlts(advFirstChoice.getCourseName()) :
							MSG.advMessageNotEnrolledCriticalCourseWithAlts(advFirstChoice.getCourseName()));
					else
						info.addNotAssignedMessage(advCritical == Critical.IMPORTANT ? MSG.advMessageNotEnrolledImportantCourse(advFirstChoice.getCourseName()) :
							advCritical == Critical.VITAL ? MSG.advMessageNotEnrolledVitalCourse(advFirstChoice.getCourseName()) :
							MSG.advMessageNotEnrolledCriticalCourse(advFirstChoice.getCourseName()));
				} else {
					if (nrCourses > 1)
						info.addNotAssignedMessage(MSG.advMessageNotEnrolledCourseWithAlts(advFirstChoice.getCourseName()));
					else
						info.addNotAssignedMessage(MSG.advMessageNotEnrolledCourse(advFirstChoice.getCourseName()));
				}
			} else if (last.isSubstitute() && nrCoursesAssigned > 0) {
				notAssignedPrim --;
				info.addNotAssignedMessage(MSG.advMessageHasEnrolledSubstituteCourse(firstEnrolled.getCourseName()));
			}

			// 1st choice course match
			maxPoints += 4 + (firstChoiceCritical ? 2 : 0);
			if (firstChoice != null) {
				points += 2; // first choice course is present
				if (firstChoiceCritical && firstChoice.getCourseDemand().isCriticalOrImportant()) points +=2; // also is critical
				if (firstChoice.getCourseDemand().isAlternative() == last.isSubstitute()) points ++; // in the same table
				if (firstChoice.getCourseDemand().getFirstChoiceCourseOffering().equals(advFirstChoice)) points ++; // also first as choice (not alt to something else)
			}

			// number of courses match
			maxPoints += 2;
			if (nrCourses == nrCoursesFound) points += 2; // all courses exist

			if (nrCourses > 1) maxPoints ++;
			if (nrCourses > 1 && nrCoursesFound >= 1) points ++; // at least one

			if (nrCourses > 0 && !last.isSubstitute()) maxPoints += 2;
			if (nrCourses > 0 && nrCoursesFound >= 1 && !last.isSubstitute() && nrCoursesFound - nrSubstMisMatch > 0) points += 2; // and are proprity

			if (nrCourses > 1) maxPoints ++;
			if (nrCourses > 1 && nrCoursesFound > 1) points++; // at least two

			if (nrCourses > 2) maxPoints ++;
			if (nrCourses > 2 && nrCoursesFound > 2) points++; // at least three

			if (nrCoursesFound > 1) maxPoints ++;
			if (nrCoursesFound > 1 && foundPrioMin == foundPrioMax) points++; // and have same priority

			maxPoints ++;
			if (nrSubstMisMatch == 0 && nrCoursesFound > 0) points ++; // not priority mismatch

			if (nrCriticalCourses > 1) maxPoints += 3;
			if (nrCriticalCourses > 1 && nrCriticalCoursesFound > 1) points += 3; // critical
		}
		info.setPercentage(((float)points)/maxPoints);
		info.setMissingCritical(missingCrit);
		info.setMissingPrimary(missingPrim);
		info.setNotAssignedCritical(notAssignedCrit);
		info.setNotAssignedPrimary(notAssignedPrim);
		info.setAdvisorCritical(advCritical.ordinal());

		return info;
	}
}


text:
package org.elasticsearch.hadoop.rest;

import java.io.Closeable;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Random;

import org.apache.commons.logging.Log;
import org.elasticsearch.hadoop.EsHadoopIllegalArgumentException;
import org.elasticsearch.hadoop.cfg.ConfigurationOptions;
import org.elasticsearch.hadoop.cfg.FieldPresenceValidation;
import org.elasticsearch.hadoop.cfg.PropertiesSettings;
import org.elasticsearch.hadoop.cfg.Settings;
import org.elasticsearch.hadoop.serialization.ScrollReader;
import org.elasticsearch.hadoop.serialization.builder.ValueReader;
import org.elasticsearch.hadoop.serialization.dto.Node;
import org.elasticsearch.hadoop.serialization.dto.Shard;
import org.elasticsearch.hadoop.serialization.dto.mapping.Field;
import org.elasticsearch.hadoop.serialization.dto.mapping.MappingUtils;
import org.elasticsearch.hadoop.serialization.field.IndexExtractor;
import org.elasticsearch.hadoop.util.Assert;
import org.elasticsearch.hadoop.util.IOUtils;
import org.elasticsearch.hadoop.util.ObjectUtils;
import org.elasticsearch.hadoop.util.SettingsUtils;
import org.elasticsearch.hadoop.util.StringUtils;
import org.elasticsearch.hadoop.util.Version;

public abstract class RestService implements Serializable {

    public static class PartitionDefinition implements Serializable {
        public final String serializedSettings, serializedMapping;
        public final String nodeIp, nodeId, nodeName, shardId;
        public final int nodePort;
        public final boolean onlyNode;

        PartitionDefinition(Shard shard, Node node, String settings, String mapping, boolean onlyNode) {
            this(node.getIpAddress(), node.getHttpPort(), node.getName(), node.getId(), shard.getName().toString(),
                    onlyNode, settings, mapping);
        }

        public PartitionDefinition(String nodeIp, int nodePort, String nodeName, String nodeId, String shardId,
                boolean onlyNode, String settings, String mapping) {
            this.nodeIp = nodeIp;
            this.nodePort = nodePort;
            this.nodeName = nodeName;
            this.nodeId = nodeId;
            this.shardId = shardId;

            this.serializedSettings = settings;
            this.serializedMapping = mapping;

            this.onlyNode = onlyNode;
        }

        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder();
            builder.append("EsPartition [node=[").append(nodeId).append("/").append(nodeName)
            .append("|").append(nodeIp).append(":").append(nodePort)
            .append("],shard=").append(shardId).append("]");
            return builder.toString();
        }

        public Settings settings() {
            return new PropertiesSettings(new Properties()).load(serializedSettings);
        }
    }

    public static class PartitionReader implements Closeable {
        public final ScrollReader scrollReader;
        public final RestRepository client;
        public final QueryBuilder queryBuilder;

        private ScrollQuery scrollQuery;

        private boolean closed = false;

        PartitionReader(ScrollReader scrollReader, RestRepository client, QueryBuilder queryBuilder) {
            this.scrollReader = scrollReader;
            this.client = client;
            this.queryBuilder = queryBuilder;
        }

        @Override
        public void close() {
            if (!closed) {
                closed = true;
                if (scrollQuery != null) {
                    scrollQuery.close();
                }
                client.close();
            }
        }

        public ScrollQuery scrollQuery() {
            if (scrollQuery == null) {
                scrollQuery = queryBuilder.build(client, scrollReader);
            }

            return scrollQuery;
        }
    }

    public static class PartitionWriter implements Closeable {
        public final RestRepository repository;
        public final int number;
        public final int total;
        public final Settings settings;

        private boolean closed = false;

        PartitionWriter(Settings settings, int splitIndex, int splitsSize, RestRepository repository) {
            this.settings = settings;
            this.repository = repository;
            this.number = splitIndex;
            this.total = splitsSize;
        }

        @Override
        public void close() {
            if (!closed) {
                closed = true;
                repository.close();
            }
        }
    }

    public static class MultiReaderIterator implements Closeable, Iterator {
        private final List<PartitionDefinition> definitions;
        private final Iterator<PartitionDefinition> definitionIterator;
        private PartitionReader currentReader;
        private ScrollQuery currentScroll;
        private boolean finished = false;

        private final Settings settings;
        private final Log log;

        MultiReaderIterator(List<PartitionDefinition> defs, Settings settings, Log log) {
            this.definitions = defs;
            definitionIterator = defs.iterator();

            this.settings = settings;
            this.log = log;
        }

        @Override
        public void close() {
            if (finished) {
                return;
            }

            ScrollQuery sq = getCurrent();
            if (sq != null) {
                sq.close();
            }
            if (currentReader != null) {
                currentReader.close();
            }

            finished = true;
        }

        @Override
        public boolean hasNext() {
            ScrollQuery sq = getCurrent();
            return (sq != null ? sq.hasNext() : false);
        }

        private ScrollQuery getCurrent() {
            if (finished) {
                return null;
            }

            for (boolean hasValue = false; !hasValue;) {
                if (currentReader == null) {
                    if (definitionIterator.hasNext()) {
                        currentReader = RestService.createReader(settings, definitionIterator.next(), log);
                    }
                    else {
                        finished = true;
                        return null;
                    }
                }

                if (currentScroll == null) {
                    currentScroll = currentReader.scrollQuery();
                }

                hasValue = currentScroll.hasNext();

                if (!hasValue) {
                    currentScroll.close();
                    currentScroll = null;

                    currentReader.close();
                    currentReader = null;
                }
            }

            return currentScroll;
        }

        @Override
        public Object[] next() {
            ScrollQuery sq = getCurrent();
            return sq.next();
        }

        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }
    }

    @SuppressWarnings("unchecked")
    public static List<PartitionDefinition> findPartitions(Settings settings, Log log) {
        Version.logVersion();

        boolean overlappingShards = false;
        Map<Shard, Node> targetShards = null;

        InitializationUtils.validateSettings(settings);
        InitializationUtils.discoverEsVersion(settings, log);
        InitializationUtils.discoverNodesIfNeeded(settings, log);
        InitializationUtils.filterNonClientNodesIfNeeded(settings, log);
        InitializationUtils.filterNonDataNodesIfNeeded(settings, log);

        String savedSettings = settings.save();

        RestRepository client = new RestRepository(settings);
        boolean indexExists = client.indexExists(true);

        if (!indexExists) {
            if (settings.getIndexReadMissingAsEmpty()) {
                log.info(String.format("Index [%s] missing - treating it as empty", settings.getResourceRead()));
                targetShards = Collections.emptyMap();
            }
            else {
                client.close();
                throw new EsHadoopIllegalArgumentException(
                        String.format("Index [%s] missing and settings [%s] is set to false", settings.getResourceRead(), ConfigurationOptions.ES_FIELD_READ_EMPTY_AS_NULL));
            }
        }
        else {
            Object[] result = client.getReadTargetShards(settings.getNodesClientOnly());
            overlappingShards = (Boolean) result[0];
            targetShards = (Map<Shard, Node>) result[1];

            if (log.isTraceEnabled()) {
                log.trace("Creating splits for shards " + targetShards);
            }
        }

        log.info(String.format("Reading from [%s]", settings.getResourceRead()));

        String savedMapping = null;
        if (!targetShards.isEmpty()) {
            Field mapping = client.getMapping();
            log.info(String.format("Discovered mapping {%s} for [%s]", mapping, settings.getResourceRead()));
            // validate if possible
            FieldPresenceValidation validation = settings.getFieldExistanceValidation();
            if (validation.isRequired()) {
                MappingUtils.validateMapping(settings.getScrollFields(), mapping, validation, log);
            }

            //TODO: implement this more efficiently
            savedMapping = IOUtils.serializeToBase64(mapping);
        }

        client.close();

        List<PartitionDefinition> partitions = new ArrayList<PartitionDefinition>(targetShards.size());

        for (Entry<Shard, Node> entry : targetShards.entrySet()) {
            partitions.add(new PartitionDefinition(entry.getKey(), entry.getValue(), savedSettings, savedMapping, !overlappingShards));
        }

        return partitions;
    }

    public static PartitionReader createReader(Settings settings, PartitionDefinition partition, Log log) {

        if (!SettingsUtils.hasPinnedNode(settings)) {
            if (log.isDebugEnabled()) {
                log.debug(String.format("Partition reader instance [%s] assigned to [%s]:[%s]", partition,
                        partition.nodeId, partition.nodePort));
            }

            SettingsUtils.pinNode(settings, partition.nodeIp, partition.nodePort);
        }

        ValueReader reader = ObjectUtils.instantiate(settings.getSerializerValueReaderClassName(), settings);

        Field fieldMapping = null;

        if (StringUtils.hasText(partition.serializedMapping)) {
            fieldMapping = IOUtils.deserializeFromBase64(partition.serializedMapping);
        }
        else {
            log.warn(String.format("No mapping found for [%s] - either no index exists or the partition configuration has been corrupted", partition));
        }

        ScrollReader scrollReader = new ScrollReader(reader, fieldMapping, settings.getReadMetadata(), settings.getReadMetadataField(), settings.getOutputAsJson());

        // initialize REST client
        RestRepository client = new RestRepository(settings);

        if (settings.getNodesClientOnly()) {
            String clientNode = client.getRestClient().getCurrentNode();
            if (log.isDebugEnabled()) {
                log.debug(String.format("Client-node routing detected; partition reader instance [%s] assigned to [%s]",
                        partition, clientNode));
            }
            SettingsUtils.pinNode(settings, clientNode);
        }

        // take into account client node routing
        QueryBuilder queryBuilder = QueryBuilder.query(settings).shard(partition.shardId)
                                                .node(partition.nodeId).restrictToNode(partition.onlyNode && (!settings.getNodesClientOnly() && !settings.getNodesWANOnly()));
        queryBuilder.fields(settings.getScrollFields());
        queryBuilder.filter(SettingsUtils.getFilters(settings));

        return new PartitionReader(scrollReader, client, queryBuilder);
    }

    // expects currentTask to start from 0
    public static List<PartitionDefinition> assignPartitions(List<PartitionDefinition> partitions, int currentTask, int totalTasks) {
        int esPartitions = partitions.size();
        if (totalTasks >= esPartitions) {
            return (currentTask >= esPartitions ? Collections.<PartitionDefinition> emptyList() : Collections.singletonList(partitions.get(currentTask)));
        }
        else {
            int partitionsPerTask = esPartitions / totalTasks;
            int remainder = esPartitions % totalTasks;

            int partitionsPerCurrentTask = partitionsPerTask;

            // spread the reminder against the tasks
            if (currentTask < remainder) {
                partitionsPerCurrentTask++;
            }

            // find the offset inside the collection
            int offset = partitionsPerTask * currentTask;
            if (currentTask != 0) {
                offset += (remainder > currentTask ? 1 : remainder);
            }

            // common case
            if (partitionsPerCurrentTask == 1) {
                return Collections.singletonList(partitions.get(offset));
            }

            List<PartitionDefinition> pa = new ArrayList<PartitionDefinition>(partitionsPerCurrentTask);
            for (int index = offset; index < offset + partitionsPerCurrentTask; index++) {
                pa.add(partitions.get(index));
            }
            return pa;
        }
    }

    public static MultiReaderIterator multiReader(Settings settings, List<PartitionDefinition> definitions, Log log) {
        return new MultiReaderIterator(definitions, settings, log);
    }

    public static PartitionWriter createWriter(Settings settings, int currentSplit, int totalSplits, Log log) {
        Version.logVersion();

        InitializationUtils.validateSettings(settings);
        InitializationUtils.discoverEsVersion(settings, log);
        InitializationUtils.discoverNodesIfNeeded(settings, log);
        InitializationUtils.filterNonClientNodesIfNeeded(settings, log);
        InitializationUtils.filterNonDataNodesIfNeeded(settings, log);

        List<String> nodes = SettingsUtils.discoveredOrDeclaredNodes(settings);

        // check invalid splits (applicable when running in non-MR environments) - in this case fall back to Random..
        int selectedNode = (currentSplit < 0) ? new Random().nextInt(nodes.size()) : currentSplit % nodes.size();

        // select the appropriate nodes first, to spread the load before-hand
        SettingsUtils.pinNode(settings, nodes.get(selectedNode));

        Resource resource = new Resource(settings, false);

        log.info(String.format("Writing to [%s]", resource));

        // single index vs multi indices
        IndexExtractor iformat = ObjectUtils.instantiate(settings.getMappingIndexExtractorClassName(), settings);
        iformat.compile(resource.toString());

        RestRepository repository = (iformat.hasPattern() ? initMultiIndices(settings, currentSplit, resource, log) : initSingleIndex(settings, currentSplit, resource, log));

        return new PartitionWriter(settings, currentSplit, totalSplits, repository);
    }

    private static RestRepository initSingleIndex(Settings settings, int currentInstance, Resource resource, Log log) {
        if (log.isDebugEnabled()) {
            log.debug(String.format("Resource [%s] resolves as a single index", resource));
        }

        RestRepository repository = new RestRepository(settings);
        // create the index if needed
        if (repository.touch()) {
            if (repository.waitForYellow()) {
                log.warn(String.format("Timed out waiting for index [%s] to reach yellow health", resource));
            }
        }

        if (settings.getNodesWANOnly()) {
            return randomNodeWrite(settings, currentInstance, resource, log);
        }

        // if client-nodes are used, simply use the underlying nodes
        if (settings.getNodesClientOnly()) {
            String clientNode = repository.getRestClient().getCurrentNode();
            if (log.isDebugEnabled()) {
                log.debug(String.format("Client-node routing detected; partition writer instance [%s] assigned to [%s]",
                        currentInstance, clientNode));
            }

            return repository;
        }

        // no routing necessary; select the relevant target shard/node
        Map<Shard, Node> targetShards = Collections.emptyMap();

        targetShards = repository.getWriteTargetPrimaryShards(settings.getNodesClientOnly());
        repository.close();

        Assert.isTrue(!targetShards.isEmpty(),
                String.format("Cannot determine write shards for [%s]; likely its format is incorrect (maybe it contains illegal characters?)", resource));

        List<Shard> orderedShards = new ArrayList<Shard>(targetShards.keySet());
        // make sure the order is strict
        Collections.sort(orderedShards);
        if (log.isTraceEnabled()) {
            log.trace(String.format("Partition writer instance [%s] discovered [%s] primary shards %s", currentInstance, orderedShards.size(), orderedShards));
        }

        // if there's no task info, just pick a random bucket
        if (currentInstance <= 0) {
            currentInstance = new Random().nextInt(targetShards.size()) + 1;
        }
        int bucket = currentInstance % targetShards.size();
        Shard chosenShard = orderedShards.get(bucket);
        Node targetNode = targetShards.get(chosenShard);

        // pin settings
        SettingsUtils.pinNode(settings, targetNode.getIpAddress(), targetNode.getHttpPort());
        String node = SettingsUtils.getPinnedNode(settings);
        repository = new RestRepository(settings);

        if (log.isDebugEnabled()) {
            log.debug(String.format("Partition writer instance [%s] assigned to primary shard [%s] at address [%s]",
                    currentInstance, chosenShard.getName(), node));
        }

        return repository;
    }

    private static RestRepository initMultiIndices(Settings settings, int currentInstance, Resource resource, Log log) {
        if (log.isDebugEnabled()) {
            log.debug(String.format("Resource [%s] resolves as an index pattern", resource));
        }

        return randomNodeWrite(settings, currentInstance, resource, log);
    }

    private static RestRepository randomNodeWrite(Settings settings, int currentInstance, Resource resource, Log log) {
        // multi-index write - since we don't know before hand what index will be used, pick a random node from the given list
        List<String> nodes = SettingsUtils.discoveredOrDeclaredNodes(settings);
        String node = nodes.get(new Random().nextInt(nodes.size()));
        // override the global settings to communicate directly with the target node
        SettingsUtils.pinNode(settings, node);

        if (log.isDebugEnabled()) {
            log.debug(String.format("Partition writer instance [%s] assigned to [%s]", currentInstance, node));
        }

        return new RestRepository(settings);
    }
}

redpajama_set_name: RedPajamaGithub


text:
date: '2018-04-17 11:43 -0400'
text field: dereklieu
button stuff: true
published: false
title: A title
---
## A New Post

Enter text in [Markdown](http://daringfireball.net/projects/markdown/). Use the toolbar above, or click the **?** button for formatting help.


text:
#ifndef SKLAND_CORE_DELEGATE_HPP_
#define SKLAND_CORE_DELEGATE_HPP_

#include <cstring>

namespace skland {
namespace core {

/// @cond IGNORE

// generic classes to calculate method pointer:
class GenericBase1 {};
class GenericBase2 {};
class GenericMultiInherit : GenericBase1, GenericBase2 {};

typedef void (GenericMultiInherit::*GenericMethodPointer)();

// A forward declaration
// This is the key to use the template format of Delegate<return_type (args...)>
template<typename _Signature>
class Delegate;

template<typename _Signature>
class DelegateRef;

/// @endcond

/**
 * @ingroup core
 * @brief Template class for delegates
 *
 * @see Fast C++ Delegats
 */
template<typename ReturnType, typename ... ParamTypes>
class Delegate<ReturnType(ParamTypes...)> {
  typedef ReturnType (*MethodStubType)(void *object_ptr, GenericMethodPointer, ParamTypes...);

  struct PointerData {

    PointerData()
        : object_pointer(nullptr),
          method_stub(nullptr),
          method_pointer(nullptr) {}

    PointerData(const PointerData &orig)
        : object_pointer(orig.object_pointer),
          method_stub(orig.method_stub),
          method_pointer(orig.method_pointer) {}

    PointerData &operator=(const PointerData &orig) {
      object_pointer = orig.object_pointer;
      method_stub = orig.method_stub;
      method_pointer = orig.method_pointer;
      return *this;
    }

    void *object_pointer;
    MethodStubType method_stub;
    GenericMethodPointer method_pointer;
  };

  template<typename T, typename TFxn>
  struct MethodStub {
    static ReturnType invoke(void *obj_ptr, GenericMethodPointer any, ParamTypes ... Args) {
      T *obj = static_cast<T *>(obj_ptr);
      return (obj->*reinterpret_cast<TFxn>(any))(Args...);
    }
  };

 public:

  /**
   * @brief Create a delegate from the given object and a member function pointer
   * @tparam T The object type
   * @param object_ptr A pointer to an object
   * @param method A pointer to a member function in class T
   * @return A delegate object
   */
  template<typename T>
  static inline Delegate FromMethod(T *object_ptr,
                                    ReturnType (T::*method)(ParamTypes...)) {
    typedef ReturnType (T::*TMethod)(ParamTypes...);

    Delegate d;
    d.data_.object_pointer = object_ptr;
    d.data_.method_stub = &MethodStub<T, TMethod>::invoke;
    d.data_.method_pointer = reinterpret_cast<GenericMethodPointer>(method);

    return d;
  }

  /**
   * @brief Create a delegate from the given object and a const member function pointer
   * @tparam T The object type
   * @param object_ptr A pointer to an object
   * @param method A pointer to a member function in class T
   * @return A delegate object
   */
  template<typename T>
  static inline Delegate FromMethod(T *object_ptr,
                                    ReturnType (T::*method)(ParamTypes...) const) {
    typedef ReturnType (T::*TMethod)(ParamTypes...) const;

    Delegate d;
    d.data_.object_pointer = object_ptr;
    d.data_.method_stub = &MethodStub<T, TMethod>::invoke;
    d.data_.method_pointer = reinterpret_cast<GenericMethodPointer>(method);

    return d;
  }

  /**
   * @brief Default constructor
   *
   * Create an empty delegate object, which cannot be called by operator() or
   * Invoke(), and the bool operator returns false.
   */
  Delegate() {}

  /**
   * @brief Constructor to create a delegate by given object and method
   * @tparam T The type of object
   * @param object_ptr A pointer to the object
   * @param method A pointer to a member function in class T
   */
  template<typename T>
  Delegate(T *object_ptr, ReturnType (T::*method)(ParamTypes...)) {
    typedef ReturnType (T::*TMethod)(ParamTypes...);

    data_.object_pointer = object_ptr;
    data_.method_stub = &MethodStub<T, TMethod>::invoke;
    data_.method_pointer = reinterpret_cast<GenericMethodPointer>(method);
  }

  /**
   * @brief Constructor to create a delegate by given object and const method
   * @tparam T The type of object
   * @param object_ptr A pointer to the object
   * @param method A pointer to a const member function in class T
   */
  template<typename T>
  Delegate(T *object_ptr, ReturnType (T::*method)(ParamTypes...) const) {
    typedef ReturnType (T::*TMethod)(ParamTypes...) const;

    data_.object_pointer = object_ptr;
    data_.method_stub = &MethodStub<T, TMethod>::invoke;
    data_.method_pointer = reinterpret_cast<GenericMethodPointer>(method);
  }

  /**
   * @brief Copy constructor
   * @param orig Another delegate
   */
  Delegate(const Delegate &orig)
      : data_(orig.data_) {}

  /**
   * @brief Destructor
   */
  ~Delegate() {}

  /**
   * @brief Reset this delegate
   *
   * Reset pointers in this delegate to nullptr (0)
   *
   * @note After reset, invoke this delegate by operator() or Invoke() will cause segment fault.
   * The bool operator will return false.
   */
  void Reset() {
    memset(&data_, 0, sizeof(PointerData));
  }

  /**
   * @brief Assignment operator overloading
   * @param orig Another delegate
   * @return
   */
  Delegate &operator=(const Delegate &orig) {
    data_ = orig.data_;
    return *this;
  }

  ReturnType operator()(ParamTypes... Args) const {
    return (*data_.method_stub)(data_.object_pointer, data_.method_pointer, Args...);
  }

  ReturnType Invoke(ParamTypes... Args) const {
    return (*data_.method_stub)(data_.object_pointer, data_.method_pointer, Args...);
  }

  /**
   * @brief Bool operator
   * @return True if pointer to a method is set, false otherwise
   */
  operator bool() const {
    return (nullptr != data_.object_pointer) && (nullptr != data_.method_stub) && (nullptr != data_.method_pointer);
  }

  /**
   * @brief Compare this delegate to a member function of an object
   * @tparam T
   * @param object_ptr
   * @param method
   * @return
   */
  template<typename T>
  bool Equal(T *object_ptr, ReturnType(T::*method)(ParamTypes...)) const {
    typedef ReturnType (T::*TMethod)(ParamTypes...);

    return (data_.object_pointer == object_ptr) &&
        (data_.method_stub == &MethodStub<T, TMethod>::invoke) &&
        (data_.method_pointer == reinterpret_cast<GenericMethodPointer>(method));
  }

  /**
   * @brief Compare this delegate to a const member function of an object
   * @tparam T
   * @param object_ptr
   * @param method
   * @return
   */
  template<typename T>
  bool Equal(T *object_ptr, ReturnType(T::*method)(ParamTypes...) const) const {
    typedef ReturnType (T::*TMethod)(ParamTypes...) const;

    return (data_.object_pointer == object_ptr) &&
        (data_.method_stub == &MethodStub<T, TMethod>::invoke) &&
        (data_.method_pointer == reinterpret_cast<GenericMethodPointer>(method));
  }

 private:

  template<typename ReturnTypeAlias, typename ... ParamTypesAlias>
  friend inline bool operator==(const Delegate<ReturnTypeAlias(ParamTypesAlias...)> &src,
                                const Delegate<ReturnTypeAlias(ParamTypesAlias...)> &dst);

  template<typename ReturnTypeAlias, typename ... ParamTypesAlias>
  friend inline bool operator!=(const Delegate<ReturnTypeAlias(ParamTypesAlias...)> &src,
                                const Delegate<ReturnTypeAlias(ParamTypesAlias...)> &dst);

  template<typename ReturnTypeAlias, typename ... ParamTypesAlias>
  friend inline bool operator<(const Delegate<ReturnTypeAlias(ParamTypesAlias...)> &src,
                               const Delegate<ReturnTypeAlias(ParamTypesAlias...)> &dst);

  template<typename ReturnTypeAlias, typename ... ParamTypesAlias>
  friend inline bool operator>(const Delegate<ReturnTypeAlias(ParamTypesAlias...)> &src,
                               const Delegate<ReturnTypeAlias(ParamTypesAlias...)> &dst);

  PointerData data_;
};

/**
 * @brief Compare 2 delegates
 * @tparam ReturnType
 * @tparam ParamTypes
 * @param src
 * @param dst
 * @return True if 2 delegates point to the same method in one object
 */
template<typename ReturnType, typename ... ParamTypes>
inline bool operator==(const Delegate<ReturnType(ParamTypes...)> &src,
                       const Delegate<ReturnType(ParamTypes...)> &dst) {
  return memcmp(&src.data_, &dst.data_,
                sizeof(typename Delegate<ReturnType(ParamTypes...)>::PointerData)
  ) == 0;
}

template<typename ReturnType, typename ... ParamTypes>
inline bool operator!=(const Delegate<ReturnType(ParamTypes...)> &src,
                       const Delegate<ReturnType(ParamTypes...)> &dst) {
  return memcmp(&src.data_, &dst.data_,
                sizeof(typename Delegate<ReturnType(ParamTypes...)>::PointerData)
  ) != 0;
}

template<typename ReturnType, typename ... ParamTypes>
inline bool operator<(const Delegate<ReturnType(ParamTypes...)> &src,
                      const Delegate<ReturnType(ParamTypes...)> &dst) {
  return memcmp(&src.data_, &dst.data_,
                sizeof(typename Delegate<ReturnType(ParamTypes...)>::PointerData)
  ) < 0;
}

template<typename ReturnType, typename ... ParamTypes>
inline bool operator>(const Delegate<ReturnType(ParamTypes...)> &src,
                      const Delegate<ReturnType(ParamTypes...)> &dst) {
  return memcmp(&src.data_, &dst.data_,
                sizeof(typename Delegate<ReturnType(ParamTypes...)>::PointerData)
  ) > 0;
}

/**
 * @ingroup core
 * @brief A reference to a corresponding delegate
 */
template<typename ReturnType, typename ... ParamTypes>
class DelegateRef<ReturnType(ParamTypes...)> {

  DelegateRef() = delete;
  DelegateRef &operator=(const DelegateRef &) = delete;

 public:

  DelegateRef(Delegate<ReturnType(ParamTypes...)> &delegate)
      : delegate_(&delegate) {}

  DelegateRef(const DelegateRef &orig)
      : delegate_(orig.delegate_) {}

  ~DelegateRef() {}

  DelegateRef &operator=(Delegate<ReturnType(ParamTypes...)> &delegate) {
    delegate_ = &delegate;
    return *this;
  }

  template<typename T>
  void Set(T *obj, ReturnType (T::*method)(ParamTypes...)) {
    *delegate_ = Delegate<ReturnType(ParamTypes...)>::template FromMethod<T>(obj, method);
  }

  template<typename T>
  void Set(T *obj, ReturnType (T::*method)(ParamTypes...) const) {
    *delegate_ = Delegate<ReturnType(ParamTypes...)>::template FromMethod<T>(obj, method);
  }

  void Reset() {
    delegate_->Reset();
  }

  template<typename T>
  bool IsAssignedTo(T *obj, ReturnType (T::*method)(ParamTypes...)) const {
    return delegate_->Equal(obj, method);
  }

  template<typename T>
  bool IsAssignedTo(T *obj, ReturnType (T::*method)(ParamTypes...) const) const {
    return delegate_->Equal(obj, method);
  }

  operator bool() const {
    return (*delegate_);
  }

 private:
  Delegate<ReturnType(ParamTypes...)> *delegate_;
};

} // namespace core
} // namespace skland

#endif  // SKLAND_CORE_DELEGATE_HPP_


text:
'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
  GeoJSON = require('mongoose-geojson-schema'),
  Schema = mongoose.Schema;

/**
 * Polls Schema
 */
var PollSchema = new Schema({
  created: {
    type: Date,
    default: Date.now
  },
  user: {
    type: Schema.ObjectId,
    ref: 'User'
  },
  geoLocation: GeoJSON.Point,
  questionnaire: {
    type: Schema.ObjectId,
    required: true,
    ref: 'Questionnaire'
  },
  questionnaireLanguage: {
    type: String,
    required: true
  },
  geolocation: {
  },
  answers: [{
    title: String,
    answer: String
  }]

});

PollSchema.statics.load = function(id, cb) {
  this.findOne({
    _id: id
  }).populate('user', 'name username').populate('questionnaire').exec(cb);
};

mongoose.model('Poll', PollSchema);


text:
package org.ado.biblio.update.kimono;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;
import org.ado.biblio.update.BiblioUpdateException;
import org.ado.biblio.update.kimono.json.KimonoRelease;
import org.ado.biblio.update.kimono.json.KimonoResponse;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.net.URL;

/**
 * @author Andoni del Olmo,
 * @since 25.01.15
 */
public class KimonoBiblioApi {

    private static final String KIMONO_ENDPOINT_TEMPLATE = "https://www.kimonolabs.com/api/ondemand/%s?apikey=%s&kimmodify=%d";
    private static final String API_ID = "2fwithpm";
    private static final String API_KEY = "a506e75ca96092b0f73b0ff59c15abe6";
    private static final String KIMONO_URL = String.format(KIMONO_ENDPOINT_TEMPLATE, API_ID, API_KEY, 1);

    public KimonoRelease getLatestRelease() throws BiblioUpdateException {
        return getKimonoResponse().getKimonoRelease();
    }

    private KimonoResponse getKimonoResponse() throws BiblioUpdateException {
        String jsonResponse = null;
        try {
            final Gson gson = new GsonBuilder().create();
            jsonResponse = IOUtils.toString(new URL(KIMONO_URL).openStream());
            return gson.fromJson(jsonResponse, KimonoResponse.class);

        } catch (IOException e) {
            throw new BiblioUpdateException("Cannot establish connection to Kimonolabs server", e);
        } catch (JsonSyntaxException e) {
            throw new BiblioUpdateException(String.format("Unable to parse latest release details from Kimonolabs response: %s", jsonResponse), e);
        }
    }
}


text:
set -e  # exit on first error

echo
echo "Some of libwave's dependencies are available from a Personal Package" \
"Archive (PPA). You will be prompted to add the PPA to your sources list." \
"Note these packages are not supported by Ubuntu or the authors of libwave."
echo

# Echo the commands before running them
set -x
sudo add-apt-repository ppa:lkoppel/robotics-wavelab
sudo apt-get update

sudo apt-get install cmake \
  libboost-dev \
  libyaml-cpp-dev \
  libeigen3-dev \
  libpcl1.8-dev \
  libceres-wavelab-dev

set +x
echo
echo "Done installing libwave dependencies."


text:
'use strict';

var context = require('./context');
var input = require('./input');

function mapping(request, integrationRequest, next) {
    var ret = {};

    ret.context = context(request);

    Object.defineProperty(ret, 'util', {
        __proto__: null,
        value: {}
    });

    input(integrationRequest, request, function (err, data) {
        if (err) {
            return next(err);
        }

        ret.input = data;
        next(null, ret);
    });
}

module.exports = mapping;


text:
===============
The http Module
===============

The http module wraps the underlying raw HTTP request module (requests currently) to separate the job of exposing the API from the job of dealing with lower-level interfacing with the HTTP client in use today.

.. automodule:: pyrabbit.http
    :members:
    :undoc-members:


text:
namespace views {
class Separator;
class View;
}  // namespace views

namespace ash {

class BluetoothDetailedView;
class BluetoothDeviceListItemView;

// BluetoothDeviceListController implementation.
class ASH_EXPORT BluetoothDeviceListControllerImpl
    : public BluetoothDeviceListController {
 public:
  explicit BluetoothDeviceListControllerImpl(
      BluetoothDetailedView* bluetooth_detailed_view);
  BluetoothDeviceListControllerImpl(const BluetoothDeviceListControllerImpl&) =
      delete;
  BluetoothDeviceListControllerImpl& operator=(
      const BluetoothDeviceListControllerImpl&) = delete;
  ~BluetoothDeviceListControllerImpl() override;

 private:
  friend class BluetoothDeviceListControllerTest;

  // BluetoothDeviceListController:
  void UpdateBluetoothEnabledState(bool enabled) override;
  void UpdateDeviceList(
      const PairedBluetoothDevicePropertiesPtrs& connected,
      const PairedBluetoothDevicePropertiesPtrs& previously_connected) override;

  // Creates a sub-header with text represented by the |text_id| message ID when
  // |sub_header| is |nullptr|, otherwise uses the provided |sub_header|. The
  // used sub-header is then moved to index |index| within the device list and
  // returned.
  views::View* CreateSubHeaderIfMissingAndReorder(views::View* sub_header,
                                                  int text_id,
                                                  size_t index);

  // Creates and initializes a view for each of the device properties within
  // |device_property_list| if a view does not already exist, otherwise re-using
  // the existing view to avoid disrupting a11y. Each view will be reordered to
  // start at |index| and will be removed from |previous_views|. The index of
  // the position after the final view that was added is returned.
  size_t CreateViewsIfMissingAndReorder(
      const PairedBluetoothDevicePropertiesPtrs& device_property_list,
      base::flat_map<std::string, BluetoothDeviceListItemView*>* previous_views,
      size_t index);

  BluetoothDetailedView* const bluetooth_detailed_view_;

  bool is_bluetooth_enabled_ = false;
  base::flat_map<std::string, BluetoothDeviceListItemView*>
      device_id_to_view_map_;
  views::Separator* device_list_separator_ = nullptr;
  views::View* connected_sub_header_ = nullptr;
  views::View* no_device_connected_sub_header_ = nullptr;
  views::View* previously_connected_sub_header_ = nullptr;
};

}  // namespace ash

#endif  // ASH_SYSTEM_BLUETOOTH_BLUETOOTH_DEVICE_LIST_CONTROLLER_IMPL_H_


text:
<?php

namespace Symfony\Component\Form\Exception;

/**
 * Base ExceptionInterface for the Form component.
 *
 * @author Bernhard Schussek <bschussek@gmail.com>
 */
interface ExceptionInterface
{
}


text:
using SistemaMundoAnimal.Forms;
using SistemaMundoAnimal.Testes;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SistemaMundoAnimal {
    static class Program {

        public static FormPrincipal Form;

        /// <summary>
        /// Ponto de partida da aplicação.
        /// </summary>
        [STAThread]
        public static void Main(string[] args) {
            Teste.RodarTodosTestes();

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            Form = new FormPrincipal();

            Application.Run(Form);
        }
    }
}


text:
package com.cybernostics.nanorest.test;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;

import com.cybernostics.nanorest.example.api.v1.Greeting;

public final class MatchesGreeting extends BaseMatcher<Greeting> {
	private Greeting toCompare;

	public MatchesGreeting(Greeting toPut) {
		this.toCompare = toPut;
	}

	@Override
	public boolean matches(Object item) {
		if (item instanceof Greeting) {
			Greeting greeting = (Greeting) item;
			return greeting.getContent().equals(toCompare.getContent())&&
					greeting.getDescription().equals(toCompare.getDescription());
		}
		return false;
	}

	@Override
	public void describeTo(Description description) {
		description.appendText("match greeting without id");
	}

	public static MatchesGreeting matches(Greeting toMatch) {
		return new MatchesGreeting(toMatch);
	}

}


text:
from __future__ import unicode_literals

from django.db import models, migrations
from django.conf import settings

def default_categories(apps, schema_editor):
    Category = apps.get_model("spirit_category", "Category")

    if not Category.objects.filter(pk=settings.ST_TOPIC_PRIVATE_CATEGORY_PK).exists():
        private_category = Category.objects.create(
            title="Private",
            slug="private",
            is_private=True)

        # Check the user has the right setting,
        # if the migration is re-ran with a bad
        # setting again then a duplicated is created
        # and it can be manually removed later
        if private_category.pk != settings.ST_TOPIC_PRIVATE_CATEGORY_PK:
            raise ValueError(
                'ST_TOPIC_PRIVATE_CATEGORY_PK setting does not matches '
                'the private category. The expected value was: {pk}'.format(
                    pk=private_category.pk))

    # Create a dummy category in case
    # there are no categories other than
    # the Private one
    if len(Category.objects.all()[:2]) == 1:
        Category.objects.create(
            title="Uncategorized",
            slug="uncategorized")

class Migration(migrations.Migration):

    dependencies = [
        ('spirit_category', '0001_initial'),
    ]

    operations = [
        migrations.RunPython(default_categories),
    ]


text:
namespace Envoy {
namespace Extensions {
namespace HttpFilters {
namespace GrpcJsonTranscoder {

int64_t TranscoderInputStreamImpl::BytesAvailable() const { return buffer_->length() - position_; }

bool TranscoderInputStreamImpl::Finished() const { return finished_; }

} // namespace GrpcJsonTranscoder
} // namespace HttpFilters
} // namespace Extensions
} // namespace Envoy


text:
#import "MTRTestStorage.h"

@interface MTRTestStorage ()
@property (strong, nonatomic) NSMutableDictionary<NSString *, NSData *> * values;
@end

@implementation MTRTestStorage

- (nullable NSData *)storageDataForKey:(NSString *)key
{
    return _values[key];
}
- (BOOL)setStorageData:(NSData *)value forKey:(NSString *)key
{
    _values[key] = value;
    return YES;
}
- (BOOL)removeStorageDataForKey:(NSString *)key
{
    BOOL present = (_values[key] != nil);
    [_values removeObjectForKey:key];
    return present;
}

- (instancetype)init
{
    if (!(self = [super init])) {
        return nil;
    }

    _values = [[NSMutableDictionary alloc] init];
    if (_values == nil) {
        return nil;
    }

    return self;
}

@end


text:
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium.interactions;

/**
 * Interface representing basic touch screen operations.
 *
 * @deprecated Replaced by {@link Actions} and {@link PointerInput}.
 */
@Deprecated
public interface TouchScreen {

  /**
   * Allows the execution of single tap on the screen, analogous to click using a Mouse.
   *
   * @param where The location on the screen. Typically a {@link org.openqa.selenium.WebElement}
   */
  void singleTap(Coordinates where);

  /**
   * Allows the execution of the gesture 'down' on the screen. It is typically the first of a
   * sequence of touch gestures.
   *
   * @param x The x coordinate relative to the viewport
   * @param y The y coordinate relative to the viewport
   */
  void down(int x, int y);

  /**
   * Allows the execution of the gesture 'up' on the screen. It is typically the last of a sequence
   * of touch gestures.
   *
   * @param x The x coordinate relative to the viewport
   * @param y The y coordinate relative to the viewport
   */
  void up(int x, int y);

  /**
   * Allows the execution of the gesture 'move' on the screen.
   *
   * @param x The x coordinate relative to the viewport
   * @param y The y coordinate relative to the viewport
   */
  void move(int x, int y);

  /**
   * Creates a scroll gesture that starts on a particular screen location.
   *
   * @param where the location where the scroll starts, usually a {@link org.openqa.selenium.WebElement}.
   * @param xOffset The x offset to scroll
   * @param yOffset The y offset to scroll
   */
  void scroll(Coordinates where, int xOffset, int yOffset);

  /**
   * Allows the execution of double tap on the screen, analogous to double click using a Mouse.
   *
   * @param where The coordinates of the element to double tap
   */
  void doubleTap(Coordinates where);

  /**
   * Allows the execution of long press gestures.
   *
   * @param where The coordinate of the element to long press
   */
  void longPress(Coordinates where);

  /**
   * Allows the view to be scrolled by an x and y offset.
   *
   * @param xOffset The horizontal offset relative to the viewport
   * @param yOffset The vertical offset relative to the viewport
   */
  void scroll(int xOffset, int yOffset);

  /**
   * Sends a flick gesture to the current view.
   *
   * @param xSpeed The horizontal speed in pixels/second
   * @param ySpeed The vertical speed in pixels/second
   */
  void flick(int xSpeed, int ySpeed);

  /**
   * Allows the execution of flick gestures starting in a location's element.
   *
   * @param where The coordinate of the element to flick on
   * @param xOffset The x offset relative to the viewport
   * @param yOffset The y offset relative to the viewport
   * @param speed speed in pixels/second
   */
  void flick(Coordinates where, int xOffset, int yOffset, int speed);
}