<repo_name>paulgi/openmentor<file_sep>/src/test/java/uk/org/openmentor/report/constituents/ReportBarChartTest.java
package uk.org.openmentor.report.constituents;

import java.io.ByteArrayOutputStream;

import junit.framework.TestCase;

import uk.org.openmentor.businesslogic.BusinessLogicException;
import uk.org.openmentor.report.constituents.ReportBarChart;
import uk.org.openmentor.report.properties.ReportBarChartProperties;


public class ReportBarChartTest extends TestCase
{
  private ReportBarChart jcb_;
  
  protected void setUp() throws Exception
  {
    super.setUp();
    jcb_ = new ReportBarChart( new ReportBarChartProperties() );
  }

  protected void tearDown() throws Exception
  {
    super.tearDown();
    jcb_ = null;
  }

  public void testBarChart()
  {
    ByteArrayOutputStream out = null;
    
    assertTrue( jcb_ != null );
    
    try { 
    	out = new ByteArrayOutputStream();
    	out.reset();
    	jcb_.writeChartToStream(out, "image/jpeg");
    	out.reset();
    	jcb_.writeChartToStream(out, "image/png");
    } catch ( BusinessLogicException re ) {
      fail( "Report exception encountered when generating a buffered image " +
      		"of a Bar Chart" );
    }
    
    assertTrue( out.size() > 0 );
  }
}
<file_sep>/src/main/java/uk/org/openmentor/report/constituents/ReportClusteredBarChart.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.report.constituents;

import java.awt.Color;
import java.awt.Font;

import org.jCharts.axisChart.AxisChart;
import org.jCharts.chartData.AxisChartDataSet;
import org.jCharts.chartData.ChartDataException;
import org.jCharts.chartData.DataSeries;
import org.jCharts.types.ChartType;
import org.jCharts.chartData.interfaces.IAxisDataSeries;
import org.jCharts.properties.AxisProperties;
import org.jCharts.properties.ChartProperties;
import org.jCharts.properties.ClusteredBarChartProperties;
import org.jCharts.properties.LegendProperties;
import org.jCharts.properties.PropertyException;
//import org.jCharts.chartData.interfaces.*;
import org.jCharts.properties.util.ChartFont;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import uk.org.openmentor.report.properties.ReportClusteredBarChartProperties;

/**
 * Provides a clustered bar chart implementation using the jCharts
 * library.
 */
public class ReportClusteredBarChart extends ReportChart {

    /**
     * Our usual logger.
     */
    private static Log log = LogFactory.getLog(ReportClusteredBarChart.class);

    /**
     * Whether the chart is vertival.
     */
    private static final boolean DIRECTION_VERTICAL = false;

    /**
     * The chart data series.
     */
    private IAxisDataSeries dataSeries;

    /**
     * The chart legend properties.
     */
    private LegendProperties legendProperties;

    /**
     * The chart axis properties.
     */
    private AxisProperties axisProperties;

    /**
     * The other chart properties.
     */
    private ChartProperties chartProperties;

    /**
     * Constructs a clustered bar chart object using the properties
     * supplied.
     *
     * @param bcp The clustered bar chart properties that describe the
     *            details of the chart.
     */

    public ReportClusteredBarChart(final ReportClusteredBarChartProperties bcp) {
        this.dataSeries = null;
        this.legendProperties = null;
        this.axisProperties = null;
        this.chartProperties = null;

        try {
            createDataSeries(bcp);
            createChart(bcp);
        } catch (ChartDataException cde) {
            log.warn("CDE: " + cde.getMessage());
            // Need some suitable code in here to make sure we handle
            // exceptions during chart creation gracefully. Ideally we
            // need to create something as a placeholder for the chart
            // that also contains error information so that we can
            // continue to process and render the report request
        } catch (PropertyException pe) {
            log.warn("PE: " + pe.getMessage());
        }
    }

    /**
     * Describe <code>createDataSeries</code> method here.
     *
     * @param bcp an <code>OMClusteredBarChartProperties</code> value
     * @exception ChartDataException if an error occurs
     */
    private void createDataSeries(final ReportClusteredBarChartProperties bcp)
            throws ChartDataException {
        if (log.isDebugEnabled()) {
            log.debug("Creating dataSeries");
        }
        this.dataSeries = new DataSeries(bcp.getAxisLabels(),
                                         bcp.getXAxisTitle(),
                                         bcp.getYAxisTitle(),
                                         bcp.getChartTitle());
        this.dataSeries.addIAxisPlotDataSet
            (new AxisChartDataSet(convertData(bcp.getData()),
                                  bcp.getLegendLabels(),
                                  ReportChartUtils.generatePaints
                                  (bcp.getData().length),
                                  ChartType.BAR_CLUSTERED,
                                  new ClusteredBarChartProperties()));

    }

    /**
     * Describe <code>createChart</code> method here.
     *
     * @param bcp an <code>OMClusteredBarChartProperties</code> value
     * @exception ChartDataException if an error occurs
     * @exception PropertyException if an error occurs
     */
    private void createChart(final ReportClusteredBarChartProperties bcp)
            throws ChartDataException, PropertyException {
        setupProperties();
        this.chart = new AxisChart(this.dataSeries,
                                   this.chartProperties,
                                   this.axisProperties,
                                   this.legendProperties,
                                   bcp.getWidth(), bcp.getHeight());
        this.chart.renderWithImageMap();
    }

    private void setupProperties() {
        this.chartProperties = new ChartProperties();
        this.legendProperties = new LegendProperties();
        this.axisProperties = new AxisProperties(DIRECTION_VERTICAL);

        ChartFont axisScaleFont = new
            ChartFont(new Font("Arial", Font.PLAIN, 12), Color.black);
        this.axisProperties.getXAxisProperties().
            setScaleChartFont(axisScaleFont);
        this.axisProperties.getYAxisProperties().
            setScaleChartFont(axisScaleFont);

        ChartFont axisTitleFont = new
            ChartFont(new Font("Arial", Font.BOLD, 14), Color.black);
        this.axisProperties.getXAxisProperties().
            setAxisTitleChartFont(axisTitleFont);
        this.axisProperties.getYAxisProperties().
            setAxisTitleChartFont(axisTitleFont);

        ChartFont titleFont = new
            ChartFont(new Font("Arial", Font.BOLD, 14), Color.black);
        this.chartProperties.setTitleFont(titleFont);
        this.legendProperties.setFont(new Font("Arial", Font.PLAIN, 12));
    }
}
<file_sep>/src/main/java/uk/org/openmentor/web/ReportController.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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.
 * ========================================================================
 */
/*
 * Created on 21-Dec-2005
 *
 */
package uk.org.openmentor.web;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import uk.org.openmentor.report.ReportRenderEngine;
import uk.org.openmentor.report.constituents.ChartConstituent;
import uk.org.openmentor.report.constituents.ReportConstituent;
import uk.org.openmentor.model.DataBook;
import uk.org.openmentor.businesslogic.BusinessLogic;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

/**
 * Describe class <code>ReportController</code> here. Originally by
 * Hassan, adapted to fit in the Spring framework by Stuart.
 *
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 * @version 1.0
 */
public class ReportController implements Controller {

    public static final String  ATTRIB_REPORT_VEC   = "ReportVec";
    /*Define query string variables here for more reporting options*/
    private static final String PARAM_REQUEST_TYPE  = "RequestType";
    private static final String PARAM_REPORT_FOR    = "ReportFor";
    private static final String PARAM_ID            = "ID";
    private static final String PARAM_CHART_TYPE    = "ChartType";
    private static final String REQ_CHART_IMAGE     = "ChartImage";
    private static final String REQ_TABLE           = "Table";

        /** Used in SQL code to delimit comments. */
    private static final String SQL_COMMENT_SEPARATOR = "__SEP__";

    /**
     * The ModelAndView to be returned. This is an instance variable
     * because we need to return null if we are handling the view
     * directly, otherwise an object. This allows the decision about
     * what to return to be delegated to methods.
     */
    private ModelAndView mav = null;

    /**
     * Our usual logger.
     */
    private static Log log = LogFactory.getLog(ReportController.class);

    /**
     * Describe variable <code>businessLogic</code> here.
     */
    private BusinessLogic businessLogic = null;

    /**
     * Describe <code>setBusinessLogic</code> method here.
     *
     * @param theBusinessLogic a <code>BusinessLogic</code> value
     */
    public final void setBusinessLogic(final BusinessLogic theBusinessLogic) {
        this.businessLogic = theBusinessLogic;
    }

    /**
     * Describe <code>handleRequest</code> method here.
     *
     * @param request a <code>HttpServletRequest</code> value
     * @param response a <code>HttpServletResponse</code> value
     * @return a <code>ModelAndView</code> value
     * @exception ServletException if an error occurs
     * @exception IOException if an error occurs
     */
    public final ModelAndView handleRequest(final HttpServletRequest request,
                                            final HttpServletResponse response)
        throws ServletException, IOException {

        if (log.isDebugEnabled()) {
            log.debug("Handling ReportController request");
        }

        if (!request.getMethod().equals("GET")) {
            throw new ServletException("ReportController only supports"
                                       + " GET requests");
        }

        String requestType = request.getParameter(PARAM_REQUEST_TYPE);

        if (requestType == null) {
            throw new ServletException("Missing request type");
        }
        if (log.isDebugEnabled()) {
            log.debug("Request for chart type " + requestType);
        }
        try {
                DataBook dataBook = createDataBook(request, response);

            if (requestType.equals(REQ_CHART_IMAGE)) {
                createChartImage(request, response, dataBook);
            } else if (requestType.equals(REQ_TABLE)) {
                    createTableModel(request, response, dataBook);
            } else {
                throw new Exception("The request being processed didn't"
                                    + " have " + "a recognized type ("
                                    + requestType + ")");
            }
        } catch (Exception re) {
            throw new
                ServletException("Unable to continue generating the report"
                                 + "as a report exception was encountered: "
                                 + re.getClass().getName() + ", "
                                 + re.getMessage()
                                 + " at "
                                 + re.getStackTrace()[0].getFileName() + ":"
                                 + re.getStackTrace()[0].getLineNumber());
        }
        return mav;
    }

    /**
     * Creates a reportable DataBook according to the current request.
     * @param req                the request
     * @param resp                the response
     * @return                        a new (and completed) DataBook
     * @throws Exception        if something happens
     */
    private synchronized DataBook createDataBook(final HttpServletRequest req,
            final HttpServletResponse resp)
            throws Exception {

        String reportFor = req.getParameter(PARAM_REPORT_FOR);

        String stringID = req.getParameter(PARAM_ID);
        if (stringID == null) {
            throw new ServletException("Missing ID parameter"
                                       + " for ReportController");
        }

        String courseId = (String) req.getSession().getAttribute("course");
        if (courseId == null) {
            throw new Exception("Can't get session variable");
        }

        businessLogic.setRequestType(reportFor);
        businessLogic.setCourseId(courseId);
        businessLogic.setId(stringID);
        if (log.isDebugEnabled()) {
            log.debug("Set reportFor as " + reportFor
                      + " and stringId as " + stringID
                      + " in businessLogic");
        }
        DataBook dataBook = businessLogic.buildDataBook();
        return dataBook;
    }

    private Map<String, Object>
        convertToMap(final List<Object> data,
                     final List<String> categories) {
        Map<String, Object> values = new HashMap<String, Object>();
        int c = 0;
        for (Object o : data) {
        if (log.isDebugEnabled()) {
            log.debug("Converting " + categories.get(c)
                      + ", " + o.toString() + " to a map");
        }
            values.put(categories.get(c++), o);
        }
        return values;
    }

    /**
     * Creates a table model, basically putting together a set
     * of values that can be retrieved through the reporting
     * system through the expression language in JSTL.
     *
     * @param req                the request
     * @param resp                the response
     * @param dataBook        the DataBook
     * @throws Exception        if something bad happens
     */
    private void createTableModel(final HttpServletRequest req,
                                  final HttpServletResponse resp,
                                  final DataBook dataBook)
        throws Exception {
        Map<String, Object> model = new HashMap<String, Object>();
        List<String> categories = dataBook.getDataPoints();
        List actualComments = dataBook.getDataSeries("ActualComments");

        Map<String, List> commentValues = new HashMap<String, List>();
        int c = 0;
        for (Object comments: actualComments) {
            commentValues.put(categories.get(c++), (List<String>)comments);
        }

        List<Object> actualRange = dataBook.getDataSeries("ActualRange");
        List<Object> idealRange = dataBook.getDataSeries("IdealRange");
        List<Object> actualCounts = dataBook.getDataSeries("ActualCounts");
        List<Object> idealCounts = dataBook.getDataSeries("IdealCounts");

        model.put("categories", categories);
        model.put("actual", convertToMap(actualRange, categories));
        model.put("ideal", convertToMap(idealRange, categories));
        model.put("actualcounts", convertToMap(actualCounts, categories));
        model.put("idealcounts", convertToMap(idealCounts, categories));
        model.put("comments", commentValues);
        model.put("full", req.getParameter("full"));
        model.put("type", req.getParameter(PARAM_REPORT_FOR));
        model.put("id", req.getParameter(PARAM_ID));

        mav = new ModelAndView("report", model);
    }


    /**
     * Describe <code>createChartImage</code> method here.
     *
     * @param req a <code>HttpServletRequest</code> value
     * @param resp a <code>HttpServletResponse</code> value
     * @param dataBook a <code>DataBook</code> value
     * @exception Exception if an error occurs
     */
    private void createChartImage(final HttpServletRequest req,
                                  final HttpServletResponse resp,
                                  final DataBook dataBook)
        throws Exception {

        if (log.isDebugEnabled()) {
            log.debug("About to create chart image");
        }

        //calling to generate chart
        ReportRenderEngine rre = new
            ReportRenderEngine(req.getParameter(PARAM_CHART_TYPE),
                               dataBook);
        List<ReportConstituent> v = rre.renderRequest();

        ReportConstituent c = v.get(0);
        ChartConstituent chart = (ChartConstituent) c;
        chart.writeChartToStream(resp.getOutputStream(),
                                 ChartConstituent.JPEG_MIME_TYPE);
    }
}
<file_sep>/src/test/java/uk/org/openmentor/report/constituents/TextTest.java
package uk.org.openmentor.report.constituents;

import junit.framework.TestCase;
import uk.org.openmentor.report.constituents.Text;

public class TextTest extends TestCase
{
  private static final String INIT_TEXT = "Test text";
  
  private Text t1_;
  
  protected void setUp()
    throws Exception
  {
    super.setUp();
    t1_ = new Text( INIT_TEXT );
  }

  protected void tearDown()
    throws Exception
  {
    super.tearDown();
    t1_ = null;
  }

  public void testText()
  {
    assertTrue( t1_.getText().equals( INIT_TEXT ) ); 
  }
}
<file_sep>/src/main/java/uk/org/openmentor/businesslogic/impl/SQLWrapper.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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.
 * ========================================================================
 */
/*
 * Created on 13-Jan-2006
 *
 */
package uk.org.openmentor.businesslogic.impl;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


/**
 * Much manipulated (and I hope not badly screwed up) by <NAME>.
 * @author <NAME> - IET, The Open University
 */
public class SQLWrapper {

    /**
     * The database connection which, if not null, can be closed.
     */
    private Connection theConnection;

    /**
     * Our usual logger.
     */
    private static Log log = LogFactory.getLog(SQLWrapper.class);

    /**
     * Creates a new <code>SQLWrapper</code> instance, but privately!
     * So the effect is to stop the class being instantiated without
     * the full constructor.  See Bloch, page12.
     */
    private SQLWrapper() {
    }

    /**
     * Standard accessor.
     *
     * @return a <code>Connection</code> value
     */
    final Connection getConnection() {
        return theConnection;
    }

    /**
     * Our only useful constructor.  It does all the database
     * manipulation, which reflects how it is used in practice.
     *
     * @param driverClassName the JDBC class to use.
     * @param url the URL needed to access the database
     * @param username the name to use when accessing the database
     * @param password the password needed by the database.
     */
    public SQLWrapper(final String driverClassName, final String url,
                      final String username, final String password) {

        if (log.isDebugEnabled()) {
            log.debug("Setting jdbc connection parameters:"
                      + "driverClassName: " + driverClassName
                      + "; URL: " + url
                      + "; User: " + username
                      + "; Password: " + password);
        }
        try {
            Class.forName(driverClassName);
        } catch (Exception ex) {
            log.warn("Failed to find driver" + ex.getMessage());
        }
        try {
            theConnection = DriverManager.
                getConnection(url, username, password);

            // Allow long responses from GROUP_CONCAT
            String theSQL = "SET SESSION group_concat_max_len = 32000";
            PreparedStatement stmt = theConnection.prepareStatement(theSQL);
            stmt.executeQuery();

        } catch (Exception ex) {
            log.warn("Failed to connect to database " + ex.getMessage());
        }
    }

    /**
     * Release the resources and closeg the database connection.
     */
    public final void closeDB() {
        if (log.isDebugEnabled()) {
            log.debug("Closing database connection.");
        }
        try {
            theConnection.close();
        } catch (Exception ex) {
            log.warn("Unhandled exception " + ex.getMessage());
        }
    }

    /**
     * The <code>sqlTeacherCommentsCount</code> method seems to
     * prepare SQL to count the number of comments for a given tutor.
     *
     * @param courseId the ID of the course
     * @param teacherId a <code>String</code> value
     * @return a <code>String</code> value
     */
    final String sqlTeacherCommentsCount(final String courseId,
                                         final String teacherId) {
        StringBuilder b = new StringBuilder();
        b.append(" SELECT tbl_submission_tutors.tutor_id AS ObjectID,");
        b.append(" LEFT(tbl_comment_classes.category_name, 1) as CCode,");
        b.append(" tbl_submission.grade AS Grade,");
        b.append(" COUNT(DISTINCT tbl_comment.text) as CommentsCounter,");
        b.append(" GROUP_CONCAT(DISTINCT tbl_comment.text");
        b.append(" SEPARATOR '__SEP__') AS Comments");
        b.append(" FROM tbl_submission_tutors");
        b.append(" JOIN tbl_submission");
        b.append(" ON tbl_submission_tutors.submission_id = tbl_submission.id");
        b.append(" JOIN tbl_assignment ON");
        b.append(" tbl_assignment.id = tbl_submission.assignment_id");
        b.append(" JOIN tbl_comment");
        b.append(" ON tbl_submission.id = tbl_comment.submission_id");
        b.append(" JOIN tbl_comment_classes");
        b.append(" ON tbl_comment.id = tbl_comment_classes.comment_id");
        b.append(" WHERE tbl_assignment.course_id = '");
        b.append(courseId);
        b.append("'");
        b.append(" AND (tbl_submission_tutors.tutor_id in ('");
        b.append(teacherId);
        b.append("') )");
        b.append(" GROUP BY ObjectID, CCode, Grade");
        b.append(" ORDER BY ObjectID, Ccode, Grade");
        String theSQL = b.toString();
        if (log.isDebugEnabled()) {
            log.debug("Generating SQL: " + theSQL);
        }
        return theSQL;
    }

    /**
     * Describe <code>sqlStudentCommentsCount</code> method here.
     *
     * @param courseId  the course identifier
     * @param studentId a <code>String</code> value
     * @return a <code>String</code> value
     */
    final String sqlStudentCommentsCount(final String courseId,
                                         final String studentId) {
        StringBuilder b = new StringBuilder();
        b.append(" SELECT tbl_submission_students.student_id AS ObjectID,");
        b.append(" LEFT(tbl_comment_classes.category_name, 1) as CCode,");
        b.append(" tbl_submission.grade AS Grade,");
        b.append(" COUNT(DISTINCT tbl_comment.text) as CommentsCounter,");
        b.append(" GROUP_CONCAT(DISTINCT tbl_comment.text");
        b.append(" SEPARATOR '__SEP__') AS Comments");
        b.append(" FROM tbl_submission_students");
        b.append(" JOIN tbl_submission");
        b.append(" ON tbl_submission_students.submission_id");
        b.append(" = tbl_submission.id");
        b.append(" JOIN tbl_assignment ON");
        b.append(" tbl_assignment.id = tbl_submission.assignment_id");
        b.append(" JOIN tbl_comment");
        b.append(" ON tbl_submission.id = tbl_comment.submission_id");
        b.append(" JOIN tbl_comment_classes");
        b.append(" ON tbl_comment.id = tbl_comment_classes.comment_id");
        b.append(" WHERE tbl_assignment.course_id = '");
        b.append(courseId);
        b.append("'");
        b.append(" AND (tbl_submission_students.student_id in ('");
        b.append(studentId);
        b.append("' ) )");
        b.append(" GROUP BY ObjectID, CCode, Grade");
        b.append(" ORDER BY ObjectID, CCode, Grade");
        String theSQL = b.toString();
        if (log.isDebugEnabled()) {
            log.debug("Generating SQL: " + theSQL);
        }
        return theSQL;
    }

    /**
     * The <code>sqlCourseCommentsCount</code> method returns the sql
     * statement which will fetch the total number of comments for
     * each assignment for a specified course here.
     *
     * @param courseId the ID of the course
     * @return the SQL
     */
    final String sqlCourseCommentsCount(final String courseId) {
        StringBuilder b = new StringBuilder();
        b.append(" SELECT tbl_assignment.course_id AS ObjectID,");
        b.append(" LEFT(tbl_comment_classes.category_name, 1) as CCode,");
        b.append(" tbl_submission.grade AS Grade,");
        b.append(" COUNT(DISTINCT tbl_comment.text) as CommentsCounter,");
        b.append(" GROUP_CONCAT(DISTINCT tbl_comment.text");
        b.append(" SEPARATOR '__SEP__') AS Comments");
        b.append(" FROM tbl_submission JOIN tbl_assignment ON");
        b.append(" tbl_assignment.id = tbl_submission.assignment_id");
        b.append(" JOIN tbl_comment");
        b.append(" ON tbl_submission.id = tbl_comment.submission_id");
        b.append(" JOIN tbl_comment_classes");
        b.append(" ON tbl_comment.id = tbl_comment_classes.comment_id");
        b.append(" WHERE tbl_assignment.course_id = '");
        b.append(courseId);
        b.append("'");
        b.append(" GROUP BY ObjectID, CCode, Grade");
        b.append(" ORDER BY ObjectID, CCode, Grade");
        String theSQL = b.toString();
        if (log.isDebugEnabled()) {
            log.debug("Generating SQL: " + theSQL);
        }
        return theSQL;
    }

