001/**
002 * Copyright 2005-2016 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.repository;
017
018
019import org.apache.commons.lang.StringUtils;
020import org.kuali.rice.core.api.exception.RiceIllegalStateException;
021import org.kuali.rice.krad.service.BusinessObjectService;
022import org.kuali.rice.krms.api.repository.action.ActionDefinition;
023import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
024import org.kuali.rice.krms.api.repository.type.KrmsAttributeDefinition;
025import org.kuali.rice.krms.impl.util.KrmsImplConstants.PropertyNames;
026
027import java.util.*;
028
029public final class RuleBoServiceImpl implements RuleBoService {
030
031        private BusinessObjectService businessObjectService;
032    private KrmsAttributeDefinitionService attributeDefinitionService;
033
034        /**
035         * This overridden creates a KRMS Rule in the repository
036         * 
037         * @see org.kuali.rice.krms.impl.repository.RuleBoService#createRule(org.kuali.rice.krms.api.repository.rule.RuleDefinition)
038         */
039        @Override
040        public RuleDefinition createRule(RuleDefinition rule) {
041                if (rule == null){
042                        throw new IllegalArgumentException("rule is null");
043                }
044                final String nameKey = rule.getName();
045                final String namespaceKey = rule.getNamespace();
046                final RuleDefinition existing = getRuleByNameAndNamespace(nameKey, namespaceKey);
047                if (existing != null){
048                        throw new IllegalStateException("the rule to create already exists: " + rule);                  
049                }       
050                RuleBo ruleBo = RuleBo.from(rule);
051                businessObjectService.save(ruleBo);
052                return RuleBo.to(ruleBo);
053        }
054
055        /**
056         * This overridden updates an existing Rule in the Repository
057         * 
058         * @see org.kuali.rice.krms.impl.repository.RuleBoService#updateRule(org.kuali.rice.krms.api.repository.rule.RuleDefinition)
059         */
060        @Override
061        public void updateRule(RuleDefinition rule) {
062                if (rule == null){
063                        throw new IllegalArgumentException("rule is null");
064                }
065
066                // must already exist to be able to update
067                final String ruleIdKey = rule.getId();
068                final RuleBo existing = businessObjectService.findBySinglePrimaryKey(RuleBo.class, ruleIdKey);
069                if (existing == null) {
070                        throw new IllegalStateException("the rule does not exist: " + rule);
071                }
072                final RuleDefinition toUpdate;
073                if (!existing.getId().equals(rule.getId())){
074                        // if passed in id does not match existing id, correct it
075                        final RuleDefinition.Builder builder = RuleDefinition.Builder.create(rule);
076                        builder.setId(existing.getId());
077                        toUpdate = builder.build();
078                } else {
079                        toUpdate = rule;
080                }
081             
082                // copy all updateable fields to bo
083                RuleBo boToUpdate = RuleBo.from(toUpdate);
084        updateActionAttributes(boToUpdate.getActions());
085
086                // delete any old, existing attributes
087                Map<String,String> fields = new HashMap<String,String>(1);
088                fields.put(PropertyNames.Rule.RULE_ID, toUpdate.getId());
089                businessObjectService.deleteMatching(RuleAttributeBo.class, fields);
090        
091                // update the rule and create new attributes
092                businessObjectService.save(boToUpdate);
093        }
094
095    @Override
096    public void deleteRule(String ruleId) {
097        if (ruleId == null){ throw new IllegalArgumentException("ruleId is null"); }
098        final RuleDefinition existing = getRuleByRuleId(ruleId);
099        if (existing == null){ throw new IllegalStateException("the Rule to delete does not exists: " + ruleId);}
100        businessObjectService.delete(from(existing));
101    }
102
103        /**
104         * This method retrieves a rule from the repository given the rule id.
105         * 
106         * @see org.kuali.rice.krms.impl.repository.RuleBoService#getRuleByRuleId(java.lang.String)
107         */
108        @Override
109        public RuleDefinition getRuleByRuleId(String ruleId) {
110                if (StringUtils.isBlank(ruleId)){
111                        throw new IllegalArgumentException("rule id is null");
112                }
113                RuleBo bo = businessObjectService.findBySinglePrimaryKey(RuleBo.class, ruleId);
114                return RuleBo.to(bo);
115        }
116
117        /**
118         * This method retrieves a rule from the repository given the name of the rule
119         * and namespace.
120         * 
121         * @see org.kuali.rice.krms.impl.repository.RuleBoService#getRuleByRuleId(java.lang.String)
122         */
123        @Override
124        public RuleDefinition getRuleByNameAndNamespace(String name, String namespace) {
125        if (StringUtils.isBlank(name)) {
126            throw new IllegalArgumentException("name is null or blank");
127        }
128        if (StringUtils.isBlank(namespace)) {
129            throw new IllegalArgumentException("namespace is null or blank");
130        }
131
132        final Map<String, Object> map = new HashMap<String, Object>();
133        map.put("name", name);
134        map.put("namespace", namespace);
135
136        RuleBo myRule = businessObjectService.findByPrimaryKey(RuleBo.class, Collections.unmodifiableMap(map));
137                return RuleBo.to(myRule);
138        }
139
140//      /**
141//       * This overridden method ...
142//       * 
143//       * @see org.kuali.rice.krms.impl.repository.RuleBoService#createRuleAttribute(org.kuali.rice.krms.api.repository.rule.RuleAttribute)
144//       */
145//      @Override
146//      public void createRuleAttribute(RuleAttribute attribute) {
147//              if (attribute == null){
148//                      throw new IllegalArgumentException("rule attribute is null");
149//              }
150//              final String attrIdKey = attribute.getId();
151//              final RuleAttribute existing = getRuleAttributeById(attrIdKey);
152//              if (existing != null){
153//                      throw new IllegalStateException("the rule attribute to create already exists: " + attribute);                   
154//              }
155//
156//              businessObjectService.save(RuleAttributeBo.from(attribute));            
157//      }
158//
159//      /**
160//       * This overridden method ...
161//       * 
162//       * @see org.kuali.rice.krms.impl.repository.RuleBoService#updateRuleAttribute(org.kuali.rice.krms.api.repository.rule.RuleAttribute)
163//       */
164//      @Override
165//      public void updateRuleAttribute(RuleAttribute attribute) {
166//              if (attribute == null){
167//                      throw new IllegalArgumentException("rule attribute is null");
168//              }
169//              final String attrIdKey = attribute.getId();
170//              final RuleAttribute existing = getRuleAttributeById(attrIdKey);
171//              if (existing == null) {
172//                      throw new IllegalStateException("the rule attribute does not exist: " + attribute);
173//              }
174//              final RuleAttribute toUpdate;
175//              if (!existing.getId().equals(attribute.getRuleId())){
176//                      final RuleAttribute.Builder builder = RuleAttribute.Builder.create(attribute);
177//                      builder.setId(existing.getId());
178//                      toUpdate = builder.build();
179//              } else {
180//                      toUpdate = attribute;
181//              }
182//
183//              businessObjectService.save(RuleAttributeBo.from(toUpdate));
184//      }
185//
186        /**
187         * This method ...
188         * 
189         * @see org.kuali.rice.krms.impl.repository.RuleBoService#getRuleAttributeById(java.lang.String)
190         */
191        public RuleAttributeBo getRuleAttributeById(String attrId) {
192                if (StringUtils.isBlank(attrId)){
193                        return null;                    
194                }
195                RuleAttributeBo bo = businessObjectService.findBySinglePrimaryKey(RuleAttributeBo.class, attrId);
196                return bo;
197        }
198
199    /**
200     * Converts a immutable {@link RuleDefinition} to its mutable {@link RuleBo} counterpart.
201     * @param rule the immutable object.
202     * @return a {@link RuleBo} the mutable RuleBo.
203     *
204     */
205    public RuleBo from(RuleDefinition rule) {
206        if (rule == null) { return null; }
207        RuleBo ruleBo = new RuleBo();
208        ruleBo.setName(rule.getName());
209        ruleBo.setDescription(rule.getDescription());
210        ruleBo.setNamespace(rule.getNamespace());
211        ruleBo.setTypeId(rule.getTypeId());
212        ruleBo.setPropId(rule.getPropId());
213        ruleBo.setProposition(PropositionBo.from(rule.getProposition()));
214        ruleBo.setId(rule.getId());
215        ruleBo.setActive(rule.isActive());
216        ruleBo.setVersionNumber(rule.getVersionNumber());
217        // TODO collections, etc.
218//        Set<RuleAttributeBo> attributes = buildAttributeBo(rule);
219        ruleBo.setActions(buildActionBoList(rule));
220        ruleBo.setAttributeBos(buildAttributeBoList(rule));
221        return ruleBo;
222    }
223
224    private Set<RuleAttributeBo> buildAttributeBo(RuleDefinition im) {
225        Set<RuleAttributeBo> attributes = new HashSet<RuleAttributeBo>();
226
227        // build a map from attribute name to definition
228        Map<String, KrmsAttributeDefinition> attributeDefinitionMap = new HashMap<String, KrmsAttributeDefinition>();
229
230        List<KrmsAttributeDefinition> attributeDefinitions = getAttributeDefinitionService().findAttributeDefinitionsByType(im.getTypeId());
231
232        for (KrmsAttributeDefinition attributeDefinition : attributeDefinitions) {
233            attributeDefinitionMap.put(attributeDefinition.getName(), attributeDefinition);
234        }
235
236        // for each entry, build a RuleAttributeBo and add it to the set
237        if (im.getAttributes() != null) {
238            for (Map.Entry<String,String> entry  : im.getAttributes().entrySet()) {
239                KrmsAttributeDefinition attrDef = attributeDefinitionMap.get(entry.getKey());
240
241                if (attrDef != null) {
242                    RuleAttributeBo attributeBo = new RuleAttributeBo();
243                    attributeBo.setRuleId( im.getId() );
244                    attributeBo.setAttributeDefinitionId(attrDef.getId());
245                    attributeBo.setValue(entry.getValue());
246                    attributeBo.setAttributeDefinition(KrmsAttributeDefinitionBo.from(attrDef));
247                    attributes.add( attributeBo );
248                } else {
249                    throw new RiceIllegalStateException("there is no attribute definition with the name '" +
250                            entry.getKey() + "' that is valid for the rule type with id = '" + im.getTypeId() +"'");
251                }
252            }
253        }
254        return attributes;
255    }
256
257    private List<RuleAttributeBo> buildAttributeBoList(RuleDefinition im) {
258        List<RuleAttributeBo> attributes = new LinkedList<RuleAttributeBo>();
259
260        // build a map from attribute name to definition
261        Map<String, KrmsAttributeDefinition> attributeDefinitionMap = new HashMap<String, KrmsAttributeDefinition>();
262
263        List<KrmsAttributeDefinition> attributeDefinitions = getAttributeDefinitionService().findAttributeDefinitionsByType(im.getTypeId());
264
265        for (KrmsAttributeDefinition attributeDefinition : attributeDefinitions) {
266            attributeDefinitionMap.put(attributeDefinition.getName(), attributeDefinition);
267        }
268
269        // for each entry, build a RuleAttributeBo and add it to the set
270        if (im.getAttributes() != null) {
271            for (Map.Entry<String,String> entry  : im.getAttributes().entrySet()) {
272                KrmsAttributeDefinition attrDef = attributeDefinitionMap.get(entry.getKey());
273
274                if (attrDef != null) {
275                    RuleAttributeBo attributeBo = new RuleAttributeBo();
276                    attributeBo.setRuleId( im.getId() );
277                    attributeBo.setAttributeDefinitionId(attrDef.getId());
278                    attributeBo.setValue(entry.getValue());
279                    attributeBo.setAttributeDefinition(KrmsAttributeDefinitionBo.from(attrDef));
280                    attributes.add( attributeBo );
281                } else {
282                    throw new RiceIllegalStateException("there is no attribute definition with the name '" +
283                            entry.getKey() + "' that is valid for the rule type with id = '" + im.getTypeId() +"'");
284                }
285            }
286        }
287        return attributes;
288    }
289
290    private List<ActionBo> buildActionBoList(RuleDefinition im) {
291        List<ActionBo> actions = new LinkedList<ActionBo>();
292
293        for (ActionDefinition actionDefinition : im.getActions()) {
294            actions.add(ActionBo.from(actionDefinition));
295        }
296        updateActionAttributes(actions);
297
298        return actions;
299    }
300
301    private void updateActionAttributes(List<ActionBo> actionBos) {
302        for (ActionBo action : actionBos) {
303            for (ActionAttributeBo aa : action.getAttributeBos()) {
304                Map<String, Object> map = new HashMap<String, Object>();
305                map.put("actionId", action.getId());
306                Collection<ActionAttributeBo> aaBos = businessObjectService.findMatching(ActionAttributeBo.class, map);
307
308                for (ActionAttributeBo aaBo : aaBos) {
309                    if (aaBo.getAttributeDefinitionId().equals(aa.getAttributeDefinitionId())) {
310                        aa.setId(aaBo.getId());
311                        aa.setVersionNumber(aaBo.getVersionNumber());
312                    }
313                }
314            }
315        }
316    }
317
318    /**
319         * Sets the businessObjectService attribute value.
320         *
321         * @param businessObjectService The businessObjectService to set.
322         */
323        public void setBusinessObjectService(final BusinessObjectService businessObjectService) {
324                this.businessObjectService = businessObjectService;
325        }
326
327        /**
328         * Converts a List<RuleBo> to an Unmodifiable List<Rule>
329         *
330         * @param ruleBos a mutable List<RuleBo> to made completely immutable.
331         * @return An unmodifiable List<Rule>
332         */
333        public List<RuleDefinition> convertListOfBosToImmutables(final Collection<RuleBo> ruleBos) {
334                ArrayList<RuleDefinition> rules = new ArrayList<RuleDefinition>();
335                for (RuleBo bo : ruleBos) {
336                        RuleDefinition rule = RuleBo.to(bo);
337                        rules.add(rule);
338                }
339                return Collections.unmodifiableList(rules);
340        }
341
342
343    protected KrmsAttributeDefinitionService getAttributeDefinitionService() {
344        if (attributeDefinitionService == null) {
345            attributeDefinitionService = KrmsRepositoryServiceLocator.getKrmsAttributeDefinitionService();
346        }
347        return attributeDefinitionService;
348    }
349
350}