Android Open Source - GradeCalculator-Android Course






From Project

Back to project page GradeCalculator-Android.

License

The source code is released under:

Copyright (C) 2012 Jimmy Theis Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in ...

If you think the Android project GradeCalculator-Android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (C) 2012 Jimmy Theis. Licensed under the MIT License:
 * /*from   ww  w.j a va  2  s.  c  om*/
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.jetheis.android.grades.model;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;

import android.os.Parcel;
import android.os.Parcelable;

import com.jetheis.android.grades.storage.CourseStorageAdapter;
import com.jetheis.android.grades.storage.CourseStorageAdapter.CourseStorageIterator;
import com.jetheis.android.grades.storage.GradeComponentStorageAdapter;
import com.jetheis.android.grades.storage.Storable;

/**
 * Representation of a single academic course. Each course has a name and a
 * {@link CourseType} (given by {@link #getCourseType()}), which represents how
 * the courses final grade is calculated: either by adding points for a total
 * score ({@link CourseType#POINT_TOTAL}), or by weighting categories'
 * percentages ({@link CourseType#PERCENTAGE_WEIGHTING}) together.
 */
public class Course extends Storable implements Comparable<Course>, Parcelable {

    /**
     * A simple representation of a course type.
     * 
     */
    public enum CourseType {
        /**
         * The course calculates its final grade by summing the points of its
         * included grade components
         */
        POINT_TOTAL(1),
        /**
         * The course calculates its final grade by applying a specific weight
         * to the percentage grade of each of its included grade components.
         */
        PERCENTAGE_WEIGHTING(2);

        private final int mIntIdentifier;;

        /**
         * Constructor for {@link CourseType}.
         * 
         * @param intIdenfifier
         *            The integer that will be used to uniquely identify the
         *            {@link CourseType} when it is stored as a database value.
         */
        CourseType(int intIdenfifier) {
            mIntIdentifier = intIdenfifier;
        }

        /**
         * Convert this {@link CourseType} to an integer for storage in a
         * database row.
         * 
         * @return This {@link CourseType}'s unique integer identifier.
         */
        public int toInt() {
            return mIntIdentifier;
        }

        /**
         * Convert an integer identifier from a database row back into a proper
         * {@link CourseType}.
         * 
         * @param intIdentifier
         *            The integer identifier from a database row.
         * @return The {@link CourseType} corresponding to the given integer
         *         identifier.
         */
        public static CourseType fromInt(int intIdentifier) {
            switch (intIdentifier) {
            case 1:
                return POINT_TOTAL;
            case 2:
                return PERCENTAGE_WEIGHTING;
            }

            return null;
        }
    }

    private String mName;
    private CourseType mCourseType;
    private double mOverallScore;
    private double mTotalPossibleScore;

    private Collection<GradeComponent> mGradeComponents;

    /**
     * Default constructor (has no side effects)
     */
    public Course() {

    }

    /**
     * Get the human readable name of the course.
     * 
     * @return The human readable name of the course.
     */
    public String getName() {
        return mName;
    }

    /**
     * Set the human readable name of the course.
     * 
     * @param name
     *            The new human readable name of the course.
     */
    public void setName(String name) {
        mName = name;
    }

    /**
     * Get the "type" ({@link CourseType}) of the course.
     * 
     * @return The {@link CourseType} that designates how this course calculates
     *         its final score.
     */
    public CourseType getCourseType() {
        return mCourseType;
    }

    /**
     * Set the "type" ({@link CourseType}) of the course. If this setting is a
     * change from this {@link Course}'s current type, all
     * {@link GradeComponent}s will be destroyed as a side effect.
     * 
     * @param courseType
     *            The new {@link CourseType} of this course.
     */
    public void setCourseType(CourseType courseType) {
        if (mCourseType != courseType) {
            initializeGradeComponents();

            for (GradeComponent gradeComponent : mGradeComponents) {
                gradeComponent.destroy();
            }
        }

        mCourseType = courseType;
    }