    /**
     * The <code>sqlAssignmentCommentsCount</code> method returns the
     * sql statement to fetch number of comments for each assignment
     * file for a specified assignment.
     *
     * @param assignmentID an <code>int</code> value
     * @return a <code>String</code> value
     */
    final String sqlAssignmentCommentsCount(final int assignmentID) {
        StringBuilder b = new StringBuilder();
        b.append(" SELECT tbl_assignment.id,");
        b.append(" Left(tbl_BalesCategory.BalesCategoryCode, 1) as CCode,");
        b.append(" Count(tbl_comment_classes.comment_id) as CommentsCounter");
        b.append(" FROM tbl_assignment");
        b.append(" Left Outer JOIN tbl_submission");
        b.append(" ON tbl_assignment.id = tbl_submission.assignment_id");
        b.append(" Left Outer JOIN tbl_submission_students");
        b.append(" ON tbl_submission.id");
        b.append(" = tbl_submission_students.submission_id");
        b.append(" Left Outer JOIN tbl_comment");
        b.append(" ON tbl_submission_students.submission_id");
        b.append(" = tbl_comment.submission_id");
        b.append(" INNER JOIN tbl_comment_classes");
        b.append(" ON tbl_comment.id = tbl_comment_classes.comment_id ");
        b.append(" Right Outer Join tbl_BalesCategory");
        b.append(" on tbl_comment_classes.category_name");
        b.append(" = tbl_BalesCategory.BalesCategoryCode");
        b.append(" WHERE     (tbl_assignment.id  in (");
        b.append(assignmentID);
        b.append(" ) )");
        b.append(" GROUP BY Left(tbl_BalesCategory.BalesCategoryCode, 1)");
        b.append(" ORDER BY Left(tbl_BalesCategory.BalesCategoryCode, 1)");
        String theSQL = b.toString();
        if (log.isDebugEnabled()) {
            log.debug("Generating SQL: " + theSQL);
        }
        return theSQL;
    }

    /**
     * Finds the list of categories to be used. This should really come from
     * the evaluator bean. Actually, this code really generates the SQL to
     * find the list of categories to be used, rather than actually doing it.
     *
     * @return the SQL statement.
     */
    final String sqlCommentsCategoriesList() {
        StringBuilder b = new StringBuilder();
        b.append(" Select DISTINCT Left(BalesCategoryCode, 1) As CCode ");
        b.append(" From tbl_BalesCategory ");
        b.append(" Order by Left(BalesCategoryCode, 1) ");
        String theSQL = b.toString();
        if (log.isDebugEnabled()) {
            log.debug("Generating SQL: " + theSQL);
        }
        return theSQL;
    }

    /**
     * The <code>sqlTeacherAssignmentList</code> method is used for
     * the first page of the report to show list of tutors and total
     * number of assignments they've marked. Feedback column will be
     * calculated using GetCommentCount method.
     *
     * @param selectedTeacherIDs a <code>String</code> value
     * @return the SQL statement.
     */
    final String sqlTeacherAssignmentList(final String selectedTeacherIDs) {
        StringBuilder b = new StringBuilder();
        b.append(" Select tbl_Tutor.ID,");
        b.append(" Concat(tbl_Tutor.first_name, ' ' , tbl_Tutor.last_name)");
        b.append(" as TeacherName,");
        b.append(" Count(Submission_ID) as TotalAssignments, tbl_Tutor.Org_ID");
        b.append(" from tbl_Tutor");
        b.append(" Inner Join tbl_submission_tutors");
        b.append(" on tbl_Tutor.ID = tbl_submission_tutors.tutor_id");
        if (!selectedTeacherIDs.equals("")) {
            b.append(" Where tbl_Tutor.ID in (");
            b.append(selectedTeacherIDs);
            b.append(")");
        }
        b.append(" Group by tbl_Tutor.ID");
        b.append(" Order by tbl_Tutor.ID");
        String theSQL = b.toString();
        if (log.isDebugEnabled()) {
            log.debug("Generating SQL: " + theSQL);
        }
        return theSQL;
    }

    /**
     * This function to be used for first page of report to show list
     * of assignment, course title and total number of student
     * submission for each assignment. Feedback column will be
     * calculated using GetCommentCount function
     *
     * @param selectedAssignmentIDs a <code>String</code> value
     * @return a <code>String</code> value
     */
    final String sqlAssignmentCourseList(final String selectedAssignmentIDs) {
        StringBuilder b = new StringBuilder();
        b.append("Select tbl_assignment.id,");
        b.append(" tbl_assignment.assignment_title, tbl_assignment.course_id,");
        b.append(" Course_Title, Count(tbl_submission.id) as TotalAssignments");
        b.append(" From tbl_submission");
        b.append(" Inner Join tbl_assignment");
        b.append(" on tbl_submission.assignment_id = tbl_assignment.id");
        b.append(" Inner Join tbl_Course");
        b.append(" on tbl_assignment.course_id = tbl_Course.ID");
        if (!selectedAssignmentIDs.equals("")) {
            b.append(" Where tblAssignment.AssignmentID in (");
            b.append(selectedAssignmentIDs);
            b.append(")");
        }
        b.append(" Group By tbl_assignment.id");
        String theSQL = b.toString();
        if (log.isDebugEnabled()) {
            log.debug("Generating SQL: " + theSQL);
        }
        return theSQL;
    }

    /**
     * Describe <code>sqlCourseAssignmentList</code> method here. This
     * function to be used for first page of report to show list of
     * courses, course title and total number of student submission
     * for each course. Feedback column will be calculated using
     * GetCommentCount function.
     *
     * @param selectedCourseIDs a <code>String</code> value
     * @return a <code>String</code> value
     */
    final String sqlCourseAssignmentList(final String selectedCourseIDs) {
        StringBuilder b = new StringBuilder();
        b.append(" Select tbl_assignment.course_id,");
        b.append(" Course_Title, Count(tbl_submission.id) as TotalAssignments");
        b.append(" From tbl_submission");
        b.append(" Inner Join tbl_assignment");
        b.append(" on tbl_submission.assignment_id = tbl_assignment.id");
        b.append(" Inner Join tbl_Course");
        b.append(" on tbl_assignment.course_id = tbl_Course.ID");
        if (!selectedCourseIDs.equals("")) {
            b.append(" Where tbl_Course.ID in (");
            b.append(selectedCourseIDs);
            b.append(")");
        }
        b.append(" Group By tbl_Course.ID");
        String theSQL = b.toString();
        if (log.isDebugEnabled()) {
            log.debug("Generating SQL: " + theSQL);
        }
        return theSQL;
    }

    /**
     * The <code>sqlStudentAssignmentList</code> method builds an SQL
     * query to be used for first page of report to show list of
     * tutors and total number of assignments they've marked. Feedback
     * column will be calculated using GetCommentCount functionhere.
     *
     * @param selectedStudentIDs a <code>String</code> value
     * @return a <code>String</code> value
     */
    final String sqlStudentAssignmentList(final String selectedStudentIDs) {
        StringBuilder b = new StringBuilder();
        b.append(" Select tbl_Student.ID,");
        b.append(" Concat(tbl_Student.first_name, ' ' ,tbl_Student.last_name)");
        b.append(" as StudentName,");
        b.append(" Count(tbl_submission_students.submission_id)");
        b.append(" as TotalAssignments, tbl_Student.Org_ID");
        b.append(" From tbl_student");
        b.append(" Inner Join tbl_submission_students");
        b.append(" on tbl_Student.ID = tbl_submission_students.student_id");
        if (!selectedStudentIDs.equals("")) {
            b.append(" Where tbl_student.ID in (");
            b.append(selectedStudentIDs);
            b.append(")");
        }
        b.append(" Group by tbl_student.ID");
        b.append(" Order by tbl_student.ID");
        String theSQL = b.toString();
        if (log.isDebugEnabled()) {
            log.debug("Generating SQL: " + theSQL);
        }
        return theSQL;
    }

    /**
     * The <code>sqlAvergageMarks</code> method this function returns
     * average marks SQL for the given function parameters.
     *
     * @param marksFor a <code>String</code> value
     * @param selectedID a <code>String</code> value
     * @return a <code>String</code> value
     */
    final String sqlAvergageMarks(final String marksFor,
                                  final String selectedID) {
        StringBuilder b = new StringBuilder();
        // all strings end the same way (after the if statement).
        if (marksFor.equals("tutor")) {
            b.append(" Select Round(Avg(tbl_submission_students.score_int),2)");
            b.append(" as AverageMarks");
            b.append(" From tbl_submission_tutors");
            b.append(" Inner join tbl_submission_students");
            b.append(" on tbl_submission_tutors.submission_id");
            b.append(" = tbl_submission_students.submission_id");
            b.append(" Where  tbl_submission_tutors.tutor_id in (");
        } else if (marksFor.equals("assignment")) {
            b.append(" Select Round(Avg(tbl_submission_students.Score_Int),2)");
            b.append(" as AverageMarks");
            b.append(" From tbl_submission_students");
            b.append(" Inner join tbl_submission on");
            b.append(" tbl_submission_students.submission_id");
            b.append(" = tbl_submission.id");
            b.append(" Where  tbl_submission.assignment_id in (");
        } else if (marksFor.equals("course")) {
            b.append(" Select Round(Avg(tbl_submission_students.Score_Int),2)");
            b.append(" as AverageMarks ");
            b.append(" From tbl_assignment");
            b.append(" Inner join tbl_submission on tbl_assignment.id");
            b.append(" = tbl_submission.assignment_id");
            b.append(" Inner join tbl_submission_students");
            b.append(" on tbl_submission.id");
            b.append(" = tbl_submission_students.submission_id");
            b.append(" Where  tbl_assignment.course_id in (");
        } else {        //otherwise for students
            b.append(" Select Round(Avg(Score_Int),2) as AverageMarks ");
            b.append(" From tbl_submission_students");
            b.append(" Where  Student_ID in (");
        }
        b.append(selectedID);
        b.append(")");
        String theSQL = b.toString();
        if (log.isDebugEnabled()) {
            log.debug("Generating SQL: " + theSQL);
        }
        return theSQL;
    }

    /**
     * The <code>sqlFullComments</code> method generates the SQL to
     * extract categories and corresponding list of comments.
     *
     * @param marksFor a <code>String</code> value
     * @param selectedID a <code>String</code> value
     * @return a <code>String</code> value
     */
    final String sqlFullComments(final String marksFor,
                                 final String selectedID) {
        StringBuilder b = new StringBuilder();
        // All queries begin and end in the same way.
        b.append(" Select left(category_name,1) as CategoryName,");
        b.append(" tbl_comment.text as CommentText");
        if (marksFor.equals("tutor")) {
            b.append(" From tbl_submission_tutors");
            b.append(" Inner join tbl_comment");
            b.append(" on tbl_submission_tutors.submission_id");
            b.append(" = tbl_comment.submission_id");
            b.append(" Inner join tbl_comment_classes");
            b.append(" on tbl_comment.id = tbl_comment_classes.comment_id");
            b.append(" Where tbl_submission_tutors.tutor_id in (");
        } else if (marksFor.equals("assignment")) {
            b.append(" From tbl_submission_students");
            b.append(" Inner join tbl_comment");
            b.append(" on tbl_submission_students.submission_id");
            b.append(" = tbl_comment.submission_id");
            b.append(" Inner join tbl_comment_classes");
            b.append(" on tbl_comment.id = tbl_comment_classes.comment_id");
            b.append(" Where  tbl_submission.assignment_id in (");
        } else if (marksFor.equals("course")) {
            b.append(" From tbl_assignment");
            b.append(" Inner join tbl_submission");
            b.append(" on tbl_assignment.id = tbl_submission.assignment_id");
            b.append(" Inner join tbl_comment");
            b.append(" on tbl_submission.id = tbl_comment.submission_id");
            b.append(" Inner join tbl_comment_classes");
            b.append(" on tbl_comment.id = tbl_comment_classes.comment_id");
            b.append(" Where  tbl_assignment.course_id in (");
        } else { //otherwise for students
            b.append(" From tbl_submission_students");
            b.append(" Inner join tbl_comment");
            b.append(" on tbl_submission_stu.submission_id");
            b.append(" = tbl_comment.submission_id");
            b.append(" Inner join tbl_comment_classes");
            b.append(" on tbl_comment.id = tbl_comment_classes.comment_id");
            b.append(" Where  Student_ID in (");
        }
        b.append(selectedID);
        b.append(")");
        b.append(" order by left(category_name,1)");
        String theSQL = b.toString();
        if (log.isDebugEnabled()) {
            log.debug("Generating SQL: " + theSQL);
        }
        return theSQL;
    }
}
<file_sep>/src/test/java/uk/org/openmentor/report/properties/ClusteredBarChartPropertiesTest.java
package uk.org.openmentor.report.properties;

import junit.framework.TestCase;

import uk.org.openmentor.report.properties.ReportClusteredBarChartProperties;
import uk.org.openmentor.report.properties.ReportChartProperties;

public class ClusteredBarChartPropertiesTest extends TestCase
{
  private static final String   TEST_INIT_X_AXIS_LABEL  = "test x-axis label";
  private static final String   TEST_INIT_Y_AXIS_LABEL  = "test y-axis label";
  private static final String[] TEST_INIT_LEGEND_LABELS = 
  	{ "test legend label", "test legend label 2" };
  private static final int[][]  TEST_INIT_DATA          = 
  	{ { 4, 5, 6 }, { 1, 2, 3 } };
  private static final String[] TEST_INIT_AXIS_LABELS   = { "X", "Y", "Z" };

  private static final String   TEST_X_AXIS_LABEL  = "x-axis label";
  private static final String   TEST_Y_AXIS_LABEL  = "y-axis label";
  private static final String[] TEST_LEGEND_LABELS = 
  	{ "legend label", "legend label 2" };
  private static final int[][]  TEST_DATA          = 
  	{ { 1, 2, 3 }, { 4, 5, 6 } };
  private static final String[] TEST_AXIS_LABELS   = { "AX", "BY", "CZ" };
  
  private ReportClusteredBarChartProperties p1_;
  private ReportClusteredBarChartProperties p2_;
  
  protected void setUp() throws Exception
  {
    super.setUp();
    p1_ = 
      new ReportClusteredBarChartProperties( TEST_INIT_X_AXIS_LABEL, 
                                         TEST_INIT_Y_AXIS_LABEL, 
                                         TEST_INIT_LEGEND_LABELS,
                                         TEST_INIT_DATA, 
                                         TEST_INIT_AXIS_LABELS,
                                         ChartPropertiesTest.TEST_INIT_TITLE,
                                         ChartPropertiesTest.TEST_INIT_WIDTH,
                                         ChartPropertiesTest.TEST_INIT_HEIGHT );
    p2_ = new ReportClusteredBarChartProperties();
  }

  protected void tearDown() throws Exception
  {
    super.tearDown();
    p1_ = null;
    p2_ = null;
  }

  public void testProperties()
  {   
    assertTrue( p1_.getXAxisTitle().equals( TEST_INIT_X_AXIS_LABEL ) );
    assertTrue( p1_.getYAxisTitle().equals( TEST_INIT_Y_AXIS_LABEL ) );
    assertTrue( p1_.getLegendLabels().equals( TEST_INIT_LEGEND_LABELS ) );
    assertTrue( p1_.getData() == TEST_INIT_DATA );
    assertTrue( p1_.getAxisLabels() == TEST_INIT_AXIS_LABELS );
    assertTrue( 
      p1_.getChartTitle().equals( ChartPropertiesTest.TEST_INIT_TITLE ) 
    );
    assertTrue( p1_.getWidth() == ChartPropertiesTest.TEST_INIT_WIDTH );
    assertTrue( p1_.getHeight() == ChartPropertiesTest.TEST_INIT_HEIGHT );    

    
    assertTrue( 
      p2_.getXAxisTitle().equals( ReportBarChartProperties.DEFAULT_X_AXIS_TITLE ) 
    );
    assertTrue( 
      p2_.getYAxisTitle().equals( ReportBarChartProperties.DEFAULT_Y_AXIS_TITLE ) 
    );
    assertTrue( 
      p2_.getLegendLabels().equals( 
        ReportClusteredBarChartProperties.DEFAULT_LEGEND_LABELS 
      ) 
    );
    assertTrue( p2_.getData() == ReportClusteredBarChartProperties.DEFAULT_DATA );
    assertTrue( p2_.getAxisLabels() 
                == ReportClusteredBarChartProperties.DEFAULT_AXIS_LABELS );
    assertTrue( 
      p2_.getChartTitle().equals( ReportChartProperties.DEFAULT_CHART_TITLE ) 
    );
    assertTrue( p2_.getWidth() == ReportChartProperties.DEFAULT_CHART_WIDTH );
    assertTrue( p2_.getHeight() == ReportChartProperties.DEFAULT_CHART_HEIGHT );  
    
    p2_.setAxisLabels( TEST_AXIS_LABELS );
    p2_.setData( TEST_DATA );
    p2_.setLegendLabels( TEST_LEGEND_LABELS );
    p2_.setXAxisTitle( TEST_X_AXIS_LABEL );
    p2_.setYAxisTitle( TEST_Y_AXIS_LABEL );
    assertTrue( p2_.getAxisLabels().equals( TEST_AXIS_LABELS ) );
    assertTrue( p2_.getData() == TEST_DATA );
    assertTrue( p2_.getLegendLabels().equals( TEST_LEGEND_LABELS ) );
    assertTrue( p2_.getXAxisTitle().equals( TEST_X_AXIS_LABEL ) );
    assertTrue( p2_.getYAxisTitle().equals( TEST_Y_AXIS_LABEL ) );                                          
  }
}
<file_sep>/src/main/java/uk/org/openmentor/dao/hibernate/AssignmentDAOHibernate.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.dao.hibernate;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import uk.org.openmentor.dao.AssignmentDAO;
import uk.org.openmentor.model.Assignment;
import uk.org.openmentor.model.Submission;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

/**
 * This class interacts with Spring and Hibernate to save and
 * retrieve Course and Assignment objects.
 *
 * @author <NAME>
 */
public class AssignmentDAOHibernate extends HibernateDaoSupport
    implements AssignmentDAO {

    /**
     * Our usual log.
     */
    private static Log log = LogFactory.getLog(AssignmentDAOHibernate.class);

    /**
     * Return the known assignments for a given course.
     *
     * @param courseId the given course
     * @return a <code>List</code> of the assignments
     */
    public final List getAssignments(final String courseId) {
        return getHibernateTemplate().
            find("from Assignment where course_id = ? order by assignmentTitle",
                 courseId);
    }

    /**
     * Get the <code>Assignment</code> corresponding to a given Id.
     *
     * @param id the given id
     * @return the corresponding <code>Assignment</code>.
     */
    public final Assignment getAssignment(final int id) {
        return (Assignment) getHibernateTemplate().
            get(Assignment.class, new Integer(id));
    }

    /**
     * Save a given <code>Assignment</code>.
     *
     * @param ass the <code>Assignment</code> to save.
     */
    public final void saveAssignment(final Assignment ass) {
        getHibernateTemplate().saveOrUpdate(ass);
        if (log.isDebugEnabled()) {
            log.debug("Assignment id set to: " + ass.getId());
        }
    }

    /**
     * Delete a given <code>Assignment</code>.
     *
     * @param id the <code>Assignment</code> to delete.
     */
    public final void removeAssignment(final int id) {
        Assignment ass = (Assignment) getHibernateTemplate()
            .load(Assignment.class, new Integer(id));
        getHibernateTemplate().delete(ass);
        if (log.isDebugEnabled()) {
            log.debug("Deleted assignment" + ass.getAssignmentTitle());
        }
    }

    /**
     * Get the <code>Submission</code> corresponding to a given id.
     *
     * @param id the given id.
     * @return the corresponding <code>Submission</code>.
     */
    public final Submission getSubmission(final int id) {
        return (Submission) getHibernateTemplate().
            get(Submission.class, new Integer(id));
    }
}
<file_sep>/src/main/java/uk/org/openmentor/web/AnalysisController.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.web;

import java.util.Map;
import java.util.HashMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import uk.org.openmentor.service.AssignmentManager;
import uk.org.openmentor.evaluator.Evaluator;
import uk.org.openmentor.model.Submission;

/**
 * Describe class <code>AnalysisController</code> here.
 *
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 * @version 1.0
 */
public class AnalysisController implements Controller {

    /**
     * Our usual logger.
     */
    private static Log log = LogFactory.getLog(AnalysisController.class);

    /**
     * The <code>mgr</code> is normally set by Spring.
     */
    private AssignmentManager mgr = null;

    /**
     * The <code>evaluator</code> actually does the work, and is
     * normally set by Spring.
     *
     */
    private Evaluator evaluator = null;

    /**
     * Standard mutator eg for use by Spring.
     *
     * @param assignmentMgr the required manager.
     */
    public final void setAssignmentManager(final AssignmentManager
                                           assignmentMgr) {
        this.mgr = assignmentMgr;
    }

    /**
     * Standard mutator eg as used by Spring.
     *
     * @param newEvaluator an <code>Evaluator</code> value
     */
    public final void setEvaluator(final Evaluator newEvaluator) {
        this.evaluator = newEvaluator;
    }

    /**
     * Describe <code>handleRequest</code> method here.
     *
     * @param request a <code>HttpServletRequest</code> value
     * @param response a <code>HttpServletResponse</code> value
     * @return a <code>ModelAndView</code> value
     * @exception Exception if an error occurs
     */
    public final ModelAndView handleRequest(final HttpServletRequest request,
                                            final HttpServletResponse response)
        throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("Entering 'handleRequest' method.");
        }

        String commentlimit = request.getParameter("show");
        if (commentlimit == null) {
            commentlimit = "3";
        }
        if (commentlimit.equals("all")) {
            commentlimit = "1000";
        }
        if (log.isDebugEnabled()) {
            log.debug("commentlimit is " + commentlimit);
        }
        String submissionId = request.getParameter("id");
        if (submissionId == null) { // real trouble if true
            submissionId = "0";     // only useful for testing
        }
        if (log.isDebugEnabled()) {
            log.debug("Parameter: submission = " + submissionId);
        }
        Submission submission = mgr.getSubmission(submissionId);
        // Can't do the next bit if we are testing
        if (log.isDebugEnabled() && !submissionId.equals("0")) {
            log.debug("\nSubmission parameter: id = " + submission.getId()
                      + "\nSubmission parameter: grade = "
                      + submission.getGrade()
                      + "\nSubmission parameter: students = "
                      + submission.getStudents()
                      + "\nSubmission parameter: tutors = "
                      + submission.getTutors()
                      + "\nSubmission parameter: type = "
                      + submission.getType());
        }
        // The order here is influenced by the need to test without
        // doing too much mocking; but maybe it is better to fail in
        // these null cases and show there is a problem?
        Map<String, Object> model = new HashMap<String, Object>();
        model.put("submission", submission);
        model.put("commentlimit", commentlimit);
        if (submission != null) {
            Object result = evaluator.getFeedback(submission);
            model.put("analysis", result);
        }

        return new ModelAndView("analysis", "model", model);
    }
}
<file_sep>/src/test/java/uk/org/openmentor/analyzer/AnalyzerTest.java
package uk.org.openmentor.analyzer;

import java.util.HashSet;
import java.util.Set;

