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.rule;
017
018import java.sql.Timestamp;
019import java.util.ArrayList;
020import java.util.Date;
021import java.util.HashMap;
022import java.util.Iterator;
023import java.util.List;
024import java.util.Map;
025
026import javax.persistence.CascadeType;
027import javax.persistence.Column;
028import javax.persistence.Convert;
029import javax.persistence.Entity;
030import javax.persistence.FetchType;
031import javax.persistence.GeneratedValue;
032import javax.persistence.Id;
033import javax.persistence.JoinColumn;
034import javax.persistence.ManyToOne;
035import javax.persistence.OneToMany;
036import javax.persistence.OneToOne;
037import javax.persistence.Table;
038import javax.persistence.Transient;
039
040import org.apache.commons.lang.StringUtils;
041import org.joda.time.DateTime;
042import org.kuali.rice.core.api.util.RiceConstants;
043import org.kuali.rice.kew.api.KewApiConstants;
044import org.kuali.rice.kew.api.rule.RuleContract;
045import org.kuali.rice.kew.api.rule.RuleExtension;
046import org.kuali.rice.kew.api.util.CodeTranslator;
047import org.kuali.rice.kew.doctype.bo.DocumentType;
048import org.kuali.rice.kew.lookupable.MyColumns;
049import org.kuali.rice.kew.routeheader.DocumentContent;
050import org.kuali.rice.kew.rule.bo.RuleAttribute;
051import org.kuali.rice.kew.rule.bo.RuleTemplateAttributeBo;
052import org.kuali.rice.kew.rule.bo.RuleTemplateBo;
053import org.kuali.rice.kew.rule.service.RuleServiceInternal;
054import org.kuali.rice.kew.rule.xmlrouting.GenericXMLRuleAttribute;
055import org.kuali.rice.kew.service.KEWServiceLocator;
056import org.kuali.rice.kim.api.services.KimApiServiceLocator;
057import org.kuali.rice.kim.impl.group.GroupBo;
058import org.kuali.rice.kim.impl.identity.PersonImpl;
059import org.kuali.rice.krad.bo.PersistableBusinessObjectBase;
060import org.kuali.rice.krad.data.jpa.PortableSequenceGenerator;
061import org.kuali.rice.krad.data.jpa.converters.Boolean01Converter;
062import org.kuali.rice.krad.data.provider.annotation.SerializationContext;
063import org.kuali.rice.krad.data.provider.annotation.Serialized;
064
065/*import org.kuali.rice.kim.api.group.Group;*/
066
067
068/**
069 * A model bean for a Rule within the KEW rules engine.
070 *
071 * @author Kuali Rice Team (rice.collab@kuali.org)
072 */
073@Entity
074@Table(name="KREW_RULE_T")
075public class RuleBaseValues extends PersistableBusinessObjectBase implements RuleContract {
076
077    private static final long serialVersionUID = 6137765574728530156L;
078    @Id
079    @GeneratedValue(generator = "KREW_RTE_TMPL_S")
080    @PortableSequenceGenerator(name = "KREW_RTE_TMPL_S")
081        @Column(name="RULE_ID")
082    private String id;
083
084    /**
085     * Unique Rule name
086     */
087    @Column(name="NM")
088        private String name;
089
090    @Column(name="RULE_TMPL_ID")
091        private String ruleTemplateId;
092
093    @Column(name="PREV_VER_RULE_ID")
094        private String previousRuleId;
095
096    @Column(name="ACTV_IND")
097    @Convert(converter=Boolean01Converter.class)
098        private boolean active = true;
099
100    @Column(name="RULE_BASE_VAL_DESC")
101        private String description;
102
103    @Column(name="DOC_TYP_NM")
104        private String docTypeName;
105
106    @Column(name="DOC_HDR_ID")
107        private String documentId;
108
109        @Column(name="FRM_DT")
110        private Timestamp fromDateValue;
111
112        @Column(name="TO_DT")
113        private Timestamp toDateValue;
114
115        @Column(name="DACTVN_DT")
116        private Timestamp deactivationDate;
117
118    @Column(name="CUR_IND")
119    @Convert(converter=Boolean01Converter.class)
120        private Boolean currentInd = Boolean.TRUE;
121
122    @Column(name="RULE_VER_NBR")
123        private Integer versionNbr = 0;
124
125    @Column(name="FRC_ACTN")
126    @Convert(converter=Boolean01Converter.class)
127        private boolean forceAction;
128
129    @OneToMany(fetch=FetchType.EAGER,cascade=CascadeType.ALL, orphanRemoval = true)
130    @JoinColumn(name = "RULE_ID", referencedColumnName = "RULE_ID")
131        private List<RuleResponsibilityBo> ruleResponsibilities;
132
133    @OneToMany(fetch=FetchType.EAGER,cascade=CascadeType.ALL, orphanRemoval = true)
134    @JoinColumn(name = "RULE_ID", referencedColumnName = "RULE_ID")
135        private List<RuleExtensionBo> ruleExtensions;
136
137    @ManyToOne(fetch=FetchType.EAGER)
138        @JoinColumn(name="RULE_TMPL_ID", insertable = false, updatable = false)
139        private RuleTemplateBo ruleTemplate;
140
141    @OneToOne(fetch=FetchType.EAGER, cascade=CascadeType.ALL, orphanRemoval = true)
142        @JoinColumn(name="RULE_EXPR_ID")
143        private RuleExpressionDef ruleExpressionDef;
144
145    @Transient
146    private RuleBaseValues previousVersion;
147
148    @Column(name="ACTVN_DT")
149        private Timestamp activationDate;
150
151    @Column(name="DLGN_IND")
152    @Convert(converter=Boolean01Converter.class)
153    private Boolean delegateRule = Boolean.FALSE;
154
155    /**
156     * Indicator that signifies that this rule is a defaults/template rule which contains
157     * template-defined rule defaults for other rules which use the associated template
158     */
159    @Column(name="TMPL_RULE_IND")
160    @Convert(converter=Boolean01Converter.class)
161    private Boolean templateRuleInd = Boolean.FALSE;
162
163    // required to be lookupable
164    @Transient
165    private String returnUrl;
166    @Transient
167    private String destinationUrl;
168    @Transient
169    private MyColumns myColumns;
170    @Transient
171    @Serialized(enabled=true,forContexts=SerializationContext.MAINTENANCE)
172    private List<PersonRuleResponsibility> personResponsibilities = new ArrayList<PersonRuleResponsibility>();
173    @Transient
174    @Serialized(enabled=true,forContexts=SerializationContext.MAINTENANCE)
175    private List<GroupRuleResponsibility> groupResponsibilities = new ArrayList<GroupRuleResponsibility>();
176    @Transient
177    @Serialized(enabled=true,forContexts=SerializationContext.MAINTENANCE)
178    private List<RoleRuleResponsibility> roleResponsibilities = new ArrayList<RoleRuleResponsibility>();
179    @Transient
180    private Map<String, String> fieldValues;
181    @Transient
182    private String groupReviewerName;
183    @Transient
184    private String groupReviewerNamespace;
185    @Transient
186    private String personReviewer;
187    @Transient
188    private String personReviewerType;
189
190    public RuleBaseValues() {
191        ruleResponsibilities = new ArrayList<RuleResponsibilityBo>();
192        ruleExtensions = new ArrayList<RuleExtensionBo>();
193        /*personResponsibilities = new AutoPopulatingList<PersonRuleResponsibility>(PersonRuleResponsibility.class);
194        groupResponsibilities = new AutoPopulatingList<GroupRuleResponsibility>(GroupRuleResponsibility.class);
195        roleResponsibilities = new AutoPopulatingList<RoleRuleResponsibility>(RoleRuleResponsibility.class);*/
196        fieldValues = new HashMap<String, String>();
197    }
198
199    /**
200     * @return the rule expression definition for this rule, if defined
201     */
202    @Override
203    public RuleExpressionDef getRuleExpressionDef() {
204        return ruleExpressionDef;
205    }
206
207    /**
208     * @param ruleExpressionDef the rule expression definition to set for this rule
209     */
210    public void setRuleExpressionDef(RuleExpressionDef ruleExpressionDef) {
211        this.ruleExpressionDef = ruleExpressionDef;
212    }
213
214    @Override
215    public String getRuleTemplateName() {
216        if (ruleTemplate != null) {
217            return ruleTemplate.getName();
218        }
219        return null;
220    }
221
222    public RuleBaseValues getPreviousVersion() {
223        if (previousVersion == null && previousRuleId != null) {
224            RuleServiceInternal ruleService = (RuleServiceInternal) KEWServiceLocator.getService(KEWServiceLocator.RULE_SERVICE);
225            return ruleService.findRuleBaseValuesById(previousRuleId);
226        }
227        return previousVersion;
228    }
229
230    public void setPreviousVersion(RuleBaseValues previousVersion) {
231        this.previousVersion = previousVersion;
232    }
233
234    public RuleResponsibilityBo getResponsibility(int index) {
235        while (getRuleResponsibilities().size() <= index) {
236            RuleResponsibilityBo ruleResponsibility = new RuleResponsibilityBo();
237            ruleResponsibility.setRuleBaseValues(this);
238            getRuleResponsibilities().add(ruleResponsibility);
239        }
240        return getRuleResponsibilities().get(index);
241    }
242
243    public RuleExtensionBo getRuleExtension(int index) {
244        while (getRuleExtensions().size() <= index) {
245            getRuleExtensions().add(new RuleExtensionBo());
246        }
247        return getRuleExtensions().get(index);
248    }
249
250    public RuleExtensionValue getRuleExtensionValue(String key) {
251        for (Iterator iter = getRuleExtensions().iterator(); iter.hasNext();) {
252            RuleExtensionBo ruleExtension = (RuleExtensionBo) iter.next();
253            for (Iterator iterator = ruleExtension.getExtensionValues().iterator(); iterator.hasNext();) {
254                RuleExtensionValue ruleExtensionValue = (RuleExtensionValue) iterator.next();
255                if (ruleExtensionValue.getKey().equals(key)) {
256                    return ruleExtensionValue;
257                }
258            }
259        }
260        return null;
261    }
262
263    public RuleExtensionValue getRuleExtensionValue(String ruleTemplateAttributeId, String key) {
264        for (Iterator iter = getRuleExtensions().iterator(); iter.hasNext();) {
265            RuleExtensionBo ruleExtension = (RuleExtensionBo) iter.next();
266            if (ruleExtension.getRuleTemplateAttributeId().equals(ruleTemplateAttributeId)) {
267                for (Iterator iterator = ruleExtension.getExtensionValues().iterator(); iterator.hasNext();) {
268                    RuleExtensionValue ruleExtensionValue = (RuleExtensionValue) iterator.next();
269                    if (ruleExtensionValue.getKey().equals(key)) {
270                        return ruleExtensionValue;
271                    }
272                }
273            }
274        }
275        return null;
276    }
277
278    @Override
279    public String getPreviousRuleId() {
280        return previousRuleId;
281    }
282
283    public void setPreviousRuleId(String previousVersion) {
284        this.previousRuleId = previousVersion;
285    }
286
287    public void addRuleResponsibility(RuleResponsibilityBo ruleResponsibility) {
288        addRuleResponsibility(ruleResponsibility, new Integer(getRuleResponsibilities().size()));
289    }
290
291    public void addRuleResponsibility(RuleResponsibilityBo ruleResponsibility, Integer counter) {
292        boolean alreadyAdded = false;
293        int location = 0;
294        if (counter != null) {
295            for (RuleResponsibilityBo ruleResponsibilityRow : getRuleResponsibilities()) {
296                if (counter.intValue() == location) {
297                    ruleResponsibilityRow.setPriority(ruleResponsibility.getPriority());
298                    ruleResponsibilityRow.setActionRequestedCd(ruleResponsibility.getActionRequestedCd());
299                    // CHECKME : We probably should not be overriding the version number
300                    ruleResponsibilityRow.setVersionNumber(ruleResponsibility.getVersionNumber());
301                    ruleResponsibilityRow.setRuleBaseValuesId(ruleResponsibility.getRuleBaseValuesId());
302                    ruleResponsibilityRow.setRuleResponsibilityName(ruleResponsibility.getRuleResponsibilityName());
303                    ruleResponsibilityRow.setRuleResponsibilityType(ruleResponsibility.getRuleResponsibilityType());
304                    //ruleResponsibilityRow.setDelegationRules(ruleResponsibility.getDelegationRules());
305                    ruleResponsibilityRow.setApprovePolicy(ruleResponsibility.getApprovePolicy());
306                    alreadyAdded = true;
307                }
308                location++;
309            }
310        }
311        if (!alreadyAdded) {
312            getRuleResponsibilities().add(ruleResponsibility);
313        }
314    }
315
316    @Override
317    public RuleTemplateBo getRuleTemplate() {
318        return ruleTemplate;
319    }
320
321    public void setRuleTemplate(RuleTemplateBo ruleTemplate) {
322        this.ruleTemplate = ruleTemplate;
323    }
324
325    public String getRuleTemplateId() {
326        return ruleTemplateId;
327    }
328
329    public void setRuleTemplateId(String ruleTemplateId) {
330        this.ruleTemplateId = ruleTemplateId;
331    }
332
333    public DocumentType getDocumentType() {
334        return KEWServiceLocator.getDocumentTypeService().findByName(getDocTypeName());
335    }
336
337    @Override
338    public String getDocTypeName() {
339        return docTypeName;
340    }
341
342    public void setDocTypeName(String docTypeName) {
343        this.docTypeName = docTypeName;
344    }
345
346    @Override
347    public List<RuleExtensionBo> getRuleExtensions() {
348        return ruleExtensions;
349    }
350
351    public Map<String, String> getRuleExtensionMap() {
352        Map<String, String> extensions = new HashMap<String, String>();
353        for (RuleExtensionBo ext : this.getRuleExtensions()) {
354            for (RuleExtensionValue value : ext.getExtensionValues()) {
355                extensions.put(value.getKey(), value.getValue());
356            }
357        }
358        return extensions;
359    }
360
361    public void setRuleExtensions(List<RuleExtensionBo> ruleExtensions) {
362        this.ruleExtensions = ruleExtensions;
363    }
364
365    @Override
366    public List<RuleResponsibilityBo> getRuleResponsibilities() {
367        return this.ruleResponsibilities;
368    }
369
370    public void setRuleResponsibilities(List<RuleResponsibilityBo> ruleResponsibilities) {
371        this.ruleResponsibilities = ruleResponsibilities;
372    }
373
374    public RuleResponsibilityBo getResponsibility(Long ruleResponsibilityKey) {
375        for (Iterator iterator = getRuleResponsibilities().iterator(); iterator.hasNext();) {
376            RuleResponsibilityBo responsibility = (RuleResponsibilityBo) iterator.next();
377            if (responsibility.getId() != null
378                    && responsibility.getId().equals(ruleResponsibilityKey)) {
379                return responsibility;
380            }
381        }
382        return null;
383    }
384
385    public void removeResponsibility(int index) {
386        getRuleResponsibilities().remove(index);
387    }
388
389    @Override
390    public boolean isActive() {
391        return active;
392    }
393
394    public void setActive(boolean active) {
395        this.active = active;
396    }
397
398    public String getActiveIndDisplay() {
399        return CodeTranslator.getActiveIndicatorLabel(isActive());
400    }
401
402    public Boolean getCurrentInd() {
403        return currentInd;
404    }
405
406    public void setCurrentInd(Boolean currentInd) {
407        this.currentInd = currentInd;
408    }
409
410    public Timestamp getFromDateValue() {
411        return fromDateValue;
412    }
413
414    @Override
415    public DateTime getFromDate() {
416        if (this.fromDateValue == null) {
417            return null;
418        }
419        return new DateTime(this.fromDateValue.getTime());
420    }
421
422    public void setFromDateValue(Timestamp fromDateValue) {
423        this.fromDateValue = fromDateValue;
424    }
425
426    @Override
427    public String getDescription() {
428        return description;
429    }
430
431    public void setDescription(String description) {
432        this.description = description;
433    }
434
435    @Override
436    public String getId() {
437        return id;
438    }
439
440    public void setId(String id) {
441        this.id = id;
442    }
443
444    public Timestamp getToDateValue() {
445        return toDateValue;
446    }
447
448    @Override
449    public DateTime getToDate() {
450        if (this.toDateValue == null) {
451            return null;
452        }
453        return new DateTime(this.toDateValue.getTime());
454    }
455
456    public void setToDateValue(Timestamp toDateValue) {
457        this.toDateValue = toDateValue;
458    }
459
460    public Integer getVersionNbr() {
461        return versionNbr;
462    }
463
464    public void setVersionNbr(Integer versionNbr) {
465        this.versionNbr = versionNbr;
466    }
467
468    public String getReturnUrl() {
469        return returnUrl;
470    }
471
472    public void setReturnUrl(String returnUrl) {
473        this.returnUrl = returnUrl;
474    }
475
476    public String getFromDateString() {
477        if (this.fromDateValue != null) {
478            return RiceConstants.getDefaultDateFormat().format(this.fromDateValue);
479        }
480        return null;
481    }
482
483    public String getToDateString() {
484        if (this.toDateValue != null) {
485            return RiceConstants.getDefaultDateFormat().format(this.toDateValue);
486        }
487        return null;
488    }
489
490    @Override
491    public boolean isForceAction() {
492        return forceAction;
493    }
494
495    public void setForceAction(boolean forceAction) {
496        this.forceAction = forceAction;
497    }
498
499    public boolean isActive(Date date) {
500        boolean isAfterFromDate = getFromDateValue() == null || date.after(getFromDateValue());
501        boolean isBeforeToDate = getToDateValue() == null || date.before(getToDateValue());
502        return isActive() && isAfterFromDate && isBeforeToDate;
503    }
504
505    public boolean isMatch(DocumentContent docContent) {
506        for (RuleTemplateAttributeBo ruleTemplateAttribute : getRuleTemplate().getActiveRuleTemplateAttributes()) {
507            if (!ruleTemplateAttribute.isWorkflowAttribute()) {
508                continue;
509            }
510            WorkflowRuleAttribute routingAttribute = ruleTemplateAttribute.getWorkflowAttribute();
511
512            RuleAttribute ruleAttribute = ruleTemplateAttribute.getRuleAttribute();
513            if (ruleAttribute.getType().equals(KewApiConstants.RULE_XML_ATTRIBUTE_TYPE)) {
514                ((GenericXMLRuleAttribute) routingAttribute).setExtensionDefinition(RuleAttribute.to(ruleAttribute));
515            }
516            String className = ruleAttribute.getResourceDescriptor();
517            List<RuleExtension> editedRuleExtensions = new ArrayList<RuleExtension>();
518            for (RuleExtensionBo extension : getRuleExtensions()) {
519                if (extension.getRuleTemplateAttribute().getRuleAttribute().getResourceDescriptor().equals(className)) {
520                    editedRuleExtensions.add(RuleExtensionBo.to(extension));
521                }
522            }
523            if (!routingAttribute.isMatch(docContent, editedRuleExtensions)) {
524                return false;
525            }
526        }
527        return true;
528    }
529
530    public RuleResponsibilityBo findResponsibility(String roleName) {
531        for (Iterator iter = getRuleResponsibilities().iterator(); iter.hasNext();) {
532            RuleResponsibilityBo resp = (RuleResponsibilityBo) iter.next();
533            if (KewApiConstants.RULE_RESPONSIBILITY_ROLE_ID.equals(resp.getRuleResponsibilityType())
534                    && roleName.equals(resp.getRuleResponsibilityName())) {
535                return resp;
536            }
537        }
538        return null;
539    }
540
541    public String getDocumentId() {
542        return documentId;
543    }
544
545    public void setDocumentId(String documentId) {
546        this.documentId = documentId;
547    }
548
549    public Boolean getDelegateRule() {
550        return delegateRule;
551    }
552
553    public void setDelegateRule(Boolean isDelegateRule) {
554        this.delegateRule = isDelegateRule;
555    }
556
557    public Timestamp getActivationDate() {
558        return activationDate;
559    }
560
561    public void setActivationDate(Timestamp activationDate) {
562        this.activationDate = activationDate;
563    }
564
565    public MyColumns getMyColumns() {
566        return myColumns;
567    }
568
569    public void setMyColumns(MyColumns additionalColumns) {
570        this.myColumns = additionalColumns;
571    }
572
573    public String getDestinationUrl() {
574        return destinationUrl;
575    }
576
577    public void setDestinationUrl(String destinationUrl) {
578        this.destinationUrl = destinationUrl;
579    }
580
581    public Timestamp getDeactivationDate() {
582        return deactivationDate;
583    }
584
585    public void setDeactivationDate(Timestamp deactivationDate) {
586        this.deactivationDate = deactivationDate;
587    }
588
589    /**
590     * @return whether this is a defaults/template rule
591     */
592    public Boolean getTemplateRuleInd() {
593        return templateRuleInd;
594    }
595
596    /**
597     * @param templateRuleInd whether this is a defaults/template rule
598     */
599    public void setTemplateRuleInd(Boolean templateRuleInd) {
600        this.templateRuleInd = templateRuleInd;
601    }
602
603    /**
604     * Get the rule name
605     * @return the rule name
606     */
607    @Override
608    public String getName() {
609        return name;
610    }
611
612    /**
613     * Set the rule name
614     * @param name the rule name
615     */
616    public void setName(String name) {
617        this.name = name;
618    }
619
620        public List<PersonRuleResponsibility> getPersonResponsibilities() {
621                return this.personResponsibilities;
622        }
623
624        public void setPersonResponsibilities(List<PersonRuleResponsibility> personResponsibilities) {
625                this.personResponsibilities = personResponsibilities;
626        }
627
628        public List<GroupRuleResponsibility> getGroupResponsibilities() {
629                return this.groupResponsibilities;
630        }
631
632        public void setGroupResponsibilities(List<GroupRuleResponsibility> groupResponsibilities) {
633                this.groupResponsibilities = groupResponsibilities;
634        }
635
636        public List<RoleRuleResponsibility> getRoleResponsibilities() {
637                return this.roleResponsibilities;
638        }
639
640        public void setRoleResponsibilities(List<RoleRuleResponsibility> roleResponsibilities) {
641                this.roleResponsibilities = roleResponsibilities;
642        }
643
644        /**
645         * @return the fieldValues
646         */
647        public Map<String, String> getFieldValues() {
648                return this.fieldValues;
649        }
650
651        /**
652         * @param fieldValues the fieldValues to set
653         */
654        public void setFieldValues(Map<String, String> fieldValues) {
655                this.fieldValues = fieldValues;
656        }
657
658    public String getGroupReviewerName() {
659        return this.groupReviewerName;
660    }
661
662    public String getGroupReviewerNamespace() {
663        return this.groupReviewerNamespace;
664    }
665
666    public String getPersonReviewer() {
667        return this.personReviewer;
668    }
669
670    public void setGroupReviewerName(String groupReviewerName) {
671        this.groupReviewerName = groupReviewerName;
672    }
673
674    public void setGroupReviewerNamespace(String groupReviewerNamespace) {
675        this.groupReviewerNamespace = groupReviewerNamespace;
676    }
677
678    public void setPersonReviewer(String personReviewer) {
679        this.personReviewer = personReviewer;
680    }
681
682    public GroupBo getGroupBo() {
683        GroupBo groupBo = null;
684        if (StringUtils.isNotBlank(getGroupReviewerName())) {
685            if ( groupBo == null ) {
686                groupBo = GroupBo.from(KimApiServiceLocator.getGroupService().getGroupByNamespaceCodeAndName(
687                        getGroupReviewerNamespace(), getGroupReviewerName()));
688            }
689        }
690        return groupBo;
691    }
692
693    public PersonImpl getPersonImpl() {
694        return new PersonImpl();
695    }
696
697    public String getPersonReviewerType() {
698        return this.personReviewerType;
699    }
700
701    public void setPersonReviewerType(String personReviewerType) {
702        this.personReviewerType = personReviewerType;
703    }
704
705        /**
706     * Converts a mutable bo to its immutable counterpart
707     * @param bo the mutable business object
708     * @return the immutable object
709     */
710    public static org.kuali.rice.kew.api.rule.Rule to(RuleBaseValues bo) {
711        if (bo == null) {
712            return null;
713        }
714        return org.kuali.rice.kew.api.rule.Rule.Builder.create(bo).build();
715    }
716
717    @Override
718    public void refresh() {
719    }
720}