/**********************************************************************************
*
* $Id: GradeMapping.java 105077 2012-02-24 22:54:29Z ottenhoff@longsight.com $
*
***********************************************************************************
*
* Copyright (c) 2005, 2006, 2007, 2008 The Sakai Foundation, The MIT Corporation
*
* Licensed under the Educational Community 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.opensource.org/licenses/ECL-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.sakaiproject.tool.gradebook;
import java.io.Serializable;
import java.util.*;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* A GradeMapping provides a means to convert between an arbitrary set of grades
* (letter grades, pass / not pass, 4,0 scale) and numeric percentages.
*
*/
public class GradeMapping implements Serializable, Comparable {
protected Log log = LogFactory.getLog(GradeMapping.class);
protected Long id;
protected int version;
protected Gradebook gradebook;
protected Map<String, Double> gradeMap;
private GradingScale gradingScale;
public GradeMapping() {
}
public GradeMapping(GradingScale gradingScale) {
setGradingScale(gradingScale);
gradeMap = new HashMap<String, Double>(gradingScale.getDefaultBottomPercents());
}
public String getName() {
return getGradingScale().getName();
}
/**
* Sets the percentage values for this GradeMapping to their default values.
*/
public void setDefaultValues() {
gradeMap = new HashMap<String, Double>(getDefaultBottomPercents());
}
/**
* Backwards-compatible wrapper to get to grading scale.
*/
public Map<String, Double> getDefaultBottomPercents() {
GradingScale gradingScale = getGradingScale();
if (gradingScale != null) {
return gradingScale.getDefaultBottomPercents();
} else {
Map<String, Double> defaultBottomPercents = new HashMap<String, Double>();
Iterator defaultValuesIter = getDefaultValues().iterator();
Iterator gradesIter = getGrades().iterator();
while (gradesIter.hasNext()) {
String grade = (String)gradesIter.next();
Double value = (Double)defaultValuesIter.next();
defaultBottomPercents.put(grade, value);
}
return defaultBottomPercents;
}
}
/**
*
* @return An (ordered) collection of the available grade values
*/
public Collection<String> getGrades() {
return getGradingScale().getGrades();
}
/**
*
* @return A List of the default grade values. Only used for backward
* compatibility to pre-grading-scale mappings.
*/
public List<Double> getDefaultValues() {
throw new UnsupportedOperationException("getDefaultValues called for GradeMapping " + getName() + " in Gradebook " + getGradebook());
}
/**
* Gets the percentage mapped to a particular grade.
*/
public Double getValue(String grade) {
return (Double) gradeMap.get(grade);
}
/**
* This algorithm is slow, since it checks each grade option, starting from
* the "top" (in this case an 'A'). We can make it faster by starting in the
* middle (as in a bubble sort), but since there are so few grade options, I
* think I'll leave it for now.
*
* @see org.sakaiproject.tool.gradebook.GradeMapping#getGrade(Double)
*/
public String getGrade(Double value) {
if(value == null) {
return null;
}
for (Iterator iter = getGrades().iterator(); iter.hasNext();) {
String grade = (String) iter.next();
Double mapVal = (Double) gradeMap.get(grade);
// If the value in the map is less than the value passed, then the
// map value is the letter grade for this value
if (mapVal != null && mapVal.compareTo(value) <= 0) {
return grade;
}
}
// As long as 'F' is zero, this should never happen.
return null;
}
////// Bean accessors and mutators //////
/**
* @return Returns the id.
*/
public Long getId() {
return id;
}
/**
* @param id
* The id to set.
*/
public void setId(Long id) {
this.id = id;
}
/**
* @return Returns the version.
*/
public int getVersion() {
return version;
}
/**
* @param version
* The version to set.
*/
public void setVersion(int version) {
this.version = version;
}
/**
* @return Returns the gradeMap.
*/
public Map<String, Double> getGradeMap() {
return gradeMap;
}
/**
* @param gradeMap
* The gradeMap to set.
*/
public void setGradeMap(Map<String, Double> gradeMap) {
this.gradeMap = gradeMap;
}
/**
* @return Returns the gradebook.
*/
public Gradebook getGradebook() {
return gradebook;
}
/**
* @param gradebook
* The gradebook to set.
*/
public void setGradebook(Gradebook gradebook) {
this.gradebook = gradebook;
}
public int compareTo(Object o) {
return getName().compareTo(((GradeMapping)o).getName());
}
public String toString() {
return new ToStringBuilder(this).
append(getName()).
append(id).toString();
}
/**
* Enable any-case input of grades (typically lowercase input
* for uppercase grades). Look for a case-insensitive match
* to the input text and if it's found, return the official
* version.
*
* @return The normalized version of the grade, or null if not found.
*/
public String standardizeInputGrade(String inputGrade) {
String standardizedGrade = null;
for (Iterator iter = getGrades().iterator(); iter.hasNext(); ) {
String grade = (String)iter.next();
if (grade.equalsIgnoreCase(inputGrade)) {
standardizedGrade = grade;
break;
}
}
return standardizedGrade;
}
/**
* @return the GradingScale used to define this mapping, or null if
* this is an old Gradebook which uses hard-coded scales
*/
public GradingScale getGradingScale() {
return gradingScale;
}
public void setGradingScale(GradingScale gradingScale) {
this.gradingScale = gradingScale;
}
}