import java.io.File;
import java.io.InputStream;
import java.io.FileInputStream;

import org.hibernate.SessionFactory;
import org.hibernate.Session;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import uk.org.openmentor.analyzer.Analyzer;
import uk.org.openmentor.service.AssignmentManager;

import uk.org.openmentor.dao.BaseDAOTestCase;
import uk.org.openmentor.model.Assignment;
import uk.org.openmentor.model.Submission;

public class AnalyzerTest extends BaseDAOTestCase {
	protected ApplicationContext ctx = null;
	private Analyzer analyzer = null;
	private SessionFactory sf = null;
	private AssignmentManager mgr = null;
 
	private static Log log = LogFactory.getLog(AnalyzerTest.class);
	
	public AnalyzerTest() {
        String[] paths = {"/WEB-INF/applicationContext.xml"};
        ctx = new ClassPathXmlApplicationContext(paths);
    }
	
	public void setUp() throws Exception {
		sf = (SessionFactory) ctx.getBean("sessionFactory");
		mgr = (AssignmentManager) ctx.getBean("assignmentManager");
	}
	
	protected void startTransaction() throws Exception{
		Session s = sf.openSession();
		TransactionSynchronizationManager.bindResource(sf, new SessionHolder(s));
	}
	
	protected void endTransaction() throws Exception{
		SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sf);
		Session s = holder.getSession();
		s.flush();
		TransactionSynchronizationManager.unbindResource(sf);
		SessionFactoryUtils.releaseSession(s, sf);
	}
	
	protected void tearDown() throws Exception {
		super.tearDown();
		sf = null;
		mgr = null;
	}
	
	public void testAnalyzer() throws Exception {
		startTransaction();
		Assignment ass = new Assignment();
		ass.setAssignmentTitle("New assignment");
		ass.setCourseId("CM2006");
		ass.setSubmissions(new HashSet<Submission>());
		
		File file = new File("src/test/resources/test1.doc");
		InputStream input = new FileInputStream(file);
		Set<String> students = new HashSet<String>();
		Set<String> tutors = new HashSet<String>();
		students.add("M0000001");
		tutors.add("00900001");
		analyzer = (Analyzer) ctx.getBean("analyzer");
		Submission sub = analyzer.addSubmission("1", students, 
                                                        tutors, "Good", input);

		ass.getSubmissions().add(sub);
		mgr.saveAssignment(ass);
		
		if (log.isDebugEnabled()) {
			log.debug("New submission added: "+sub.getId());
		}
		
		if (log.isDebugEnabled()) {
			log.debug("Attempting to delete submission: "+sub.getId());
		}

		// For some reason, this is not cascading
                String id = Integer.toString(ass.getId());
		ass = mgr.getAssignment(id);
		mgr.removeAssignment(id);
		endTransaction();
 	}
}
<file_sep>/src/test/java/uk/org/openmentor/web/LoginWebTestCase.java
package uk.org.openmentor.web;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * The class <code>LoginWebTestCase</code> currently test much of
 * the functionality of the completed application, concentrating on
 * simple things and security aspects.
 *
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 * @version $Id$
 */
public class LoginWebTestCase extends BaseWebTestCase {

    private static Log log = LogFactory.getLog(LoginWebTestCase.class);

    /**
     * Creates a new <code>LoginWebTestCase</code> instance.
     * @param name a <code>String</code> value
     */
    public LoginWebTestCase(String name) {
        super(name);
    }

    /**
       If we go directly to the login page, we expect a correct login
       to pass to the index page, but with the user name set
       correctly.
    **/
    public void testLoginPage() {
        loginAsUser();
        assertTitleEquals(titlePrefix + "Welcome");
        assertTextPresent(username);
    }

    /**
       If we go directly to the login page, we expect a correct login
       to pass to the index page, but with the user name set
       correctly.
    **/
    public void testAdminLogin() {
        loginAsAdmin();
        assertTitleEquals(titlePrefix + "Welcome");
        assertTextPresent(adminName);
    }

    /**
     * If we give an incorrect user id or password we should be told
     * about it and invited to try agian.
     */
    public void testWrongPassword() {
        beginAt("/signin.jsp");
        loginAction(username,"junk");
        assertTextPresent("problem with your login");
        assertLinkPresentWithText("try again");
        clickLinkWithText("try again");
        assertFormPresent("loginForm");
    }
    /**
     * The <code>testForceLoginAsUser</code> method checks that an
     * attempt to access a protected page will force redirection to
     * the login page, but will eventually get to the requested page.
     *
     */
    public void testForceLoginAsUser() {
        beginAt("/");
        assertLinkPresentWithText("Choose course");
        clickLinkWithText("Choose course");
        //We should be forced to the login page.
        assertTitleEquals(titlePrefix + "Sign in to Open Mentor");
        loginAction(username,password);
        // And the go back to the page we requested.
        assertTextPresent("Choose a course");
    }
    /**
     * The <code>testForceLoginAsUser</code> method checks that an
     * attempt to access a protected page will force redirection to
     * the login page, but next time round, will allow acccess.  TODO
     * - I can't even check I get a 403 exception.  Need to sort out
     * logging?
     */
    public void testForceLoginAsAdmin() {
        loginAsUser();
        //Try to access a forbidden resource
        assertLinkPresentWithText("Site index");
        try {
            clickLinkWithText("Site index");
            fail("Should have got a 403 error");
        } catch (Exception e) {
            log.error("Found " + e);
            // We expect a 403 exception
        }
        /*        // Expect a complaint and redirection
        assertTextPresent("don't have an appropriate permission");
        clickLinkWithText("signin");
        // Now login with the correct credentials
        loginAction(adminName,adminPassword);
        // And try again
        clickLinkWithText("Site index");
        // It is OK now
        assertTextPresent("Site Index"); */
    }
}

<file_sep>/src/main/java/uk/org/openmentor/model/mock/MockSubmission.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.model.mock;

import java.util.HashSet;
import java.util.Set;

import uk.org.openmentor.model.Comment;
import uk.org.openmentor.model.Submission;


/**
 * I'm still learning to write mocks; so let me start with a very
 * naive one, which I nevertheless believe will be useful.  It simply
 * provides access to two properly structured comments.
 *
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 * @version 1.0
 */
public class MockSubmission extends Submission{

    public static final Submission getSubmission1(Integer grade) {
        Set<Comment> comments = new HashSet<Comment>();
        for (int i = 0; i < 16; i++) {
            comments.add(MockComment.getComment(i));
        }
        Submission s = new Submission();
        s.setComments(comments);
        s.setGrade(grade.toString());
        return s;
    }

    public static final Submission getSubmission2(Integer grade) {
        Set<Comment> comments = new HashSet<Comment>();
        for (int i = 0; i < 11; i++) {
            comments.add(MockComment.getComment2(i));
        }
        Submission s = new Submission();
        s.setComments(comments);
        s.setGrade(grade.toString());
        return s;
    }

    public static final Submission getSubmission3(Integer grade) {
        Set<Comment> comments = new HashSet<Comment>();
        for (int i = 0; i < 20; i++) {
            comments.add(MockComment.getComment3(i));
        }
        Submission s = new Submission();
        s.setComments(comments);
        s.setGrade(grade.toString());
        return s;
    }
}
<file_sep>/src/conf/jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/hibernate_openmentor
jdbc.username=sa
jdbc.password=

jdbc.courseinfo.driverClassName=com.mysql.jdbc.Driver
jdbc.courseinfo.url=jdbc:mysql://localhost/courseinfo
jdbc.courseinfo.username=sa
jdbc.courseinfo.password=
<file_sep>/src/main/java/uk/org/openmentor/report/properties/ReportBarChartProperties.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.report.properties;

/**
 * Provides a facility for setting general bar chart properties in a charting
 * library independent fashion.
 *
 * @author OpenMentor team
 */

public class ReportBarChartProperties extends ReportChartProperties {

    /**
     * The default title to use for the x-axis of the associated bar chart.
     */
    public static final String   DEFAULT_X_AXIS_TITLE = "Default x-axis title";

    /**
     * The default title to use for the y-axis of the associated bar chart.
     */
    public static final String   DEFAULT_Y_AXIS_TITLE = "Default y-axis title";

    /**
     * The default legend label to describe the data series of the
     * associated bar chart.
     */
    public static final String   DEFAULT_LEGEND_LABEL = "Default legend label";

    /**
     * The default array of data to plot on the associated bar chart.
     */
    public static final int[]    DEFAULT_DATA         = {1, 2, 3};

    /**
     * The default tick mark labels to use on the axis of the
     * associated bar chart.
     */
    public static final String[] DEFAULT_AXIS_LABELS  = {"A", "B", "C"};

    /**
     * The title to use for the x-axis of the associated bar chart.
     */
    private String   xAxisTitle;

    /**
     * The title to use for the y-axis of the associated bar chart.
     */
    private String   yAxisTitle;

    /**
     * The legend label to describe the data series of the associated
     * bar chart.
     */
    private String   legendLabel;

    /**
     * The array of data to plot on the associated bar chart.
     */
    private int[]    data;

    /**
     * The tick mark labels to use on the axis of the associated bar
     * chart.
     */
    private String[] axisLabels;

  /**
   * Creates an instance of the property class using user supplied values. Note
   * that values must also be supplied for the base OMChartProperties instance
   * as well.
   *
   * @param newXAxisTitle  the title to use for the x-axis
   * @param newYAxisTitle  the title to use for the y-axis
   * @param newLegendLabel the label to use to describe the data series
   * @param newData        the array of data to plot
   * @param newAxisLabels  the tick mark labels to use on the axis
   * @param chartTitle     the title to use for the associated chart
   * @param width          the width to use when plotting the chart
   * @param height         the height to use when plotting the chart
   */

    public ReportBarChartProperties(final String newXAxisTitle,
                                final String newYAxisTitle,
                                final String newLegendLabel,
                                final int[] newData,
                                final String[] newAxisLabels,
                                final String chartTitle,
                                final int width,
                                final int height) {
        super(chartTitle, width, height);
        this.xAxisTitle = newXAxisTitle;
        this.yAxisTitle = newYAxisTitle;
        this.legendLabel = newLegendLabel;
        this.data = newData;
        this.axisLabels = newAxisLabels;
    }

    /**
     * Creates an instance of the property class with dummy values. Caller must
     * set values for a sensible chart to be produced
     */

    public ReportBarChartProperties() {
        this(DEFAULT_X_AXIS_TITLE, DEFAULT_Y_AXIS_TITLE,
             DEFAULT_LEGEND_LABEL, DEFAULT_DATA,
             DEFAULT_AXIS_LABELS, ReportChartProperties.DEFAULT_CHART_TITLE,
             ReportChartProperties.DEFAULT_CHART_WIDTH,
             ReportChartProperties.DEFAULT_CHART_HEIGHT);
    }

    /**
     * Gets the title to use for the x-axis for the associated chart.
     *
     * @return String with the x-axis title to use
     */
    public final String getXAxisTitle() {
        return this.xAxisTitle;
    }

    /**
     * Gets the title to use for the y-axis for the associated chart.
     *
     * @return String with the y-axis title to use
     */
    public final String getYAxisTitle() {
        return this.yAxisTitle;
    }

    /**
     * Gets the legend label to use with the plotted data set in the associated
     * chart.
     *
     * @return String with the legend label to use
     */
    public final String getLegendLabel() {
        return this.legendLabel;
    }

    /**
     * Gets the axis labels used to indicate the relevance of each
     * bar. Note that there should be one label for each data point in
     * the data array
     *
     * @return Array of strings with the labels
     */
    public final String[] getAxisLabels() {
        return this.axisLabels;
    }

    /**
     * Gets the data to use to plot the associated bar chart.
     *
     * @return Array containing the data
     */
    public final int[] getData() {
        return this.data;
    }

    /**
     * Sets the title to use for the x-axis in the associated chart.
     *
     * @param newXAxisTitle String with the title to use for the x-axis
     */
    public final void setXAxisTitle(final String newXAxisTitle) {
        this.xAxisTitle = newXAxisTitle;
    }

    /**
     * Sets the title to use for the y-axis iin the associated chart.
     *
     * @param newYAxisTitle String with the title to use for the y-axis
     */
    public final void setYAxisTitle(final String newYAxisTitle) {
        this.yAxisTitle = newYAxisTitle;
    }

    /**
     * Sets the legend label to use for the plotted data set in the associated
     * chart.
     *
     * @param newLegendLabel String with the legend label to use for
     *                       the data set
     */
    public final void setLegendLabel(final String newLegendLabel) {
        this.legendLabel = newLegendLabel;
    }

    /**
     * Sets the labels to use to indicate the meaning of each bar in the
     * associated chart.
     *
     * @param newAxisLabels The labels to use with the plot
     */
    public final void setAxisLabels(final String[] newAxisLabels) {
        this.axisLabels = newAxisLabels;
    }

    /**
     * Sets the data values to use to plot each bar of the associated chart.
     *
     * @param newData The data values to use to plot each bar
     */
    public final void setData(final int[] newData) {
        this.data = newData;
    }
}

<file_sep>/src/main/java/uk/org/openmentor/service/AssignmentManager.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.service;

import java.util.List;

import uk.org.openmentor.model.Assignment;
import uk.org.openmentor.model.Submission;

/**
 * This interface is a web-friendly interface specifying how we use
 * Assignments.  the methods are similar to the corresponding ones in
 * the model package, but the corresponding parameters are strings.
 *
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 * @version 1.0
 */
public interface AssignmentManager {

    /**
     * Describe <code>getAssignments</code> method here.
     *
     * @param courseId a <code>String</code> value
     * @return a <code>List</code> value
     */
    List getAssignments(String courseId);

    /**
     * Describe <code>getAssignment</code> method here.
     *
     * @param assId a <code>String</code> value
     * @return an <code>Assignment</code> value
     */
    Assignment getAssignment(String assId);

    /**
     * Describe <code>saveAssignment</code> method here.
     *
     * @param ass an <code>Assignment</code> value
     */
    void saveAssignment(Assignment ass);

    /**
     * Describe <code>removeAssignment</code> method here.
     *
     * @param assId a <code>String</code> value
     */
    void removeAssignment(String assId);

    /**
     * Describe <code>getSubmission</code> method here.
     *
     * @param id a <code>String</code> value
     * @return a <code>Submission</code> value
     */
    Submission getSubmission(String id);
}
<file_sep>/src/main/java/uk/org/openmentor/report/constituents/ReportBarChart.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.report.constituents;

import java.awt.Paint;
import java.awt.Color;
import java.awt.Font;

import org.jCharts.axisChart.AxisChart;
import org.jCharts.chartData.AxisChartDataSet;
import org.jCharts.chartData.ChartDataException;
import org.jCharts.chartData.DataSeries;
import org.jCharts.chartData.interfaces.IAxisDataSeries;
import org.jCharts.types.ChartType;
import org.jCharts.properties.AxisProperties;
import org.jCharts.properties.BarChartProperties;
import org.jCharts.properties.ChartProperties;
import org.jCharts.properties.LegendProperties;
import org.jCharts.properties.PropertyException;
import org.jCharts.properties.util.ChartFont;

import uk.org.openmentor.report.properties.ReportBarChartProperties;
import uk.org.openmentor.report.constituents.ReportChart;

/**
 * Provides a bar chart implementation that makes use of the jCharts library
 * 
 * @author OpenMentor team
 */

class ReportBarChart extends ReportChart {
    private static final boolean DIRECTION_VERTICAL = false;

    private IAxisDataSeries dataSeries_;

    private LegendProperties legendProperties_;

    private AxisProperties axisProperties_;

    private ChartProperties chartProperties_;

    /**
     * Constructs a bar chart object using the properties supplied as an
     * argument
     * 
     * @param bcp
     *            The bar chart properties that describe the details of the
     *            chart
     */

    public ReportBarChart(ReportBarChartProperties bcp) {
        dataSeries_ = null;
        legendProperties_ = null;
        axisProperties_ = null;
        chartProperties_ = null;

        try {
            createDataSeries(bcp);
            createChart(bcp);
        } catch (ChartDataException cde) {
            // Need some suitable code in here to make sure we handle exceptions
            // during chart creation gracefully. Ideally we need to create
            // something
            // as a placeholder for the chart that also contains error
            // information
            // so that we can continue to process and render the report request
        } catch (PropertyException pe) {
            // As above - or combine to one
        }
    }

    private void createDataSeries(ReportBarChartProperties bcp)
            throws ChartDataException {
        dataSeries_ = new DataSeries(bcp.getAxisLabels(), bcp.getXAxisTitle(),
                bcp.getYAxisTitle(), bcp.getChartTitle());
        dataSeries_.addIAxisPlotDataSet(new AxisChartDataSet(
                new double[][] { convertData(bcp.getData()) },
                new String[] { bcp.getLegendLabel() },
                new Paint[] { Color.BLACK }, ChartType.BAR,
                new BarChartProperties()));

    }

    private void createChart(ReportBarChartProperties bcp)
            throws ChartDataException, PropertyException {
        setupProperties();
        this.chart = new AxisChart(dataSeries_, chartProperties_,
                                   axisProperties_, legendProperties_,
                                   bcp.getWidth(), bcp.getHeight());
        this.chart.renderWithImageMap();
    }

    private void setupProperties() {
        chartProperties_ = new ChartProperties();
        legendProperties_ = new LegendProperties();
        axisProperties_ = new AxisProperties(DIRECTION_VERTICAL);

        ChartFont axisScaleFont = new ChartFont(new Font("Arial", Font.PLAIN,
                12), Color.black);
        axisProperties_.getXAxisProperties().setScaleChartFont(axisScaleFont);
        axisProperties_.getYAxisProperties().setScaleChartFont(axisScaleFont);

        ChartFont axisTitleFont = new ChartFont(
                new Font("Arial", Font.BOLD, 14), Color.black);
        axisProperties_.getXAxisProperties().setAxisTitleChartFont(
                axisTitleFont);
        axisProperties_.getYAxisProperties().setAxisTitleChartFont(
                axisTitleFont);

        ChartFont titleFont = new ChartFont(new Font("Arial", Font.BOLD, 14),
                Color.black);
        chartProperties_.setTitleFont(titleFont);

        legendProperties_.setFont(new Font("Arial", Font.PLAIN, 12));
    }
}
<file_sep>/src/main/java/uk/org/openmentor/businesslogic/impl/SQLBusinessLogic.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.businesslogic.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.List;
import java.util.Iterator;
import java.util.HashMap;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import uk.org.openmentor.businesslogic.BusinessLogic;
import uk.org.openmentor.businesslogic.BusinessLogicException;
import uk.org.openmentor.evaluator.EvaluationScheme;
import uk.org.openmentor.model.DataBook;
import uk.org.openmentor.model.DataBookImpl;


/* Further big revisions by Stuart. It's still not brilliantly
 * structured, but at least the functionality is now more or
 * less complete. Note that we no longer do different reporting
 * calculations for different kinds of report, more or less
 * everything now at least looks similar.
 */

/**
 * Implementation of the business logic component, incorporated
 * as a Spring component, and with all the calculations of ideal
 * values done properly.
 *
 * @author <NAME> and <NAME>
 */
public class SQLBusinessLogic implements BusinessLogic {

    /** Used in SQL code to delimit comments. */
    private static final String SQL_COMMENT_SEPARATOR = "__SEP__";

    /** Logger for log4j. */
    private static Log log = LogFactory.getLog(SQLBusinessLogic.class);

    /** Number of categories. */
    private static final int CATEGORY_COUNT = 4;

    /**
     * Current request type, to find out if the request is for a student,
     * teacher, course or an assignment. */
    private String requestType;

    /**
     * ID of student, teacher, course or assignment for whom report is
     * requested.
     */
    private String id;

    /** The current course identifier. */
    private String courseId;

    /** A list of the comment categories. */
    private List commentsCategories;

    /** A DataBook to store data for later display. */
    private DataBook dataBook;

    /** A reference to the SQLWrapper which handles the queries. */
    private SQLWrapper sqlwObj;

    /** The EvaluationScheme, set through dependency injection. */
    private EvaluationScheme evaluationScheme = null;

    /**
     * The java JDBC class name to use when accessing the database in
     * SQLWrapper.  It will be set by Spring and is just used to
     * initialise the SQLWrapper object.
     */
    private String driverClassName;

    /**
     * The url to use when accessing the database in SQLWrapper.  It
     * will be set by Spring and is just used to initialise the
     * SQLWrapper object.
     */
    private String url;

    /**
     * The name to use when accessing the database in SQLWrapper.  It
     * will be set by Spring and is just used to initialise the
     * SQLWrapper object.
     */
    private String username;

    /**
     * The password to use when accessing the database in SQLWrapper.
     * It will be set by Spring and is just used to initialise the
     * SQLWrapper object.
     */

    private String password;

    /**
     * Set the DriverClassName value.
     * @param newDriverClassName The new DriverClassName value.
     */
    public final void setDriverClassName(final String newDriverClassName) {
        this.driverClassName = newDriverClassName;
    }

    /**
     * Set the Url value.
     * @param newUrl The new Url value.
     */
    public final void setUrl(final String newUrl) {
        this.url = newUrl;
    }

    /**
     * Set the Username value.
     * @param newUsername The new Username value.
     */
    public final void setUsername(final String newUsername) {
        this.username = newUsername;
    }

    /**
     * Set the Password value.
     * @param newPassword The new Password value.
     */
    public final void setPassword(final String newPassword) {
        this.password = newPassword;
    }

    /** Constructs a new component for calculating the data for
     * charting.
     */
    public SQLBusinessLogic() {
        setCategoryCounts();
    }

    /**
     * Constructs a new component for calculating the data for
     * charting.
     *
     * @param newRequestType        the request type
     */
    public SQLBusinessLogic(final String newRequestType) {
        this();
        setRequestType(newRequestType);
    }

    /**
     * Setter for the request type.
     * @param newRequestType        the request type
     */
    public final void setRequestType(final String newRequestType) {
        this.requestType = newRequestType;
    }

    /**
     * Setter for the course identifier.
     * @param newCourseId           the course identifier
     */
    public final void setCourseId(final String newCourseId) {
        this.courseId = newCourseId;
    }

    /**
     * Getter for the course identifier.
     * @return                      the course identifier
     */
    public final String getCourseId() {
        return this.courseId;
    }

    /**
     * Setter for the identifier of the object to calculate
     * chart data for.
     * @param newId                 the object identifier
     */
    public final void setId(final String newId) {
        this.id = newId;
    }

    /**
     * Setter for the evaluation scheme.
     * @param newEvaluationScheme   the evaluation scheme
     */
    public final void setEvaluationScheme(final EvaluationScheme
                                            newEvaluationScheme) {
        this.evaluationScheme = newEvaluationScheme;
    }

    /**
     * Initialises the category counts.
     */
    private void setCategoryCounts() {
        this.commentsCategories = new ArrayList(CATEGORY_COUNT);
    }

