001    /**
002     * Copyright 2005-2012 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     */
016    package org.kuali.rice.krms.impl.repository;
017    
018    import java.util.Collection;
019    import java.util.Collections;
020    import java.util.HashMap;
021    import java.util.HashSet;
022    import java.util.List;
023    import java.util.Map;
024    import java.util.Map.Entry;
025    import java.util.Set;
026    
027    import org.apache.commons.lang.StringUtils;
028    import org.kuali.rice.core.api.exception.RiceIllegalStateException;
029    import org.kuali.rice.krad.service.BusinessObjectService;
030    import org.kuali.rice.krad.service.KRADServiceLocator;
031    import org.kuali.rice.krad.service.SequenceAccessorService;
032    import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition;
033    import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition;
034    import org.kuali.rice.krms.api.repository.type.KrmsAttributeDefinition;
035    import org.kuali.rice.krms.impl.util.KrmsImplConstants.PropertyNames;
036    
037    public final class AgendaBoServiceImpl implements AgendaBoService {
038    
039        // TODO: deal with active flag
040    
041        private BusinessObjectService businessObjectService;
042        private KrmsAttributeDefinitionService attributeDefinitionService;
043        private SequenceAccessorService sequenceAccessorService;
044    
045        /**
046         * This overridden method creates a KRMS Agenda in the repository
047         */
048        @Override
049        public AgendaDefinition createAgenda(AgendaDefinition agenda) {
050            if (agenda == null){
051                throw new IllegalArgumentException("agenda is null");
052            }
053            final String nameKey = agenda.getName();
054            final String contextId = agenda.getContextId();
055            final AgendaDefinition existing = getAgendaByNameAndContextId(nameKey, contextId);
056            if (existing != null){
057                throw new IllegalStateException("the agenda to create already exists: " + agenda);
058            }
059    
060            AgendaBo agendaBo = from(agenda);
061            businessObjectService.save(agendaBo);
062            return to(agendaBo);
063        }
064    
065        /**
066         * This overridden method updates an existing Agenda in the repository
067         */
068        @Override
069        public void updateAgenda(AgendaDefinition agenda) {
070            if (agenda == null){
071                throw new IllegalArgumentException("agenda is null");
072            }
073    
074            // must already exist to be able to update
075            final String agendaIdKey = agenda.getId();
076            final AgendaBo existing = businessObjectService.findBySinglePrimaryKey(AgendaBo.class, agendaIdKey);
077            if (existing == null) {
078                throw new IllegalStateException("the agenda does not exist: " + agenda);
079            }
080            final AgendaDefinition toUpdate;
081            if (existing.getId().equals(agenda.getId())) {
082                toUpdate = agenda;
083            } else {
084                // if passed in id does not match existing id, correct it
085                final AgendaDefinition.Builder builder = AgendaDefinition.Builder.create(agenda);
086                builder.setId(existing.getId());
087                toUpdate = builder.build();
088            }
089    
090            // copy all updateable fields to bo
091            AgendaBo boToUpdate = from(toUpdate);
092    
093            // delete any old, existing attributes
094            Map<String,String> fields = new HashMap<String,String>(1);
095            fields.put(PropertyNames.Agenda.AGENDA_ID, toUpdate.getId());
096            businessObjectService.deleteMatching(AgendaAttributeBo.class, fields);
097    
098            // update new agenda and create new attributes
099            businessObjectService.save(boToUpdate);
100        }
101    
102        /**
103         * This overridden method retrieves an Agenda from the repository
104         */
105        @Override
106        public AgendaDefinition getAgendaByAgendaId(String agendaId) {
107            if (StringUtils.isBlank(agendaId)){
108                throw new IllegalArgumentException("agenda id is null");
109            }
110            AgendaBo bo = businessObjectService.findBySinglePrimaryKey(AgendaBo.class, agendaId);
111            return to(bo);
112        }
113    
114        /**
115         * This overridden method retrieves an agenda from the repository
116         */
117        @Override
118        public AgendaDefinition getAgendaByNameAndContextId(String name, String contextId) {
119            if (StringUtils.isBlank(name)) {
120                throw new IllegalArgumentException("name is blank");
121            }
122            if (StringUtils.isBlank(contextId)) {
123                throw new IllegalArgumentException("contextId is blank");
124            }
125    
126            final Map<String, Object> map = new HashMap<String, Object>();
127            map.put("name", name);
128            map.put("contextId", contextId);
129    
130            AgendaBo myAgenda = businessObjectService.findByPrimaryKey(AgendaBo.class, Collections.unmodifiableMap(map));
131            return to(myAgenda);
132        }
133    
134        /**
135         * This overridden method retrieves a set of agendas from the repository
136         */
137        @Override
138        public Set<AgendaDefinition> getAgendasByContextId(String contextId) {
139            if (StringUtils.isBlank(contextId)){
140                throw new IllegalArgumentException("context ID is null or blank");
141            }
142            final Map<String, Object> map = new HashMap<String, Object>();
143            map.put("contextId", contextId);
144            Set<AgendaBo> bos = (Set<AgendaBo>) businessObjectService.findMatching(AgendaBo.class, map);
145            return convertListOfBosToImmutables(bos);
146        }
147    
148        /**
149         * This overridden method creates a new Agenda in the repository
150         */
151        @Override
152        public AgendaItemDefinition createAgendaItem(AgendaItemDefinition agendaItem) {
153            if (agendaItem == null){
154                throw new IllegalArgumentException("agendaItem is null");
155            }
156            if (agendaItem.getId() != null){
157                final AgendaDefinition existing = getAgendaByAgendaId(agendaItem.getId());
158                if (existing != null){
159                    throw new IllegalStateException("the agendaItem to create already exists: " + agendaItem);
160                }
161            }
162    
163            AgendaItemBo bo = AgendaItemBo.from(agendaItem);
164            businessObjectService.save(bo);
165            return AgendaItemBo.to(bo);
166        }
167    
168        /**
169         * This overridden method updates an existing Agenda in the repository
170         */
171        @Override
172        public void updateAgendaItem(AgendaItemDefinition agendaItem) {
173            if (agendaItem == null){
174                throw new IllegalArgumentException("agendaItem is null");
175            }
176            final String agendaItemIdKey = agendaItem.getId();
177            final AgendaItemDefinition existing = getAgendaItemById(agendaItemIdKey);
178            if (existing == null) {
179                throw new IllegalStateException("the agenda item does not exist: " + agendaItem);
180            }
181            final AgendaItemDefinition toUpdate;
182            if (existing.getId().equals(agendaItem.getId())) {
183                toUpdate = agendaItem;
184            } else {
185                final AgendaItemDefinition.Builder builder = AgendaItemDefinition.Builder.create(agendaItem);
186                builder.setId(existing.getId());
187                toUpdate = builder.build();
188            }
189    
190            businessObjectService.save(AgendaItemBo.from(toUpdate));
191        }
192    
193        /**
194         * This overridden method adds a new AgendaItemDefinition to the repository
195         */
196        @Override
197        public void addAgendaItem(AgendaItemDefinition agendaItem, String parentId, Boolean position) {
198            if (agendaItem == null){
199                throw new IllegalArgumentException("agendaItem is null");
200            }
201            AgendaItemDefinition parent = null;
202            if (parentId != null){
203                parent = getAgendaItemById(parentId);
204                if (parent == null){
205                    throw new IllegalStateException("parent agendaItem does not exist in repository. parentId = " + parentId);
206                }
207            }
208            // create new AgendaItemDefinition
209            final AgendaItemDefinition toCreate;
210            if (agendaItem.getId() == null) {
211                SequenceAccessorService sas = getSequenceAccessorService();
212                final AgendaItemDefinition.Builder builder = AgendaItemDefinition.Builder.create(agendaItem);
213                final String newId =sas.getNextAvailableSequenceNumber(
214                        "KRMS_AGENDA_ITM_S", AgendaItemBo.class).toString();
215                builder.setId(newId);
216                toCreate = builder.build();
217            } else {
218                toCreate = agendaItem;
219            }
220            createAgendaItem(toCreate);
221    
222            // link it to it's parent (for whenTrue/whenFalse, sibling for always
223            if (parentId != null) {
224                final AgendaItemDefinition.Builder builder = AgendaItemDefinition.Builder.create(parent);
225                if (position == null){
226                    builder.setAlwaysId( toCreate.getId() );
227                } else if (position.booleanValue()){
228                    builder.setWhenTrueId( toCreate.getId() );
229                } else if (!position.booleanValue()){
230                    builder.setWhenFalseId( toCreate.getId() );
231                }
232                final AgendaItemDefinition parentToUpdate = builder.build();
233                updateAgendaItem( parentToUpdate );
234            }
235        }
236    
237        /**
238         * This overridden method retrieves an AgendaItemDefinition from the repository
239         */
240        @Override
241        public AgendaItemDefinition getAgendaItemById(String id) {
242            if (StringUtils.isBlank(id)){
243                throw new IllegalArgumentException("agenda item id is null");
244            }
245            AgendaItemBo bo = businessObjectService.findBySinglePrimaryKey(AgendaItemBo.class, id);
246            return AgendaItemBo.to(bo);
247        }
248    
249        /**
250         * Sets the businessObjectService attribute value.
251         *
252         * @param businessObjectService The businessObjectService to set.
253         */
254        public void setBusinessObjectService(final BusinessObjectService businessObjectService) {
255            this.businessObjectService = businessObjectService;
256        }
257    
258        protected BusinessObjectService getBusinessObjectService() {
259            if ( businessObjectService == null ) {
260                businessObjectService = KRADServiceLocator.getBusinessObjectService();
261            }
262            return businessObjectService;
263        }
264    
265        /**
266         * Sets the sequenceAccessorService attribute value.
267         *
268         * @param sequenceAccessorService The sequenceAccessorService to set.
269         */
270        public void setSequenceAccessorService(final SequenceAccessorService sequenceAccessorService) {
271            this.sequenceAccessorService = sequenceAccessorService;
272        }
273    
274        protected SequenceAccessorService getSequenceAccessorService() {
275            if ( sequenceAccessorService == null ) {
276                sequenceAccessorService = KRADServiceLocator.getSequenceAccessorService();
277            }
278            return sequenceAccessorService;
279        }
280    
281        protected KrmsAttributeDefinitionService getAttributeDefinitionService() {
282            if (attributeDefinitionService == null) {
283                attributeDefinitionService = KrmsRepositoryServiceLocator.getKrmsAttributeDefinitionService();
284            }
285            return attributeDefinitionService;
286        }
287    
288        public void setAttributeDefinitionService(KrmsAttributeDefinitionService attributeDefinitionService) {
289            this.attributeDefinitionService = attributeDefinitionService;
290        }
291    
292        /**
293         * Converts a Set<AgendaBo> to an Unmodifiable Set<Agenda>
294         *
295         * @param agendaBos a mutable Set<AgendaBo> to made completely immutable.
296         * @return An unmodifiable Set<Agenda>
297         */
298        public Set<AgendaDefinition> convertListOfBosToImmutables(final Collection<AgendaBo> agendaBos) {
299            Set<AgendaDefinition> agendas = new HashSet<AgendaDefinition>();
300            if (agendaBos != null){
301                for (AgendaBo bo : agendaBos) {
302                    AgendaDefinition agenda = to(bo);
303                    agendas.add(agenda);
304                }
305            }
306            return Collections.unmodifiableSet(agendas);
307        }
308    
309        /**
310         * Converts a mutable bo to it's immutable counterpart
311         * @param bo the mutable business object
312         * @return the immutable object
313         */
314        @Override
315        public AgendaDefinition to(AgendaBo bo) {
316            if (bo == null) { return null; }
317            return org.kuali.rice.krms.api.repository.agenda.AgendaDefinition.Builder.create(bo).build();
318        }
319    
320    
321        /**
322         * Converts a immutable object to it's mutable bo counterpart
323         * @param im immutable object
324         * @return the mutable bo
325         */
326        @Override
327        public AgendaBo from(AgendaDefinition im) {
328            if (im == null) { return null; }
329    
330            AgendaBo bo = new AgendaBo();
331            bo.setId(im.getId());
332            bo.setName( im.getName() );
333            bo.setTypeId( im.getTypeId() );
334            bo.setContextId( im.getContextId() );
335            bo.setFirstItemId( im.getFirstItemId() );
336            bo.setVersionNumber( im.getVersionNumber() );
337            Set<AgendaAttributeBo> attributes = buildAgendaAttributeBo(im);
338    
339            bo.setAttributeBos(attributes);
340    
341            return bo;
342        }
343    
344        private Set<AgendaAttributeBo> buildAgendaAttributeBo(AgendaDefinition im) {
345            Set<AgendaAttributeBo> attributes = new HashSet<AgendaAttributeBo>();
346    
347            ContextBo context = getBusinessObjectService().findBySinglePrimaryKey(ContextBo.class, im.getContextId());
348    
349            // build a map from attribute name to definition
350            Map<String, KrmsAttributeDefinition> attributeDefinitionMap = new HashMap<String, KrmsAttributeDefinition>();
351    
352            List<KrmsAttributeDefinition> attributeDefinitions =
353                    getAttributeDefinitionService().findAttributeDefinitionsByType(im.getTypeId());
354    
355            for (KrmsAttributeDefinition attributeDefinition : attributeDefinitions) {
356                attributeDefinitionMap.put(attributeDefinition.getName(), attributeDefinition);
357            }
358    
359            // for each entry, build an AgendaAttributeBo and add it to the set
360            for (Entry<String,String> entry  : im.getAttributes().entrySet()){
361                KrmsAttributeDefinition attrDef = attributeDefinitionMap.get(entry.getKey());
362    
363                if (attrDef != null) {
364                    AgendaAttributeBo attributeBo = new AgendaAttributeBo();
365                    attributeBo.setAgendaId( im.getId() );
366                    attributeBo.setAttributeDefinitionId(attrDef.getId());
367                    attributeBo.setValue(entry.getValue());
368                    attributeBo.setAttributeDefinition(KrmsAttributeDefinitionBo.from(attrDef));
369                    attributes.add( attributeBo );
370                } else {
371                    throw new RiceIllegalStateException("there is no attribute definition with the name '" +
372                            entry.getKey() + "' that is valid for the agenda type with id = '" + im.getTypeId() +"'");
373                }
374            }
375            return attributes;
376        }
377    
378    }