    /**
     * Calculate the total score for this course by combining scores for all
     * contained {@link GradeComponent}s.
     * 
     * @return The overall score for this course, as a double less than or equal
     *         to {@code 1.0}.
     */
    public double getOverallScore() {
        if (getGradeComponents().size() == 0) {
            loadConnectedObjects();
        }

        calculateOverallScore();

        if (Double.isNaN(mOverallScore)) {
            return 0;
        }

        return mOverallScore;
    }

    /**
     * Get the total possible score for this course, based on the
     * {@link CourseType} of this course (given by {@link #getCourseType()}). If
     * this course is of type {@link CourseType#POINT_TOTAL}, this value will be
     * the total available points. If this course of is type
     * {@link CourseType#PERCENTAGE_WEIGHTING} , this value will be the total
     * sum of all the weights of the contained {@link PercentageGradeComponent}
     * s. In this case, the expected value will be {@code 1.0}.
     * 
     * @return The total possible score for this course, based on the
     *         {@link CourseType} of this course (given by
     *         {@link #getCourseType()}).
     */
    public double getTotalPossibleScore() {
        calculateOverallScore();

        return mTotalPossibleScore;
    }

    /**
     * Calculate the overall and total score values, saving them off in the
     * private member variables {@link #mOverallScore} and
     * {@link #mTotalPossibleScore}.
     */
    private void calculateOverallScore() {
        mTotalPossibleScore = 0;

        if (getCourseType() == CourseType.POINT_TOTAL) {

            // Sum total points
            double totalPoints = 0;
            double totalEarned = 0;

            for (GradeComponent component : getGradeComponents()) {
                PointTotalGradeComponent pointComponent = (PointTotalGradeComponent) component;
                totalPoints += pointComponent.getTotalPoints();
                totalEarned += pointComponent.getPointsEarned();
            }

            // Store the point total for reference
            mTotalPossibleScore = totalPoints;

            mOverallScore = totalEarned / totalPoints;
        } else {

            // Sum weighted individual scores
            double totalScore = 0;
            double totalWeight = 0;

            for (GradeComponent component : getGradeComponents()) {
                PercentageGradeComponent percentageComponent = (PercentageGradeComponent) component;
                totalScore += percentageComponent.getEarnedPercentage() / 100.0
                        * percentageComponent.getWeight() / 100.0;
                totalWeight += percentageComponent.getWeight();
            }

            // Store the weighting total for reference
            mTotalPossibleScore = totalWeight;

            mOverallScore = totalScore;
        }
    }

    /**
     * Get the collection of grade components that this course contains. If this
     * course contains no grade components, an empty collection will be
     * returned. If this object is persisted in the database, grade components
     * will be loaded automatically (if they have not be already) here.
     * 
     * @return The collection of grade components that this course contains.
     */
    public List<GradeComponent> getGradeComponents() {
        initializeGradeComponents();

        // Copy components to a new list so the original can't be modified
        ArrayList<GradeComponent> result = new ArrayList<GradeComponent>();
        result.addAll(mGradeComponents);

        Collections.sort(result, new Comparator<GradeComponent>() {

            @Override
            public int compare(GradeComponent lhs, GradeComponent rhs) {
                return (int) Math.signum(lhs.getId() - rhs.getId());
            }
        });

        return result;
    }

    /**
     * Add a grade component to this course. This method also takes care of
     * calling {@link GradeComponent#setCourse(Course)} on the added component,
     * thus establishing a proper back link between the objects. This method
     * also prevents duplicate {@link GradeComponent}s from being added to the
     * course, simply by ignoring components that have already been added.
     * 
     * @param gradeComponent
     *            The grade component to add to this course.
     */
    public void addGradeComponent(GradeComponent gradeComponent) throws IllegalArgumentException {
        initializeGradeComponents();

        validateGradeComponent(gradeComponent);

        if (!mGradeComponents.contains(gradeComponent)) {
            mGradeComponents.add(gradeComponent);
            gradeComponent.setCourse(this);
        }
    }