    /**
     * Builds a DataBook for charting purposes.
     * @return  the DataBook instance
     * @throws BusinessLogicException if there is a calculation problem
     */
    public final DataBook buildDataBook() throws BusinessLogicException {
        // If possible this should use Hibernate and correct the
        // database problems.
        if (log.isDebugEnabled()) {
            log.debug("Building a plain databook");
        }

        dataBook = new DataBookImpl();
        sqlwObj = new SQLWrapper(driverClassName, url, username, password);

        //getting list of Bales categories from the database
        commentsCategories = getCommentsCategories();
        dataBook.setDataPoints(commentsCategories);

        //calling the appropriate function to prepare data
        //adding the actual comments to data collection
        calculateCommentsCounts(dataBook, commentsCategories);

        sqlwObj.closeDB();
        return dataBook;
    }

    /**
     * Returns a List of comment categories.
     * @return      the List of comment categories
     */
    private List getCommentsCategories() {
        List categories = evaluationScheme.getCategories();
        return categories;
    }

    /**
     * This method should store per-category values for the actual
     * comments selected according to the passed IDs, which may actually
     * vary in type according to the objects concerned. This is a key
     * part of the analysis system. The results should be stored
     * in the actualRangeValues vector.
     * @param databook a <code>DataBook</code> value
     * @param categories a <code>List</code> value
     * @exception BusinessLogicException if an error occurs
     */
    private synchronized void calculateCommentsCounts(final DataBook databook,
                                                      final List categories)
        throws BusinessLogicException {
        // use RequestType and ID values to cover all the cases
        // i.e. students, teachers, courses and assignments these
        // should be percentage values get ideal range from database
        // based on average marks

        List<Float> actualRangeValues = new ArrayList<Float>();
        List<Float> idealRangeValues = new ArrayList<Float>();
        List<String> actualCommentTexts = new ArrayList<String>();

        ResultSet rs = null;
        String theSQL = "";

        if (log.isDebugEnabled()) {
            log.debug("Setting actual comments for request type "
                      + requestType + " and id " + id);
        }

        if (this.requestType == null) {
            throw new BusinessLogicException("Missing ReportFor parameter");
        } else if (this.requestType.equals("tutor")) {
            theSQL = sqlwObj.sqlTeacherCommentsCount(courseId, id);
        } else if (this.requestType.equals("student")) {
            theSQL = sqlwObj.sqlStudentCommentsCount(courseId, id);
        } else if (this.requestType.equals("course")) {
            theSQL = sqlwObj.sqlCourseCommentsCount(id);
        } else if (this.requestType.equals("assignment")) {
            theSQL = sqlwObj.
                sqlAssignmentCommentsCount(Integer.parseInt(this.id));
        } else {
            throw new BusinessLogicException("Invalid ReportFor parameter:- "
                                             + this.requestType);
        }

        if (log.isDebugEnabled()) {
            log.debug("Calculating comment counts.  SQL query: " + theSQL);
        }

        // Build a band proportion table from the evaluationScheme
        // Note this isn't robust; it depends on their being a
        // declared proportion for each of the categories; better
        // would have been to set that proportion to zero.
        List bands = evaluationScheme.getBands();
        Map<String, Map> bandMap = new HashMap<String, Map>();
        for (Iterator i = bands.iterator(); i.hasNext();) {
            String band = (String) i.next();
            Map bandProportions = evaluationScheme.getBandProportions(band);
            bandMap.put(band, bandProportions);
            if (log.isDebugEnabled()) {
                log.debug("Adding band proportions for " + band);
            }
        }

        try {
            int totalComments = 0;
            Map<String, Number> actualCounts = new HashMap<String, Number>();
            Map<String, Number> idealCounts = new HashMap<String, Number>();
            Map<String, String> actualComments = new HashMap<String, String>();

            PreparedStatement stmt = sqlwObj.
                getConnection().prepareStatement(theSQL);
            rs = stmt.executeQuery();

            // getting records count to generate dynamic array later on
            while (rs.next()) {
                int theComments = rs.getInt("CommentsCounter");
                String theGrade = rs.getString("Grade");
                String theCategory = rs.getString("CCode");
                String theTexts = rs.getString("Comments");
                if (log.isDebugEnabled()) {
                    log.debug("Found: " + theComments + " x " + theCategory);
                }

                String texts = (String) actualComments.get(theCategory);
                if (texts == null) {
                    texts = theTexts;
                } else {
                    texts = texts + SQL_COMMENT_SEPARATOR + theTexts;
                }
                actualComments.put(theCategory, texts);

                float previousActual = 0;
                Number value = (Number) actualCounts.get(theCategory);
                if (value != null) {
                    previousActual = value.intValue();
                }
                actualCounts.put(theCategory,
                                 new Float(theComments + previousActual));
                totalComments = totalComments + theComments;

                Map gradeMap = (Map) bandMap.get(theGrade);

                List cats = evaluationScheme.getCategories();
                for (Iterator i = cats.iterator(); i.hasNext();) {
                    String cat = (String) i.next();
                    Number idealProportion = (Number) gradeMap.get(cat);

                    float previousIdeal = 0;
                    value = (Number) idealCounts.get(cat);
                    if (value != null) {
                        previousIdeal = value.intValue();
                    }
                    float add = idealProportion.floatValue() * theComments;
                    idealCounts.put(cat, new Float(add + previousIdeal));
                }
            }

            stmt.close();

            if (totalComments == 0) {
                totalComments = 1;
            }

            // Go through the categories in order, independent of the
            // SQL query results - added code to handle what happens
            // when there are no comments for a category.
            for (Iterator i = categories.iterator(); i.hasNext();) {
                String category = (String) i.next();
                Float actual = (Float) actualCounts.get(category);
                Float ideal = (Float) idealCounts.get(category);
                if (actual == null) {
                    actual = new Float(0);
                }
                if (ideal == null) {
                    ideal = new Float(0);
                }
                actualRangeValues.add(actual);
                idealRangeValues.add(ideal);
                actualCommentTexts.add(actualComments.get(category));
            }
            // Now take this list apart and build it as a list of
            // lists of comments - as we used to do in
            // RepoprtController.
            List<List<String>> commentsList = new ArrayList<List<String>>();
            for (Iterator i = actualCommentTexts.iterator(); i.hasNext();) {
                String comments = (String) i.next();
                List<String> values;
                if (comments == null) {
                    values = new ArrayList<String>();
                } else {
                    values = Arrays.asList(comments.
                                           split(SQL_COMMENT_SEPARATOR));
                }
                commentsList.add(values);
            }

            dataBook.setDataSeries("ActualRange",
                                    calculatePercentages(actualRangeValues));

            // Calculation of ideal values is a bit more complex,
            // largely because nobody really thought this through
            // until now. It should be a case of handling this,
            // probably by grade as well as by comment category.

            // Calculate the ideal counts from the percentage, and round
            List idealPercentages = calculatePercentages(idealRangeValues);
            List<Integer> normalizedIdealValues = new ArrayList<Integer>();
            for (Iterator i = idealPercentages.iterator(); i.hasNext();) {
                Number next = ((Number) i.next());
                float value = next.floatValue() * totalComments / 100;
                normalizedIdealValues.add(new Integer(Math.round(value)));
            }

            // Just round all the counts
            List<Integer> normalizedActualValues = new ArrayList<Integer>();
            for (Iterator i = actualRangeValues.iterator(); i.hasNext();) {
                Number next = ((Number) i.next());
                normalizedActualValues.add(
                        new Integer(Math.round(next.floatValue())));
            }
            dataBook.setDataSeries("IdealRange", idealPercentages);
            dataBook.setDataSeries("ActualCounts", normalizedActualValues);
            dataBook.setDataSeries("IdealCounts", normalizedIdealValues);

            //            dataBook.setDataSeries("ActualComments", actualCommentTexts);
            dataBook.setDataSeries("ActualComments", commentsList);

        } catch (SQLException ex) {
            if (log.isDebugEnabled()) {
                log.debug("SQL exception: " + ex.getMessage());
            }
            throw new BusinessLogicException("Database error: "
                                             + ex.getMessage());
        }

    }

    /**
     * Converts a List from values to percentages, rounded to Integer
     * objects.
     * @param theValues the List of values to be converted
     * @return  a new List of percentages
     */
    private List<Integer> calculatePercentages(final List theValues) {
        float theTotal = 0;
        List<Integer> result = new ArrayList<Integer>();

        for (Iterator i = theValues.iterator(); i.hasNext();) {
            Number next = ((Number) i.next());
            if (next == null) {
                continue;
            }
            theTotal += next.floatValue();
        }

        // To save us from division by zero, if there are no comments
        // pretend we have some. Note that all subdivisions must be
        // zero, so 0 / 1 = 0 and we get 0% in all categories.

        if (theTotal == 0) {
            theTotal = 1;
        }

        for (Iterator i = theValues.iterator(); i.hasNext();) {
            Number next = ((Number) i.next());
            float theValue = next.floatValue();
            result.add(new Integer(Math.round((theValue * 100) / theTotal)));
        }
        return result;
    }
}
<file_sep>/src/test/java/uk/org/openmentor/businesslogic/BusinessLogicTest.java
package uk.org.openmentor.businesslogic;


import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import org.hibernate.SessionFactory;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import junit.framework.TestCase;
import uk.org.openmentor.businesslogic.BusinessLogic;
import uk.org.openmentor.model.DataBook;

public class BusinessLogicTest extends TestCase {
	
    protected ApplicationContext ctx = null;
    
    private SessionFactory sf = null;

    private BusinessLogic logic = null;
    
    public BusinessLogicTest() {
    	String[] paths = {"/WEB-INF/applicationContext.xml"};
    	ctx = new ClassPathXmlApplicationContext(paths);
    }
    
    protected void setUp() throws Exception {
        super.setUp();
        sf = (SessionFactory) ctx.getBean("sessionFactory");
        logic = (BusinessLogic) ctx.getBean("businessLogic");
    }

    protected void startTransaction() throws Exception{
        Session s = sf.openSession();
        TransactionSynchronizationManager.
            bindResource(sf, new SessionHolder(s));
    }
    
    protected void endTransaction() throws Exception{
        SessionHolder holder = (SessionHolder) 
            TransactionSynchronizationManager.getResource(sf);
        Session s = holder.getSession();
        s.flush();
        TransactionSynchronizationManager.unbindResource(sf);
        SessionFactoryUtils.releaseSession(s, sf);
    }
    
    protected void tearDown() throws Exception {
        super.tearDown();
        sf = null;
        logic = null;
    }
    
    public final void testTeachers() throws Exception {
        startTransaction();
        logic.setCourseId("CM2006");
        logic.setRequestType("tutor");
        logic.setId("00900001");
        
        // Do the work
        DataBook book = logic.buildDataBook();
        
        // Check we get something
        assertNotNull(book);
        assertTrue(book.getDataPoints().size() > 0);
        endTransaction();
    }
    
    public final void testStudents() throws Exception {
        startTransaction();
        logic.setCourseId("CM2006");
        logic.setRequestType("student");
        logic.setId("M0000002");
        
        // Do the work
        DataBook book = logic.buildDataBook();
        
        // Check we get something
        assertNotNull(book);
        assertTrue(book.getDataPoints().size() > 0);
        endTransaction();
    }
    
    public final void testCourses() throws Exception {
        startTransaction();
        logic.setCourseId("CM2006");
        logic.setRequestType("course");
        logic.setId("CM2006");
        
        // Do the work
        DataBook book = logic.buildDataBook();
        
        // Check we get something
        assertNotNull(book);
        assertTrue(book.getDataPoints().size() > 0);
        endTransaction();
    }
}
<file_sep>/src/main/java/uk/org/openmentor/extractor/impl/Utils.java
/* ====================================================================
 Copyright 2002-2004   Apache Software Foundation

 Licensed 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 uk.org.openmentor.extractor.impl;

/**
 * Utility class for the Microsoft Word text extraction code.
 * This was originally derived from the POI implementation by
 * <NAME>, but has been significantly revised and modified
 * as part of the Open Mentor implementation effort.
 *
 * @author <NAME>
 */

public class Utils {

    public static short convertBytesToShort(byte firstByte, byte secondByte) {
        return (short) convertBytesToInt((byte) 0, (byte) 0, firstByte,
                secondByte);
    }

    public static int convertBytesToInt(byte firstByte, byte secondByte,
            byte thirdByte, byte fourthByte) {
        int firstInt = 0xff & firstByte;
        int secondInt = 0xff & secondByte;
        int thirdInt = 0xff & thirdByte;
        int fourthInt = 0xff & fourthByte;

        return (firstInt << 24) | (secondInt << 16) | (thirdInt << 8)
                | fourthInt;
    }

    public static short convertBytesToShort(byte[] array, int offset) {
        return convertBytesToShort(array[offset + 1], array[offset]);
    }

    public static int convertBytesToInt(byte[] array, int offset) {
        return convertBytesToInt(array[offset + 3], array[offset + 2],
                array[offset + 1], array[offset]);
    }

    public static int convertUnsignedByteToInt(byte b) {
        return (0xff & b);
    }

    public static char getUnicodeCharacter(byte[] array, int offset) {
        return (char) convertBytesToShort(array, offset);
    }
}
<file_sep>/src/main/java/uk/org/openmentor/model/Assignment.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.model;

import java.io.Serializable;
import java.util.Set;

/**
 * The model class used to represent assignments in Open Mentor.
 * This class can be persisted through whatever persistence
 * system you choose to use; it is the start of the object
 * system within the main Open Mentor data storage systems. Each
 * <code>Assignment</code> contains a set of {@link Submission}s,
 * which are where the grading information, and the associations
 * to students and tutors, begin to come in. 
 *
 * @author <NAME>
 */
public class Assignment implements Serializable {
    
    static final long serialVersionUID = 1754543682880894503L;

    /** The assignment identifier. */
    private int id;

    /** The course identifier. */
    private String courseId;

    /** The assignment title. */
    private String assignmentTitle;

    /** The set of submissions. */
    private Set<Submission> submissions;

    /**
     * Constructs a new {@link Assignment} for the given course identifier,
     * assignment title, and list of submissions.
     *
     * @param courseId          the course identifier
     * @param assignmentTitle   the assignment title
     * @param submissions       the set of submissions
     */
    public Assignment(final String courseId,
                      final String assignmentTitle,
                      final Set<Submission> submissions) {
        this.courseId = courseId;
        this.assignmentTitle = assignmentTitle;
        this.submissions = submissions;
    }

    /**
     * Constructs a new {@link Assignment}.
     */
    public Assignment() {
    }

    /**
     * Constructs a new {@link Assignment} for the given course identifier
     * and assignment title.
     *
     * @param courseId          the course identifier
     * @param assignmentTitle   the assignment title
     */
    public Assignment(final String assignmentTitle,
                      final Set<Submission> submissions) {
        this.assignmentTitle = assignmentTitle;
        this.submissions = submissions;
    }

    /**
     * Gets the assignment identifier.
     * @return                  the assignment identifier
     */
    public final int getId() {
        return this.id;
    }

    /**
     * Sets the assignment identifier.
     * @param newId             the new assignment identifier
     */
    public final void setId(final int newId) {
        this.id = newId;
    }

    /**
     * Gets the course code.
     * @return                  the course code
     */
    public final String getCourseId() {
        return this.courseId;
    }

    /**
     * Sets the course code.
     * @param newCourseId       the new course code
     */
    public final void setCourseId(final String newCourseId) {
        this.courseId = newCourseId;
    }

    /**
     * Gets the assignment title.
     * @return                  the assignment title
     */
    public final String getAssignmentTitle() {
        return this.assignmentTitle;
    }

    /**
     * Sets the assignment title.
     * @param newAssignmentTitle the new assignment title
     */
    public final void setAssignmentTitle(final String newAssignmentTitle) {
        this.assignmentTitle = newAssignmentTitle;
    }

    /**
     * Gets the Set of submissions.
     * @return                  the set of submissions
     */
    public final Set<Submission> getSubmissions() {
        return this.submissions;
    }

    /**
     * Sets the set of submissions
     * @param newSubmissions    the new set of submissions
     */
    public final void setSubmissions(final Set<Submission> newSubmissions) {
        this.submissions = newSubmissions;
    }
}
<file_sep>/src/main/java/uk/org/openmentor/businesslogic/BusinessLogic.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.businesslogic;

import uk.org.openmentor.model.DataBook;

/**
 * This interface is used to structure the business logic for
 * OpenMentor.  The implementations of this will pass the data
 * structures needed to the charting component.
 *
 * @author <NAME>
 */

public interface BusinessLogic {

    /**
     * Describe <code>buildDataBook</code> method here.
     *
     * @return a <code>DataBook</code> value
     * @exception BusinessLogicException if an error occurs
     */
    DataBook buildDataBook() throws BusinessLogicException;

    /**
     * Describe <code>setRequestType</code> method here.
     *
     * @param requestType a <code>String</code> value
     */
    void setRequestType(String requestType);

    /**
     * Sets the current course, which is the scope for all
     * following calculations.
     * @param courseId       the new course identifier
     */

    void setCourseId(String courseId);

    /**
     * Describe <code>setId</code> method here.
     *
     * @param id a <code>String</code> value
     */
    void setId(String id);
}
<file_sep>/src/main/java/uk/org/openmentor/web/ReportListController.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.web;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import uk.org.openmentor.model.Course;
import uk.org.openmentor.service.CourseInfoManager;
import uk.org.openmentor.service.ModelManager;

/**
 * Describe class <code>ReportListController</code> here.
 *
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 * @version 1.0
 */
public class ReportListController implements Controller {

    /**
     * Our usual logger.
     */
    private static Log log = LogFactory.getLog(ReportListController.class);

    /**
     * The <code>CourseInfoManager</code> bean is set by Spring.
     */
    private CourseInfoManager mgr = null;

    /**
     * The <code>ModelManager</code> bean is set by Spring.
     */
    private ModelManager mmgr = null;

    /**
     * Usual <code>CourseInfoManager</code> mutator.
     *
     * @param courseInfoManager the <code>CourseInfoManager</code> value.
     */
    public final void setCourseInfoManager(final CourseInfoManager
                                           courseInfoManager) {
        this.mgr = courseInfoManager;
    }

    /**
     * Usual <code>ModelManager</code> mutator.
     *
     * @param newModelManager the <code>ModelManager</code> value.
     */
    public final void setModelManager(final ModelManager
                                      newModelManager) {
        this.mmgr = newModelManager;
    }

    /**
     * Describe <code>handleRequest</code> method here.
     *
     * @param request a <code>HttpServletRequest</code> value
     * @param response a <code>HttpServletResponse</code> value
     * @return a <code>ModelAndView</code> value
     * @exception Exception if an error occurs
     */
    public final ModelAndView handleRequest(final HttpServletRequest request,
                                      final HttpServletResponse response)
    throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("entering 'handleRequest' method...");
        }

        String courseId =
            (String) request.getSession().getAttribute("course");
        if (courseId == null) {
            throw new Exception("Can't get session variable");
        }

        Map<String, Object> values = new HashMap<String, Object>();
        Course currentCourse = mgr.getCourse(courseId);
        String type = request.getParameter("type");
        if (log.isDebugEnabled()) {
            log.debug("Reporting for course: " + courseId
                      + ", type " + type);
        }
        values.put("type", type);
        values.put("course", currentCourse);
        // Only return those tutors or students (principals) we can
        // report on; and only if necessary.
        if (type.equals("student")) {
            values.put("students", mmgr.getActiveStudents(currentCourse));
        } else if (type.equals("tutor")) {
            values.put("tutors", mmgr.getActiveTutors(currentCourse));
        }
        return new ModelAndView("listReports", values);
    }
}
<file_sep>/src/main/java/uk/org/openmentor/report/ReportRenderEngine.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.report;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import uk.org.openmentor.report.constituents.ChartFactory;
import uk.org.openmentor.report.exceptions.NoSuchChartLibException;
import uk.org.openmentor.report.exceptions.UnknownReportTypeException;
import uk.org.openmentor.report.properties.ReportClusteredBarChartProperties;
import uk.org.openmentor.report.constituents.ReportConstituent;
import uk.org.openmentor.report.constituents.Table;
import uk.org.openmentor.report.constituents.Text;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import uk.org.openmentor.model.DataBook;

/**
 * Main rendering engine for reports.  Actually no renedering happens
 * here; this is in effect a filter that reformats data.
 *
 * @author <NAME>, significantly erwritten by <NAME> in order
 *         to see what is going on.
 */
public class ReportRenderEngine {

    /**
     * Our usual logger.
     */
    private static Log log = LogFactory.getLog(ReportRenderEngine.class);

    /**
     * At the moment, there are only 4 categories i.e A, B, C and D.
     * Change this value accordingly if there are more categories.
     * But why can't we count them?
     */
    private static final int COMMENTS_CATEGORIES_COUNTER = 4;

    private String chartType;

    private DataBook dataBook;

    /**
     * The only useful constructor.
     *
     * @param newChartType the chart type to render.
     * @param newDataBook the data with which to render.
     * @exception NoSuchChartLibException if an error occurs.
     */
    public ReportRenderEngine(final String newChartType,
                              final DataBook newDataBook)
            throws NoSuchChartLibException {
        this.chartType = newChartType;
        this.dataBook = newDataBook;
    }

    /**
     * The heart of this (essentially static) class is relatively
     * simple; decide which type of chart is wanted and preparee the
     * data accordingly.
     *
     * @return the data in a suitable form.
     * @exception Exception if an error occurs
     */
    public List<ReportConstituent> renderRequest() throws Exception {
        List<ReportConstituent> chartData;
        if (this.chartType.equals("BarChart")) {
            // then creating the image
            chartData = generateClusteredChart();
        } else if (this.chartType.equals("SimpleTable")) {
            // then creating the image
            chartData = generateSimpleTable();
        } else {
            chartData = null;
        }
        return chartData;
    }

    /**
     * Modified considerably, now that we have added the concept
     * of data points to the DataBook. It returns an array as this
     * seems to be what we need to pass back to the charting 
     * side. 
     *
     * @return our preferred way of providing these data.
     */
    private String[] getCommentsCategoryArray() {
    	String[] values = new String[0];
        return (String[]) dataBook.getDataPoints().toArray(values);
    }

