001    /**
002     * Copyright 2005-2013 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     */
016    package org.kuali.rice.krad.datadictionary;
017    
018    import org.apache.commons.lang.StringUtils;
019    import org.kuali.rice.krad.datadictionary.exception.ClassValidationException;
020    import org.kuali.rice.krad.datadictionary.exception.DuplicateEntryException;
021    import org.kuali.rice.krad.document.Document;
022    import org.kuali.rice.krad.document.DocumentAuthorizer;
023    import org.kuali.rice.krad.document.DocumentAuthorizerBase;
024    import org.kuali.rice.krad.document.DocumentPresentationController;
025    import org.kuali.rice.krad.document.DocumentPresentationControllerBase;
026    import org.kuali.rice.krad.keyvalues.KeyValuesFinder;
027    import org.kuali.rice.krad.rules.rule.BusinessRule;
028    
029    import java.util.ArrayList;
030    import java.util.LinkedHashMap;
031    import java.util.List;
032    import 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     */
044    public 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    }