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