    /**
     * Add several grade components to this course. This method also takes care
     * of calling {@link GradeComponent#setCourse(Course)} on the added
     * components, thus establishing a proper back link between the objects.
     * This method also prevents duplicate {@link GradeComponent}s from being
     * added to the course, simply by ignoring components that have already been
     * added.
     * 
     * @param gradeComponents
     *            The grade components to add to this course.
     */
    public void addGradeComponents(Collection<GradeComponent> gradeComponents)
            throws IllegalArgumentException {
        initializeGradeComponents();

        for (GradeComponent gradeComponent : gradeComponents) {
            validateGradeComponent(gradeComponent);

            if (!mGradeComponents.contains(gradeComponent)) {
                mGradeComponents.add(gradeComponent);
                gradeComponent.setCourse(this);
            }
        }
    }

    /**
     * Validate a to-be-added {@link GradeComponent}, making sure its type
     * corresponds with this {@link Course}'s {@link CourseType}. If there is a
     * mismatch, a {@link IllegalArgumentException} is thrown.
     * 
     * @param gradeComponent
     *            The {@link GradeComponent} to be added.
     * @throws IllegalArgumentException
     *             When the given {@link GradeComponent}'s type does not
     *             correspond to this {@link Course}'s {@link CourseType}.
     */
    private void validateGradeComponent(GradeComponent gradeComponent)
            throws IllegalArgumentException {
        if (gradeComponent instanceof PercentageGradeComponent
                && getCourseType() == CourseType.POINT_TOTAL
                || gradeComponent instanceof PointTotalGradeComponent
                && getCourseType() == CourseType.PERCENTAGE_WEIGHTING) {
            throw new IllegalArgumentException("Grading type argument mismatch");
        }
    }

    /**
     * Remove a {@link GradeComponent} from this {@link Course}. If the
     * component is not a member of this course, nothing is done.
     * 
     * @param gradeComponent
     *            The grade component to remove from this course.
     */
    public void removeGradeComponent(GradeComponent gradeComponent) {
        initializeGradeComponents();

        if (mGradeComponents.contains(gradeComponent)) {
            mGradeComponents.remove(gradeComponent);
        }
    }

    /**
     * Remove several grade components from this course.
     * 
     * @param gradeComponents
     *            The grade components to remove from this course.
     */
    public void removeGradeComponents(Collection<GradeComponent> gradeComponents) {
        initializeGradeComponents();

        for (GradeComponent gradeComponent : gradeComponents) {
            if (mGradeComponents.contains(gradeComponent)) {
                mGradeComponents.remove(gradeComponent);
            }
        }
    }

    /**
     * Clear all {@link GradeComponent}s from this {@link Course}.
     */
    public void clearGradeComponents() {
        removeGradeComponents(getGradeComponents());
    }

    /**
     * A helper method to load connected {@link GradeComponent}s or instantiate
     * a new empty {@link Collection} in the {@link #mGradeComponents} member
     * variable.
     */
    private void initializeGradeComponents() {
        if (mGradeComponents == null) {
            mGradeComponents = new HashSet<GradeComponent>();
        }
    }

    /**
     * Get all database-tracked {@link Course}s.
     * 
     * @return A {@link List} of all database-tracked {@link Course}s, sorted
     *         alphabetically by name.
     */
    public static List<Course> getAllCourses() {
        CourseStorageIterator allCourses = new CourseStorageAdapter().getAllCourses();
        ArrayList<Course> result = new ArrayList<Course>(allCourses.getCount());

        for (Course course : allCourses) {
            result.add(course);
        }

        return result;
    }

    /**
     * Retrieve a specific {@link Course} from the database, using its unique
     * identifier.
     * 
     * @param id
     *            The unique identifier of the {@link Course} to be retrieved.
     * @return The {@link Course} if found, null otherwise.
     */
    public static Course getCourseById(long id) {
        return new CourseStorageAdapter().getCourseById(id);
    }

    /**
     * Delete all {@link Course} entries from the database
     * 
     * @return The number of database records affected.
     */
    public static int destroyAllCourses() {
        return new CourseStorageAdapter().deleteAllCourses();
    }

    @Override
    public void save() {
        new CourseStorageAdapter().saveCourse(this);

        if (mGradeComponents != null) {
            for (GradeComponent component : mGradeComponents) {
                component.save();
            }
        }
    }

