001/**
002 * Copyright 2005-2015 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.kuali.rice.krad.maintenance.MaintainableImpl;
020import org.kuali.rice.krad.maintenance.MaintenanceDocument;
021import org.kuali.rice.krad.uif.util.ObjectPropertyUtils;
022import org.kuali.rice.krad.uif.view.ViewModel;
023import org.kuali.rice.krad.util.KRADConstants;
024import org.kuali.rice.krad.web.form.MaintenanceDocumentForm;
025import org.kuali.rice.krms.api.repository.context.ContextDefinition;
026import org.kuali.rice.krms.impl.repository.ContextBo;
027import org.kuali.rice.krms.impl.repository.ContextValidTermBo;
028import org.kuali.rice.krms.impl.repository.KrmsRepositoryServiceLocator;
029import org.kuali.rice.krms.impl.repository.TermSpecificationBo;
030
031import java.util.ArrayList;
032import java.util.List;
033import java.util.ListIterator;
034import java.util.Map;
035
036/**
037 * {@link org.kuali.rice.krad.maintenance.Maintainable} for the {@link AgendaEditor}
038 *
039 * @author Kuali Rice Team (rice.collab@kuali.org)
040 *
041 */
042public class TermSpecificationMaintainable extends MaintainableImpl {
043        
044        private static final long serialVersionUID = 1L;
045
046    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(TermSpecificationMaintainable.class);
047
048    @Override
049    public Object retrieveObjectForEditOrCopy(MaintenanceDocument document, Map<String, String> dataObjectKeys) {
050
051        TermSpecificationBo termSpecificationBo = (TermSpecificationBo) super.retrieveObjectForEditOrCopy(document,
052                dataObjectKeys);
053
054        if (KRADConstants.MAINTENANCE_COPY_ACTION.equals(getMaintenanceAction())) {
055            document.getDocumentHeader().setDocumentDescription("New Term Specification Document");
056        }
057
058        return termSpecificationBo;
059    }
060
061    /**
062         * {@inheritDoc}
063         */
064        @Override
065        public void processAfterNew(MaintenanceDocument document,
066                Map<String, String[]> requestParameters) {
067
068                super.processAfterNew(document, requestParameters);
069        document.getDocumentHeader().setDocumentDescription("New Term Specification Document");
070
071        }
072
073    /**
074     * {@inheritDoc}
075     */
076    @Override
077    public void processAfterEdit(MaintenanceDocument document, Map<String, String[]> requestParameters) {
078        super.processAfterEdit(document, requestParameters);
079        document.getDocumentHeader().setDocumentDescription("Edited Term Specification Document");
080    }
081
082    @Override
083    public void processAfterCopy(MaintenanceDocument document, Map<String, String[]> requestParameters) {
084        super.processAfterCopy(document, requestParameters);
085        copyContextsOldToNewBo(document);
086        setNewContextValidTermsNewBO(document);
087    }
088
089    /**
090     * Copy the contexts from the old TermSpecificationBo to the newTermSpecificationBo of the maintenance document.
091     * <p>
092     * Since the contexts is a transient field it doesn't get copied by the deepCopy in
093     * MaintenanceDocumentServiceImpl.setupMaintenanceObject, we manually need to copy the values over.
094     * For performance reasons a shallow copy is done since the ContextBo themselves are never changed.
095     * </p>
096     * @param document that contains the old and new TermSpecificationBos
097     */
098    private void copyContextsOldToNewBo(MaintenanceDocument document) {
099        TermSpecificationBo newTermSpec = (TermSpecificationBo) document.getNewMaintainableObject().getDataObject();
100
101        // Hydrade contexts as they are transient
102        newTermSpec.getContexts().clear();
103        newTermSpec.getContexts();
104    }
105
106    private void setNewContextValidTermsNewBO(MaintenanceDocument document) {
107        TermSpecificationBo newTermSpec = (TermSpecificationBo) document.getNewMaintainableObject().getDataObject();
108        for (ContextValidTermBo contextValidTermBo : newTermSpec.getContextValidTerms()) {
109            contextValidTermBo.setId(null);
110            contextValidTermBo.setTermSpecification(newTermSpec);
111        }
112    }
113
114    /**
115     * Overrides the parent method to additionaly clear the contexts list, which is needed for serialization performance
116     * & space.
117     *
118     * @see org.kuali.rice.krad.maintenance.Maintainable#prepareForSave
119     */
120    @Override
121    public void prepareForSave() {
122        super.prepareForSave();
123
124        TermSpecificationBo termSpec = (TermSpecificationBo) getDataObject();
125        termSpec.getContexts().clear();
126    }
127
128    /**
129     * For context addition, adds the item to the persisted contextValidTerms collection on the data object.
130     *
131     * <p>Without this step, the context is only added to a transient collection and the relationship will never be
132     * persisted. </p>
133     */
134    @Override
135    public void processAfterAddLine(ViewModel viewModel, Object addLine, String collectionId, String collectionPath,
136            boolean isValidLine) {
137        // we only want to do our custom processing if a context has been added
138        if (addLine == null || !(addLine instanceof ContextBo)) {
139            super.processAfterAddLine(viewModel, addLine, collectionId, collectionPath, isValidLine);
140            return;
141        }
142
143        ContextBo addedContext = (ContextBo) addLine;
144        TermSpecificationBo termSpec = getDataObjectFromForm(viewModel);
145
146        boolean alreadyHasContextValidTerm = false;
147
148        for (ContextValidTermBo contextValidTerm : termSpec.getContextValidTerms()) {
149            if (contextValidTerm.getContextId().equals(addedContext.getId())) {
150                alreadyHasContextValidTerm = true;
151                break;
152            }
153        }
154
155        if (!alreadyHasContextValidTerm) {
156            ContextValidTermBo contextValidTerm = new ContextValidTermBo();
157            contextValidTerm.setContextId(addedContext.getId());
158            contextValidTerm.setTermSpecification(termSpec);
159            termSpec.getContextValidTerms().add(contextValidTerm);
160        }
161    }
162
163    /**
164     * For context removal, removes the given item from the persisted contextValidTerms collection on the data object.
165     *
166     * <p>Without this step, the context is only removed from a transient collection and the severed relationship will
167     * never be persisted. </p>
168     */
169    @Override
170    public void processCollectionDeleteLine(ViewModel viewModel, String collectionId, String collectionPath,
171            int lineIndex) {
172        List collection = ObjectPropertyUtils.getPropertyValue(viewModel, collectionPath);
173        Object deletedItem = collection.get(lineIndex);
174
175        // we only want to do our custom processing if a context has been deleted
176        if (deletedItem == null || !(deletedItem instanceof ContextBo)) {
177            super.processCollectionDeleteLine(viewModel, collectionId, collectionPath, lineIndex);
178            return;
179        }
180
181        ContextBo context = (ContextBo) deletedItem;
182        TermSpecificationBo termSpec = getDataObjectFromForm(viewModel);
183
184        // find the context and remove it using the special powers of ListIterator
185        ListIterator<ContextValidTermBo> contextValidTermListIter = termSpec.getContextValidTerms().listIterator();
186        while (contextValidTermListIter.hasNext()) {
187            ContextValidTermBo contextValidTerm = contextValidTermListIter.next();
188
189            if (contextValidTerm.getContextId().equals(context.getId())) {
190                contextValidTerm.setTermSpecification(null);
191                contextValidTermListIter.remove();
192                termSpec.getContexts().remove(context);
193            }
194        }
195    }
196
197    /**
198     * Pulls the data object out of the given form and returns it, casting it to the desired type.
199     *
200     * <p>Assumes that the form is actually a MaintenanceDocumentForm.  The
201     * form.document.newMaintainableObject.dataObject is returned.</p>
202     *
203     * @param form
204     * @param <T> the type of data object to return
205     * @return the data object
206     */
207    private static <T> T getDataObjectFromForm(ViewModel form) {
208        if (form == null) { return null; }
209
210        return (T) ((MaintenanceDocumentForm)form).getDocument().getNewMaintainableObject().getDataObject();
211    }
212
213    @Override
214    public Class getDataObjectClass() {
215        return TermSpecificationBo.class;
216    }
217
218    /**
219     * Recreate the contexts from the contextIDs (needed due to serialization)
220     *
221     * @see org.kuali.rice.krad.maintenance.Maintainable#processAfterRetrieve
222     */
223    @Override
224    public void processAfterRetrieve() {
225        super.processAfterRetrieve();
226    }
227}