001package org.kuali.rice.krms.impl.util;
002
003import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
004import org.kuali.rice.core.api.exception.RiceIllegalStateException;
005import org.kuali.rice.krms.api.repository.RuleManagementService;
006import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition;
007import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition;
008import org.kuali.rice.krms.api.repository.agenda.AgendaTreeDefinition;
009import org.kuali.rice.krms.api.repository.agenda.AgendaTreeEntryDefinitionContract;
010import org.kuali.rice.krms.api.repository.proposition.PropositionDefinition;
011import org.kuali.rice.krms.api.repository.proposition.PropositionParameter;
012import org.kuali.rice.krms.api.repository.proposition.PropositionParameterType;
013import org.kuali.rice.krms.api.repository.proposition.PropositionType;
014import org.kuali.rice.krms.api.repository.reference.ReferenceObjectBinding;
015import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
016import org.kuali.rice.krms.api.repository.term.TermDefinition;
017import org.kuali.rice.krms.api.repository.term.TermParameterDefinition;
018
019import java.util.ArrayList;
020import java.util.List;
021import javax.xml.namespace.QName;
022import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
023import org.kuali.rice.krms.api.KrmsConstants;
024import org.kuali.rice.krms.api.repository.term.TermRepositoryService;
025import org.kuali.student.r2.common.exceptions.DataValidationErrorException;
026import org.kuali.student.r2.common.exceptions.DoesNotExistException;
027import org.kuali.student.r2.common.exceptions.InvalidParameterException;
028import org.kuali.student.r2.common.exceptions.MissingParameterException;
029import org.kuali.student.r2.common.exceptions.OperationFailedException;
030import org.kuali.student.r2.common.exceptions.PermissionDeniedException;
031import org.kuali.student.r2.common.exceptions.ReadOnlyException;
032import org.kuali.student.r2.common.exceptions.UnsupportedActionException;
033import org.kuali.student.common.util.security.ContextUtils;
034import org.kuali.student.r2.core.constants.KSKRMSServiceConstants;
035import org.kuali.student.r2.core.krms.naturallanguage.TermParameterTypes;
036import org.kuali.student.r2.lum.clu.dto.CluSetInfo;
037import org.kuali.student.r2.lum.clu.service.CluService;
038import org.kuali.student.r2.lum.util.constants.CluServiceConstants;
039
040/**
041 * This class contains the copy logic for copying object references from one object to another
042 *
043 * @author christoff
044 */
045public class KrmsRuleManagementCopyMethodsImpl implements KrmsRuleManagementCopyMethods {
046
047    private RuleManagementService ruleManagementService;
048    private TermRepositoryService termRepositoryService;
049    private CluService cluService;
050
051    @Override
052    public List<ReferenceObjectBinding> deepCopyReferenceObjectBindingsFromTo(String fromReferenceDiscriminatorType,
053                                                                              String fromReferenceObjectId,
054                                                                              String toReferenceDiscriminatorType,
055                                                                              String toReferenceObjectId,
056                                                                              List<String> optionKeys)
057            throws RiceIllegalArgumentException,
058            RiceIllegalStateException, PermissionDeniedException, MissingParameterException, InvalidParameterException, OperationFailedException {
059        _checkEmptyParam(fromReferenceDiscriminatorType, "fromReferenceDiscriminatorType");
060        _checkEmptyParam(fromReferenceObjectId, "fromReferenceObjectId");
061        _checkEmptyParam(toReferenceDiscriminatorType, "toReferenceDiscriminatorType");
062        _checkEmptyParam(toReferenceObjectId, "toReferenceObjectId");
063        List<ReferenceObjectBinding> copiedRefList = new ArrayList<ReferenceObjectBinding>();
064        List<ReferenceObjectBinding> refsToCopy = this.getRuleManagementService().findReferenceObjectBindingsByReferenceObject(fromReferenceDiscriminatorType, fromReferenceObjectId);
065        for (ReferenceObjectBinding reference : refsToCopy) {
066            ReferenceObjectBinding.Builder refBldr = null;
067            //At the moment we only support agendas.
068            if (reference.getKrmsDiscriminatorType().equals(KSKRMSServiceConstants.KRMS_DISCRIMINATOR_TYPE_AGENDA)) {
069                AgendaTreeDefinition agendaTree = getRuleManagementService().getAgendaTree(reference.getKrmsObjectId());
070
071                AgendaDefinition copiedAgenda = deepCopyAgenda(agendaTree, toReferenceObjectId);
072                refBldr = ReferenceObjectBinding.Builder.create(reference);
073                refBldr.setId(null);
074                refBldr.setVersionNumber(null);
075                refBldr.setReferenceObjectId(toReferenceObjectId);
076                refBldr.setReferenceDiscriminatorType(toReferenceDiscriminatorType);
077                refBldr.setKrmsObjectId(copiedAgenda.getId());
078            } else {
079                //no support for copying any other krms types yet
080                throw new RiceIllegalStateException("unknown/unhandled KRMS discriminator type " + reference.getKrmsDiscriminatorType());
081            }
082            ReferenceObjectBinding refBinding = getRuleManagementService().createReferenceObjectBinding(refBldr.build());
083            copiedRefList.add(refBinding);
084        }
085        return copiedRefList;
086    }
087
088    private AgendaDefinition deepCopyAgenda(AgendaTreeDefinition agendaTree, String refObjectId) throws MissingParameterException, InvalidParameterException, OperationFailedException, PermissionDeniedException {
089        //clone the Agenda
090        AgendaDefinition oldAgenda = getRuleManagementService().getAgenda(agendaTree.getAgendaId());
091        AgendaDefinition.Builder copiedAgendaBldr = AgendaDefinition.Builder.create(oldAgenda);
092        copiedAgendaBldr.setId(null);
093        copiedAgendaBldr.setVersionNumber(null);
094        copiedAgendaBldr.setFirstItemId(null);
095        copiedAgendaBldr.setName(refObjectId + ":" + oldAgenda.getTypeId() + ":1");
096        AgendaDefinition copiedAgenda = getRuleManagementService().createAgenda(copiedAgendaBldr.build());
097
098        AgendaItemDefinition.Builder previousAgendaItemBldr = null;
099        AgendaItemDefinition.Builder firstAgendaItemBldr = AgendaItemDefinition.Builder.create(null, copiedAgenda.getId());
100        AgendaItemDefinition firstAgendaItem = getRuleManagementService().createAgendaItem(firstAgendaItemBldr.build());
101        copiedAgendaBldr = AgendaDefinition.Builder.create(copiedAgenda);
102        copiedAgendaBldr.setFirstItemId(firstAgendaItem.getId());
103        getRuleManagementService().updateAgenda(copiedAgendaBldr.build());
104        copiedAgenda = getRuleManagementService().getAgenda(copiedAgenda.getId());
105
106        boolean isFirstItem = true;
107        for (AgendaTreeEntryDefinitionContract entry : agendaTree.getEntries()) {
108            AgendaItemDefinition currentAgendaItem = getRuleManagementService().getAgendaItem(entry.getAgendaItemId());
109            AgendaItemDefinition.Builder copiedAgendaItemBldr = AgendaItemDefinition.Builder.create(currentAgendaItem);
110            deepUpdateAgendaItem(copiedAgendaItemBldr, copiedAgenda.getId(), refObjectId);
111            if (isFirstItem) {
112                copiedAgendaItemBldr.setId(firstAgendaItem.getId());
113                copiedAgendaItemBldr.setVersionNumber(firstAgendaItem.getVersionNumber());
114                firstAgendaItemBldr = copiedAgendaItemBldr;
115                previousAgendaItemBldr = firstAgendaItemBldr;
116                isFirstItem = false;
117            } else {
118                copiedAgendaItemBldr.setId(null);
119                copiedAgendaItemBldr.setVersionNumber(null);
120                previousAgendaItemBldr.setWhenTrue(copiedAgendaItemBldr);
121                previousAgendaItemBldr = copiedAgendaItemBldr;
122            }
123        }
124        getRuleManagementService().updateAgendaItem(firstAgendaItemBldr.build());
125        return copiedAgenda;
126    }
127
128    private void deepUpdateAgendaItem(AgendaItemDefinition.Builder copiedAgendaItemBldr, String copiedAgendaID, String refObjectId) throws MissingParameterException, PermissionDeniedException, InvalidParameterException, OperationFailedException {
129        copiedAgendaItemBldr.setId(null);
130        copiedAgendaItemBldr.setVersionNumber(null);
131        copiedAgendaItemBldr.setAgendaId(copiedAgendaID);
132        if (copiedAgendaItemBldr.getWhenTrueId() != null) {
133            deepUpdateAgendaItem(copiedAgendaItemBldr.getWhenTrue(), copiedAgendaID, refObjectId);
134            copiedAgendaItemBldr.setWhenTrueId(null);
135        }
136        if (copiedAgendaItemBldr.getWhenFalseId() != null) {
137            deepUpdateAgendaItem(copiedAgendaItemBldr.getWhenFalse(), copiedAgendaID, refObjectId);
138            copiedAgendaItemBldr.setWhenFalseId(null);
139        }
140        if (copiedAgendaItemBldr.getAlwaysId() != null) {
141            deepUpdateAgendaItem(copiedAgendaItemBldr.getAlways(), copiedAgendaID, refObjectId);
142            copiedAgendaItemBldr.setAlwaysId(null);
143        }
144        copiedAgendaItemBldr.setRuleId(null);
145        RuleDefinition.Builder copiedRuleBldr = copiedAgendaItemBldr.getRule();
146        copiedRuleBldr.setId(null);
147        copiedRuleBldr.setVersionNumber(null);
148        copiedRuleBldr.setPropId(null);
149        copiedRuleBldr.setName(refObjectId + ":" + copiedRuleBldr.getTypeId() + ":1");
150        if (copiedRuleBldr.getProposition() != null){
151            deepUpdateForProposition(copiedRuleBldr.getProposition());
152        }
153
154    }
155
156    private void deepUpdateForProposition(PropositionDefinition.Builder propBldr) throws PermissionDeniedException, MissingParameterException, InvalidParameterException, OperationFailedException {
157        propBldr.setId(null);
158        propBldr.setRuleId(null);
159        propBldr.setVersionNumber(null);
160        for (PropositionParameter.Builder propParmBldr : propBldr.getParameters()) {
161            propParmBldr.setId(null);
162            propParmBldr.setPropId(null);
163            propParmBldr.setVersionNumber(null);
164            if (PropositionParameterType.TERM.getCode().equals(propParmBldr.getParameterType())) {
165                TermDefinition termDef = null;
166                if (propParmBldr.getTermValue() != null) {
167                    termDef = propParmBldr.getTermValue();
168                } else {
169                    termDef = getTermRepositoryService().getTerm(propParmBldr.getValue());
170                }
171                propParmBldr.setValue(null);
172                TermDefinition.Builder termBldr = TermDefinition.Builder.create(termDef);
173                termBldr.setId(null);
174                termBldr.setVersionNumber(null);
175                for (TermParameterDefinition.Builder termParmBldr : termBldr.getParameters()) {
176                    termParmBldr.setId(null);
177                    termParmBldr.setTermId(null);
178                    termParmBldr.setVersionNumber(null);
179                    if (TermParameterTypes.COURSE_CLUSET_KEY.getId().equals(termParmBldr.getName()) || TermParameterTypes.PROGRAM_CLUSET_KEY.getId().equals(termParmBldr.getName()) || TermParameterTypes.CLUSET_KEY.getId().equals(termParmBldr.getName())) {
180                        termParmBldr.setValue(createAdhocCluSet(termParmBldr.getValue()));
181                    }
182                }
183                propParmBldr.setTermValue(termBldr.build());
184            }
185        }
186        if (PropositionType.COMPOUND.getCode().equals(propBldr.getPropositionTypeCode())) {
187            for (PropositionDefinition.Builder subPropBldr : propBldr.getCompoundComponents()) {
188                deepUpdateForProposition(subPropBldr);
189            }
190        }
191    }
192
193    private String createAdhocCluSet(String cluSetId) throws InvalidParameterException, MissingParameterException, OperationFailedException, PermissionDeniedException {
194        try {
195            //get the set to check if its an ad-hoc set
196            CluSetInfo cluSet = getCluService().getCluSet(cluSetId, ContextUtils.createDefaultContextInfo());
197
198            //if not reusable, create a copy of the set and use that id in the term parameter value.
199            if(!cluSet.getIsReusable()){
200                cluSet.setId(null);
201                //Clear clu ids if membership info exists, they will be re-added based on membership info
202                if (cluSet.getMembershipQuery() != null) {
203                    cluSet.getCluIds().clear();
204                    cluSet.getCluSetIds().clear();
205                } else {
206                    List<String> subCluSetIds = new ArrayList<String>();
207                    for(String subCluSetid : cluSet.getCluSetIds()){
208                        subCluSetIds.add(createAdhocCluSet(subCluSetid));
209                    }
210                    cluSet.setCluSetIds(subCluSetIds);
211                }
212
213                cluSet = getCluService().createCluSet(cluSet.getTypeKey(), cluSet, ContextUtils.createDefaultContextInfo());
214                return cluSet.getId();
215            }
216        } catch (DoesNotExistException e) {
217            throw new OperationFailedException(e.getMessage());
218        } catch (UnsupportedActionException e) {
219            throw new OperationFailedException(e.getMessage());
220        } catch (DataValidationErrorException e) {
221            throw new OperationFailedException(e.getMessage());
222        } catch (ReadOnlyException e) {
223            throw new OperationFailedException(e.getMessage());
224        }
225        return cluSetId;
226    }
227
228    @Override
229    public int deleteReferenceObjectBindingsCascade(String referenceDiscriminatorType,
230                                                    String referenceObjectId)
231            throws RiceIllegalArgumentException, RiceIllegalStateException {
232        _checkEmptyParam(referenceDiscriminatorType, "referenceDiscriminatorType");
233        _checkEmptyParam(referenceObjectId, "referenceObjectId");
234
235        List<ReferenceObjectBinding> refsToDelete = this.getRuleManagementService().findReferenceObjectBindingsByReferenceObject(referenceDiscriminatorType,
236                referenceObjectId);
237        for (ReferenceObjectBinding refToDelete : refsToDelete) {
238            if (refToDelete.getKrmsDiscriminatorType().equals(KSKRMSServiceConstants.KRMS_DISCRIMINATOR_TYPE_AGENDA)) {
239                this._deleteAgendaCascade(refToDelete.getKrmsObjectId());
240            } else {
241                throw new RiceIllegalStateException("unknown/unhandled KRMS discriminator type " + refToDelete.getKrmsDiscriminatorType());
242            }
243        }
244        for (ReferenceObjectBinding refToDelete : refsToDelete) {
245            this.getRuleManagementService().deleteReferenceObjectBinding(refToDelete.getId());
246        }
247        return refsToDelete.size();
248    }
249
250    private int _deleteAgendaCascade(String agendaId) {
251        AgendaDefinition agenda = this.getRuleManagementService().getAgenda(agendaId);
252        this._deleteAgendaItemCascade(agenda.getFirstItemId());
253        this.getRuleManagementService().deleteAgenda(agendaId);
254        return 1;
255    }
256
257    private int _deleteAgendaItemCascade(String agendaItemId) {
258        AgendaItemDefinition item = this.getRuleManagementService().getAgendaItem(agendaItemId);
259        if (item.getAlwaysId() != null) {
260            AgendaItemDefinition.Builder bldr = AgendaItemDefinition.Builder.create(item);
261            bldr.setAlways(null);
262            bldr.setAlwaysId(null);
263            this.getRuleManagementService().updateAgendaItem(bldr.build());
264            this._deleteAgendaItemCascade(item.getAlwaysId());
265            item = this.getRuleManagementService().getAgendaItem(agendaItemId);
266        }
267        if (item.getWhenTrueId() != null) {
268            AgendaItemDefinition.Builder bldr = AgendaItemDefinition.Builder.create(item);
269            bldr.setWhenTrue(null);
270            bldr.setWhenTrueId(null);
271            this.getRuleManagementService().updateAgendaItem(bldr.build());
272            this._deleteAgendaItemCascade(item.getWhenTrueId());
273            item = this.getRuleManagementService().getAgendaItem(agendaItemId);
274        }
275        if (item.getWhenFalseId() != null) {
276            AgendaItemDefinition.Builder bldr = AgendaItemDefinition.Builder.create(item);
277            bldr.setWhenFalse(null);
278            bldr.setWhenFalseId(null);
279            this.getRuleManagementService().updateAgendaItem(bldr.build());
280            this._deleteAgendaItemCascade(item.getWhenFalseId());
281            item = this.getRuleManagementService().getAgendaItem(agendaItemId);
282        }
283        if (item.getSubAgendaId() != null) {
284            this._deleteAgendaCascade(item.getSubAgendaId());
285        }
286        if (item.getRuleId() != null) {
287            AgendaItemDefinition.Builder bldr = AgendaItemDefinition.Builder.create(item);
288            bldr.setRule(null);
289            bldr.setRuleId(null);
290            this.getRuleManagementService().updateAgendaItem(bldr.build());
291            this._deleteRuleCascade(item.getRuleId());
292        }
293        this.getRuleManagementService().deleteAgendaItem(agendaItemId);
294        return 1;
295    }
296
297
298    private int _deleteRuleCascade(String ruleId) {
299        RuleDefinition rule = this.getRuleManagementService().getRule(ruleId);
300        // we can stop here because delete rule does a cascade (I think it does)
301        this.getRuleManagementService().deleteRule(ruleId);
302        return 1;
303    }
304
305    private void _checkEmptyParam(String param, String message)
306            throws RiceIllegalArgumentException {
307        if (param == null) {
308            throw new RiceIllegalArgumentException(message);
309        }
310        if (param.trim().isEmpty()) {
311            throw new RiceIllegalArgumentException(message);
312        }
313    }
314
315    public RuleManagementService getRuleManagementService() {
316        if (ruleManagementService == null) {
317            ruleManagementService = (RuleManagementService) GlobalResourceLoader.getService(new QName(KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0, "ruleManagementService"));
318        }
319        return ruleManagementService;
320    }
321
322    public void setRuleManagementService(RuleManagementService ruleManagementService) {
323        this.ruleManagementService = ruleManagementService;
324    }
325
326    public TermRepositoryService getTermRepositoryService() {
327        if (termRepositoryService == null) {
328            termRepositoryService = (TermRepositoryService) GlobalResourceLoader.getService(new QName(KrmsConstants.Namespaces.KRMS_NAMESPACE_2_0, "termRepositoryService"));
329        }
330        return termRepositoryService;
331    }
332
333    public void setTermRepositoryService(TermRepositoryService termRepositoryService) {
334        this.termRepositoryService = termRepositoryService;
335    }
336
337    public CluService getCluService() {
338        if (cluService == null) {
339            cluService = (CluService) GlobalResourceLoader.getService(new QName(CluServiceConstants.NAMESPACE, "CluService"));
340        }
341        return cluService;
342    }
343
344    public void setCluService(CluService cluService) {
345        this.cluService = cluService;
346    }
347}