    /**
     * Again almost nothing happens; we simply unload and load data.
     * the complication comes because the array is bigger in one case
     * than the other, so that "this.chartType" is a hidden parameter
     * to this method.
     *
     * @return the required data.
     */
    private int[][] getCommentsPercentageArray() {
        int firstIndex = 1;
        if (this.chartType.equals("BarChart")) {
            firstIndex = 2;
        } else if (this.chartType.equals("SimpleTable")) {
            firstIndex = 3;
        } else {
            log.error("Unknown type.");
            return null;
        }
        int[][] commentsPercentageArray =
            new int[firstIndex][COMMENTS_CATEGORIES_COUNTER];
        int i = 0;
        ArrayList v;
        // Array to hold values of actual and ideal comments percentage
        // Put the actual data first, then ideal
        v = (ArrayList) this.dataBook.getDataSeries("ActualRange");
        for (Iterator e = v.iterator(); e.hasNext(); i++) {
            commentsPercentageArray[0][i] = ((Integer) e.next())
                    .intValue();
        }
        v = (ArrayList) this.dataBook.getDataSeries("IdealRange");
        i = 0;
        for (Iterator e = v.iterator(); e.hasNext(); i++) {
            commentsPercentageArray[1][i] = ((Integer) e.next())
                    .intValue();
        }
        if (this.chartType.equals("SimpleTable")) {
            for (i = 0; i < COMMENTS_CATEGORIES_COUNTER; i++) {
                //deviation of actual from ideal on third index which is 2
                commentsPercentageArray[2][i] =
                    commentsPercentageArray[0][i]
                    - commentsPercentageArray[1][i];
            }
        }
        return commentsPercentageArray;
    }

    /**
     * Here is how we build an OMClusteredBarChart.  Actually the
     * returned List consists of just one element (sigh!) in the
     * only case we use at present!
     *
     * @return a <code>List</code> value
     * @exception UnknownReportTypeException if an error occurs
     * @exception NoSuchChartLibException if an error occurs
     * @exception Exception if an error occurs
     */
    public List<ReportConstituent> generateClusteredChart()
        throws UnknownReportTypeException, NoSuchChartLibException,
               Exception {
        // The properties contain all that is needed to render the chart
        ReportClusteredBarChartProperties bcp = 
            new ReportClusteredBarChartProperties
            ("Categories", "Percent of Comments",
             new String[] {"Actual", "Ideal"},
             getCommentsPercentageArray(),
             getCommentsCategoryArray(),
             "Chart", 400, 300);
        // Now hand it off to jCharts.
        ChartFactory cf = new ChartFactory(
                ChartFactory.REGISTERED_LIBS[ChartFactory.JCHARTS]);
        List<ReportConstituent> constituents = 
        	new ArrayList<ReportConstituent>();
        constituents.add(cf.createClusteredBarChart(bcp));
        return constituents;
    }

    /**
     * And here is how we build a SimpleTable.
     *
     * @return a <code>List</code> value
     * @exception UnknownReportTypeException if an error occurs
     * @exception NoSuchChartLibException if an error occurs
     */
    public List<ReportConstituent> generateSimpleTable() 
    		throws UnknownReportTypeException, NoSuchChartLibException {
        List<ReportConstituent> constituents = new ArrayList<ReportConstituent>();
        constituents.add(new Text("Some text before the Table"));
        constituents.add
            (new Table(getCommentsPercentageArray(),
                       getCommentsCategoryArray(),
                       new String[] {"% of actual comments",
                                     "% of ideal comments",
                                     "Deviation of actual from ideal %"},
                       new String("All Data")));
        return constituents;
    }
}
<file_sep>/src/conf/hibernate.properties
hibernate.connection.driver_class=com.mysql.jdbc.Driver
hibernate.connection.url=jdbc:mysql://127.0.0.1/hibernate_openmentor
hibernate.connection.username=
hibernate.connection.password=
hibernate.dialect=org.hibernate.dialect.MySQLDialect
<file_sep>/src/etc/course_info.sql
alter table tbl_course_tutors drop foreign key FKD3855E0575ED7BF;
alter table tbl_course_students drop foreign key <KEY>;

drop table if exists tbl_course;
drop table if exists tbl_tutor;
drop table if exists tbl_course_tutors;
drop table if exists tbl_student;
drop table if exists tbl_course_students;

create table tbl_course (
   id varchar(16) not null,
   course_title varchar(255),
   primary key (id)
);
create table tbl_tutor (
   org_id varchar(255) not null,
   first_name varchar(255) not null,
   last_name varchar(255) not null,
   primary key (org_id)
);
create table tbl_course_tutors (
   course_id varchar(16) not null,
   tutor_id varchar(255) not null,
   primary key (course_id, tutor_id)
);
create table tbl_student (
   org_id varchar(16) not null,
   first_name varchar(255) not null,
   last_name varchar(255) not null,
   primary key (org_id)
);
create table tbl_course_students (
   course_id varchar(16) not null,
   student_id varchar(16) not null,
   primary key (course_id, student_id)
);
alter table tbl_course_tutors add index FKD3855E0575ED7BF (course_id), add constraint FKD3855E0575ED7BF foreign key (course_id) references tbl_course (id);
alter table tbl_course_students add index FK4C6DEBBC276819F (student_id), add constraint FK4C6DEBBC276819F foreign key (student_id) references tbl_student (org_id);

insert into tbl_course (id, course_title) values ('CM2006', 'Interface Design');
insert into tbl_course (id, course_title) values ('CMM520', 'Human Interface Design');
insert into tbl_course (id, course_title) values ('CMM007', 'Intranet Systems Development');

insert into tbl_tutor (org_id, first_name, last_name) values ('00900001', 'Stuart', 'Watt');
insert into tbl_tutor (org_id, first_name, last_name) values ('00900002', 'Denise', 'Whitelock');
insert into tbl_tutor (org_id, first_name, last_name) values ('00900003', 'Hassan', 'Sheikh');
insert into tbl_tutor (org_id, first_name, last_name) values ('00900004', 'Aggelos', 'Liapis');

insert into tbl_student (org_id, first_name, last_name) values ('M0000001', 'Sam', 'Smith');
insert into tbl_student (org_id, first_name, last_name) values ('M0000002', 'Pat', 'Parker');
insert into tbl_student (org_id, first_name, last_name) values ('M0000003', 'Sandy', 'Jones');

insert into tbl_course_tutors (course_id, tutor_id) values ('CM2006', '00900001');
insert into tbl_course_tutors (course_id, tutor_id) values ('CM2006', '00900002');
insert into tbl_course_tutors (course_id, tutor_id) values ('CM2006', '00900003');
insert into tbl_course_tutors (course_id, tutor_id) values ('CM2006', '00900004');
insert into tbl_course_tutors (course_id, tutor_id) values ('CMM520', '00900001');
insert into tbl_course_tutors (course_id, tutor_id) values ('CMM007', '00900002');

insert into tbl_course_students (course_id, student_id) values ('CM2006', 'M0000001');
insert into tbl_course_students (course_id, student_id) values ('CM2006', 'M0000002');
insert into tbl_course_students (course_id, student_id) values ('CM2006', 'M0000003');
insert into tbl_course_students (course_id, student_id) values ('CMM520', 'M0000001');
insert into tbl_course_students (course_id, student_id) values ('CMM007', 'M0000002');


<file_sep>/src/main/java/uk/org/openmentor/businesslogic/Category.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.businesslogic;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * This is a fine Category; I have to work out how to interface
 * this.
 *
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 * @version 1.0
 */
public final class Category {

    /**
     * Our usual logger.
     */
    private static Log log = LogFactory.getLog(Category.class);

    /**
     * The string by which this class is known in the database, such
     * as "A".
     */
    private String label;

    /**
     * The corresponding description, such as "Positive reactions".
     */
    private String descriptor;

    /**
     * Again a private constructor to avoid unnecessary creation.
     */
    Category() {
        // Intentionally empty.
    }

    /**
     * Try to make sure these aren't created on demand; rather we
     * supply an exising singleton.  Note that the class is imutable.
     *
     * @param theLabel a <code>String</code> value
     * @param theDescriptor a <code>String</code> value
     */
    protected Category(String theLabel, String theDescription) {
        this.label = theLabel;
        this.descriptor = theDescription;
        if (log.isDebugEnabled()) {
            log.debug("Created " + this.label
                      + " (" + this.descriptor + ")");
        }
    }

    /**
     * Get the Descriptor value.
     * @return the Descriptor value.
     */
    public String getDescriptor() {
        return descriptor;
    }

    /**
     * Set the Descriptor value.
     * @param newDescriptor The new Descriptor value.
     */
    public void setDescriptor(String newDescriptor) {
        this.descriptor = newDescriptor;
    }


    /**
     * Get the Label value.
     * @return the Label value.
     */
    public String getLabel() {
        return label;
    }

    /**
     * Just to ease the use.
     * @return a <code>String</code> value
     */
    public String toString() {
        return label;
    }
}
<file_sep>/src/test/java/uk/org/openmentor/report/constituents/ReportChartUtilsTest.java
package uk.org.openmentor.report.constituents;

import java.awt.Paint;
import java.awt.Color;

import junit.framework.TestCase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import uk.org.openmentor.report.constituents.ReportChartUtils;

public class ReportChartUtilsTest extends TestCase
{
  private static Log log = LogFactory.getLog(ReportChartUtilsTest.class);
  private static final int     NUM_PAINTS              = 5;
  private static final Paint[] PAINTS_INTERNAL_CHECK   = 
  	{ Color.BLACK, Color.BLUE, Color.RED, Color.RED };
  
  public void testGeneratePaints()
  {
    assertFalse( checkPaintsUnique( PAINTS_INTERNAL_CHECK ) );
    
    Paint[] p = ReportChartUtils.generatePaints( NUM_PAINTS );
    
    assertTrue( p.length == NUM_PAINTS );
    assertTrue( checkPaintsUnique( p ) );
  }
  
  private boolean checkPaintsUnique( Paint[] p )
  {
    boolean unique = true;
    
    for ( int i = 0; i != p.length - 1 && unique; ++i ) {
      for ( int j = i + 1; j != p.length && unique; ++j ) {
        if (log.isDebugEnabled()) {
            log.debug("i = " + i + "; j = " + j + ".");
            if (p[j] == null ) {
                log.debug("Undefined color p[" + j + "].");
            }
        }
        unique = ( ( Color )p[ i ] != ( Color )p[ j ] );
        if (log.isDebugEnabled()) {
            log.debug("Unique is " + unique);
        }
      }
    }
    if (log.isDebugEnabled()) {
        log.debug("Returning " + unique);
    }
    
    return unique;
  }
}
<file_sep>/src/main/java/uk/org/openmentor/service/impl/CourseInfoManagerImpl.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.service.impl;

import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import uk.org.openmentor.dao.CourseInfoDAO;
import uk.org.openmentor.model.Course;
import uk.org.openmentor.model.Student;
import uk.org.openmentor.model.Tutor;
import uk.org.openmentor.service.CourseInfoManager;


public class CourseInfoManagerImpl implements CourseInfoManager {
    private static Log log = LogFactory.getLog(CourseInfoManagerImpl.class);
    private CourseInfoDAO dao;

    public void setCourseInfoDAO(CourseInfoDAO dao) {
        this.dao = dao;
    }

    public List getCourses() {
        return dao.getCourses();
    }

    public Course getCourse(String courseId) {
        Course course = dao.getCourse(courseId);

        if (course == null) {
            log.warn("CourseId '" + courseId + "' not found in database.");
        }

        return course;
    }

    public List getStudents(Course course) {
        return dao.getStudents(course);
    }

    public List getTutors(Course course) {
        return dao.getTutors(course);
    }

    public final Student getStudent(final Course course, final String id) {
        Student student = dao.getStudent(course, id);
        if (student == null) {
            log.warn("StudentId '" + id + "' not found in database.");
        }
        return student;
    }

    public final Tutor getTutor(final Course course, final String id) {
        Tutor tutor = dao.getTutor(course, id);
        if (tutor == null) {
            log.warn("TutorId '" + id + "' not found in database.");
        }
        return tutor;
    }
}
<file_sep>/src/main/java/uk/org/openmentor/extractor/impl/ExtractStandard.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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.
 * ========================================================================
 */


/*
 * Substantially modified by <NAME> to implement text extraction.
 * A lot of this we do not need, and may therefore dispense with.
 * Non-complex files are now more or less irrelevant, so we can forget
 * them for the moment. On the other hand, a good amount of the code
 * here is incorrect in small details, and needs to be fixed.
 *
 * Much of the problem revolves around file offsets or FCs. These are
 * not always as they should be. fcMin is used, yet it should not really
 * be useful to complex files. 
 */

package uk.org.openmentor.extractor.impl;

import uk.org.openmentor.extractor.Extractor;
import uk.org.openmentor.extractor.impl.util.BTreeSet;
import uk.org.openmentor.extractor.impl.util.PropertyNode;

import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.poifs.filesystem.DocumentEntry;

import org.apache.poi.util.LittleEndian;

public class ExtractStandard implements Extractor {
    /** Byte buffer containing the main Document stream. */
    private byte[] header;

    /** Document's text blocks. */
    private BTreeSet text = new BTreeSet();

    /** Length of main document text stream. */
    private int ccpText;

    /** Length of footnotes text. */
    private int ccpFtn;

    /** Length of header text. */
    private int ccpHdd;

    /** Length of annotation text. */
    private int ccpAtn;

    /*
     * Annotations in a few cases may contain fields. Actually, so may all the
     * text, which is irritating. So we need a way of skipping out all the
     * field stuff, and for this, regular expressions are a good solution.
     * Essentially, we should look for ASCII 19, which starts a field, and
     * ASCII 21, which ends the field. Delete these, and all other text, except
     * stuff which follows ASCII 20, which is the text view.
     */
    private Pattern fieldPattern = Pattern
            .compile("\\x13[^\\x14\\x15]*(?:\\x14([^\\x15]*))?\\x15");

    private Pattern cleanPattern = Pattern.compile("\\x0b");

    /** OLE stuff*/
    private InputStream istream;

    /** OLE stuff*/
    private POIFSFileSystem filesystem;

    private static Log log = LogFactory.getLog(ExtractStandard.class);

    public ExtractStandard() {
    }

    /**
     * Constructs a Word document from the input. Parses the document and places
     * all the important stuff into data structures.
     *
     * @param stream The stream representing the original file
     * @throws IOException if there is a problem while parsing the document.
     */
    public synchronized void extract(InputStream stream) throws IOException {
        extractStream(stream);
    }
    
    /*
     * Most of this code is broken - in a complex file, you can't assume that
     * the file offset can be calculated in this way. Instead, we should not use
     * fcMin, but use the piece table to calculate the offsetting. This is best
     * done once and cached.
     */

    private void extractStream(InputStream inputStream) throws IOException {
        //Clear the text buffer
        text.clear();
        
        //do OLE stuff
        istream = inputStream;
        filesystem = new POIFSFileSystem(istream);

        //get important stuff from the Header block and parse all the
        //data structures
        readFIB();
        
        istream.close();
    }

    /**
     * Extracts the main document stream from the POI file then hands off to other
     * functions that parse other areas.
     *
     * @throws IOException
     */
    private void readFIB() throws IOException {
        //get the main document stream
        DocumentEntry headerProps = (DocumentEntry) filesystem.getRoot()
                .getEntry("WordDocument");

        //I call it the header but its also the main document stream
        header = new byte[headerProps.getSize()];
        InputStream input = filesystem.createDocumentInputStream("WordDocument");
        input.read(header);
        input.close();
        
        if (log.isTraceEnabled()) {
            log.trace("Got datablock, length: "+header.length);
        }

        //Get the information we need from the header
        int info = LittleEndian.getShort(header, 0xa);

        ccpText = LittleEndian.getInt(header, 0x4c);
        ccpFtn = LittleEndian.getInt(header, 0x50);
        ccpHdd = LittleEndian.getInt(header, 0x54);
        ccpAtn = LittleEndian.getInt(header, 0x5c);

        int charPLC = LittleEndian.getInt(header, 0xfa);
        int charPlcSize = LittleEndian.getInt(header, 0xfe);
        int parPLC = LittleEndian.getInt(header, 0x102);
        int parPlcSize = LittleEndian.getInt(header, 0x106);
        boolean useTable1 = (info & 0x200) != 0;

        //process the text and formatting properties
        processComplexFile(useTable1, charPLC, charPlcSize, parPLC, parPlcSize);
    }

    /**
     * Extracts the correct Table stream from the POI filesystem then hands off to
     * other functions to process text and formatting info. the name is based on
     * the fact that in Word 8(97) all text (not character or paragraph formatting)
     * is stored in complex format.
     *
     * @param useTable1 boolean that specifies if we should use table1 or table0
     * @param charTable offset in table stream of character property bin table
     * @param charPlcSize size of character property bin table
     * @param parTable offset in table stream of paragraph property bin table.
     * @param parPlcSize size of paragraph property bin table.
     * @return boolean indocating success of
     * @throws IOException
     */
    private void processComplexFile(boolean useTable1, int charTable,
            int charPlcSize, int parTable, int parPlcSize) throws IOException {

        //get the location of the piece table
        int complexOffset = LittleEndian.getInt(header, 0x1a2);

        String tablename = null;
        DocumentEntry tableEntry = null;
        if (useTable1) {
            tablename = "1Table";
        } else {
            tablename = "0Table";
        }
        tableEntry = (DocumentEntry) filesystem.getRoot().getEntry(tablename);

        //load the table stream into a buffer
        int size = tableEntry.getSize();
        byte[] tableStream = new byte[size];
        filesystem.createDocumentInputStream(tablename).read(tableStream);

        findText(tableStream, complexOffset);
    }

    public String getBody() {
        return getText(0, ccpText);
    }

    public Set getAnnotations() {
        String result = getText(ccpText + ccpFtn + ccpHdd, ccpAtn);

        /*
         * Splitting needs to be handled carefully. In practice, annotation
         * marks identified by ASCII 05, but these may occur after a field (!).
         * There is a proper way to handle annotations, which involves using a
         * different table elsewhere in the Word file, but this also needs to be
         * decoded and it is a lot of work for little gain.
         */
        String[] all = result.split("\005");
        Set<String> values = new HashSet<String>();
        for (int x = 1; x < all.length; x++) {
            values.add(removeFields(all[x]).trim());
        }
        return (Set) values;
    }

    private String removeFields(String text) {
        /*
         * Amended to remove all field information - even the visible text. This
         * is a bad idea in one sense - strictly it is invalid, as we are not
         * really correct about annotation boundaries. But in another sense,
         * fields are not written by people, so we can probably ignore them
         * validly anyway.
         */
        String result = fieldPattern.matcher(text).replaceAll("");
        result = cleanPattern.matcher(result).replaceAll("");
        if (log.isTraceEnabled()) {
            log.trace("Removed field: " + result);
        }
        return result;
    }

    private String getText(int start, int length) {
        List<PropertyNode> result = getTextChunks(start, length, text);
        StringBuffer buf = new StringBuffer();
        int textSize = result.size();
        int oStart = -1;

        for (int z = 0; z < textSize; z++) {

            TextPiece piece = (TextPiece) result.get(z);
            int pStart = piece.getStart();
            int pEnd = piece.getEnd();
            int textStart = piece.getPhysical();
            int textEnd = textStart + (pEnd - pStart);

            oStart = Math.max(oStart, pStart);
            
            if (log.isTraceEnabled()) {
                log.trace("Adding text from "+textStart+" to "+textEnd);
            }

            if (piece.usesUnicode()) {
                addUnicodeText(textStart, textEnd, buf);
            } else {
                addText(textStart, textEnd, buf);
            }

        }
        String theResult = buf.toString();
        start = start - oStart;

        if (log.isTraceEnabled()) {
            log.trace("Substring from " + start + " to " + (start + length));
        }

        return theResult.substring(start, start + length);
    }

    private List<PropertyNode> getTextChunks(int start, int length, BTreeSet set) {
        BTreeSet.BTreeNode theRoot = set.root;
        List<PropertyNode> results = new ArrayList<PropertyNode>();
        int end = start + length;

        BTreeSet.Entry[] entries = theRoot.entries;
        if (log.isTraceEnabled()) {
            log.trace("Start: " + start);
            log.trace("End: " + end);
            log.trace("Count of entries: " + entries.length);
        }

        for (Iterator i = set.iterator(); i.hasNext();) {
            PropertyNode xNode = (PropertyNode) i.next();
            int xStart = xNode.getStart();
            int xEnd = xNode.getEnd();
            if (log.isTraceEnabled()) {
                log.trace("Entry: start=" + xStart + ", end=" + xEnd);
            }
            if (xStart < end) {
                if (start < xEnd) {
                    if (log.isTraceEnabled()) {
                        log.trace("Adding entry");
                    }
                    results.add(xNode);
                }
            } else {
                break;
            }
        }

        if (log.isTraceEnabled()) {
            log.trace("Done");
        }
        return results;
    }

    /*
     * Somewhere round here there is an off by 512 error, at least when a 
     * header appears. The header is not being found properly, and we are
     * getting text from 512 bytes earlier than we should. The data from
     * findText seems to be more or less OK. 
     */
    private void findText(byte[] tableStream, int complexOffset)
            throws IOException {
        //actual text
        int pos = complexOffset;
        //skips through the prms before we reach the piece table. These contain data
        //for actual fast saved files
        //System.out.println("pos1: "+pos);
        while (tableStream[pos] == 1) {
            pos++;
            int skip = LittleEndian.getShort(tableStream, pos);
            pos += 2 + skip;
        }
        //System.out.println("pos2: "+pos);
        if (tableStream[pos] != 2) {
            throw new IOException("corrupted Word file");
        } else {
            //parse out the text pieces
            //System.out.println("pos3: "+pos);
            int pieceTableSize = LittleEndian.getInt(tableStream, ++pos);
            pos += 4;
            int pieces = (pieceTableSize - 4) / 12;
            //System.out.println("tab: "+pos);
            int start = 0;
            for (int x = 0; x < pieces; x++) {
                int filePos = LittleEndian.getInt(tableStream, pos
                        + ((pieces + 1) * 4) + (x * 8) + 2);
                if (log.isTraceEnabled()) {
                    log.trace("POS: 0x"+Integer.toHexString(filePos));
                }
                boolean unicode = false;
                if ((filePos & 0x40000000) == 0) {
                    unicode = true;
                } else {
                    unicode = false;
                    filePos &= ~(0x40000000);//gives me FC in doc stream
                    filePos /= 2;
                }
                int lStart = LittleEndian.getInt(tableStream, pos + (x * 4));
                int lEnd = LittleEndian.getInt(tableStream, pos + (x + 1) * 4);
                int totLength = lEnd - lStart;

                if (log.isTraceEnabled()) {
                    log.trace("Piece: " + (1 + x) + ", start=" + start
                            + ", len=" + totLength + ", phys=" + filePos
                            + ", uni=" + unicode);
                }
                TextPiece piece = new TextPiece(start, totLength, filePos,
                        unicode);
                start = start + totLength;
                text.add(piece);
            }
        }
    }

