org.kuali.student.enrollment.class2.courseoffering.service.impl.CourseOfferingCodeGeneratorDBImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.student.enrollment.class2.courseoffering.service.impl.CourseOfferingCodeGeneratorDBImpl.java

Source

/**
 * Copyright 2012 The Kuali Foundation Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 * http://www.osedu.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * Created by Daniel on 6/7/12
 */
package org.kuali.student.enrollment.class2.courseoffering.service.impl;

import org.apache.commons.lang.StringUtils;
import org.kuali.student.common.UUIDHelper;
import org.kuali.student.enrollment.class2.courseoffering.dao.CodeGeneratorLocksDaoApi;
import org.kuali.student.enrollment.class2.courseoffering.service.CourseOfferingCodeGenerator;
import org.kuali.student.enrollment.courseoffering.dto.CourseOfferingInfo;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;

/**
 * This class Generates Coures Offering codes (CO,AO), but is backed by a DB to be thread safe.
 *
 * @author Kuali Student Team
 */
public class CourseOfferingCodeGeneratorDBImpl implements CourseOfferingCodeGenerator {

    private CodeGeneratorLocksDaoApi codeGeneratorLocksDao;

    /**
     * In this implementation we're going to pass in a list of existing aoCodes into the map
     *
     * @param generatorPropertiesMap A map containing the properties needed to generate the codes
     * @return The next available activity offering code
     */
    @Override
    public String generateActivityOfferingCode(Map<String, Object> generatorPropertiesMap) {

        String nextCode;
        String uniqueCourseCode = (String) generatorPropertiesMap
                .get(CourseOfferingCodeGenerator.COURSE_OFFERING_CODE_KEY);
        List<String> aoCodes = (List<String>) generatorPropertiesMap
                .get(CourseOfferingCodeGenerator.ACTIVITY_OFFERING_CODE_LIST_KEY);

        // If this is the first code, send back "A"
        if (aoCodes == null || aoCodes.isEmpty()) {
            nextCode = "A";
        } else {
            nextCode = calculateNextCode(aoCodes);
        }

        String uuid = UUIDHelper.genStringUUID();
        String pendingKey = uniqueCourseCode + "-P";
        String namespace = "activityOfferingCode";

        //System.out.println("Create Pending: " + namespace + " " + pendingKey + " " + uuid);
        getCodeGeneratorLocksDao().createLock(uuid, pendingKey, namespace);
        while (!isCodeValid(nextCode, uniqueCourseCode, namespace)) {
            aoCodes.add(nextCode);
            nextCode = calculateNextCode(aoCodes);
        }

        //System.out.println("Delete Pending: " + namespace + " " + pendingKey + " " + uuid);
        getCodeGeneratorLocksDao().removeLock(uuid, pendingKey, namespace);
        //System.out.println("Remove All Locks: " + namespace + " " + pendingKey + " " + uuid);
        getCodeGeneratorLocksDao().cleanLocks(uniqueCourseCode, pendingKey, namespace);
        return nextCode;
    }

    @Override
    public String generateCourseOfferingInternalCode(List<CourseOfferingInfo> existingCourseOfferings) {

        String courseOfferingCode = "";
        String nextCode;
        List<String> coCodes = new ArrayList<String>();

        //If this is the first code, send back "A"
        if (existingCourseOfferings == null || existingCourseOfferings.isEmpty()) {
            return "A";
        }

        List<String> internalCodes = new ArrayList<String>();
        for (CourseOfferingInfo coInfo : existingCourseOfferings) {
            if (coInfo.getCourseNumberSuffix() != null) {
                internalCodes.add(coInfo.getCourseNumberSuffix());
            }
            courseOfferingCode = coInfo.getCourseOfferingCode();
        }

        String uuid = UUIDHelper.genStringUUID();
        String pendingKey = courseOfferingCode + "-P";
        String namespace = "courseOfferingCode";

        //System.out.println("Create Pending: " + namespace + " " + pendingKey + " " + uuid);
        getCodeGeneratorLocksDao().createLock(uuid, pendingKey, namespace);
        nextCode = calculateNextCode(internalCodes);

        if (courseOfferingCode != null && !courseOfferingCode.equals("") && nextCode != null
                && !nextCode.equals("")) {
            while (!isCodeValid(nextCode, courseOfferingCode, namespace)) {
                coCodes.add(new String(nextCode));
                nextCode = calculateNextCode(coCodes);
            }
        }

        //System.out.println("Delete Pending: " + namespace + " " + pendingKey + " " + uuid);
        getCodeGeneratorLocksDao().removeLock(uuid, pendingKey, namespace);
        //System.out.println("Remove All Locks: " + namespace + " " + pendingKey + " " + uuid);
        getCodeGeneratorLocksDao().cleanLocks(courseOfferingCode, pendingKey, namespace);

        return nextCode;
    }

    public String calculateNextCode(List<String> codes) {
        // Always start with A if it's not there
        if (!codes.contains("A")) {
            return "A";
        }

        // Sort the list so we can fill in gaps
        Collections.sort(codes, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                if (o1.length() == o2.length()) {
                    return o1.compareTo(o2);
                } else {
                    return o1.length() - o2.length();
                }
            }
        });

        // Fill in the gaps of letters
        for (String s : codes) {
            //For each existing code, find the next valid generated code and see if it exists
            String nextCode = getNextCode(s);
            if (!codes.contains(nextCode)) {
                return nextCode;
            }
        }

        // This should never be reached unless there is an infinite list of strings passed in
        throw new RuntimeException("Error generating codes");
    }

    /*
     * Gets the next letter of the alphabet in caps in the form A,B...Z,AA,AB...AZ,BA,BB..BZ,CA,CB....ZZ,AAA,AAB...
     *
     * protected so it can be accessed in the Test class
     *
     * @param source source code string
     * @return next code
     */
    protected String getNextCode(String source) {
        if (StringUtils.isEmpty(source)) {
            return "A";
        } else if (StringUtils.endsWithIgnoreCase(source, "Z")) {
            return getNextCode(StringUtils.substringBeforeLast(source, "Z")) + "A";
        } else {
            char lastLetter = source.charAt(source.length() - 1);
            return StringUtils.substringBeforeLast(source, "" + lastLetter) + ++lastLetter;
        }
    }

    private boolean isCodeValid(String newCode, String uniqueKey, String namespace) {
        boolean bRet = false;
        try {
            getCodeGeneratorLocksDao().createLock(newCode, uniqueKey, namespace);
            bRet = true;
        } catch (Exception ex) {
            System.out.println(
                    "Code not unique." + newCode + ", " + uniqueKey + ", " + namespace + " " + ex.getMessage());
        }

        return bRet;
    }

    public CodeGeneratorLocksDaoApi getCodeGeneratorLocksDao() {
        return codeGeneratorLocksDao;
    }

    public void setCodeGeneratorLocksDao(CodeGeneratorLocksDaoApi codeGeneratorLocksDao) {
        this.codeGeneratorLocksDao = codeGeneratorLocksDao;
    }
}