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}