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.krms.impl.ui;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
020import org.kuali.rice.core.api.util.ConcreteKeyValue;
021import org.kuali.rice.core.api.util.KeyValue;
022import org.kuali.rice.krad.uif.control.UifKeyValuesFinderBase;
023import org.kuali.rice.krad.uif.view.ViewModel;
024import org.kuali.rice.krad.web.form.MaintenanceDocumentForm;
025import org.kuali.rice.krms.api.KrmsApiServiceLocator;
026import org.kuali.rice.krms.api.repository.RuleManagementService;
027import org.kuali.rice.krms.api.repository.context.ContextDefinition;
028import org.kuali.rice.krms.api.repository.function.FunctionDefinition;
029import org.kuali.rice.krms.api.repository.operator.CustomOperator;
030import org.kuali.rice.krms.api.repository.type.KrmsTypeDefinition;
031import org.kuali.rice.krms.api.repository.type.KrmsTypeRepositoryService;
032import org.kuali.rice.krms.api.repository.typerelation.RelationshipType;
033import org.kuali.rice.krms.api.repository.typerelation.TypeTypeRelation;
034import org.kuali.rice.krms.framework.engine.expression.ComparisonOperator;
035import org.kuali.rice.krms.impl.util.KrmsImplConstants;
036import org.kuali.rice.krms.impl.util.KrmsServiceLocatorInternal;
037
038import javax.xml.namespace.QName;
039import java.util.ArrayList;
040import java.util.Collections;
041import java.util.List;
042
043/**
044 * ValueFinder for the operators available while editing a proposition.
045 *
046 * <p>Fetches the KeyValues for the operators available for use in propositions within the current context.</p>
047 *
048 * @author Kuali Rice Team (rice.collab@kuali.org)
049 */
050public class PropositionOpCodeValuesFinder extends UifKeyValuesFinderBase {
051
052    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(PropositionOpCodeValuesFinder.class);
053
054    private static final List<KeyValue> LABELS;
055
056    static {
057        final List<KeyValue> labels = new ArrayList<KeyValue>( ComparisonOperator.values().length );
058        for (ComparisonOperator operator : ComparisonOperator.values()) {
059            labels.add(new ConcreteKeyValue(operator.getCode(), operator.getCode()));
060        }
061
062        LABELS = Collections.unmodifiableList(labels);
063    }
064
065    /**
066     * @see UifKeyValuesFinderBase#getKeyValues(org.kuali.rice.krad.uif.view.ViewModel)
067     */
068    @Override
069    public List<KeyValue> getKeyValues(ViewModel model) {
070
071        List<KeyValue> keyValues = new ArrayList<KeyValue>(LABELS);
072
073        MaintenanceDocumentForm maintenanceForm = (MaintenanceDocumentForm) model;
074        AgendaEditor agendaEditor =
075                (AgendaEditor) maintenanceForm.getDocument().getNewMaintainableObject().getDataObject();
076
077        // context should never be null as otherwise rule editing would not be allowed
078        ContextDefinition context = getRuleManagementService().getContext(agendaEditor.getAgenda().getContextId());
079
080        keyValues.addAll(getCustomOperatorsKeyValuesForContextType(context.getTypeId()));
081
082        return keyValues;
083    }
084
085    /**
086     * Gets the {@link KeyValue}s for the {@link CustomOperator}s allowed to be used by the given context type.
087     *
088     * @param contextTypeId the context's type id
089     * @return the KeyValue list for the allowed custom operator types
090     */
091    private List<KeyValue> getCustomOperatorsKeyValuesForContextType(String contextTypeId) {
092        List<KeyValue> keyValues = new ArrayList<KeyValue>();
093
094        if (contextTypeId == null) {
095            return keyValues;
096        }
097
098        // Runtime checking for the CustomOperator service interface needs to be done
099        List<TypeTypeRelation> typeRelations =
100                getTypeRepositoryService().findTypeTypeRelationsByFromType(contextTypeId);
101
102        for (TypeTypeRelation typeRelation : typeRelations) {
103            if (typeRelation.getRelationshipType().equals(RelationshipType.USAGE_ALLOWED)) {
104                KrmsTypeDefinition krmsType = getTypeRepositoryService().getTypeById(typeRelation.getToTypeId());
105
106                Object service = getTypeServiceImplementation(krmsType);
107
108                if (service != null && service instanceof CustomOperator) {
109                    // Bingo, we have a custom operator to add to the list
110                    keyValues.add(getKeyValueForCustomOperator(krmsType, (CustomOperator)service));
111                }
112            }
113        }
114
115        return keyValues;
116    }
117
118    /**
119     * Gets the KeyValue for the given custom operator and corresponding KRMS type.
120     *
121     * <p>A special convention is used for the key values: {@code customOperator:<nameSpace>:<serviceName>}</p>
122     * <p>Values are the function name, which is what will be displayed in the operator dropdown.</p>
123     *
124     * @param krmsType the krms type for the given customOperator
125     * @param customOperator the custom operator, assumed to be not null
126     * @return a KeyValue
127     */
128    private ConcreteKeyValue getKeyValueForCustomOperator(KrmsTypeDefinition krmsType, CustomOperator customOperator) {
129        FunctionDefinition operatorFunctionDefinition =
130                customOperator.getOperatorFunctionDefinition();
131        String key = KrmsImplConstants.CUSTOM_OPERATOR_PREFIX
132                + krmsType.getNamespace() +
133                ":" + krmsType.getServiceName();
134
135        return new ConcreteKeyValue(key, operatorFunctionDefinition.getName());
136    }
137
138    /**
139     * Returns the service for the given KRMS type, or null if none can be found.
140     *
141     * @param krmsType the type to return the service for
142     * @return the service or null if none can be found
143     */
144    private Object getTypeServiceImplementation(KrmsTypeDefinition krmsType) {
145        Object service = null;
146
147        if (krmsType != null && !StringUtils.isEmpty(krmsType.getServiceName())) {
148            QName serviceQName = new QName(krmsType.getNamespace(), krmsType.getServiceName());
149            service = GlobalResourceLoader.getService(serviceQName);
150        }
151
152        return service;
153    }
154
155    /**
156     * lazy initialization holder class idiom, see Effective Java 2nd Ed. item # 71, J. Bloch
157     */
158    private static class TypeRepositoryServiceHolder {
159        static final KrmsTypeRepositoryService typeRepositoryService =
160                KrmsApiServiceLocator.getKrmsTypeRepositoryService();
161    }
162
163    // getter for lazy init service
164    private KrmsTypeRepositoryService getTypeRepositoryService() {
165        return TypeRepositoryServiceHolder.typeRepositoryService;
166    }
167
168    /**
169     * lazy initialization holder class idiom, see Effective Java 2nd Ed. item # 71, J. Bloch
170     */
171    private static class RuleManagementServiceHolder {
172        static final RuleManagementService ruleManagementService = KrmsServiceLocatorInternal.getService(
173                "ruleManagementService");
174    }
175
176    // getter for lazy init service
177    private RuleManagementService getRuleManagementService() {
178        return RuleManagementServiceHolder.ruleManagementService;
179    }
180
181}