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.ui;
017
018import org.apache.commons.collections.CollectionUtils;
019import org.apache.commons.lang.StringUtils;
020import org.apache.ojb.broker.metadata.ClassNotPersistenceCapableException;
021import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
022import org.kuali.rice.core.api.uif.DataType;
023import org.kuali.rice.core.api.uif.RemotableAttributeField;
024import org.kuali.rice.core.api.uif.RemotableTextInput;
025import org.kuali.rice.core.api.util.tree.Node;
026import org.kuali.rice.core.api.util.tree.Tree;
027import org.kuali.rice.core.impl.cache.DistributedCacheManagerDecorator;
028import org.kuali.rice.krad.bo.PersistableBusinessObject;
029import org.kuali.rice.krad.maintenance.Maintainable;
030import org.kuali.rice.krad.maintenance.MaintainableImpl;
031import org.kuali.rice.krad.maintenance.MaintenanceDocument;
032import org.kuali.rice.krad.service.BusinessObjectService;
033import org.kuali.rice.krad.service.KRADServiceLocator;
034import org.kuali.rice.krad.service.SequenceAccessorService;
035import org.kuali.rice.krad.uif.container.CollectionGroup;
036import org.kuali.rice.krad.uif.container.Container;
037import org.kuali.rice.krad.uif.view.View;
038import org.kuali.rice.krad.util.KRADConstants;
039import org.kuali.rice.krad.util.ObjectUtils;
040import org.kuali.rice.krad.web.form.MaintenanceDocumentForm;
041import org.kuali.rice.krms.api.KrmsConstants;
042import org.kuali.rice.krms.api.repository.action.ActionDefinition;
043import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition;
044import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition;
045import org.kuali.rice.krms.api.repository.agenda.AgendaTreeDefinition;
046import org.kuali.rice.krms.api.repository.context.ContextDefinition;
047import org.kuali.rice.krms.api.repository.function.FunctionDefinition;
048import org.kuali.rice.krms.api.repository.operator.CustomOperator;
049import org.kuali.rice.krms.api.repository.proposition.PropositionDefinition;
050import org.kuali.rice.krms.api.repository.proposition.PropositionParameterType;
051import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
052import org.kuali.rice.krms.api.repository.term.TermDefinition;
053import org.kuali.rice.krms.api.repository.term.TermResolverDefinition;
054import org.kuali.rice.krms.api.repository.term.TermSpecificationDefinition;
055import org.kuali.rice.krms.api.repository.type.KrmsAttributeDefinition;
056import org.kuali.rice.krms.api.repository.type.KrmsTypeDefinition;
057import org.kuali.rice.krms.impl.repository.ActionBo;
058import org.kuali.rice.krms.impl.repository.AgendaBo;
059import org.kuali.rice.krms.impl.repository.AgendaItemBo;
060import org.kuali.rice.krms.impl.repository.ContextBoService;
061import org.kuali.rice.krms.impl.repository.KrmsAttributeDefinitionService;
062import org.kuali.rice.krms.impl.repository.KrmsRepositoryServiceLocator;
063import org.kuali.rice.krms.impl.repository.PropositionBo;
064import org.kuali.rice.krms.impl.repository.PropositionParameterBo;
065import org.kuali.rice.krms.impl.repository.RuleBo;
066import org.kuali.rice.krms.impl.repository.TermBo;
067import org.kuali.rice.krms.impl.repository.TermParameterBo;
068import org.kuali.rice.krms.impl.util.KrmsImplConstants;
069import org.kuali.rice.krms.impl.util.KrmsRetriever;
070import org.kuali.rice.krms.impl.util.KrmsServiceLocatorInternal;
071
072import java.util.ArrayList;
073import java.util.Collections;
074import java.util.Date;
075import java.util.HashMap;
076import java.util.List;
077import java.util.Map;
078
079/**
080 * {@link Maintainable} for the {@link AgendaEditor}
081 *
082 * @author Kuali Rice Team (rice.collab@kuali.org)
083 */
084public class AgendaEditorMaintainable extends MaintainableImpl {
085
086    private static final long serialVersionUID = 1L;
087
088    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(
089            AgendaEditorMaintainable.class);
090
091    public static final String NEW_AGENDA_EDITOR_DOCUMENT_TEXT = "New Agenda Editor Document";
092
093    private transient SequenceAccessorService sequenceAccessorService;
094
095    private transient KrmsRetriever krmsRetriever = new KrmsRetriever();
096
097    /**
098     * @return the boService
099     */
100    public BusinessObjectService getBoService() {
101        return KRADServiceLocator.getBusinessObjectService();
102    }
103
104    /**
105     * return the contextBoService
106     */
107    private ContextBoService getContextBoService() {
108        return KrmsRepositoryServiceLocator.getContextBoService();
109    }
110
111    public List<RemotableAttributeField> retrieveAgendaCustomAttributes(View view, Object model, Container container) {
112        AgendaEditor agendaEditor = getAgendaEditor(model);
113        return krmsRetriever.retrieveAgendaCustomAttributes(agendaEditor);
114    }
115
116    /**
117     * Retrieve a list of {@link RemotableAttributeField}s for the parameters (if any) required by the resolver for
118     * the selected term in the proposition that is under edit.
119     */
120    public List<RemotableAttributeField> retrieveTermParameters(View view, Object model, Container container) {
121
122        List<RemotableAttributeField> results = new ArrayList<RemotableAttributeField>();
123
124        AgendaEditor agendaEditor = getAgendaEditor(model);
125
126        // Figure out which rule is being edited
127        RuleBo rule = agendaEditor.getAgendaItemLine().getRule();
128        if (null != rule) {
129
130            // Figure out which proposition is being edited
131            Tree<RuleTreeNode, String> propositionTree = rule.getPropositionTree();
132            Node<RuleTreeNode, String> editedPropositionNode = findEditedProposition(propositionTree.getRootElement());
133
134            if (editedPropositionNode != null) {
135                PropositionBo propositionBo = editedPropositionNode.getData().getProposition();
136                if (StringUtils.isEmpty(propositionBo.getCompoundOpCode()) && CollectionUtils.size(
137                        propositionBo.getParameters()) > 0) {
138                    // Get the term ID; if it is a new parameterized term, it will have a special prefix
139                    PropositionParameterBo param = propositionBo.getParameters().get(0);
140                    if (param.getValue().startsWith(KrmsImplConstants.PARAMETERIZED_TERM_PREFIX)) {
141                        String termSpecId = param.getValue().substring(
142                                KrmsImplConstants.PARAMETERIZED_TERM_PREFIX.length());
143                        TermResolverDefinition simplestResolver = getSimplestTermResolver(termSpecId,
144                                rule.getNamespace());
145
146                        // Get the parameters and build RemotableAttributeFields
147                        if (simplestResolver != null) {
148                            List<String> parameterNames = new ArrayList<String>(simplestResolver.getParameterNames());
149                            Collections.sort(parameterNames); // make param order deterministic
150
151                            for (String parameterName : parameterNames) {
152                                // TODO: also allow for DD parameters if there are matching type attributes
153                                RemotableTextInput.Builder controlBuilder = RemotableTextInput.Builder.create();
154                                controlBuilder.setSize(64);
155
156                                RemotableAttributeField.Builder builder = RemotableAttributeField.Builder.create(
157                                        parameterName);
158
159                                builder.setRequired(true);
160                                builder.setDataType(DataType.STRING);
161                                builder.setControl(controlBuilder);
162                                builder.setLongLabel(parameterName);
163                                builder.setShortLabel(parameterName);
164                                builder.setMinLength(Integer.valueOf(1));
165                                builder.setMaxLength(Integer.valueOf(64));
166
167                                results.add(builder.build());
168                            }
169                        }
170                    }
171                }
172            }
173        }
174        return results;
175    }
176
177    /**
178     * finds the term resolver with the fewest parameters that resolves the given term specification
179     *
180     * @param termSpecId the id of the term specification
181     * @param namespace the  namespace of the term specification
182     * @return the simples {@link TermResolverDefinition} found, or null if none was found
183     */
184    // package access so that AgendaEditorController can use it too
185    static TermResolverDefinition getSimplestTermResolver(String termSpecId,
186            String namespace) {// Get the term resolver for the term spec
187
188        List<TermResolverDefinition> resolvers =
189                KrmsRepositoryServiceLocator.getTermBoService().findTermResolversByOutputId(termSpecId, namespace);
190
191        TermResolverDefinition simplestResolver = null;
192
193        for (TermResolverDefinition resolver : resolvers) {
194            if (simplestResolver == null || simplestResolver.getParameterNames().size() < resolver.getParameterNames()
195                    .size()) {
196                simplestResolver = resolver;
197            }
198        }
199
200        return simplestResolver;
201    }
202
203    /**
204     * Find and return the node containing the proposition that is in currently in edit mode
205     *
206     * @param node the node to start searching from (typically the root)
207     * @return the node that is currently being edited, if any.  Otherwise, null.
208     */
209    private Node<RuleTreeNode, String> findEditedProposition(Node<RuleTreeNode, String> node) {
210        Node<RuleTreeNode, String> result = null;
211        if (node.getData() != null && node.getData().getProposition() != null && node.getData().getProposition()
212                .getEditMode()) {
213            result = node;
214        } else {
215            for (Node<RuleTreeNode, String> child : node.getChildren()) {
216                result = findEditedProposition(child);
217                if (result != null) {
218                    break;
219                }
220            }
221        }
222        return result;
223    }
224
225    /**
226     * Get the AgendaEditor out of the MaintenanceDocumentForm's newMaintainableObject
227     *
228     * @param model the MaintenanceDocumentForm
229     * @return the AgendaEditor
230     */
231    private AgendaEditor getAgendaEditor(Object model) {
232        MaintenanceDocumentForm maintenanceForm = (MaintenanceDocumentForm) model;
233        return (AgendaEditor) maintenanceForm.getDocument().getNewMaintainableObject().getDataObject();
234    }
235
236    public List<RemotableAttributeField> retrieveRuleActionCustomAttributes(View view, Object model,
237            Container container) {
238        AgendaEditor agendaEditor = getAgendaEditor((MaintenanceDocumentForm) model);
239        return krmsRetriever.retrieveRuleActionCustomAttributes(agendaEditor);
240    }
241
242    /**
243     * This only supports a single action within a rule.
244     */
245    public List<RemotableAttributeField> retrieveRuleCustomAttributes(View view, Object model, Container container) {
246        AgendaEditor agendaEditor = getAgendaEditor((MaintenanceDocumentForm) model);
247        return krmsRetriever.retrieveRuleCustomAttributes(agendaEditor);
248    }
249
250    @Override
251    public Object retrieveObjectForEditOrCopy(MaintenanceDocument document, Map<String, String> dataObjectKeys) {
252        Object dataObject = null;
253
254        try {
255            // Since the dataObject is a wrapper class we need to build it and populate with the agenda bo.
256            AgendaEditor agendaEditor = new AgendaEditor();
257            AgendaBo agenda = getLookupService().findObjectBySearch(
258                    ((AgendaEditor) getDataObject()).getAgenda().getClass(), dataObjectKeys);
259            if (KRADConstants.MAINTENANCE_COPY_ACTION.equals(getMaintenanceAction())) {
260                String dateTimeStamp = (new Date()).getTime() + "";
261                String newAgendaName = AgendaItemBo.COPY_OF_TEXT + agenda.getName() + " " + dateTimeStamp;
262
263                AgendaBo copiedAgenda = agenda.copyAgenda(newAgendaName, dateTimeStamp);
264
265                document.getDocumentHeader().setDocumentDescription(NEW_AGENDA_EDITOR_DOCUMENT_TEXT);
266                document.setFieldsClearedOnCopy(true);
267                agendaEditor.setAgenda(copiedAgenda);
268            } else {
269                // set custom attributes map in AgendaEditor
270                //                agendaEditor.setCustomAttributesMap(agenda.getAttributes());
271                agendaEditor.setAgenda(agenda);
272            }
273            agendaEditor.setCustomAttributesMap(agenda.getAttributes());
274
275            // set extra fields on AgendaEditor
276            agendaEditor.setNamespace(agenda.getContext().getNamespace());
277            agendaEditor.setContextName(agenda.getContext().getName());
278
279            dataObject = agendaEditor;
280        } catch (ClassNotPersistenceCapableException ex) {
281            if (!document.getOldMaintainableObject().isExternalBusinessObject()) {
282                throw new RuntimeException("Data Object Class: "
283                        + getDataObjectClass()
284                        + " is not persistable and is not externalizable - configuration error");
285            }
286            // otherwise, let fall through
287        }
288
289        return dataObject;
290    }
291
292    /**
293     * Returns the sequenceAssessorService
294     *
295     * @return {@link SequenceAccessorService}
296     */
297    private SequenceAccessorService getSequenceAccessorService() {
298        if (sequenceAccessorService == null) {
299            sequenceAccessorService = KRADServiceLocator.getSequenceAccessorService();
300        }
301        return sequenceAccessorService;
302    }
303
304    /**
305     * {@inheritDoc}
306     */
307    @Override
308    public void processAfterNew(MaintenanceDocument document, Map<String, String[]> requestParameters) {
309        super.processAfterNew(document, requestParameters);
310        document.getDocumentHeader().setDocumentDescription(NEW_AGENDA_EDITOR_DOCUMENT_TEXT);
311    }
312
313    @Override
314    public void processAfterEdit(MaintenanceDocument document, Map<String, String[]> requestParameters) {
315        super.processAfterEdit(document, requestParameters);
316        document.getDocumentHeader().setDocumentDescription("Modify Agenda Editor Document");
317    }
318
319    @Override
320    public void prepareForSave() {
321        // set agenda attributes
322        AgendaEditor agendaEditor = (AgendaEditor) getDataObject();
323        agendaEditor.getAgenda().setAttributes(agendaEditor.getCustomAttributesMap());
324    }
325
326    @Override
327    public void saveDataObject() {
328        AgendaBo agendaBo = ((AgendaEditor) getDataObject()).getAgenda();
329
330        // handle saving new parameterized terms and processing custom operators
331        for (AgendaItemBo agendaItem : agendaBo.getItems()) {
332            PropositionBo propositionBo = agendaItem.getRule().getProposition();
333            if (propositionBo != null) {
334                saveNewParameterizedTerms(propositionBo);
335                processCustomOperators(propositionBo);
336            }
337        }
338
339        if (agendaBo instanceof PersistableBusinessObject) {
340            Map<String, String> primaryKeys = new HashMap<String, String>();
341            primaryKeys.put("id", agendaBo.getId());
342            AgendaBo dbAgendaBo = getBusinessObjectService().findByPrimaryKey(AgendaBo.class, primaryKeys);
343
344            if (ObjectUtils.isNotNull(dbAgendaBo)) {
345                Map<String, String> primaryKeyForFirstItem = new HashMap<String, String>();
346                primaryKeyForFirstItem.put("id", dbAgendaBo.getFirstItemId());
347                AgendaItemBo dbFirstAgendaItemBo = getBusinessObjectService().findByPrimaryKey(AgendaItemBo.class,
348                    primaryKeyForFirstItem);
349
350                List<AgendaItemBo> deletionOrder = new ArrayList<AgendaItemBo>();
351                addItemsToListForDeletion(deletionOrder, dbFirstAgendaItemBo);
352
353                for (AgendaItemBo agendaItemToDelete : deletionOrder) {
354                    getBusinessObjectService().delete(agendaItemToDelete);
355                }
356
357                dbAgendaBo.setItems(null);
358                getBusinessObjectService().delete(dbAgendaBo);
359            }
360
361            flushCacheBeforeSave();
362
363            getBusinessObjectService().linkAndSave(agendaBo);
364        } else {
365            throw new RuntimeException("Cannot save object of type: " + agendaBo + " with business object service");
366        }
367    }
368
369
370    private void addItemsToListForDeletion(List<AgendaItemBo> deletionOrder, AgendaItemBo agendaItemBo){
371        if (!deletionOrder.contains(agendaItemBo) && ObjectUtils.isNotNull(agendaItemBo)) {
372            deletionOrder.add(agendaItemBo);
373        }
374        if (ObjectUtils.isNotNull(agendaItemBo)) {
375            if (StringUtils.isNotBlank(agendaItemBo.getWhenTrueId()) &&
376                !deletionOrder.contains(agendaItemBo.getWhenTrue())) {
377                    deletionOrder.add(agendaItemBo.getWhenTrue());
378                    addItemsToListForDeletion (deletionOrder, agendaItemBo.getWhenTrue());
379            }
380            if (StringUtils.isNotBlank(agendaItemBo.getWhenFalseId()) &&
381                !deletionOrder.contains(agendaItemBo.getWhenFalse())) {
382                    deletionOrder.add(agendaItemBo.getWhenFalse());
383                    addItemsToListForDeletion (deletionOrder, agendaItemBo.getWhenFalse());
384            }
385            if (StringUtils.isNotBlank(agendaItemBo.getAlwaysId()) &&
386                !deletionOrder.contains(agendaItemBo.getAlways())) {
387                    deletionOrder.add(agendaItemBo.getAlways());
388                    addItemsToListForDeletion (deletionOrder,agendaItemBo.getAlways());
389            }
390        }
391    }
392
393    private void flushCacheBeforeSave(){
394        //flush krms caches
395        DistributedCacheManagerDecorator distributedCacheManagerDecorator =
396                GlobalResourceLoader.getService(KrmsConstants.KRMS_DISTRIBUTED_CACHE);
397
398        distributedCacheManagerDecorator.getCache(ActionDefinition.Cache.NAME).clear();
399        distributedCacheManagerDecorator.getCache(AgendaItemDefinition.Cache.NAME).clear();
400        distributedCacheManagerDecorator.getCache(AgendaTreeDefinition.Cache.NAME).clear();
401        distributedCacheManagerDecorator.getCache(AgendaDefinition.Cache.NAME).clear();
402        distributedCacheManagerDecorator.getCache(ContextDefinition.Cache.NAME).clear();
403        distributedCacheManagerDecorator.getCache(KrmsAttributeDefinition.Cache.NAME).clear();
404        distributedCacheManagerDecorator.getCache(KrmsTypeDefinition.Cache.NAME).clear();
405        distributedCacheManagerDecorator.getCache(RuleDefinition.Cache.NAME).clear();
406        distributedCacheManagerDecorator.getCache(PropositionDefinition.Cache.NAME).clear();
407        distributedCacheManagerDecorator.getCache(RuleDefinition.Cache.NAME).clear();
408        distributedCacheManagerDecorator.getCache(TermDefinition.Cache.NAME).clear();
409        distributedCacheManagerDecorator.getCache(TermResolverDefinition.Cache.NAME).clear();
410        distributedCacheManagerDecorator.getCache(TermSpecificationDefinition.Cache.NAME).clear();
411    }
412
413    /**
414     * walk the proposition tree and save any new parameterized terms that are contained therein
415     *
416     * @param propositionBo the root proposition from which to search
417     */
418    private void saveNewParameterizedTerms(PropositionBo propositionBo) {
419        if (StringUtils.isBlank(propositionBo.getCompoundOpCode())) {
420            // it is a simple proposition
421            if (!propositionBo.getParameters().isEmpty() && propositionBo.getParameters().get(0).getValue().startsWith(
422                    KrmsImplConstants.PARAMETERIZED_TERM_PREFIX)) {
423                String termId = propositionBo.getParameters().get(0).getValue();
424                String termSpecId = termId.substring(KrmsImplConstants.PARAMETERIZED_TERM_PREFIX.length());
425                // create new term
426                TermBo newTerm = new TermBo();
427                newTerm.setDescription(propositionBo.getNewTermDescription());
428                newTerm.setSpecificationId(termSpecId);
429                newTerm.setId(KRADServiceLocator.getSequenceAccessorService().getNextAvailableSequenceNumber(
430                        KrmsMaintenanceConstants.Sequences.TERM_SPECIFICATION, TermBo.class).toString());
431
432                List<TermParameterBo> params = new ArrayList<TermParameterBo>();
433                for (Map.Entry<String, String> entry : propositionBo.getTermParameters().entrySet()) {
434                    TermParameterBo param = new TermParameterBo();
435                    param.setTermId(newTerm.getId());
436                    param.setName(entry.getKey());
437                    param.setValue(entry.getValue());
438                    param.setId(KRADServiceLocator.getSequenceAccessorService().getNextAvailableSequenceNumber(
439                            KrmsMaintenanceConstants.Sequences.TERM_PARAMETER, TermParameterBo.class).toString());
440
441                    params.add(param);
442                }
443
444                newTerm.setParameters(params);
445
446                KRADServiceLocator.getBusinessObjectService().linkAndSave(newTerm);
447                propositionBo.getParameters().get(0).setValue(newTerm.getId());
448            }
449        } else {
450            // recurse
451            for (PropositionBo childProp : propositionBo.getCompoundComponents()) {
452                saveNewParameterizedTerms(childProp);
453            }
454        }
455    }
456
457    /**
458     * walk the proposition tree and process any custom operators found, converting them to custom function invocations.
459     *
460     * @param propositionBo the root proposition from which to search and convert
461     */
462    private void processCustomOperators(PropositionBo propositionBo) {
463        if (StringUtils.isBlank(propositionBo.getCompoundOpCode())) {
464            // if it is a simple proposition with a custom operator
465            if (!propositionBo.getParameters().isEmpty() && propositionBo.getParameters().get(2).getValue().startsWith(
466                    KrmsImplConstants.CUSTOM_OPERATOR_PREFIX)) {
467                PropositionParameterBo operatorParam = propositionBo.getParameters().get(2);
468
469                CustomOperator customOperator =
470                        KrmsServiceLocatorInternal.getCustomOperatorUiTranslator().getCustomOperator(operatorParam.getValue());
471
472                FunctionDefinition operatorFunctionDefinition = customOperator.getOperatorFunctionDefinition();
473
474                operatorParam.setParameterType(PropositionParameterType.FUNCTION.getCode());
475                operatorParam.setValue(operatorFunctionDefinition.getId());
476            }
477        } else {
478            // recurse
479            for (PropositionBo childProp : propositionBo.getCompoundComponents()) {
480                processCustomOperators(childProp);
481            }
482        }
483    }
484
485    /**
486     * Build a map from attribute name to attribute definition from all the defined attribute definitions for the
487     * specified agenda type
488     *
489     * @param agendaTypeId
490     * @return
491     */
492    private Map<String, KrmsAttributeDefinition> buildAttributeDefinitionMap(String agendaTypeId) {
493        KrmsAttributeDefinitionService attributeDefinitionService =
494                KrmsRepositoryServiceLocator.getKrmsAttributeDefinitionService();
495
496        // build a map from attribute name to definition
497        Map<String, KrmsAttributeDefinition> attributeDefinitionMap = new HashMap<String, KrmsAttributeDefinition>();
498
499        List<KrmsAttributeDefinition> attributeDefinitions = attributeDefinitionService.findAttributeDefinitionsByType(
500                agendaTypeId);
501
502        for (KrmsAttributeDefinition attributeDefinition : attributeDefinitions) {
503            attributeDefinitionMap.put(attributeDefinition.getName(), attributeDefinition);
504        }
505        return attributeDefinitionMap;
506    }
507
508    @Override
509    public boolean isOldDataObjectInDocument() {
510        boolean isOldDataObjectInExistence = true;
511
512        if (getDataObject() == null) {
513            isOldDataObjectInExistence = false;
514        } else {
515            // dataObject contains a non persistable wrapper - use agenda from the wrapper object instead
516            Map<String, ?> keyFieldValues = getDataObjectMetaDataService().getPrimaryKeyFieldValues(
517                    ((AgendaEditor) getDataObject()).getAgenda());
518            for (Object keyValue : keyFieldValues.values()) {
519                if (keyValue == null) {
520                    isOldDataObjectInExistence = false;
521                } else if ((keyValue instanceof String) && StringUtils.isBlank((String) keyValue)) {
522                    isOldDataObjectInExistence = false;
523                }
524
525                if (!isOldDataObjectInExistence) {
526                    break;
527                }
528            }
529        }
530
531        return isOldDataObjectInExistence;
532    }
533
534    // Since the dataObject is a wrapper class we need to return the agendaBo instead.
535    @Override
536    public Class getDataObjectClass() {
537        return AgendaBo.class;
538    }
539
540    @Override
541    public boolean isLockable() {
542        return true;
543    }
544
545    @Override
546    public PersistableBusinessObject getPersistableBusinessObject() {
547        return ((AgendaEditor) getDataObject()).getAgenda();
548    }
549
550    @Override
551    protected void processBeforeAddLine(View view, CollectionGroup collectionGroup, Object model, Object addLine) {
552        AgendaEditor agendaEditor = getAgendaEditor(model);
553        if (addLine instanceof ActionBo) {
554            ((ActionBo) addLine).setNamespace(agendaEditor.getAgendaItemLine().getRule().getNamespace());
555        }
556
557        super.processBeforeAddLine(view, collectionGroup, model, addLine);
558    }
559}