001/**
002 * Copyright 2005-2017 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 org.apache.commons.collections.CollectionUtils;
019import org.apache.commons.lang.StringUtils;
020import org.kuali.rice.core.api.criteria.QueryByCriteria;
021import org.kuali.rice.core.api.criteria.QueryResults;
022import org.kuali.rice.core.api.exception.RiceIllegalArgumentException;
023import org.kuali.rice.core.api.mo.ModelObjectUtils;
024import org.kuali.rice.krad.data.DataObjectService;
025import org.kuali.rice.krad.data.PersistenceOption;
026import org.kuali.rice.krms.api.repository.function.FunctionDefinition;
027import org.kuali.rice.krms.api.repository.function.FunctionRepositoryService;
028
029import java.util.ArrayList;
030import java.util.Collection;
031import java.util.Collections;
032import java.util.HashMap;
033import java.util.List;
034import java.util.Map;
035
036/**
037 * Default implementation of the {@link FunctionRepositoryService}.
038 *
039 * @author Kuali Rice Team (rice.collab@kuali.org)
040 */
041public class FunctionBoServiceImpl implements FunctionRepositoryService, FunctionBoService {
042
043    private DataObjectService dataObjectService;
044
045    // used for converting lists of BOs to model objects
046    private static final ModelObjectUtils.Transformer<FunctionBo, FunctionDefinition> toFunctionDefinition =
047            new ModelObjectUtils.Transformer<FunctionBo, FunctionDefinition>() {
048                public FunctionDefinition transform(FunctionBo input) {
049                    return FunctionBo.to(input);
050                };
051            };
052
053    @Override
054    public FunctionDefinition getFunction(String functionId) {
055        return getFunctionById(functionId);
056    }
057
058    @Override
059    public List<FunctionDefinition> getFunctions(List<String> functionIds) {
060
061        if (functionIds == null) {
062            throw new RiceIllegalArgumentException();
063        }
064
065        List<FunctionDefinition> functionDefinitions = new ArrayList<FunctionDefinition>();
066        for (String functionId : functionIds) {
067            if (!StringUtils.isBlank(functionId)) {
068                FunctionDefinition functionDefinition = getFunctionById(functionId);
069                if (functionDefinition != null) {
070                    functionDefinitions.add(functionDefinition);
071                }
072            }
073        }
074        return Collections.unmodifiableList(functionDefinitions);
075    }
076
077    /**
078     * This method will create a {@link FunctionDefinition} as described
079     * by the function passed in.
080     *
081     * @see org.kuali.rice.krms.impl.repository.FunctionBoService#createFunction(org.kuali.rice.krms.api.repository.function.FunctionDefinition)
082     */
083    @Override
084    public FunctionDefinition createFunction(FunctionDefinition function) {
085        if (function == null) {
086            throw new IllegalArgumentException("function is null");
087        }
088
089        final String nameKey = function.getName();
090        final String namespaceKey = function.getNamespace();
091        final FunctionDefinition existing = getFunctionByNameAndNamespace(nameKey, namespaceKey);
092
093        if (existing != null && existing.getName().equals(nameKey) && existing.getNamespace().equals(namespaceKey)) {
094            throw new IllegalStateException("the function to create already exists: " + function);
095        }
096
097        FunctionBo functionBo = FunctionBo.from(function);
098        for (FunctionParameterBo param : functionBo.getParameters()) {
099            param.setFunction(functionBo);
100        }
101
102        functionBo = dataObjectService.save(functionBo, PersistenceOption.FLUSH);
103
104        return FunctionBo.to(functionBo);
105    }
106
107    /**
108     * This overridden method updates an existing Function in the repository
109     *
110     * @see org.kuali.rice.krms.impl.repository.FunctionBoService#updateFunction(org.kuali.rice.krms.api.repository.function.FunctionDefinition)
111     */
112    @Override
113    public FunctionDefinition updateFunction(FunctionDefinition function) {
114        if (function == null) {
115            throw new IllegalArgumentException("function is null");
116        }
117
118        final String functionIdKey = function.getId();
119        final FunctionDefinition existing = getFunctionById(functionIdKey);
120
121        if (existing == null) {
122            throw new IllegalStateException("the function does not exist: " + function);
123        }
124
125        final FunctionDefinition toUpdate;
126
127        if (!existing.getId().equals(function.getId())) {
128            final FunctionDefinition.Builder builder = FunctionDefinition.Builder.create(function);
129            builder.setId(existing.getId());
130            toUpdate = builder.build();
131        } else {
132            toUpdate = function;
133        }
134
135        return FunctionBo.to(dataObjectService.save(FunctionBo.from(toUpdate), PersistenceOption.FLUSH));
136        // TODO: Do we need to return the updated FunctionDefinition?
137    }
138
139    /**
140     * This overridden method retrieves a function by the given function id.
141     *
142     * @see org.kuali.rice.krms.impl.repository.FunctionBoService#getFunctionById(java.lang.String)
143     */
144    @Override
145    public FunctionDefinition getFunctionById(String functionId) {
146        if (StringUtils.isBlank(functionId)) {
147            throw new RiceIllegalArgumentException("functionId is null or blank");
148        }
149
150        FunctionBo functionBo = dataObjectService.find(FunctionBo.class, functionId);
151
152        return FunctionBo.to(functionBo);
153    }
154
155    /**
156     * This overridden method retrieves a function by the given name and namespace.
157     *
158     * @see org.kuali.rice.krms.impl.repository.FunctionBoService#getFunctionByNameAndNamespace(java.lang.String,
159     * java.lang.String)
160     */
161    public FunctionDefinition getFunctionByNameAndNamespace(String name, String namespace) {
162        if (StringUtils.isBlank(name)) {
163            throw new IllegalArgumentException("name is null or blank");
164        }
165        if (StringUtils.isBlank(namespace)) {
166            throw new IllegalArgumentException("namespace is null or blank");
167        }
168
169        final Map<String, Object> map = new HashMap<String, Object>();
170        map.put("name", name);
171        map.put("namespace", namespace);
172        QueryByCriteria query = QueryByCriteria.Builder.andAttributes(map).build();
173        QueryResults<FunctionBo> results = dataObjectService.findMatching(FunctionBo.class, query);
174
175        if (results == null || results.getResults().size() == 0) {
176            // fall through and return null
177        } else if (results.getResults().size() == 1) {
178            return FunctionBo.to(results.getResults().get(0));
179        } else if (results.getResults().size() > 1) {
180            throw new IllegalStateException("there can be only one FunctionDefinition for a given name and namespace");
181        }
182
183        return null;
184    }
185
186    /**
187     * Gets all of the {@link FunctionDefinition}s within the given namespace
188     *
189     * @param namespace the namespace in which to get the functions
190     * @return the list of function definitions, or if none are found, an empty list
191     */
192    public List<FunctionDefinition> getFunctionsByNamespace(String namespace) {
193        if (StringUtils.isBlank(namespace)) {
194            throw new IllegalArgumentException("namespace is null or blank");
195        }
196
197        QueryByCriteria criteria = QueryByCriteria.Builder.forAttribute("namespace", namespace).build();
198        QueryResults<FunctionBo> queryResults = dataObjectService.findMatching(FunctionBo.class, criteria);
199        List<FunctionBo> functionBos = queryResults.getResults();
200
201        return convertFunctionBosToImmutables(functionBos);
202    }
203
204    /**
205     * Converts a Collection of FunctionBos to an Unmodifiable List of Agendas
206     *
207     * @param functionBos a mutable List of FunctionBos to made completely immutable.
208     * @return An unmodifiable List of FunctionDefinitions
209     */
210    private List<FunctionDefinition> convertFunctionBosToImmutables(final Collection<FunctionBo> functionBos) {
211        if (CollectionUtils.isEmpty(functionBos)) {
212            return Collections.emptyList();
213        }
214        return Collections.unmodifiableList(ModelObjectUtils.transform(functionBos, toFunctionDefinition));
215    }
216
217    public void setDataObjectService(DataObjectService dataObjectService) {
218        this.dataObjectService = dataObjectService;
219    }
220}