001/**
002 * Copyright 2005-2017 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.kew.impl.document;
017
018import org.kuali.rice.coreservice.api.CoreServiceApiServiceLocator;
019import org.kuali.rice.coreservice.api.parameter.Parameter;
020import org.kuali.rice.coreservice.api.parameter.ParameterKey;
021import org.kuali.rice.kew.doctype.ApplicationDocumentStatus;
022import org.kuali.rice.kew.doctype.bo.DocumentType;
023import org.kuali.rice.kew.service.KEWServiceLocator;
024import org.springframework.util.CollectionUtils;
025
026import java.util.ArrayList;
027import java.util.Collections;
028import java.util.Comparator;
029import java.util.LinkedHashMap;
030import java.util.List;
031import java.util.regex.Matcher;
032import java.util.regex.Pattern;
033
034/**
035 * Utility class for assisting with application document status
036 */
037public class ApplicationDocumentStatusUtils {
038
039    /**
040     * @see {@link #getApplicationDocumentStatusCategories(org.kuali.rice.kew.doctype.bo.DocumentType)}
041     */
042    public static LinkedHashMap<String, List<String>> getApplicationDocumentStatusCategories(String documentTypeName) {
043        DocumentType documentType = KEWServiceLocator.getDocumentTypeService().findByName(documentTypeName);
044        return getApplicationDocumentStatusCategories(documentType);
045    }
046
047    /**
048     * <p>Returns the categories defined for the given DocumentType.  The returned ordered map's keys
049     * are the category names, and the values are Lists of the status values within the category.  Ordering is
050     * maintained so that the form field can reflect the configuration in the parameter. </p>
051     *
052     * <p>Note that the hierarchy for the given document type will be walked until application document statuses are
053     * found, or the available ancestry is exhausted.</p>
054     *
055     * @param documentType the document type for which to retrieve the defined application document status categories
056     * @return the application document status categories, or an empty map if there are none defined for the given
057     * document type
058     */
059    public static LinkedHashMap<String, List<String>> getApplicationDocumentStatusCategories(DocumentType documentType) {
060        LinkedHashMap<String, List<String>> results = new LinkedHashMap<String, List<String>>();
061
062        if (documentType != null) {
063            // check the hierarchy until we find an ancestor with app doc statuses configured, or we exhaust the
064            // ancestry in which case the docTypeAncestor will be null
065//            DocumentType docTypeAncestor = documentType;
066//            while (docTypeAncestor != null) {
067//                // save a potentially un-needed fetch of the parent doc type fetch
068//                if (!CollectionUtils.isEmpty(docTypeAncestor.getValidApplicationStatuses())) {
069//                    break;
070//                }
071//
072//                // walk up the hierarchy
073//                docTypeAncestor = docTypeAncestor.getParentDocType();
074//            }
075//
076//            if (docTypeAncestor != null) {
077            // if you un-comment the hierarchy traversal logic above, you'll also need to replace
078            // documentType with docTypeAncestor in this conditional block:
079            if (!CollectionUtils.isEmpty(documentType.getValidApplicationStatuses())) {
080                    sortBySequenceNumber(documentType.getValidApplicationStatuses());
081                // build data structure for groupings and create headings
082                for (ApplicationDocumentStatus status : documentType.getValidApplicationStatuses()) {
083                    if (status.getCategoryName() != null) {
084                        if (!results.containsKey(status.getCategoryName())) {
085                             results.put(status.getCategoryName(), new ArrayList<String>());
086                        }
087                        results.get(status.getCategoryName()).add(status.getStatusName());
088                    }
089                }
090            }
091        }
092        return results;
093    }
094
095    /**
096     * <p>Sorts a List of {@link org.kuali.rice.kew.doctype.ApplicationDocumentStatus}es by their sequenceNumber
097     * properties.  The sequenceNumber for a status may be null, and this Comparator considers a null sequenceNumber
098     * to be &lt; any non-null sequenceNumber.</p>
099     *
100     * @param statuses the List of statuses to sort
101     * @throws IllegalArgumentException if either of the given
102     * {@link org.kuali.rice.kew.doctype.ApplicationDocumentStatus} arguments is null.
103     *
104     * @see Comparator#compare(Object, Object)
105     */
106    private static void sortBySequenceNumber(List<ApplicationDocumentStatus> statuses) {
107        Collections.sort(statuses, new Comparator<ApplicationDocumentStatus>() {
108            @Override
109            public int compare(ApplicationDocumentStatus o1, ApplicationDocumentStatus o2) {
110                if (o1 == null || o2 == null) throw new IllegalArgumentException();
111                // cover null sequence number cases
112                if (o1.getSequenceNumber() == null) {
113                    if (o2.getSequenceNumber() == null) {
114                        return 0;
115                    } else {
116                        return -1; // consider null to always be less than non-null
117                    }
118                }
119                if (o2.getSequenceNumber() == null) {
120                    return 1; // consider null to always be less than non-null
121                }
122                return o1.getSequenceNumber().compareTo(o2.getSequenceNumber());
123            }
124        });
125    }
126
127}