    /**
     * Method to add a block of characters to a buffer. 
     * 
     * @param start Beginning of the sequence to add (in file offset)
     * @param end End of the sequence to add (in file offset)
     * @param buf Buffer to add the characters to
     */
    private void addText(int start, int end, StringBuffer buf) {
        for (int x = start; x < end; x++) {
            char ch = '?';

            int theCharacter = header[x];

            if (theCharacter < 0) {
                theCharacter += 255;
                if ((theCharacter == 145) || (theCharacter == 146)) {
                    ch = '\'';
                } else if ((theCharacter == 147) || (theCharacter == 148)) {
                    ch = '\"';
                } else {
                    ch = (char) theCharacter;
                }
            } else {
                ch = (char) theCharacter;
            }

            //System.out.println("Adding: "+ch+" from offset: "+x);

            buf.append(ch);
        }
    }

    /**
     * Method to add a block of unicode characters to a buffer. 
     * 
     * @param start Beginning of the sequence to add (in file offset)
     * @param end End of the sequence to add (in file offset)
     * @param buf Buffer to add the characters to
     */
    private void addUnicodeText(final int start, 
                                final int end, 
                                final StringBuffer buf) {
        for (int x = start; x < end; x += 2) {
            char ch = Utils.getUnicodeCharacter(header, x);
            buf.append(ch);
        }
    }
}

<file_sep>/src/main/java/uk/org/openmentor/businesslogic/BusinessLogicException.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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.
 * ========================================================================
 */
/**
 * @author OpenMentor team
 */

package uk.org.openmentor.businesslogic;

/**
 * Provides a wrapper exception class so that exceptions thrown from
 * within the reporting code of OpenMentor are independent of
 * exceptions from third party libraries such as the charting library.
 */
public class BusinessLogicException extends Exception {

    /** serialVersionUID is there for serializability. */
    static final long serialVersionUID = 6311830798917714229L;

    /**
     * Constructs the exception class with a message.
     *
     * @param message the message to throw with the exception
     */
    public BusinessLogicException(final String message) {
        super(message);
    }
}
<file_sep>/src/test/java/uk/org/openmentor/web/ReportControllerTest.java
package uk.org.openmentor.web;

import junit.framework.TestCase;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.mock.web.MockServletContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.servlet.ModelAndView;

import org.hibernate.SessionFactory;
import org.hibernate.Session;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.transaction.support.TransactionSynchronizationManager;

public class ReportControllerTest extends TestCase {

    private XmlWebApplicationContext ctx;

    private SessionFactory sf = null;

    private static Log log = LogFactory.getLog(ReportControllerTest.class);
    private ReportController c;
    
    public void setUp() {
        String[] paths = {"/WEB-INF/applicationContext.xml",
                          "/WEB-INF/action-servlet.xml"};
        ctx = new XmlWebApplicationContext();
        ctx.setConfigLocations(paths);
        ctx.setServletContext(new MockServletContext(""));
        ctx.refresh();
        sf = (SessionFactory) ctx.getBean("sessionFactory");
        c = (ReportController) ctx.getBean("reportController");
    }

    protected void tearDown() throws Exception {
        super.tearDown();
        sf = null;
        c = null;
    }

    protected void startTransaction() throws Exception{
        Session s = sf.openSession();
        TransactionSynchronizationManager.
            bindResource(sf, new SessionHolder(s));
    }
    
    protected void endTransaction() throws Exception{
        SessionHolder holder = (SessionHolder) 
            TransactionSynchronizationManager.getResource(sf);
        Session s = holder.getSession();
        s.flush();
        TransactionSynchronizationManager.unbindResource(sf);
        SessionFactoryUtils.releaseSession(s, sf);
    }
    

    public void testTeachersReport() throws Exception {
    	MockHttpServletRequest req = new MockHttpServletRequest();
        MockHttpServletResponse resp = new MockHttpServletResponse();
        ModelAndView mav = null;
        
        req.getSession().setAttribute("course", "CM2006");
        req.addParameter("RequestType", "ChartImage");
        req.addParameter("ReportFor", "tutor");
        req.addParameter("ChartType", "BarChart");
        req.addParameter("ID", "00900001");
        req.setMethod("GET");
        startTransaction();
        mav = c.handleRequest(req, resp);
        
        // Charts return null
        assertNull(mav);
        endTransaction();
        // OK, what happened??
        if (log.isDebugEnabled()) {
        	log.debug("Completed request, status: "+resp.getStatus());
        }
    }

    public void testStudentsReport() throws Exception {
    	MockHttpServletRequest req = new MockHttpServletRequest();
        MockHttpServletResponse resp = new MockHttpServletResponse();
        ModelAndView mav = null;

        req.getSession().setAttribute("course", "CM2006");
        req.addParameter("RequestType", "ChartImage");
        req.addParameter("ReportFor", "student");
        req.addParameter("ChartType", "BarChart");
        req.addParameter("ID", "M0000001");
        req.setMethod("GET");
        startTransaction();
        mav = c.handleRequest(req, resp);
        
        // Charts return null
        assertNull(mav);
        endTransaction();        
        // OK, what happened??
        if (log.isDebugEnabled()) {
        	log.debug("Completed request, status: "+resp.getStatus());
        }
    }
        
    public void testCoursesReport() throws Exception {
    	MockHttpServletRequest req = new MockHttpServletRequest();
        MockHttpServletResponse resp = new MockHttpServletResponse();
        ModelAndView mav = null;

        req.getSession().setAttribute("course", "CM2006");
        req.addParameter("RequestType", "ChartImage");
        req.addParameter("ReportFor", "course");
        req.addParameter("ChartType", "BarChart");
        req.addParameter("ID", "CM2006");
        req.setMethod("GET");
        startTransaction();
        mav = c.handleRequest(req, resp);
        endTransaction();
        // Charts return null
        assertNull(mav);
        
        // OK, what happened??
        if (log.isDebugEnabled()) {
        	log.debug("Completed request, status: "+resp.getStatus());
        }
    }

    public void testTeachersTableReport() throws Exception {
    	MockHttpServletRequest req = new MockHttpServletRequest();
        MockHttpServletResponse resp = new MockHttpServletResponse();
        ModelAndView mav = null;
        
        req.getSession().setAttribute("course", "CM2006");
        req.addParameter("RequestType", "Table");
        req.addParameter("ReportFor", "tutor");
        req.addParameter("ID", "00900001");
        req.setMethod("GET");
        startTransaction();
        mav = c.handleRequest(req, resp);
        endTransaction();
        // Tables don't return null
        assertNotNull(mav);
        assertNotNull(mav.getModel().get("categories"));
        assertNotNull(mav.getModel().get("actual"));
        assertNotNull(mav.getModel().get("ideal"));
        assertNotNull(mav.getModel().get("comments"));
        
        // OK, what happened??
        if (log.isDebugEnabled()) {
        	log.debug("Completed request, status: "+resp.getStatus());
        }
    }

}
<file_sep>/src/test/java/uk/org/openmentor/web/OpenMentorWebTestCase.java
package uk.org.openmentor.web;

/**
 * The class <code>OpenMentorWebTestCase</code> currently test much of
 * the functionality of the completed application, concentrating on
 * simple things and security aspects.
 *
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 * @version $Id$
 */
public class OpenMentorWebTestCase extends BaseWebTestCase {

    /**
     * Creates a new <code>OpenMentorWebTestCase</code> instance.  The
     * port would be *much* better configuerd somewhere else, but I
     * don't know how to do it yet.
     *
     * @param name a <code>String</code> value
     */
    public OpenMentorWebTestCase(String name) {
        super(name);
    }

    /**
     * The <code>testIndexPage</code> method ensures that all the
     * unprotected links are present and are working
     */
    public void testIndexPage() {
        beginAt("/");
        assertTitleEquals("OpenMentor -  Welcome");
        // Check anonymous login has worked.
        assertTextPresent("No current course");
        assertTextPresent("visitor");
        assertLinkPresentWithText("Background");
        clickLinkWithText("Background"); 
        assertTitleEquals(titlePrefix + "Background");
    }


    /**
     * The <code>testUploadPage</code> first checks that course
     * selection behaves correctly and then starts looking at the
     * assignment upload form.
     *
     */
    public void testUploadPage() {
        // First login as an empowered user 
        loginAsUser();
        // By default no course is selected, so select one.
        assertTextPresent("No current course");
        assertLinkPresentWithText("Choose course");
        clickLinkWithText("Choose course");
        assertFormPresent("selectCourses");
        checkCheckbox("course","CM2006");
        submit();
        // And check the selection succeeds
        assertTextPresent("Current course: CM2006");
        clickLinkWithText("Upload an assignment"); 
        assertTextPresent("Submit an assignment");
    }

}
<file_sep>/pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>openmentor</groupId>
  <artifactId>openmentor</artifactId>
  <packaging>war</packaging>
  <name>OpenMentor</name>
  <version>0.95-dev</version>
  <description>
    This demonstrator is designed to show the range of tools and
    recommendations available to a modern Java (web) applications.  It
    aim to show how the tools should be used and the setup need to
    make them interact simply.  In particular, this demo builds using
    either ant or maven.
  </description>
  <url>http://openmentor.comp.rgu.ac.uk/development</url>
  <issueManagement>
    <system>Scarab</system>
    <url>http://openmentor.comp.rgu.ac.uk/scarab/issues/</url>
  </issueManagement>
  <scm>
    <connection>
      scm:git:git://github.com/morungos/openmentor.git
    </connection>
    <developerConnection/>
    <tag/>
    <url/>
  </scm>

  <repositories>
    <repository>
      <id>maven2-repository.dev.java.net</id>
      <name>Java.net Repository for Maven</name>
      <url>http://download.java.net/maven/2/</url>
      <layout>default</layout>
    </repository>
    <repository>
      <id>www.mvnsearch.org</id>
      <name>Maven Search Repository for Maven</name>
      <url>http://www.mvnsearch.org/maven2/</url>
      <layout>default</layout>
    </repository>
  </repositories>

  <distributionManagement>
    <site>
      <id>website</id>
      <url>scp://www.openmentor.org.uk/openmentor</url>
    </site>
  </distributionManagement>

  <developers>
    <developer>
      <name><NAME></name>
      <id>denise</id>
      <email><EMAIL></email>
      <organization>Institute of Educational Technology, The Open University</organization>
      <timezone>0</timezone>
    </developer>
    <developer>
      <name><NAME></name>
      <id>stuart</id>
      <email><EMAIL></email>
      <organization>School of Computing, The Robert Gordon University, Aberdeen</organization>
      <url>http://www.comp.rgu.ac.uk/staff/sw</url>
      <timezone>0</timezone>
    </developer>
    <developer>
      <name><NAME></name>
      <id>jan</id>
      <email><EMAIL></email>
      <organization>Institute of Educational Technology, The Open University</organization>
      <timezone>0</timezone>
    </developer>
    <developer>
      <name><NAME></name>
      <id>ian</id>
      <email><EMAIL></email>
      <organization>Openability Consulting</organization>
      <url>www.openability.co.uk</url>
      <timezone>0</timezone>
    </developer>
    <developer>
      <name><NAME></name>
      <id>colin</id>
      <email><EMAIL></email>
      <organization>School of Computing, The Robert Gordon University</organization>
      <timezone>0</timezone>
    </developer>
    <developer>
      <name><NAME></name>
      <id>will</id>
      <email><EMAIL></email>
      <organization>Institute of Educational Technology, The Open University</organization>
      <timezone>0</timezone>
    </developer>
    <developer>
      <name><NAME></name>
      <id>maggie</id>
      <email><EMAIL></email>
      <organization>School of Computing, The Robert Gordon University</organization>
      <timezone>0</timezone>
    </developer>
    <developer>
      <name><NAME></name>
      <id>hassan</id>
      <email><EMAIL></email>
      <organization>Institute of Educational Technology, The Open University</organization>
      <timezone>0</timezone>
    </developer>
  </developers>
  <licenses>
    <license>
      <name>The Apache Software License, Version 2.0</name>
      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
      <distribution>repo</distribution>
    </license>
  </licenses>
  <organization>
    <name>The OpenMentor Team</name>
    <url>http://www.openmentor.org.uk</url>
  </organization>
  <inceptionYear>2004</inceptionYear>
  <!--    <logo>http://openmentor.comp.rgu.ac.uk/images/omlogo.jpg</logo>
       <siteDirectory>/usr/local/web</siteDirectory> -->
  <build>
    <sourceDirectory>src/main/java</sourceDirectory>
    <testSourceDirectory>src/test/java</testSourceDirectory>

    <plugins>
      <plugin>
        <!-- We need java 5 from revision 495 (SerialBlob) -->
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.3.2</version>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.7.2</version>
        <configuration>
          <excludes>
            <exclude>>**/All*Tests.java</exclude>
            <!-- The next fails: see Scarab IssueID OM13 -->
            <exclude>**/CourseInfoControllerTest.java</exclude>
            <!-- There are no tests in the next two -->
            <exclude>**/BaseDAOTestCase.java</exclude>
            <exclude>**/ClassifierTest.java</exclude>
            <!-- Comment out the next line to do integration testing. -->
            <exclude>**/*WebTestCase.java</exclude>
            <!-- And again this contains no tests. -->
            <exclude>**/*BaseWebTestCase.java</exclude>
          </excludes>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.1.1</version>
      </plugin>
      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <version>8.0.1.v20110908</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-site-plugin</artifactId>
        <version>3.0</version>
      </plugin>
    </plugins>
    <resources>
      <resource>
        <!-- Hibernate mapping files -->
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.xml</include>
        </includes>
      </resource>
      <resource>
        <directory>${basedir}/src/conf</directory>
        <includes>
          <include>*.properties</include>
        </includes>
        <filtering>false</filtering>
      </resource>
      <resource>
        <!-- Hibernate mapping files -->
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.xml</include>
        </includes>
      </resource>
      <resource>
        <!-- Other resources -->
        <directory>src/main/resources</directory>
        <includes>
          <include>**/*</include>
        </includes>
      </resource>
    </resources>
    <testResources>
      <testResource>
        <directory>src/main/webapp</directory>
        <includes>
          <include>WEB-INF/applicationContext.xml</include>
          <include>WEB-INF/action-servlet.xml</include>
          <include>WEB-INF/validation.xml</include>
          <include>WEB-INF/validator-rules.xml</include>
        </includes>
      </testResource>
      <testResource>
        <!-- Hibernate mapping files -->
        <directory>src/main/java</directory>
        <includes>
          <include>**/*.xml</include>
        </includes>
      </testResource>
      <testResource>
        <directory>${basedir}/src/conf</directory>
        <includes>
          <include>*.properties</include>
        </includes>
        <filtering>false</filtering>
      </testResource>
    </testResources>
  </build>
  <dependencies>
    <!-- Used to do authentication.  Although it is only needed at the
         moment at runtime, many dependencies are used to compile and
         test -->
    <dependency>
      <groupId>acegisecurity</groupId>
      <artifactId>acegi-security</artifactId>
      <version>0.9.0</version>
      <exclusions>
        <exclusion>
          <groupId>aopalliance</groupId>
          <artifactId>aopalliance</artifactId>
        </exclusion>
        <exclusion>
          <groupId>springframework</groupId>
          <artifactId>spring</artifactId>
        </exclusion>
        <exclusion>
          <groupId>springframework</groupId>
          <artifactId>spring-mock</artifactId>
        </exclusion>
        <exclusion>
          <groupId>cas</groupId>
          <artifactId>casclient</artifactId>
        </exclusion>
        <exclusion>
          <groupId>jcifs</groupId>
          <artifactId>jcifs</artifactId>
        </exclusion>
        <exclusion>
          <groupId>aspectj</groupId>
          <artifactId>aspectjrt</artifactId>
        </exclusion>
        <exclusion>
          <groupId>directory</groupId>
          <artifactId>apacheds-main</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <!-- Needed directly by openmentor. -->
    <dependency>
      <groupId>commons-fileupload</groupId>
      <artifactId>commons-fileupload</artifactId>
      <version>1.0</version>
      <scope>runtime</scope>
    </dependency>
    <!-- Needed directly by openmentor. -->
    <dependency>
      <groupId>commons-lang</groupId>
      <artifactId>commons-lang</artifactId>
      <version>2.0</version>
    </dependency>
    <!-- Just used to indirect to log4j logging.  -->
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.0.4</version>
    </dependency>
    <!-- Validating web page inputs. -->
    <dependency>
      <groupId>commons-validator</groupId>
      <artifactId>commons-validator</artifactId>
      <version>1.1.4</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate</artifactId>
      <version>3.0.5</version>
      <scope>runtime</scope>
    </dependency>
    <!-- Only jcharts-0.6.0 is on ibiblio - and it causes failures. -->
    <dependency>
      <groupId>jcharts</groupId>
      <artifactId>jcharts</artifactId>
      <version>0.7.5</version>
    </dependency>
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>3.1.11</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>opensymphony</groupId>
      <artifactId>sitemesh</artifactId>
      <version>2.2.1</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring</artifactId>
      <version>1.2.5</version>
      <exclusions>
        <exclusion>
          <groupId>quartz</groupId>
          <artifactId>quartz</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-support</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-orm</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-hibernate</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.springframework</groupId>
          <artifactId>spring-core</artifactId>
        </exclusion>
        <exclusion>
          <artifactId>commons-attributes-compiler</artifactId>
          <groupId>commons-attributes</groupId>
        </exclusion>
      </exclusions>
    </dependency>
    <!-- Again used in unit tests. -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-mock</artifactId>
      <version>1.2.5</version>
      <scope>test</scope>
    </dependency>
    <!-- Used to read Word files -->
    <dependency>
      <groupId>poi</groupId>
      <artifactId>poi</artifactId>
      <version>2.5.1-final-20040804</version>
    </dependency>
    <!-- This was in spring-sandbox -->
    <dependency>
      <groupId>springmodules</groupId>
      <artifactId>springmodules-validator</artifactId>
      <version>0.1</version>
      <scope>runtime</scope>
    </dependency>
    <!-- Tests were failing without some form of xerces -->
    <dependency>
      <groupId>xerces</groupId>
      <artifactId>xercesImpl</artifactId>
      <version>2.6.2</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>jwebunit</groupId>
      <artifactId>jwebunit</artifactId>
      <version>1.2</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  <reporting>
    <plugins>     
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-changelog-plugin</artifactId>
        <version>2.2</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-javadoc-plugin</artifactId>
        <version>2.8</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-project-info-reports-plugin</artifactId>
        <version>2.1.1</version>
      </plugin>
     </plugins>
  </reporting>
</project>
<file_sep>/src/main/java/uk/org/openmentor/evaluator/Evaluator.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.evaluator;

import uk.org.openmentor.model.Submission;

/**
 * @author <NAME>
 *
 * The Evaluator interface represents the key business logic for
 * the Open Mentor system - taking a section of the submission
 * data, and returning a model object that can be used to
 * display feedback on those submissions.
 */

public interface Evaluator {

    /**
     * Sets a key which can be used to influence the way data is
     * extracted or summarised
     *
     * @param key
     * @param value
     */
    //  public void setProperty(String key, String value);

    /**
     * Analysis and returns feedback on a subset of the submission
     * data.
     *
     * @param submission the input on which feedback is required;
     * @return an Object which can be modelled for viewing.
     */
    Object getFeedback(Submission submission);
}
<file_sep>/src/main/java/uk/org/openmentor/businesslogic/DescriptorFactory.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.businesslogic;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import uk.org.openmentor.evaluator.EvaluationScheme;

/**
 * One of the most implementation-dependant aspects of Openmentor is
 * the choice of terms to describe grades and categories.  Yet it
 * seems helpful to have such things as (immutable) classes.  This
 * factory aims to generate them from the text strings used in the
 * database to describe them.  To guard against corruption, only
 * strings which are known to the EvaluationScheme interface are
 * accepted; otherwise a null class will be returned.  It is (of
 * course) the responsibility of the calling class to check the
 * return, but this seems more helpful than raising an Exception.
 *
 * Note that the class is, in effect, completely static.  We use the
 * single non-static variable to enable Spring to wire in the
 * configuration information.  I guess there is a better way.
 *
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 * @version 1.0
 */
public class DescriptorFactory {

    /**
     * Our usual logger.
     */
    private static Log log = LogFactory.getLog(DescriptorFactory.class);

    /**
     * The scheme to specify allowable grades and categories.  In
     * other words, the configuration information.
     */
    private static EvaluationScheme evaluationScheme;

    /**
     * Once initialised, this list will contain all the available
     * <code>Grade</code>s.
     */
    private static List<Grade> gradeList;

    /**
     * Once initialised, this list will contain all the available
     * <code>Categories</code>s, except for the uncategorised one.
     */
    private static List<Category> categoryList;

    /**
     * Stores a copy of the default <code>Category</code>.
     */
    private static Category defaultCategory;

    /**
     * Once initialised, this map will produce <code>Grade</code>s on
     * demand.
     */
    private static Map<String, Grade> gradeMap =
        new HashMap<String, Grade>();

    /**
     * Once initialised, this map will produce <code>Category</code>s on
     * demand.
     */
    private static Map<String, Category> categoryMap =
        new HashMap<String, Category>();

    /**
     * Creates a new <code>DescriptorFactory</code> instance.
     */
    private DescriptorFactory() {
        // This is never used; so we are always properly initialised.
    }

    /**
     * Creates what will in fact be the only
     * <code>DescriptorFactory</code> instance, and then initialises
     * the two maps which store the singleton Grade and Category
     * instances.
     *
     * @param scheme specifies allowable grades and categories.
     */
    public DescriptorFactory(EvaluationScheme scheme) {
        this.evaluationScheme = scheme;
        gradeList = new ArrayList<Grade>();
        categoryList = new ArrayList<Category>();
        List<String> bands = evaluationScheme.getBands();
        List<String> descriptors = evaluationScheme.getBandDescriptions();
        for (Iterator i = bands.iterator(), j = descriptors.iterator();
             i.hasNext() && j.hasNext();) {
            String grade = (String) i.next();
            String descriptor = (String) j.next();
            Grade g = new Grade(grade,descriptor);
            this.gradeMap.put(grade, g);
            this.gradeList.add(g);
        }

        List<String> categories = evaluationScheme.getCategories();
        descriptors = evaluationScheme.getCategoryDescriptions();
        for (Iterator i = categories.iterator(), j = descriptors.iterator();
             i.hasNext() && j.hasNext();) {
            String category = (String) i.next();
            String descriptor = (String) j.next();
            Category c = new Category(category,descriptor);
            this.categoryMap.put(category, c);
            this.categoryList.add(c);
            if (log.isDebugEnabled()) {
                log.debug("Adding " + category);
            }

        }
        // But we can also have uncategorised comments!
        String category = evaluationScheme.getDefaultCategory();
        String descriptor = evaluationScheme.getDefaultDescriptor();
        this.defaultCategory = new Category(category,descriptor);
        categoryMap.put(category, defaultCategory);
    }

    /**
     * Returns an existing <code>Grade</code> whose name is given.
     *
     * @param gradeName the given name;
     * @return the required <code>Grade</code>.
     */
    public static Grade getGrade(String gradeName) {
        Grade g = gradeMap.get(gradeName);
        if (g == null) {
            log.warn("Returning a null grade " + gradeName);
        }
        return g;
    }

