001/**
002 * Copyright 2005-2013 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 KSRuleBoServiceImpl 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) {
098            throw new IllegalArgumentException("ruleId is null");
099        }
100        final RuleDefinition existing = getRuleByRuleId(ruleId);
101        if (existing == null) {
102            throw new IllegalStateException("the Rule to delete does not exists: " + ruleId);
103        }
104        businessObjectService.delete(from(existing));
105    }
106
107    /**
108     * This method retrieves a rule from the repository given the rule id.
109     *
110     * @see org.kuali.rice.krms.impl.repository.RuleBoService#getRuleByRuleId(java.lang.String)
111     */
112    @Override
113    public RuleDefinition getRuleByRuleId(String ruleId) {
114        if (StringUtils.isBlank(ruleId)) {
115            throw new IllegalArgumentException("rule id is null");
116        }
117        RuleBo bo = businessObjectService.findBySinglePrimaryKey(RuleBo.class, ruleId);
118        return RuleBo.to(bo);
119    }
120
121    /**
122     * This method retrieves a rule from the repository given the name of the rule
123     * and namespace.
124     *
125     * @see org.kuali.rice.krms.impl.repository.RuleBoService#getRuleByRuleId(java.lang.String)
126     */
127    @Override
128    public RuleDefinition getRuleByNameAndNamespace(String name, String namespace) {
129        if (StringUtils.isBlank(name)) {
130            throw new IllegalArgumentException("name is null or blank");
131        }
132        if (StringUtils.isBlank(namespace)) {
133            throw new IllegalArgumentException("namespace is null or blank");
134        }
135
136        final Map<String, Object> map = new HashMap<String, Object>();
137        map.put("name", name);
138        map.put("namespace", namespace);
139
140        RuleBo myRule = businessObjectService.findByPrimaryKey(RuleBo.class, Collections.unmodifiableMap(map));
141        return RuleBo.to(myRule);
142    }
143
144//      /**
145//       * This overridden method ...
146//       * 
147//       * @see org.kuali.rice.krms.impl.repository.RuleBoService#createRuleAttribute(org.kuali.rice.krms.api.repository.rule.RuleAttribute)
148//       */
149//      @Override
150//      public void createRuleAttribute(RuleAttribute attribute) {
151//              if (attribute == null){
152//                      throw new IllegalArgumentException("rule attribute is null");
153//              }
154//              final String attrIdKey = attribute.getId();
155//              final RuleAttribute existing = getRuleAttributeById(attrIdKey);
156//              if (existing != null){
157//                      throw new IllegalStateException("the rule attribute to create already exists: " + attribute);                   
158//              }
159//
160//              businessObjectService.save(RuleAttributeBo.from(attribute));            
161//      }
162//
163//      /**
164//       * This overridden method ...
165//       * 
166//       * @see org.kuali.rice.krms.impl.repository.RuleBoService#updateRuleAttribute(org.kuali.rice.krms.api.repository.rule.RuleAttribute)
167//       */
168//      @Override
169//      public void updateRuleAttribute(RuleAttribute attribute) {
170//              if (attribute == null){
171//                      throw new IllegalArgumentException("rule attribute is null");
172//              }
173//              final String attrIdKey = attribute.getId();
174//              final RuleAttribute existing = getRuleAttributeById(attrIdKey);
175//              if (existing == null) {
176//                      throw new IllegalStateException("the rule attribute does not exist: " + attribute);
177//              }
178//              final RuleAttribute toUpdate;
179//              if (!existing.getId().equals(attribute.getRuleId())){
180//                      final RuleAttribute.Builder builder = RuleAttribute.Builder.create(attribute);
181//                      builder.setId(existing.getId());
182//                      toUpdate = builder.build();
183//              } else {
184//                      toUpdate = attribute;
185//              }
186//
187//              businessObjectService.save(RuleAttributeBo.from(toUpdate));
188//      }
189//
190
191    /**
192     * This method ...
193     *
194     * @see org.kuali.rice.krms.impl.repository.RuleBoService#getRuleAttributeById(java.lang.String)
195     */
196    public RuleAttributeBo getRuleAttributeById(String attrId) {
197        if (StringUtils.isBlank(attrId)) {
198            return null;
199        }
200        RuleAttributeBo bo = businessObjectService.findBySinglePrimaryKey(RuleAttributeBo.class, attrId);
201        return bo;
202    }
203
204    /**
205     * Converts a immutable {@link RuleDefinition} to its mutable {@link RuleBo} counterpart.
206     *
207     * @param rule the immutable object.
208     * @return a {@link RuleBo} the mutable RuleBo.
209     */
210    public RuleBo from(RuleDefinition rule) {
211        if (rule == null) {
212            return null;
213        }
214        RuleBo ruleBo = new RuleBo();
215        ruleBo.setName(rule.getName());
216        ruleBo.setDescription(rule.getDescription());
217        ruleBo.setNamespace(rule.getNamespace());
218        ruleBo.setTypeId(rule.getTypeId());
219        ruleBo.setPropId(rule.getPropId());
220        ruleBo.setProposition(PropositionBo.from(rule.getProposition()));
221        ruleBo.setId(rule.getId());
222        ruleBo.setActive(rule.isActive());
223        ruleBo.setVersionNumber(rule.getVersionNumber());
224//        Set<RuleAttributeBo> attributes = buildAttributeBo(rule);
225        ruleBo.setActions(buildActionBoList(rule));
226        ruleBo.setAttributeBos(buildAttributeBoList(rule));
227        return ruleBo;
228    }
229
230    private Set<RuleAttributeBo> buildAttributeBo(RuleDefinition im) {
231        Set<RuleAttributeBo> attributes = new HashSet<RuleAttributeBo>();
232
233        // build a map from attribute name to definition
234        Map<String, KrmsAttributeDefinition> attributeDefinitionMap = new HashMap<String, KrmsAttributeDefinition>();
235
236        List<KrmsAttributeDefinition> attributeDefinitions = getAttributeDefinitionService().findAttributeDefinitionsByType(im.getTypeId());
237
238        for (KrmsAttributeDefinition attributeDefinition : attributeDefinitions) {
239            attributeDefinitionMap.put(attributeDefinition.getName(), attributeDefinition);
240        }
241
242        // for each entry, build a RuleAttributeBo and add it to the set
243        if (im.getAttributes() != null) {
244            for (Map.Entry<String, String> entry : im.getAttributes().entrySet()) {
245                KrmsAttributeDefinition attrDef = attributeDefinitionMap.get(entry.getKey());
246
247                if (attrDef != null) {
248                    RuleAttributeBo attributeBo = new RuleAttributeBo();
249                    attributeBo.setRuleId(im.getId());
250                    attributeBo.setAttributeDefinitionId(attrDef.getId());
251                    attributeBo.setValue(entry.getValue());
252                    attributeBo.setAttributeDefinition(KrmsAttributeDefinitionBo.from(attrDef));
253                    attributes.add(attributeBo);
254                } else {
255                    throw new RiceIllegalStateException("there is no attribute definition with the name '" +
256                            entry.getKey() + "' that is valid for the rule type with id = '" + im.getTypeId() + "'");
257                }
258            }
259        }
260        return attributes;
261    }
262
263    private List<RuleAttributeBo> buildAttributeBoList(RuleDefinition im) {
264        List<RuleAttributeBo> attributes = new LinkedList<RuleAttributeBo>();
265
266        // build a map from attribute name to definition
267        Map<String, KrmsAttributeDefinition> attributeDefinitionMap = new HashMap<String, KrmsAttributeDefinition>();
268
269        List<KrmsAttributeDefinition> attributeDefinitions = getAttributeDefinitionService().findAttributeDefinitionsByType(im.getTypeId());
270
271        for (KrmsAttributeDefinition attributeDefinition : attributeDefinitions) {
272            attributeDefinitionMap.put(attributeDefinition.getName(), attributeDefinition);
273        }
274
275        // for each entry, build a RuleAttributeBo and add it to the set
276        if (im.getAttributes() != null) {
277            for (Map.Entry<String, String> entry : im.getAttributes().entrySet()) {
278                KrmsAttributeDefinition attrDef = attributeDefinitionMap.get(entry.getKey());
279
280                if (attrDef != null) {
281                    RuleAttributeBo attributeBo = new RuleAttributeBo();
282                    attributeBo.setRuleId(im.getId());
283                    attributeBo.setAttributeDefinitionId(attrDef.getId());
284                    attributeBo.setValue(entry.getValue());
285                    attributeBo.setAttributeDefinition(KrmsAttributeDefinitionBo.from(attrDef));
286                    attributes.add(attributeBo);
287                } else {
288                    throw new RiceIllegalStateException("there is no attribute definition with the name '" +
289                            entry.getKey() + "' that is valid for the rule type with id = '" + im.getTypeId() + "'");
290                }
291            }
292        }
293        return attributes;
294    }
295
296    private List<ActionBo> buildActionBoList(RuleDefinition im) {
297        List<ActionBo> actions = new LinkedList<ActionBo>();
298
299        for (ActionDefinition actionDefinition : im.getActions()) {
300            actions.add(ActionBo.from(actionDefinition));
301        }
302        updateActionAttributes(actions);
303
304        return actions;
305    }
306
307    private void updateActionAttributes(List<ActionBo> actionBos) {
308        for (ActionBo action : actionBos) {
309            for (ActionAttributeBo aa : action.getAttributeBos()) {
310                final Map<String, Object> map = new HashMap<String, Object>();
311                map.put("actionId", action.getId());
312
313                List<ActionAttributeBo> aaBos = (List<ActionAttributeBo>) businessObjectService.findMatching(ActionAttributeBo.class, Collections.unmodifiableMap(map));
314                for (ActionAttributeBo aaBo : aaBos) {
315                    if (aaBo.getAttributeDefinitionId().equals(aa.getAttributeDefinitionId())) {
316                        aa.setId(aaBo.getId());
317                        aa.setVersionNumber(aaBo.getVersionNumber());
318                    }
319                }
320            }
321        }
322    }
323
324    /**
325     * Sets the businessObjectService attribute value.
326     *
327     * @param businessObjectService The businessObjectService to set.
328     */
329    public void setBusinessObjectService(final BusinessObjectService businessObjectService) {
330        this.businessObjectService = businessObjectService;
331    }
332
333    /**
334     * Converts a List<RuleBo> to an Unmodifiable List<Rule>
335     *
336     * @param ruleBos a mutable List<RuleBo> to made completely immutable.
337     * @return An unmodifiable List<Rule>
338     */
339    public List<RuleDefinition> convertListOfBosToImmutables(final Collection<RuleBo> ruleBos) {
340        ArrayList<RuleDefinition> rules = new ArrayList<RuleDefinition>();
341        for (RuleBo bo : ruleBos) {
342            RuleDefinition rule = RuleBo.to(bo);
343            rules.add(rule);
344        }
345        return Collections.unmodifiableList(rules);
346    }
347
348
349    protected KrmsAttributeDefinitionService getAttributeDefinitionService() {
350        if (attributeDefinitionService == null) {
351            attributeDefinitionService = KrmsRepositoryServiceLocator.getKrmsAttributeDefinitionService();
352        }
353        return attributeDefinitionService;
354    }
355
356}