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.krad.datadictionary;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.krad.datadictionary.exception.ClassValidationException;
020import org.kuali.rice.krad.datadictionary.exception.DuplicateEntryException;
021import org.kuali.rice.krad.document.Document;
022import org.kuali.rice.krad.document.DocumentAuthorizer;
023import org.kuali.rice.krad.document.DocumentAuthorizerBase;
024import org.kuali.rice.krad.document.DocumentPresentationController;
025import org.kuali.rice.krad.document.DocumentPresentationControllerBase;
026import org.kuali.rice.krad.keyvalues.KeyValuesFinder;
027import org.kuali.rice.krad.rules.rule.BusinessRule;
028
029import java.util.ArrayList;
030import java.util.LinkedHashMap;
031import java.util.List;
032import java.util.Map;
033
034/**
035 * A single Document entry in the DataDictionary, which contains information relating to the display, validation, and
036 * general maintenance of a Document (transactional or maintenance) and its attributes
037 *
038 * <p>
039 * Note: the setters do copious amounts of validation, to facilitate generating errors during the parsing process
040 * </p>
041 *
042 * @author Kuali Rice Team (rice.collab@kuali.org)
043 */
044public abstract class DocumentEntry extends DataDictionaryEntryBase {
045    private static final long serialVersionUID = 8231730871830055356L;
046
047    private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger.getLogger(DocumentEntry.class);
048
049    protected String documentTypeName;
050
051    protected Class<? extends Document> documentClass;
052    protected Class<? extends Document> baseDocumentClass;
053    protected Class<? extends BusinessRule> businessRulesClass;
054
055    protected boolean allowsNoteAttachments = true;
056    protected boolean allowsNoteFYI = false;
057    protected Class<? extends KeyValuesFinder> attachmentTypesValuesFinderClass;
058    protected boolean displayTopicFieldInNotes = false;
059    protected boolean usePessimisticLocking = false;
060    protected boolean useWorkflowPessimisticLocking = false;
061    protected boolean encryptDocumentDataInPersistentSessionStorage = false;
062
063    protected boolean allowsCopy = false;
064    protected WorkflowProperties workflowProperties;
065    protected WorkflowAttributes workflowAttributes;
066
067    protected Class<? extends DocumentAuthorizer> documentAuthorizerClass;
068    protected Class<? extends DocumentPresentationController> documentPresentationControllerClass;
069
070    protected List<ReferenceDefinition> defaultExistenceChecks = new ArrayList<ReferenceDefinition>();
071    protected Map<String, ReferenceDefinition> defaultExistenceCheckMap =
072            new LinkedHashMap<String, ReferenceDefinition>();
073
074    public DocumentEntry() {
075        super();
076
077        documentAuthorizerClass = DocumentAuthorizerBase.class;
078        documentPresentationControllerClass = DocumentPresentationControllerBase.class;
079    }
080
081    /**
082     * @see org.kuali.rice.krad.datadictionary.DataDictionaryEntry#getJstlKey()
083     */
084    public String getJstlKey() {
085        return documentTypeName;
086    }
087
088    /**
089     * The documentClass element is the name of the java class
090     * associated with the document.
091     */
092    public void setDocumentClass(Class<? extends Document> documentClass) {
093        if (documentClass == null) {
094            throw new IllegalArgumentException("invalid (null) documentClass");
095        }
096
097        this.documentClass = documentClass;
098    }
099
100    public Class<? extends Document> getDocumentClass() {
101        return documentClass;
102    }
103
104    /**
105     * The optional baseDocumentClass element is the name of the java base class
106     * associated with the document. This gives the data dictionary the ability
107     * to index by the base class in addition to the current class.
108     */
109    public void setBaseDocumentClass(Class<? extends Document> baseDocumentClass) {
110        this.baseDocumentClass = baseDocumentClass;
111    }
112
113    public Class<? extends Document> getBaseDocumentClass() {
114        return baseDocumentClass;
115    }
116
117    /**
118     * The businessRulesClass element is the full class name of the java
119     * class which contains the business rules for a document.
120     */
121    public void setBusinessRulesClass(Class<? extends BusinessRule> businessRulesClass) {
122        this.businessRulesClass = businessRulesClass;
123    }
124
125    public Class<? extends BusinessRule> getBusinessRulesClass() {
126        return businessRulesClass;
127    }
128
129    /**
130     * The documentTypeName element is the name of the document
131     * as defined in the workflow system.
132     * Example: "AddressTypeMaintenanceDocument"
133     */
134    public void setDocumentTypeName(String documentTypeName) {
135        if (StringUtils.isBlank(documentTypeName)) {
136            throw new IllegalArgumentException("invalid (blank) documentTypeName");
137        }
138        this.documentTypeName = documentTypeName;
139    }
140
141    public String getDocumentTypeName() {
142        return this.documentTypeName;
143    }
144
145    /**
146     * Validate common fields for subclass' benefit.
147     *
148     * @see org.kuali.rice.krad.datadictionary.DataDictionaryEntry#completeValidation()
149     */
150    public void completeValidation() {
151        super.completeValidation();
152
153        if (workflowProperties != null && workflowAttributes != null) {
154            throw new DataDictionaryException(documentTypeName +
155                    ": workflowProperties and workflowAttributes cannot both be defined for a document");
156        }
157    }
158
159    /**
160     * @see org.kuali.rice.krad.datadictionary.DataDictionaryEntry#getFullClassName()
161     */
162    public String getFullClassName() {
163        if (getBaseDocumentClass() != null) {
164            return getBaseDocumentClass().getName();
165        }
166        if (getDocumentClass() != null) {
167            return getDocumentClass().getName();
168        }
169        return "";
170    }
171
172    /**
173     * @see org.kuali.rice.krad.datadictionary.DataDictionaryEntryBase#getEntryClass()
174     */
175    public Class getEntryClass() {
176        return getDocumentClass();
177    }
178
179    public String toString() {
180        return "DocumentEntry for documentType " + documentTypeName;
181    }
182
183    /**
184     * Accessor method for contained displayTopicFieldInNotes
185     *
186     * @return displayTopicFieldInNotes boolean
187     */
188    public boolean getDisplayTopicFieldInNotes() {
189        return displayTopicFieldInNotes;
190    }
191
192    /**
193     * This field contains a value of true or false.
194     * If true, then the "Notes and Attachments" tab will render a column for a note topic.
195     */
196    public void setDisplayTopicFieldInNotes(boolean displayTopicFieldInNotes) {
197        this.displayTopicFieldInNotes = displayTopicFieldInNotes;
198    }
199
200    /**
201     * Accessor method for contained usePessimisticLocking
202     *
203     * @return usePessimisticLocking boolean
204     */
205    public boolean getUsePessimisticLocking() {
206        return this.usePessimisticLocking;
207    }
208
209    /**
210     * @param usePessimisticLocking
211     */
212    public void setUsePessimisticLocking(boolean usePessimisticLocking) {
213        if (LOG.isDebugEnabled()) {
214            LOG.debug("calling setUsePessimisticLocking '" + usePessimisticLocking + "'");
215        }
216
217        this.usePessimisticLocking = usePessimisticLocking;
218    }
219
220    /**
221     * Accessor method for contained useWorkflowPessimisticLocking
222     *
223     * @return useWorkflowPessimisticLocking boolean
224     */
225    public boolean getUseWorkflowPessimisticLocking() {
226        return this.useWorkflowPessimisticLocking;
227    }
228
229    /**
230     * @param useWorkflowPessimisticLocking
231     */
232    public void setUseWorkflowPessimisticLocking(boolean useWorkflowPessimisticLocking) {
233        if (LOG.isDebugEnabled()) {
234            LOG.debug("calling setuseWorkflowPessimisticLocking '" + useWorkflowPessimisticLocking + "'");
235        }
236
237        this.useWorkflowPessimisticLocking = useWorkflowPessimisticLocking;
238    }
239
240    /**
241     * The attachmentTypesValuesFinderClass specifies the name of a values finder
242     * class. This is used to determine the set of file types that are allowed
243     * to be attached to the document.
244     */
245    public void setAttachmentTypesValuesFinderClass(Class<? extends KeyValuesFinder> attachmentTypesValuesFinderClass) {
246        if (attachmentTypesValuesFinderClass == null) {
247            throw new IllegalArgumentException("invalid (null) attachmentTypesValuesFinderClass");
248        }
249
250        this.attachmentTypesValuesFinderClass = attachmentTypesValuesFinderClass;
251    }
252
253    /**
254     * @see org.kuali.rice.krad.datadictionary.control.ControlDefinition#getKeyValuesFinder()
255     */
256    public Class<? extends KeyValuesFinder> getAttachmentTypesValuesFinderClass() {
257        return attachmentTypesValuesFinderClass;
258    }
259
260    /**
261     * The allowsCopy element contains a true or false value.
262     * If true, then a user is allowed to make a copy of the
263     * record using the maintenance screen.
264     */
265    public void setAllowsCopy(boolean allowsCopy) {
266        this.allowsCopy = allowsCopy;
267    }
268
269    public boolean getAllowsCopy() {
270        return allowsCopy;
271    }
272
273    /**
274     * @return the allowsNoteAttachments
275     */
276    public boolean getAllowsNoteAttachments() {
277        return this.allowsNoteAttachments;
278    }
279
280    /**
281     * The allowsNoteAttachments element contains a true or false value.
282     * If true, then a document screen includes notes with attachments. Otherwise,
283     * only notes is displayed.
284     */
285    public void setAllowsNoteAttachments(boolean allowsNoteAttachments) {
286        this.allowsNoteAttachments = allowsNoteAttachments;
287    }
288
289    /**
290     * @return the allowsNoteFYI
291     */
292    public boolean getAllowsNoteFYI() {
293        return allowsNoteFYI;
294    }
295
296    /**
297     * This is an indicator for determining whether to render the AdHoc FYI recipient box and Send FYI button.
298     */
299    public void setAllowsNoteFYI(boolean allowsNoteFYI) {
300        this.allowsNoteFYI = allowsNoteFYI;
301    }
302
303    public WorkflowProperties getWorkflowProperties() {
304        return this.workflowProperties;
305    }
306
307    /**
308     * This element is used to define a set of workflowPropertyGroups, which are used to
309     * specify which document properties should be serialized during the document serialization
310     * process.
311     */
312    public void setWorkflowProperties(WorkflowProperties workflowProperties) {
313        this.workflowProperties = workflowProperties;
314    }
315
316    public WorkflowAttributes getWorkflowAttributes() {
317        return this.workflowAttributes;
318    }
319
320    public void setWorkflowAttributes(WorkflowAttributes workflowAttributes) {
321        this.workflowAttributes = workflowAttributes;
322    }
323
324    /**
325     * Full class name for the {@link DocumentAuthorizer} that will authorize actions for this document
326     *
327     * @return class name for document authorizer
328     */
329    public Class<? extends DocumentAuthorizer> getDocumentAuthorizerClass() {
330        return documentAuthorizerClass;
331    }
332
333    /**
334     * Setter for the document authorizer class name
335     *
336     * @param documentAuthorizerClass
337     */
338    public void setDocumentAuthorizerClass(Class<? extends DocumentAuthorizer> documentAuthorizerClass) {
339        this.documentAuthorizerClass = documentAuthorizerClass;
340    }
341
342    /**
343     * Full class name for the {@link DocumentPresentationController} that will be invoked to implement presentation
344     * logic for the document
345     *
346     * @return class name for document presentation controller
347     */
348    public Class<? extends DocumentPresentationController> getDocumentPresentationControllerClass() {
349        return documentPresentationControllerClass;
350    }
351
352    /**
353     * Setter for the document presentation controller class name
354     *
355     * @param documentPresentationControllerClass
356     */
357    public void setDocumentPresentationControllerClass(
358            Class<? extends DocumentPresentationController> documentPresentationControllerClass) {
359        this.documentPresentationControllerClass = documentPresentationControllerClass;
360    }
361
362    /**
363     * @return List of all defaultExistenceCheck ReferenceDefinitions associated with this MaintenanceDocument, in the
364     *         order in
365     *         which they were added
366     */
367    public List<ReferenceDefinition> getDefaultExistenceChecks() {
368        return defaultExistenceChecks;
369    }
370
371    /*
372           The defaultExistenceChecks element contains a list of
373           reference object names which are required to exist when maintaining a BO.
374           Optionally, the reference objects can be required to be active.
375
376           JSTL: defaultExistenceChecks is a Map of Reference elements,
377           whose entries are keyed by attributeName
378    */
379    public void setDefaultExistenceChecks(List<ReferenceDefinition> defaultExistenceChecks) {
380        this.defaultExistenceChecks = defaultExistenceChecks;
381    }
382
383    /**
384     * @return List of all defaultExistenceCheck reference fieldNames associated with this MaintenanceDocument, in the
385     *         order in
386     *         which they were added
387     */
388    public List<String> getDefaultExistenceCheckFieldNames() {
389        List<String> fieldNames = new ArrayList<String>();
390        fieldNames.addAll(this.defaultExistenceCheckMap.keySet());
391
392        return fieldNames;
393    }
394
395    public boolean isEncryptDocumentDataInPersistentSessionStorage() {
396        return this.encryptDocumentDataInPersistentSessionStorage;
397    }
398
399    public void setEncryptDocumentDataInPersistentSessionStorage(
400            boolean encryptDocumentDataInPersistentSessionStorage) {
401        this.encryptDocumentDataInPersistentSessionStorage = encryptDocumentDataInPersistentSessionStorage;
402    }
403
404    /**
405     * This overridden method ...
406     *
407     * @see org.kuali.rice.krad.datadictionary.DataDictionaryEntryBase#afterPropertiesSet()
408     */
409    @Override
410    public void afterPropertiesSet() throws Exception {
411        super.afterPropertiesSet();
412        if (defaultExistenceChecks != null) {
413            defaultExistenceCheckMap.clear();
414            for (ReferenceDefinition reference : defaultExistenceChecks) {
415                if (reference == null) {
416                    throw new IllegalArgumentException("invalid (null) defaultExistenceCheck");
417                }
418
419                String keyName = reference.isCollectionReference() ?
420                        (reference.getCollection() + "." + reference.getAttributeName()) : reference.getAttributeName();
421                if (defaultExistenceCheckMap.containsKey(keyName)) {
422                    throw new DuplicateEntryException(
423                            "duplicate defaultExistenceCheck entry for attribute '" + keyName + "'");
424                }
425                reference.setBusinessObjectClass(getEntryClass());
426                defaultExistenceCheckMap.put(keyName, reference);
427            }
428        }
429    }
430}