    /**
     * Returns an existing <code>Category</code> whose name is given.
     *
     * @param categoryName the given name;
     * @return the required <code>Category</code>.
     */
    public static Category getCategory(String categoryName) {
        Category c = categoryMap.get(categoryName);
        if (c == null) {
            log.warn("Returning a null category " + categoryName);
        }
        return c;
    }

    /**
     * Returns an existing <code>Category</code> whose ruleCategory
     * name is given.
     *
     * @param name the given name;
     * @return the required <code>Category</code>.
     */
    public static Category getCategoryFromRuleCategoryName
        (final String name) {
        String categoryName = evaluationScheme.ruleCategoryAsCategory(name);
        return getCategory(categoryName);
    }

    /**
     * Get all known grades.
     *
     * @return a <code>List</code> of distinct grades.
     */
    public static final List<Grade> getGrades() {
        return gradeList;
    }

    /**
     * Get all known categories, excluding the default (uncategorized)
     * category.
     *
     * @return a <code>List</code> of categories.
     */
    public static final List<Category> getCategories() {
        return categoryList;
    }

    /**
     * Get all known categories, including the default (uncategorized)
     * category.
     *
     * @return a <code>List</code> of all the categories.
     */
    public static final List<Category> getAllCategories() {
        List l = new ArrayList<Category>(categoryList);
        l.add(defaultCategory);
        return l;
    }
}
<file_sep>/src/main/java/uk/org/openmentor/businesslogic/impl/HibernateBusinessLogic.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.businesslogic.impl;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;
import java.util.List;
import java.util.Iterator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import uk.org.openmentor.businesslogic.BusinessLogic;
import uk.org.openmentor.businesslogic.BusinessLogicException;
import uk.org.openmentor.businesslogic.Categorization;
import uk.org.openmentor.businesslogic.impl.CategorizationImpl;
import uk.org.openmentor.businesslogic.DescriptorFactory;
import uk.org.openmentor.businesslogic.Category;
import uk.org.openmentor.businesslogic.Grade;
import uk.org.openmentor.evaluator.EvaluationScheme;
import uk.org.openmentor.model.DataBook;
import uk.org.openmentor.model.DataBookImpl;
import uk.org.openmentor.model.Submission;
import uk.org.openmentor.service.ModelManager;

/**
 * This version replaces SQLBUsinessLogic (and so also SQLWrapper),
 * although the latter can be switched back in applicationContext.xml.
 * The intention was to provide a significantly different way of
 * doing things, which removes the very detailed SQL statements of the
 * alternative.  Of courtes this way has many more database hits, so
 * at some point could well prove not to scale.  So at present this is
 * only a demo version; there has been no opportunity for any form of
 * stress testing, and as such no attempt to start tuning the caching
 * startegy eg in hibernate, which <strong>may</strong> overcome some
 * of these problems.
 *
 * @author <NAME>
 */
public class HibernateBusinessLogic implements BusinessLogic {

    /** Logger for log4j. */
    private static Log log = 
        LogFactory.getLog(HibernateBusinessLogic.class);

    /**
     * Current request type, to find out if the request is for a student,
     * teacher, course or an assignment. */
    private String requestType;

    /**
     * ID of student, teacher, course or assignment for whom report is
     * requested.
     */
    private String id;

    /** The current course identifier. */
    private String courseId;

    /** The EvaluationScheme, set through dependency injection. */
    private EvaluationScheme evaluationScheme = null;

    /** The Categorization, set through dependency injection. */
    private Categorization ctgz = null;

    /**
     * The <code>ModelManager</code> bean is set by Spring.
     */
    private ModelManager mmgr = null;

    /**
     * Usual <code>ModelManager</code> mutator.
     *
     * @param newModelManager the <code>ModelManager</code> value.
     */
    public final void setModelManager(final ModelManager
                                      newModelManager) {
        this.mmgr = newModelManager;
    }

    /** Constructs a new component for calculating the data for
     * charting.
     */
    public HibernateBusinessLogic() {
    }

    /**
     * Constructs a new component for calculating the data for
     * charting.
     *
     * @param newRequestType        the request type
     */
    public HibernateBusinessLogic(final String newRequestType) {
        setRequestType(newRequestType);
    }

    /**
     * Setter for the request type.
     * @param newRequestType        the request type
     */
    public final void setRequestType(final String newRequestType) {
        this.requestType = newRequestType;
    }

    /**
     * Setter for the course identifier.
     * @param newCourseId           the course identifier
     */
    public final void setCourseId(final String newCourseId) {
        this.courseId = newCourseId;
    }

    /**
     * Getter for the course identifier.
     * @return                      the course identifier
     */
    public final String getCourseId() {
        return this.courseId;
    }

    /**
     * Setter for the identifier of the object to calculate
     * chart data for.
     * @param newId                 the object identifier
     */
    public final void setId(final String newId) {
        this.id = newId;
    }

    /**
     * Setter for the evaluation scheme.
     * @param newEvaluationScheme   the evaluation scheme
     */
    public final void setEvaluationScheme(final EvaluationScheme
                                            newEvaluationScheme) {
        this.evaluationScheme = newEvaluationScheme;
    }

    /**
     * Setter for the categorization object;
     * @param caegorization   the categorization object.
     */
    public final void setCategorization(final Categorization
                                            categorization) {
        this.ctgz = categorization;
    }

    /**
     * Builds a DataBook for charting purposes.
     * @return  the DataBook instance
     */
    public final DataBook buildDataBook() {
        DataBook dataBook = new DataBookImpl();
        // The categorization really does want to be a local variable,
        // so why set it as an instance variable through dependency
        // injection and then risk forgetting to clear it?  In
        // contrast, I think one use case calls this method 3 times
        // running with the same arguments, so a saving is possible.
        ctgz.clear();
        ctgz.addComments(selectSubmissions()); 
        // Set up containers
        List<List<String>> comments = new ArrayList<List<String>>();
        Map<Category,Integer> actualCounts = new HashMap<Category,Integer>();
        int commentCount = 0;
        for (Category category: DescriptorFactory.getCategories()) {
            int n = ctgz.getCommentCount(category);
            commentCount += n;
            actualCounts.put(category,n);
            comments.add(ctgz.getComments(category));
            if (log.isDebugEnabled()) {
                log.debug("commentCount for " + category
                          + " is " + commentCount);
            }
        }
        Map<Grade,Integer> submissionCounts = ctgz.getSubmissionCounts();
        Map<Category,Integer> 
            idealCounts = 
            rescale(weightedIdealCounts(submissionCounts),commentCount);
        dataBook.setDataPoints(evaluationScheme.getCategories());
        dataBook.setDataSeries("IdealCounts", toList(idealCounts));
        dataBook.setDataSeries("IdealRange", 
                               toList(rescale(idealCounts,100)));
        dataBook.setDataSeries("ActualCounts", toList(actualCounts));
        dataBook.setDataSeries("ActualRange",
                               toList(rescale(actualCounts,100)));
        dataBook.setDataSeries("ActualComments", comments);
        return dataBook;
    }

    /**
     * Returns a list of Submissions which will be used to build the
     * report.  This is the *only* place the submissionId, id and
     * reportFor parameters are now to be used.
     *
     * @return a <code>List</code> value
     */
    private Set<Submission> selectSubmissions() {
        Set<Submission> set = new HashSet<Submission>();
        if (this.requestType == null) {
            log.warn("Report attempted with no requestType parameter");
        } else if (this.requestType.equals("tutor")) {
            set.addAll(mmgr.getTutorsSubmissions(courseId,id));
        } else if (this.requestType.equals("student")) {
            set.addAll(mmgr.getStudentsSubmissions(courseId,id));    
        } else if (this.requestType.equals("course")) {
            set.addAll(mmgr.getSubmissions(courseId));
        } else if (this.requestType.equals("assignment")) {
            // easy to do if it is needed.
        } else {
            log.warn("Invalid ReportFor parameter:- " + this.requestType);
        }
        return set;
    }

    /**
     * Returns a list of Submissions which will be used to build the
     * report.  This is the *only* place the submissionId, id and
     * reportFor parameters are now to be used.
     *
     * @return a <code>List</code> value
     */
    private Map<Category,Integer> 
        weightedIdealCounts(Map<Grade,Integer> count) {
        Map<Category, Integer> expected = new HashMap<Category, Integer>();
        for (Category category : DescriptorFactory.getCategories() ) {
            int sum = 0;
            for (Grade grade : DescriptorFactory.getGrades()) {
                sum += count.get(grade)
                    * evaluationScheme.getIdealProportion(category, grade);
            }
            expected.put(category,sum);
            if (log.isDebugEnabled()) {
                log.debug("weightedIdealCounts: " + category + ", " + sum);
            }
        }
        return expected;        
    }

   /**
     * We work internally with maps, indexed by Category; externally
     * we prefer a list arranged in the same Category order as that
     * provided by the DescriptorFactory; this methods does the
     * conversion.
     *
     * @param map the given map indexed by Categories;
     * @return the corresponding list.
     */
    private List<Integer> toList(Map <Category, Integer> map) {
        List l = new ArrayList<Integer>();
        for (Category category : DescriptorFactory.getCategories() ) {
            l.add(map.get(category));
        }
        return l;
    }

   /**
     * Rescale a collection of proportions, one for each Category so
     * they sum to a given total.  It is assumed that each proportion
     * is a non-negative integer.
     *
     * @param newTotal the sum of the proportions after rescaling;
     * @return the new collections of proportions as a Category - indexed map.
     */
    private Map<Category,Integer> 
        rescale(final Map<Category,Integer> proportions, final int newTotal) {
        Map<Category,Integer> newMap = new HashMap<Category,Integer>();
        float oldTotal = 0; 
        for (Category category : DescriptorFactory.getCategories() ) {
            oldTotal += proportions.get(category);
        }
        if (oldTotal == 0) { // So all counts are zero
            oldTotal = 1;
        }
        if (log.isDebugEnabled()) {
            log.debug("rescale --- oldTotal = " + oldTotal
                      + "; newTotal = " + newTotal);
        }
        for (Category category : DescriptorFactory.getCategories() ) {
            int oldValue = proportions.get(category);
            int newValue = Math.round((oldValue * newTotal) / oldTotal);
            if (log.isDebugEnabled()) {
                log.debug("rescale " + category + "; old "
                          + oldValue + " becomes " + newValue);
            }
            newMap.put(category, newValue) ;
        }
        return newMap;
    }
}
<file_sep>/build.properties
# This file is deliberately empty, and should be kept so. Project
# specific properties should be set in project.properties, and
# the user's version of build.properties (in their home directory)
# used for localisation. 
# 
# Accordingly, this file should not be edited. 
<file_sep>/src/test/java/uk/org/openmentor/web/BaseWebTestCase.java
package uk.org.openmentor.web;

import net.sourceforge.jwebunit.WebTestCase;

/**
 * The class <code>OpenMentorWebTestCase</code> currently test much of
 * the functionality of the completed application, concentrating on
 * simple things and security aspects.
 *
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 * @version $Id$
 */
public class BaseWebTestCase extends WebTestCase {

    /** A user who will authenticate and be able to choose a course. **/
    String username = "OMUser";

    /** Her password **/
    String password = "<PASSWORD>";

    /** A user who will authenticate and be able to choose a course. **/
    String adminName = "admin";

    /** Her password **/
    String adminPassword = "<PASSWORD>";

    /** Decorator title prefix.  The String set in the main decoirator
     * which is prepended to all the actual page titles. **/
    String titlePrefix = "OpenMentor -  ";

    /**
     * Creates a new <code>BaseWebTestCase</code> instance.  The
     * port would be *much* better configured somewhere else, but I
     * don't know how to do it yet.
     *
     * @param name a <code>String</code> value
     */
    public BaseWebTestCase(String name) {
        super(name);
        int port=8080;
        getTestContext().setBaseUrl("http://localhost:" + port
                                    + "/openmentor");
    }

    /**
     * The <code>loginAction</code> helper method Just logs in with
     * the given credentails.
     *
     * @param username the user to log in;
     * @param password her password.
     */
    void loginAction(String username, String password) {
        assertFormPresent("loginForm");
        assertFormElementPresent("j_username");
        assertFormElementPresent("j_password");
        setFormElement("j_username", username);
        setFormElement("j_password", password);
        submit();
    }

    /**
     * The <code>loginAsUser</code> gets us started as the normal user.
     * the given credentails.
     *
     */
    void loginAsUser() {
        beginAt("/signin.jsp");
        loginAction(username,password);
    }

    /**
     * The <code>loginAsAdmin</code> gets us started as the admin user.
     *
     */
    void loginAsAdmin() {
        beginAt("/signin.jsp");
        loginAction(adminName,adminPassword);
    }
}

<file_sep>/src/test/java/uk/org/openmentor/dao/CourseInfoDAOTest.java
package uk.org.openmentor.dao;

import uk.org.openmentor.model.Course;
import uk.org.openmentor.model.Student;
import uk.org.openmentor.model.Tutor;
import uk.org.openmentor.service.CourseInfoManagerTest;

import java.util.List;
import java.util.Iterator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.hibernate.SessionFactory;

public class CourseInfoDAOTest extends BaseDAOTestCase {
	private static Log log = LogFactory.getLog(CourseInfoManagerTest.class);
	private SessionFactory sessionFactory = null;
	private CourseInfoDAO dao = null;

	protected void setUp() throws Exception {
		super.setUp();
		sessionFactory = (SessionFactory) ctx.getBean("sessionFactoryForCourseInfo");
		setSessionFactory(sessionFactory);
		dao = (CourseInfoDAO) ctx.getBean("courseInfoDAO");
		setHibernateTemplate((Object) dao);
		startTestTransaction();
	}

	protected void tearDown() throws Exception {
		endTestTransaction();
		super.tearDown();
		dao = null;
	}
	
	public void testGetCourses() throws Exception {
		List courses = dao.getCourses();
		assertTrue(courses.size() > 0);
		for (Iterator i = courses.iterator(); i.hasNext(); ) {
			Course course = (Course) i.next();
            if (log.isDebugEnabled()) {
                log.debug("Found course: "+course.getId());
            }
		}
 	}
	
	public void testGetStudentsAndTutors() throws Exception {
		List courses = dao.getCourses();
		Course course = (Course) courses.get(0);
        if (log.isDebugEnabled()) {
            log.debug("Testing course: "+course.getId());
        }
		Iterator i;
		String studentId = null;
		String tutorId = null;
		
		List students = dao.getStudents(course);
		for (i = students.iterator(); i.hasNext(); ) {
			Student std = (Student) i.next();
			studentId = std.getOrgId();
            if (log.isDebugEnabled()) {
                log.debug("Found student: "+std.getOrgId()+", "+std.getFirstName()+" "+
                        std.getLastName());
            }
		}
		List tutors = dao.getTutors(course);
		for (i = tutors.iterator(); i.hasNext(); ) {
			Tutor tut = (Tutor) i.next();
			tutorId = tut.getOrgId();
            if (log.isDebugEnabled()) {
                log.debug("Found tutor: "+tut.getOrgId()+", "+tut.getFirstName()+" "+
                        tut.getLastName());
            }
		}
		
		assertFalse(dao.getStudent(course, studentId) == null);
		assertFalse(dao.getTutor(course, tutorId) == null);
	}
}
<file_sep>/src/main/java/uk/org/openmentor/web/AssignmentController.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.web;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import uk.org.openmentor.service.AssignmentManager;
import uk.org.openmentor.service.CourseInfoManager;

import uk.org.openmentor.model.Course;
import uk.org.openmentor.model.UploadData;
import uk.org.openmentor.businesslogic.DescriptorFactory;
import uk.org.openmentor.businesslogic.Grade;

public class AssignmentController implements Controller {
    private static Log log = LogFactory.getLog(AssignmentController.class);
    private AssignmentManager mgr = null;
    private CourseInfoManager cmgr = null;

    public final void setAssignmentManager(final AssignmentManager
                                           assignmentManager) {
        this.mgr = assignmentManager;
    }

    public final void setCourseInfoManager(final CourseInfoManager
                                           courseInfoManager) {
        this.cmgr = courseInfoManager;
    }

    public final ModelAndView handleRequest(final HttpServletRequest request,
                                            final HttpServletResponse response)
        throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("entering 'handleRequest' method...");
        }
        String courseId = (String) request.getSession().getAttribute("course");
        if (log.isDebugEnabled()) {
            log.debug("Selected course: " + courseId);
        }
        Course course = cmgr.getCourse(courseId);

        UploadData data = new UploadData();
        data.setAssignments(mgr.getAssignments(courseId));
        data.setStudents(course.getStudents());
        data.setTutors(course.getTutors());
        data.setGrades(DescriptorFactory.getGrades());

        if (log.isDebugEnabled()) {
            log.debug("Saving " + data.getStudents().size()
                      + " students, " + data.getGrades().size()
                      + " grades, " + data.getTutors().size()
                      + " tutors and " + data.getAssignments().size()
                      + " assignments.");
            log.debug("Transferring to view");
        }

        return new ModelAndView("selectAssignment", "uploadData", data);
    }
}
<file_sep>/src/test/java/uk/org/openmentor/extractor/ExtractTest.java
package uk.org.openmentor.extractor;

import java.util.Set;
import java.io.FileInputStream;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import uk.org.openmentor.extractor.Extractor;

import junit.framework.TestCase;

public class ExtractTest extends TestCase {
    protected ApplicationContext ctx = null;
    private Extractor extractor = null;
    
    public ExtractTest() {
        String[] paths = {"/WEB-INF/applicationContext.xml"};
        ctx = new ClassPathXmlApplicationContext(paths);
    }
    
    protected void setUp() throws Exception {
        extractor = (Extractor) ctx.getBean("extractor");
    }
    
    /**
     * Simple test to check that comments can be successfully
     * extracted from Word files.
     * 
     * @throws Exception
     */
    public void testExtraction() throws Exception {
        extractor.extract(new
                          FileInputStream("src/test/resources/test4.doc"));
        Set result = extractor.getAnnotations();
        assertTrue(result.contains("Balloon here!"));
        assertTrue(result.contains("Second balloon"));
        assertTrue(result.size() == 2);
    }
    
    public void testExtraction2() throws Exception {
        extractor.extract(new
                          FileInputStream("src/test/resources/test2.doc"));
        Set result = extractor.getAnnotations();
        int theSize = result.size();
        assertTrue(theSize > 0);
    }
    
    public void testExtraction3() throws Exception {
        extractor.extract(new
                          FileInputStream("src/test/resources/test3.doc"));
        Set result = extractor.getAnnotations();
        int theSize = result.size();
        assertTrue(theSize > 0);
    }
    
    public void testExtraction4() throws Exception {
        extractor.extract(new
                          FileInputStream("src/test/resources/test1.doc"));
        Set result = extractor.getAnnotations();
        int theSize = result.size();
        assertTrue(theSize > 0);
    }
    
    public void testExtraction5() throws Exception {
        extractor.extract(new 
                          FileInputStream("src/test/resources/test4.doc"));
        Set result = extractor.getAnnotations();
        assertTrue(result.contains("Balloon here!"));
        assertTrue(result.contains("Second balloon"));
        assertTrue(result.size() == 2);
    }
}
<file_sep>/src/test/java/uk/org/openmentor/web/AssignmentControllerTest.java
package uk.org.openmentor.web;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import junit.framework.TestCase;

import org.springframework.mock.web.MockServletContext;
import org.springframework.mock.web.MockHttpSession;
import org.springframework.mock.web.MockHttpServletRequest;

import org.springframework.web.context.support.XmlWebApplicationContext;
import org.springframework.web.servlet.ModelAndView;


public class AssignmentControllerTest extends TestCase {

    private XmlWebApplicationContext ctx;

    public void setUp() {
        String[] paths = {"/WEB-INF/applicationContext.xml",
                          "/WEB-INF/action-servlet.xml"};
        ctx = new XmlWebApplicationContext();
        ctx.setConfigLocations(paths);
        ctx.setServletContext(new MockServletContext(""));
        ctx.refresh();
    }

    /**
     * The <code>testGetAssignments</code> method tests that we select
     * the "model" model and that the new view is correct.  In order
     * to do this, we need to mock up a session containing an
     * appropriate course.
     *
     * @exception Exception if an error occurs
     */
    public void testGetAssignments() throws Exception {

        HttpSession session = new MockHttpSession();
        MockHttpServletRequest req = new MockHttpServletRequest();
        session.setAttribute("course","CM2006");
        req.setSession(session);
        AssignmentController c = (AssignmentController) 
                            ctx.getBean("selectAssignmentController");
        ModelAndView mav =
            c.handleRequest(req, (HttpServletResponse) null);
        //        UploadData data = mav.getModel();
        //        assertNotNull(data.getAssignments());
        assertEquals(mav.getViewName(), "selectAssignment");
    }
}
<file_sep>/src/etc/hibernate_openmentor.sql
drop table if exists tbl_course;
drop table if exists tbl_tutor;
drop table if exists tbl_course_tutors;
drop table if exists tbl_student;
drop table if exists tbl_course_students;

drop table if exists tbl_assignment;
drop table if exists tbl_comment;
drop table if exists tbl_comment_classes;
drop table if exists tbl_submission;
drop table if exists tbl_submission_students;
drop table if exists tbl_submission_tutors;

create table tbl_course (
   id varchar(16) not null,
   course_title varchar(255),
   primary key (id)
);
create table tbl_tutor (
   org_id varchar(255) not null,
   first_name varchar(255) not null,
   last_name varchar(255) not null,
   primary key (org_id)
);
create table tbl_course_tutors (
   course_id varchar(16) not null,
   tutor_id varchar(255) not null,
   primary key (course_id, tutor_id)
);
create table tbl_student (
   org_id varchar(16) not null,
   first_name varchar(255) not null,
   last_name varchar(255) not null,
   primary key (org_id)
);
create table tbl_course_students (
   course_id varchar(16) not null,
   student_id varchar(16) not null,
   primary key (course_id, student_id)
);

create table tbl_assignment (
   id integer not null auto_increment,
   course_id varchar(16) not null,
   assignment_title varchar(255) not null,
   primary key (id)
);

create table tbl_comment (
   id integer not null auto_increment,
   text longtext,
   submission_id integer,
   primary key (id)
);

create table tbl_comment_classes (
   comment_id integer not null,
   category_name varchar(8)
);

create table tbl_submission (
   id integer not null auto_increment,
   body longblob,
   filename varchar(255),
   type varchar(255),
   grade varchar(255),
   assignment_id integer,
   primary key (id)
);

create table tbl_submission_students (
   submission_id integer not null,
   student_id varchar(16)
);

create table tbl_submission_tutors (
   submission_id integer not null,
   tutor_id varchar(16)
);

