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