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.kew.impl.peopleflow;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.core.api.resourceloader.GlobalResourceLoader;
020import org.kuali.rice.core.api.uif.RemotableAttributeField;
021import org.kuali.rice.kew.api.KewApiServiceLocator;
022import org.kuali.rice.kew.api.peopleflow.PeopleFlowDefinition;
023import org.kuali.rice.kew.api.repository.type.KewTypeDefinition;
024import org.kuali.rice.kew.framework.peopleflow.PeopleFlowTypeService;
025import org.kuali.rice.krad.bo.Note;
026import org.kuali.rice.krad.maintenance.MaintainableImpl;
027import org.kuali.rice.krad.uif.container.CollectionGroup;
028import org.kuali.rice.krad.uif.container.Container;
029import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
030import org.kuali.rice.krad.uif.view.View;
031import org.kuali.rice.krad.util.KRADConstants;
032import org.kuali.rice.krad.web.form.MaintenanceForm;
033
034import javax.xml.namespace.QName;
035import java.util.ArrayList;
036import java.util.Collection;
037import java.util.Collections;
038import java.util.Comparator;
039import java.util.List;
040
041/**
042 * Custom view helper for the people flow maintenance document to retrieve the type attribute remotable fields
043 *
044 * @author Kuali Rice Team (rice.collab@kuali.org)
045 */
046public class PeopleFlowMaintainableImpl extends MaintainableImpl {
047
048
049    /**
050     * sort {@link org.kuali.rice.kew.impl.peopleflow.PeopleFlowMemberBo}s by stop number (priority)
051     *
052     * @param collection - the Collection to add the given addLine to
053     * @param addLine - the line to add to the given collection
054     */
055    @Override
056    protected void addLine(Collection<Object> collection, Object addLine) {
057        if (collection instanceof List) {
058            ((List) collection).add(0, addLine);
059            if (addLine instanceof PeopleFlowMemberBo) {
060                Collections.sort((List) collection, new Comparator<Object>() {
061                    public int compare(Object o1, Object o2) {
062                        if ((o1 instanceof PeopleFlowMemberBo) && (o1 instanceof PeopleFlowMemberBo)) {
063                            return ((PeopleFlowMemberBo) o1).getPriority() - ((PeopleFlowMemberBo) o2)
064                                    .getPriority();
065                        }
066                        return 0; // if not both PeopleFlowMemberBo something strange is going on.  Use equals as doing nothing.
067                    }
068                });
069            }
070        } else {
071            collection.add(addLine);
072        }
073    }
074
075    /**
076     * Invokes the {@link org.kuali.rice.kew.api.repository.type.KewTypeRepositoryService} to retrieve the remotable
077     * field definitions for the attributes associated with the selected type
078     *
079     * @param view - view instance
080     * @param model - object containing the form data, from which the selected type will be pulled
081     * @param container - container that holds the remotable fields
082     * @return List<RemotableAttributeField> instances for the type attributes, or empty list if not attributes exist
083     */
084    public List<RemotableAttributeField> retrieveTypeAttributes(View view, Object model, Container container) {
085        List<RemotableAttributeField> remoteFields = new ArrayList<RemotableAttributeField>();
086
087        PeopleFlowBo peopleFlow =
088                (PeopleFlowBo) ((MaintenanceForm) model).getDocument().getNewMaintainableObject().getDataObject();
089
090        // retrieve the type service and invoke to get the remotable field definitions
091        String typeId = peopleFlow.getTypeId();
092        if (StringUtils.isNotBlank(typeId)) {
093            KewTypeDefinition typeDefinition = KewApiServiceLocator.getKewTypeRepositoryService().getTypeById(typeId);
094            PeopleFlowTypeService peopleFlowTypeService = GlobalResourceLoader.<PeopleFlowTypeService>getService(
095                QName.valueOf(typeDefinition.getServiceName()));
096            remoteFields = peopleFlowTypeService.getAttributeFields(typeId);
097        }
098
099        return remoteFields;
100    }
101
102    /**
103     * Set the attribute bo list from the map of attribute key/value pairs and then calls
104     * {@link org.kuali.rice.kew.api.peopleflow.PeopleFlowService} to save the people flow instance
105     */
106    @Override
107    public void saveDataObject() {
108        ((PeopleFlowBo) getDataObject()).updateAttributeBoValues();
109
110        PeopleFlowDefinition peopleFlowDefinition;
111        if (KRADConstants.MAINTENANCE_COPY_ACTION.equals(getMaintenanceAction())) {
112            peopleFlowDefinition = PeopleFlowBo.maintenanceCopy(((PeopleFlowBo) getDataObject()));
113        } else {
114        // this to method ends up copying a versionNumber to null versionNumber 
115            peopleFlowDefinition = PeopleFlowBo.to(((PeopleFlowBo) getDataObject()));
116        }
117        if (StringUtils.isNotBlank(peopleFlowDefinition.getId())) {
118            KewApiServiceLocator.getPeopleFlowService().updatePeopleFlow(peopleFlowDefinition);
119        } else {
120            KewApiServiceLocator.getPeopleFlowService().createPeopleFlow(peopleFlowDefinition);
121        }
122    }
123
124    /**
125     * In the case of edit maintenance adds a new blank line to the old side
126     * This method is intended to override the method in MaintainableImpl
127     * but has a different set of parameters, so it is not actually an override.
128     * This version was needed to fetch the old collection from a different path
129     * than MaintainableImpl uses.
130     *
131     * @see org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl#processAfterAddLine(org.kuali.rice.krad.uif.view.View,
132     *      org.kuali.rice.krad.uif.container.CollectionGroup, java.lang.Object,
133     *      java.lang.Object)
134     */
135    protected void processAfterAddLine(View view, CollectionGroup collectionGroup, Object model, Object addLine, String collectionPath) {
136        // Check for maintenance documents in edit but exclude notes
137        if (model instanceof MaintenanceForm && KRADConstants.MAINTENANCE_EDIT_ACTION.equals(((MaintenanceForm)model).getMaintenanceAction()) && !(addLine instanceof Note)) {
138//            MaintenanceForm maintenanceForm = (MaintenanceForm) model;
139//            MaintenanceDocument document = maintenanceForm.getDocument();
140
141            // get the old object's collection
142            String oldCollectionPath = collectionPath.replace("newMaintainableObject","oldMaintainableObject");
143            Collection<Object> oldCollection = ObjectPropertyUtils.getPropertyValue(model, oldCollectionPath );
144            try {
145                Object blankLine = collectionGroup.getCollectionObjectClass().newInstance();
146                oldCollection.add(blankLine);
147            } catch (Exception e) {
148                throw new RuntimeException("Unable to create new line instance for old maintenance object", e);
149            }
150        }
151    }
152
153    /**
154     * This method is an override of ViewHelperService.processCollectionDeleteLine().
155     * It is virtually identical except that a local processAfterDeleteLine() method is called
156     * with a different parameter set than is called from within this method to delete the line
157     * from the old maintainable object.
158     * @see org.kuali.rice.krad.uif.service.ViewHelperService#processCollectionDeleteLine(org.kuali.rice.krad.uif.view.View,
159     *      java.lang.Object, java.lang.String, int)
160     */
161    @Override
162    public void processCollectionDeleteLine(View view, Object model, String collectionPath, int lineIndex) {
163        // get the collection group from the view
164        CollectionGroup collectionGroup = view.getViewIndex().getCollectionGroupByPath(collectionPath);
165        if (collectionGroup == null) {
166            logAndThrowRuntime("Unable to get collection group component for path: " + collectionPath);
167        }
168
169        // get the collection instance for adding the new line
170        Collection<Object> collection = ObjectPropertyUtils.getPropertyValue(model, collectionPath);
171        if (collection == null) {
172            logAndThrowRuntime("Unable to get collection property from model for path: " + collectionPath);
173        }
174
175        // TODO: look into other ways of identifying a line so we can deal with
176        // unordered collections
177        if (collection instanceof List) {
178            Object deleteLine = ((List<Object>) collection).get(lineIndex);
179
180            // validate the delete action is allowed for this line
181            boolean isValid = performDeleteLineValidation(view, collectionGroup, deleteLine);
182            if (isValid) {
183                ((List<Object>) collection).remove(lineIndex);
184                processAfterDeleteLine(view, collectionPath, model, lineIndex);
185            }
186        } else {
187            logAndThrowRuntime("Only List collection implementations are supported for the delete by index method");
188        }
189    }
190
191    /**
192     * In the case of edit maintenance deleted the item on the old side.
193     * This method is intended to override the method in MaintainableImpl
194     * but has a different set of parameters, so it is not actually an override.
195     * This was needed to fetch the old collection from a different path
196     * than MaintainableImpl uses. This version has the path (to the newMaintainableObject
197     * provided as a parameter, this is used to generate a path to the oldMaintainableObject
198     *
199     *
200     * @see org.kuali.rice.krad.uif.service.impl.ViewHelperServiceImpl#processAfterDeleteLine(View,
201     *      org.kuali.rice.krad.uif.container.CollectionGroup, java.lang.Object,  int)
202     */
203    protected void processAfterDeleteLine(View view, String collectionPath, Object model, int lineIndex) {
204
205        // Check for maintenance documents in edit
206        if (model instanceof MaintenanceForm && KRADConstants.MAINTENANCE_EDIT_ACTION.equals(((MaintenanceForm)model).getMaintenanceAction())) {
207
208            // get the old object's collection
209            String oldCollectionPath = collectionPath.replace("newMaintainableObject","oldMaintainableObject");
210            Collection<Object> oldCollection = ObjectPropertyUtils.getPropertyValue(model, oldCollectionPath);
211            try {
212                // Remove the object at lineIndex from the collection
213                oldCollection.remove(oldCollection.toArray()[lineIndex]);
214            } catch (Exception e) {
215                throw new RuntimeException("Unable to delete line instance for old maintenance object", e);
216            }
217        }
218    }
219
220
221}