insert into tbl_assignment (id, course_id, assignment_title) values (1, 'CM2006', 'Ians one');
insert into tbl_assignment (id, course_id, assignment_title) values (2, 'CM2006', 'New one');
insert into tbl_assignment (id, course_id, assignment_title) values (3, 'CM2006', 'Assignment3');
insert into tbl_assignment (id, course_id, assignment_title) values (4, 'CM2006', 'Which');

insert into tbl_comment_classes (comment_id, category_name) values (1, 'C1');
insert into tbl_comment_classes (comment_id, category_name) values (2, 'B1');
insert into tbl_comment_classes (comment_id, category_name) values (2, 'B5');
insert into tbl_comment_classes (comment_id, category_name) values (3, 'A1');
insert into tbl_comment_classes (comment_id, category_name) values (5, 'C1');
insert into tbl_comment_classes (comment_id, category_name) values (6, 'C1');
insert into tbl_comment_classes (comment_id, category_name) values (6, 'B5');
insert into tbl_comment_classes (comment_id, category_name) values (7, 'C2');
insert into tbl_comment_classes (comment_id, category_name) values (7, 'C1');
insert into tbl_comment_classes (comment_id, category_name) values (8, 'B1');
insert into tbl_comment_classes (comment_id, category_name) values (8, 'A1');
insert into tbl_comment_classes (comment_id, category_name) values (8, 'B5');
insert into tbl_comment_classes (comment_id, category_name) values (9, 'D1');
insert into tbl_comment_classes (comment_id, category_name) values (9, 'B1');
insert into tbl_comment_classes (comment_id, category_name) values (9, 'B5');
insert into tbl_comment_classes (comment_id, category_name) values (10, 'B5');
insert into tbl_comment_classes (comment_id, category_name) values (11, 'B1');
insert into tbl_comment_classes (comment_id, category_name) values (11, 'C1');
insert into tbl_comment_classes (comment_id, category_name) values (11, 'B5');

insert into tbl_comment (id, submission_id, text) values (1, 1, 'Would they be close enough geographically?  You haven\'t established that this would just be on offer only for those in the area of the university. From your description of your learners, a meeting as an effort to counter isolation and encourage interaction with known individuals would probably be welcome.  There is a strong argument against using these sessions to introduce learners to the technology.  Some activities done on the computer they will be using, with synchronous telephone and e-mail support, is a more authentic learning environment.');
insert into tbl_comment (id, submission_id, text) values (2, 1, 'Including assessment which, from your description, will probably be important to your learners.');
insert into tbl_comment (id, submission_id, text) values (3, 1, 'Its good to see you found this resource useful.');
insert into tbl_comment (id, submission_id, text) values (4, 1, 'Well argued.');
insert into tbl_comment (id, submission_id, text) values (5, 1, 'Do you mean ?state\' here, that is, funded by public money rather than private?');
insert into tbl_comment (id, submission_id, text) values (6, 1, 'On this subject or something similar?  It would be good to know what lessons you have learned from these experiences.');
insert into tbl_comment (id, submission_id, text) values (7, 1, 'If the exam structure for students is still the same, will the teachers be encouraged to implement this new learning into their classroom practice?');
insert into tbl_comment (id, submission_id, text) values (8, 1, 'This is a good summary.  You also need to note down which areas you will need to explore further.');
insert into tbl_comment (id, submission_id, text) values (9, 1, 'Not a word wasted here! Good introduction, concise but with all relevance information included. It would also be good to add here a statement about the source of your knowledge about your learners. I realise that some of this is woven into your discussion.');
insert into tbl_comment (id, submission_id, text) values (10, 1, 'Pre-assessment of what they can actually do will be important.');
insert into tbl_comment (id, submission_id, text) values (11, 1, 'So they could probably tell you what constitutes a good experiment but would not have designed and carried one out themselves?  And while they may not have been encouraged to reflect on their practice, in the right environment or with appropriate rewards they might adapt quite easily to a constructivist learning environment. This would be interesting to test out.');

insert into tbl_submission (id, filename, grade, assignment_id) values (1, 'comments.doc', '1', 1);

insert into tbl_submission_students (submission_id, student_id) values (1, 'M0000003');

insert into tbl_submission_tutors (submission_id, tutor_id) values (1, '00900004');

insert into tbl_course (id, course_title) values ('CM2006', 'Interface Design');
insert into tbl_course (id, course_title) values ('CMM520', 'Human Interface Design');
insert into tbl_course (id, course_title) values ('CMM007', 'Intranet Systems Development');

insert into tbl_tutor (org_id, first_name, last_name) values ('00900001', 'Stuart', 'Watt');
insert into tbl_tutor (org_id, first_name, last_name) values ('00900002', 'Denise', 'Whitelock');
insert into tbl_tutor (org_id, first_name, last_name) values ('00900003', 'Hassan', 'Sheikh');
insert into tbl_tutor (org_id, first_name, last_name) values ('00900004', 'Aggelos', 'Liapis');

insert into tbl_student (org_id, first_name, last_name) values ('M0000001', 'Sam', 'Smith');
insert into tbl_student (org_id, first_name, last_name) values ('M0000002', 'Pat', 'Parker');
insert into tbl_student (org_id, first_name, last_name) values ('M0000003', 'Sandy', 'Jones');

insert into tbl_course_tutors (course_id, tutor_id) values ('CM2006', '00900001');
insert into tbl_course_tutors (course_id, tutor_id) values ('CM2006', '00900002');
insert into tbl_course_tutors (course_id, tutor_id) values ('CM2006', '00900003');
insert into tbl_course_tutors (course_id, tutor_id) values ('CM2006', '00900004');
insert into tbl_course_tutors (course_id, tutor_id) values ('CMM520', '00900001');
insert into tbl_course_tutors (course_id, tutor_id) values ('CMM007', '00900002');

insert into tbl_course_students (course_id, student_id) values ('CM2006', 'M0000001');
insert into tbl_course_students (course_id, student_id) values ('CM2006', 'M0000002');
insert into tbl_course_students (course_id, student_id) values ('CM2006', 'M0000003');
insert into tbl_course_students (course_id, student_id) values ('CMM520', 'M0000001');
insert into tbl_course_students (course_id, student_id) values ('CMM007', 'M0000002');


<file_sep>/src/main/java/uk/org/openmentor/service/impl/AssignmentManagerImpl.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.service.impl;

import java.util.List;

import uk.org.openmentor.dao.AssignmentDAO;
import uk.org.openmentor.model.Assignment;
import uk.org.openmentor.model.Submission;
import uk.org.openmentor.service.AssignmentManager;

/**
 * Describe class <code>AssignmentManagerImpl</code> here.
 *
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 * @version 1.0
 */
public class AssignmentManagerImpl implements AssignmentManager {

    /**
     * The Data Access Object that does the work.
     *
     */
    private AssignmentDAO dao;

    /**
     * Standard mutator, typically called by Spring as part of the
     * construction process.
     *
     * @param newDao an <code>AssignmentDAO</code> value
     */
    public final void setAssignmentDAO(final AssignmentDAO newDao) {
        this.dao = newDao;
    }

    /**
     * The <code>getAssignments</code> method looks up all known
     * assignments with the given course identifier.
     *
     * @param newCourseId the identifier
     * @return the corresponding <code>List</code> of assignments.
     */
    public final List getAssignments(final String newCourseId) {
        return dao.getAssignments(newCourseId);
    }

    /**
     * The <code>getAssignment</code> method instantiates (from the
     * database of necessary) an assigment knowing only its
     * identifier.
     *
     * @param assId the assignment identifier
     * @return the corresponding <code>Assignment</code> value.
     */
    public final Assignment getAssignment(final String assId) {
        return dao.getAssignment(Integer.parseInt(assId));
    }

    /**
     * Saves the given assigment to the database.
     *
     * @param ass the <code>Assignment</code> to be saved.
     */
    public final void saveAssignment(final Assignment ass) {
        dao.saveAssignment(ass);
    }

    /**
     * Removes the given assignment from the database.
     *
     * @param assId the assignment to be removed.
     */
    public final void removeAssignment(final String assId) {
        dao.removeAssignment(Integer.parseInt(assId));
    }

    /**
     * Instantiates a given submission from the database.
     *
     * @param id the id of the submission.
     * @return the corresponding <code>Submission</code> value.
     */
    public final Submission getSubmission(final String id) {
        return dao.getSubmission(Integer.parseInt(id));
    }
}
<file_sep>/src/test/java/uk/org/openmentor/report/constituents/ChartFactoryCreationTest.java
package uk.org.openmentor.report.constituents;

import junit.framework.TestCase;

import junit.framework.Assert;

import uk.org.openmentor.report.exceptions.*;

public class ChartFactoryCreationTest extends TestCase
{
  public void testFactoryCreation()
  {
    try {
      ChartFactory cf = new ChartFactory( "jCharts" );
      assertNotNull(cf);
    } catch ( NoSuchChartLibException nscle ) {
      Assert.fail( "Threw NoSuchChartLibException when creating a " +
                   "ChartFactory instance with legitimate ID (jCharts)" );
    }
    
    try {
      ChartFactory cf2 = new ChartFactory( "Some other chart lib" );
      assertNotNull(cf2);
      Assert.fail( "Failed to throw NoSuchChartLibException when creating " + 
      "a ChartFactory instance with false library id" );
    } catch ( NoSuchChartLibException nscle ) {
    }
  }
}
<file_sep>/src/main/java/uk/org/openmentor/report/exceptions/NoSuchChartLibException.java
package uk.org.openmentor.report.exceptions;

import uk.org.openmentor.businesslogic.BusinessLogicException;

/**
 * Exception class to indicate that the specified charting library hasn't been
 * found during initialisation or use of a charting factory
 * 
 * @author OpenMentor team
 */

public class NoSuchChartLibException extends BusinessLogicException {
    
    static final long serialVersionUID = -172414568806307735L;
    
    /**
     * Constructs the exception and specifies the message to pass with the
     * exception.
     *
     * @param message
     *            The message to pass with the exception
     */

    public NoSuchChartLibException(String message) {
        super(message);
    }
}
<file_sep>/src/main/java/uk/org/openmentor/evaluator/impl/FileEvaluationScheme.java
/* ====================================================================
 Copyright 2005     Openability Consulting and The Robert Gordon University

 Licensed 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 uk.org.openmentor.evaluator.impl;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;

import uk.org.openmentor.evaluator.EvaluationScheme;
import uk.org.openmentor.businesslogic.Category;
import uk.org.openmentor.businesslogic.Grade;

/**
 * Reads details of current category and grade names etc from a
 * configuration file and makes them available to OpenMentor.
 *
 * @author <a href="mailto:<EMAIL>"><NAME></a>
 * @version 1.0
 */
public class FileEvaluationScheme implements EvaluationScheme {

    /**
     * Our usual logger.
     */
    private static Log log = LogFactory.getLog(FileEvaluationScheme.class);

    /**
     * The source of the configuration data that this class represents
     * to the rest of the application.
     */
    private String sourceFile;

    /**
     * The names of the bands or grades one of which is to be
     * assigned to a submission.
     */
    private List<String> bands = new ArrayList<String>();

    /**
     * Descriptions of the bands or grades listed in the same order as
     * the corresponding names.
     */
    private List<String> bandDescriptions = new ArrayList<String>();

    /**
     * The names of the (display) categories to be used.
     */
    private List<String> categories = new ArrayList<String>();

    /**
     * Descriptions of the (display) categories to be used, listed in
     * the same order as the corresponding names.
     */
    private List<String> categoryDescriptions = new ArrayList<String>();

    /**
     * Holds the expected proportion of each category of comment for
     * each grade or band.
     *
     */
    private Map<String, Map> bandMap = new HashMap<String, Map>();

    /**
     * A map translating rule categories to display categories.  Both
     * the rule set and the display use the cocept of a category in
     * there configuration file.  Several rule set categories can be
     * aggregated into one display category.  This map does the
     * translation.
     */
    private Map<String, String> translator = new HashMap<String, String>();

    /**
     * A category to represent strings which have been uncategorised;
     * set in the configuration file.
     *
     */
    private String defaultCategory;

    /**
     * A description of the category to represent strings which have
     * been uncategorised; set in the configuration file.  It might be
     * "Other Comments"
     *
     */
    private String defaultDescriptor;

    /**
     * Get the list of bands or grades.
     *
     * @return the bands
     */
    final public List<String> getBands() {
        return bands;
    }

     /**
     * Get the list of band or grade descriptions arranged in the same
     * oredr as the bands themselves.
     *
     * @return the band descriptions
     */
    final public List<String> getBandDescriptions() {
        return bandDescriptions;
    }

    /**
     * Get the list of (display) categories.
     *
     * @return the categories.
     */
    final public List<String> getCategories() {
        return categories;
    }

     /**
     * Get the list of (display) category descriptions arranged in the
     * same order as the categories themselves.
     *
     * @return the category descriptions
     */
    final public List<String> getCategoryDescriptions() {
        return categoryDescriptions;
    }

    /**
     * Get the name of the category used to represent strings which
     * have been uncategorised.
     *
     * @return the category name.
     */
    final public String getDefaultCategory() {
        return defaultCategory;
    }

    /**
     * Get the description of the category used to represent strings
     * which have been uncategorised.
     *
     * @return the category name.
     */
    final public String getDefaultDescriptor() {
        return defaultDescriptor;
    }

    /**
     * Get the display category name of the category to which the
     * given rule category should be assigned.
     *
     * @param ruleCategory (rule) category to be aggregated;
     * @return the (display) category to which it belongs.
     */
    final public String ruleCategoryAsCategory(final String ruleCategory) {
        return (String) translator.get(ruleCategory);
    }

    /**
     * Get the list of expected proportions of the various (display)
     * categories for the given band.
     *
     * @param band a <code>String</code> value
     * @return a <code>Map</code> value
     */
    final public Map getBandProportions(final String band) {
        return (Map) bandMap.get(band);
    }

    /**
     * These are the relative proportions expected in each of the
     * categories at the given grade.  To avoid compatibility problems
     * but still return an integer, the given proportion is scaled up
     * by 1000 and cast to an int.  At some point we should require
     * the proprortions to be specified in "mils" and drop this
     * internal factor.
     *
     * @param category a <code>Category</code> value
     * @param grade a <code>Grade</code> value
     * @return an <code>int</code> value
     */
    final public int getIdealProportion(final Category category,
                                        final Grade grade) {
        String band = grade.getLabel();
        String categoryLabel = category.getLabel();
        double p = ((Number) 
            bandMap.get(band).get(categoryLabel)).doubleValue();
        int n = (int) (p*1000);
        if (log.isDebugEnabled()) {
            log.debug("Ideal proportion for (" + grade.getLabel() 
                      + ", " + category + ") is " + n);
        }
        return n;
    }

    /**
     * Loads the configuration data from file and initialises the
     * internal maps.
     *
     * @param source the given input;
     * @exception Exception if an error occurs.
     */
    final public void setSourceFile(final String source) throws Exception {
        this.sourceFile = source;
        if (log.isDebugEnabled()) {
            log.debug("Using evaluator source file: " + this.sourceFile);
        }
        InputStream in = this.getClass().getClassLoader()
            .getResourceAsStream(sourceFile);
        if (in == null) {
            throw new Exception("Can't open evaluator resource: "
                                + sourceFile);
        }
        Reader theReader = new InputStreamReader(in);
        InputSource theInput = new InputSource(theReader);
        setRulesFromInput(theInput);
    }

    /**
     * Does the actual work of interpreting the xml input and setting
     * the internal maps.
     *
     * @param theInput an <code>InputSource</code> value
     * @exception Exception if an error occurs
     */
    private void setRulesFromInput(final InputSource theInput)
        throws Exception {
        Document document;
        NodeList all;
        int length;

        if (log.isDebugEnabled()) {
            log.debug("Setting evaluator rules");
        }

        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(false);
        factory.setNamespaceAware(true);

        try {
            DocumentBuilder builder = factory.newDocumentBuilder();
            document = builder.parse(theInput);

            // First of all, get the bands in order
            all = document.getElementsByTagName("band");
            length = all.getLength();
            for (int i = 0; i < length; i++) {
                Element elem = (Element) all.item(i);
                bands.add(elem.getAttribute("name"));
                bandDescriptions.add(elem.getAttribute("description"));
            }

            // Next, get the categories in order
            all = document.getElementsByTagName("category");
            length = all.getLength();
            for (int i = 0; i < length; i++) {
                Element elem = (Element) all.item(i);
                String category = elem.getAttribute("name");
                if (log.isDebugEnabled()) {
                    log.debug("Category read: " + category);
                }
                categories.add(category);
                String categoryDescription = elem.getAttribute("description");
                categoryDescriptions.add(categoryDescription);
                if (log.isDebugEnabled()) {
                    log.debug("Category description read: " 
                              + categoryDescription);
                }
                NodeList values = elem.getElementsByTagName("rulecategory");
                int count = values.getLength();
                if (count == 0) { // Both names are the same
                    translator.put(category, category);
                } else {
                    for (int j = 0; j < count; j++) {
                        Element rule = (Element) values.item(j);
                        String ruleCategory = rule.getAttribute("name");
                        translator.put(ruleCategory, category);
                    }
                }
            }

            // Handle the default category
            all = document.getElementsByTagName("defaultcategory");
            if (all.getLength() != 1) {
                log.warn("There should be a unique default descriptor.");
            }
            Element element = (Element) all.item(0);
            defaultCategory = element.getAttribute("name");
            defaultDescriptor = element.getAttribute("description");
            translator.put("",defaultCategory);
            // Now we can add the band proportion data
            all = document.getElementsByTagName("proportions");
            length = all.getLength();
            for (int i = 0; i < length; i++) {
                Element elem = (Element) all.item(i);
                String band = elem.getAttribute("band");

                NodeList values = elem.getElementsByTagName("proportion");
                int count = values.getLength();
                Map<String, Double> proportions = 
                    new HashMap<String, Double>();

                for (int j = 0; j < count; j++) {
                    Element prop = (Element) values.item(j);
                    String category = prop.getAttribute("category");
                    String value = prop.getAttribute("value");
                    double k = Double.parseDouble(value);
                    proportions.put(category, new Double(k));
                }

                bandMap.put(band, proportions);
            }
        } catch (SAXParseException spe) {
            throw spe;
        }
    }
}
<file_sep>/src/main/java/uk/org/openmentor/evaluator/impl/StandardEvaluator.java
/* =======================================================================
 * Copyright 2004-2006 The OpenMentor Team.
 *
 * Licensed 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 uk.org.openmentor.evaluator.impl;

import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Set;
import java.util.HashSet;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import uk.org.openmentor.evaluator.Evaluator;
import uk.org.openmentor.evaluator.EvaluationScheme;
import uk.org.openmentor.model.Submission;
import uk.org.openmentor.model.DataSheet;
import uk.org.openmentor.model.Comment;

/**
 * @author <NAME>
 *
 * This evaluates a set of submissions - strictly this belongs in the business
 * logic, but rather than placing it in that package, it is implemented here
 * as a component. Here, we return a feedback object for a submission that
 * can be presented in a view. More properties can be set to customise and
 * clarify the feedback to be presented.
 */

public class StandardEvaluator implements Evaluator {

    /**
     * Our usual logger.
     */
    private static Log log = LogFactory.getLog(StandardEvaluator.class);

    /**
     * Detail of the display categories to use when creating a
     * DataSheet Object; initialised froma configuration file.
     */
    private EvaluationScheme scheme;

    //    private Map properties = new HashMap();

    /**
     * A private <code>StandardEvaluator</code> to ensure this can't
     * be used without initialisation.
     */
    private StandardEvaluator() {
        // This can't be used; so we are always properly initialised.
    }

    /**
     * Creates a new <code>StandardEvaluator</code> instance,
     * typically created by Spring.
     *
     * @param evaluationScheme the essential scheme.
     */
    public StandardEvaluator(final EvaluationScheme evaluationScheme) {
        this.scheme = evaluationScheme;
    }

    /**
     * Sets a key which can be used to influence the way data is
     * extracted or summarised
     *
     * @param key
     * @param value
     */
     //    public void setProperty(String key, String value) {
     //        properties.put((String) key, (String) value);
     //    }

    /**
     * Analysis and returns feedback on a subset of the submission
     * data.
     *
     * @param submission the input on which feedback is required;
     * @return an Object which can be modelled for viewing.
     */
    public final Object getFeedback(final Submission submission) {
        if (log.isDebugEnabled()) {
            log.debug("Requested submission feedback");
        }

        DataSheet table = new DataSheet();
        table.setColumnLabels(Arrays.asList(new String[]
            {"Actual", "Ideal", "Comments"}));
        List<String> rowLabels = new ArrayList<String>();
        rowLabels.addAll(scheme.getCategoryDescriptions());
        rowLabels.add(scheme.getDefaultDescriptor());
        if (log.isDebugEnabled()) {
            for (Iterator i = rowLabels.iterator(); i.hasNext();) {
                log.debug(" " + (String) i.next());
            }
            log.debug(" is the rowLabels");
        }
        table.setRowLabels(rowLabels);

        // Now store the row number in the tables for each Category.
        List categories = scheme.getCategories();
        Map<String, Integer> rowNumber = new HashMap<String, Integer>();
        int rowCount = 0; // the row number of the corresponding categeory
        for (Iterator i = categories.iterator(); i.hasNext();) {
            String category = (String) i.next();
            rowNumber.put(category, new Integer(rowCount++));
        }
        String defaultCategory = scheme.getDefaultCategory();
        rowNumber.put(defaultCategory, new Integer(rowCount));
        /*  the concrete version of the above is:-
                  rowNumber.put("A", new Integer(0));
                  rowNumber.put("B", new Integer(1));
                  rowNumber.put("C", new Integer(2));
                  rowNumber.put("D", new Integer(3));
                  rowNumber.put("E", new Integer(4));
        */
        Set comments = submission.getComments();
        for (Iterator i = comments.iterator(); i.hasNext();) {
            Comment c = (Comment) i.next();

            Set<String> classes = c.getClasses();
            Set<String> newClasses = new HashSet<String>();
            for (Iterator j = classes.iterator(); j.hasNext();) {
                String ruleCategory = (String) j.next();
                String category = scheme.ruleCategoryAsCategory(ruleCategory);
                newClasses.add(category);
            }
            if (newClasses.isEmpty()) {
                newClasses.add(defaultCategory);
            }
            for (Iterator j = newClasses.iterator(); j.hasNext();) {
                String category = (String) j.next();

                // First increment the category counts
                int row = ((Integer) rowNumber.get(category)).intValue();
                Integer current = (Integer) table.getValue(0, row);
                if (current == null) {
                    current = new Integer(0);
                }
                table.setValue(0, row, new Integer(1 + current.intValue()));

                // Now add a new comment
                List<String> commentList = 
                    (List) table.getValue(2, row);
                if (commentList == null) {
                    commentList = new ArrayList<String>();
                }
                commentList.add(c.getText());
                table.setValue(2, row, (List) commentList);
            }
        }

        return table;
    }
}