    @Override
    public void destroy() {
        new CourseStorageAdapter().deleteCourse(this);

        if (mGradeComponents != null) {
            for (GradeComponent component : mGradeComponents) {
                component.destroy();
            }

            mGradeComponents.clear();
        }
    }

    @Override
    public void loadConnectedObjects() {
        clearGradeComponents();

        if (getCourseType() == CourseType.POINT_TOTAL) {
            for (PointTotalGradeComponent gradeComponent : new GradeComponentStorageAdapter()
                    .getPointTotalGradeComponentsByCourse(this)) {
                addGradeComponent(gradeComponent);
            }
        } else {
            for (PercentageGradeComponent gradeComponent : new GradeComponentStorageAdapter()
                    .getPercentageGradeComponentsByCourse(this)) {
                addGradeComponent(gradeComponent);
            }
        }
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Course))
            return false;
        return compareTo((Course) o) == 0;
    }

    @Override
    public int compareTo(Course other) {
        if (getId() > 0 && other.getId() > 0) {
            return (int) Math.signum(getId() - other.getId());
        }

        if (!getName().equals(other.getName())) {
            return getName().compareTo(other.getName());
        }

        return (int) Math.signum(getCourseType().toInt() - other.getCourseType().toInt());
    }

    @Override
    public String toString() {
        return "Course " + getName();
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeLong(getId());
        dest.writeString(getName());
        dest.writeInt(getCourseType().toInt());
    }

    public Course(Parcel parcel) {
        setId(parcel.readLong());
        setName(parcel.readString());
        setCourseType(CourseType.fromInt(parcel.readInt()));
    }

    public static final Parcelable.Creator<Course> CREATOR = new Parcelable.Creator<Course>() {

        @Override
        public Course createFromParcel(Parcel source) {
            return new Course(source);
        }

        @Override
        public Course[] newArray(int size) {
            return new Course[size];
        }
    };
}




Java Source Code List

com.jetheis.android.grades.Constants.java
com.jetheis.android.grades.activity.AboutActivity.java
com.jetheis.android.grades.activity.CourseActivity.java
com.jetheis.android.grades.activity.CourseListActivity.java
com.jetheis.android.grades.billing.BillingWrapper.java
com.jetheis.android.grades.billing.FreeBillingWrapper.java
com.jetheis.android.grades.billing.Security.java
com.jetheis.android.grades.billing.googleplay.GooglePlayBillingConstants.java
com.jetheis.android.grades.billing.googleplay.GooglePlayBillingReceiver.java
com.jetheis.android.grades.billing.googleplay.GooglePlayBillingService.java
com.jetheis.android.grades.billing.googleplay.GooglePlayBillingWrapper.java
com.jetheis.android.grades.fragment.AboutFragment.java
com.jetheis.android.grades.fragment.AddCourseDialogFragment.java
com.jetheis.android.grades.fragment.AddGradeComponentDialogFragment.java
com.jetheis.android.grades.fragment.BuyFullVersionFragment.java
com.jetheis.android.grades.fragment.CourseListFragment.java
com.jetheis.android.grades.fragment.CourseOverviewFragment.java
com.jetheis.android.grades.fragment.EditCourseDialogFragment.java
com.jetheis.android.grades.fragment.EditGradeComponentDialogFragment.java
com.jetheis.android.grades.fragment.GradeComponentListFragment.java
com.jetheis.android.grades.listadapter.CourseArrayAdapter.java
com.jetheis.android.grades.model.Course.java
com.jetheis.android.grades.model.GradeComponent.java
com.jetheis.android.grades.model.PercentageGradeComponent.java
com.jetheis.android.grades.model.PointTotalGradeComponent.java
com.jetheis.android.grades.storage.CourseStorageAdapter.java
com.jetheis.android.grades.storage.DatabaseHelper.java
com.jetheis.android.grades.storage.GradeComponentStorageAdapter.java
com.jetheis.android.grades.storage.Storable.java
com.jetheis.android.grades.storage.StorageAdapter.java
com.jetheis.android.grades.storage.StorageIterator.java