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.kim.document;
017
018import org.apache.commons.lang.StringUtils;
019import org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange;
020import org.kuali.rice.kim.api.KimConstants;
021import org.kuali.rice.kim.api.responsibility.ResponsibilityService;
022import org.kuali.rice.kim.api.services.KimApiServiceLocator;
023import org.kuali.rice.kim.api.type.KimAttributeField;
024import org.kuali.rice.kim.api.type.KimType;
025import org.kuali.rice.kim.bo.ui.KimDocumentRoleMember;
026import org.kuali.rice.kim.bo.ui.KimDocumentRolePermission;
027import org.kuali.rice.kim.bo.ui.KimDocumentRoleQualifier;
028import org.kuali.rice.kim.bo.ui.KimDocumentRoleResponsibility;
029import org.kuali.rice.kim.bo.ui.KimDocumentRoleResponsibilityAction;
030import org.kuali.rice.kim.bo.ui.RoleDocumentDelegation;
031import org.kuali.rice.kim.bo.ui.RoleDocumentDelegationMember;
032import org.kuali.rice.kim.bo.ui.RoleDocumentDelegationMemberQualifier;
033import org.kuali.rice.kim.impl.responsibility.ResponsibilityInternalService;
034import org.kuali.rice.kim.impl.services.KimImplServiceLocator;
035import org.kuali.rice.kim.impl.type.IdentityManagementTypeAttributeTransactionalDocument;
036import org.kuali.rice.kim.service.KIMServiceLocatorInternal;
037import org.kuali.rice.kim.web.struts.form.IdentityManagementRoleDocumentForm;
038import org.kuali.rice.krad.service.SequenceAccessorService;
039import org.kuali.rice.krad.util.ObjectUtils;
040import org.springframework.util.AutoPopulatingList;
041
042import javax.persistence.Transient;
043import java.util.ArrayList;
044import java.util.Comparator;
045import java.util.List;
046
047
048/**
049 * This is a description of what this class does - bhargavp don't forget to fill this in. 
050 * 
051 * @author Kuali Rice Team (rice.collab@kuali.org)
052 *
053 */
054public class IdentityManagementRoleDocument extends IdentityManagementTypeAttributeTransactionalDocument {
055
056        private static final long serialVersionUID = 1L;
057        // principal data
058        protected String roleId;
059        protected String roleTypeId;
060        protected String roleTypeName;
061        protected String roleNamespace = "";
062        protected String roleName = "";
063        protected String roleDescription = "";
064
065        protected boolean active = true;
066
067        protected boolean editing;
068        
069        protected List<KimDocumentRolePermission> permissions = new AutoPopulatingList(KimDocumentRolePermission.class);
070        protected List<KimDocumentRoleResponsibility> responsibilities = new AutoPopulatingList(KimDocumentRoleResponsibility.class);
071    protected List<KimDocumentRoleMember> modifiedMembers = new AutoPopulatingList(KimDocumentRoleMember.class);
072    private List<RoleDocumentDelegationMember> delegationMembers = new AutoPopulatingList(RoleDocumentDelegationMember.class);
073        private List<RoleDocumentDelegation> delegations = new AutoPopulatingList(RoleDocumentDelegation.class);
074
075    @Transient
076    protected List<KimDocumentRoleMember> searchResultMembers = new AutoPopulatingList(KimDocumentRoleMember.class);
077    @Transient
078    protected List<KimDocumentRoleMember> members = new AutoPopulatingList(KimDocumentRoleMember.class);
079    @Transient
080    transient private ResponsibilityService responsibilityService;
081        transient private ResponsibilityInternalService responsibilityInternalService;
082
083        public IdentityManagementRoleDocument() {
084        }
085
086        /**
087         * @return the active
088         */
089        public boolean isActive() {
090                return this.active;
091        }
092
093        /**
094         * @param active the active to set
095         */
096        public void setActive(boolean active) {
097                this.active = active;
098        }
099
100        /**
101         * @param roleId the roleId to set
102         */
103        public void setRoleId(String roleId) {
104                this.roleId = roleId;
105        }
106
107        /**
108         * @return the roleName
109         */
110        public String getRoleName() {
111                return this.roleName;
112        }
113
114        /**
115         * @param roleName the roleName to set
116         */
117        public void setRoleName(String roleName) {
118                this.roleName = roleName;
119        }
120
121        /**
122         * @return the roleDescription
123         */
124        public String getRoleDescription() {
125                return this.roleDescription;
126        }
127
128        /**
129         * @param roleDescription the roleDescription to set
130         */
131        public void setRoleDescription(String roleDescription) {
132                this.roleDescription = roleDescription;
133        }
134
135        /**
136         * @return the roleNamespace
137         */
138        public String getRoleNamespace() {
139                return this.roleNamespace;
140        }
141
142        /**
143         * @param roleNamespace the roleNamespace to set
144         */
145        public void setRoleNamespace(String roleNamespace) {
146                this.roleNamespace = roleNamespace;
147        }
148
149        /**
150         * @return the roleTypeId
151         */
152        public String getRoleTypeId() {
153                return this.roleTypeId;
154        }
155
156        /**
157         * @param roleTypeId the roleTypeId to set
158         */
159        public void setRoleTypeId(String roleTypeId) {
160                this.roleTypeId = roleTypeId;
161        }
162
163        /**
164         * @return the roleTypeName
165         */
166        public String getRoleTypeName() {
167                if ( roleTypeName == null ) {
168                        if ( kimType != null ) {
169                                roleTypeName = kimType.getName();
170                        } else if ( roleTypeId != null ) {
171                                setKimType( KimApiServiceLocator.getKimTypeInfoService().getKimType(roleTypeId) );
172                        if ( kimType != null ) {
173                                roleTypeName = kimType.getName();
174                        }
175                        }
176                }
177                return this.roleTypeName;
178        }
179
180        /**
181         * @param roleTypeName the roleTypeName to set
182         */
183        public void setRoleTypeName(String roleTypeName) {
184                this.roleTypeName = roleTypeName;
185        }
186
187        /**
188         * @return the delegationMembers
189         */
190        public List<RoleDocumentDelegationMember> getDelegationMembers() {
191                return this.delegationMembers;
192        }
193
194        /**
195         * @param delegationMembers the delegationMembers to set
196         */
197        public void setDelegationMembers(
198                        List<RoleDocumentDelegationMember> delegationMembers) {
199                this.delegationMembers = delegationMembers;
200        }
201
202        /**
203         * @return the permissions
204         */
205        public List<KimDocumentRolePermission> getPermissions() {
206                return this.permissions;
207        }
208
209        /**
210         * @param permissions the permissions to set
211         */
212        public void setPermissions(List<KimDocumentRolePermission> permissions) {
213                this.permissions = permissions;
214        }
215
216        /**
217         * @return the responsibilities
218         */
219        public List<KimDocumentRoleResponsibility> getResponsibilities() {
220                return this.responsibilities;
221        }
222
223        /**
224         * @param responsibilities the responsibilities to set
225         */
226        public void setResponsibilities(
227                        List<KimDocumentRoleResponsibility> responsibilities) {
228                this.responsibilities = responsibilities;
229        }
230
231        /**
232         * @return the members
233         */
234        public List<KimDocumentRoleMember> getMembers() {
235                return this.members;
236        }
237
238    public enum RoleMemberMetaDataType implements Comparator<KimDocumentRoleMember>{
239        MEMBER_ID("memberId"),
240        MEMBER_NAME("memberName"),
241        FULL_MEMBER_NAME("memberFullName");
242
243        private final String attributeName;
244
245        RoleMemberMetaDataType(String anAttributeName) {
246            this.attributeName = anAttributeName;
247        }
248
249        public String getAttributeName() {
250            return attributeName;
251        }
252
253        @Override
254        public int compare(KimDocumentRoleMember m1, KimDocumentRoleMember m2) {
255            if (m1 == null && m2 == null) {
256                return 0;
257            } else if (m1 == null) {
258                return -1;
259            } else if (m2 == null) {
260                return 1;
261            }
262            if (this.getAttributeName().equals(MEMBER_ID.getAttributeName())) {
263                return m1.getMemberId().compareToIgnoreCase(m2.getMemberId());
264            }
265            else if (this.getAttributeName().equals(FULL_MEMBER_NAME.getAttributeName())) {
266                return m1.getMemberFullName().compareToIgnoreCase(m2.getMemberFullName());
267            }
268            return m1.getMemberName().compareToIgnoreCase(m2.getMemberName());
269        }
270    }
271
272    public void setMemberMetaDataTypeToSort(Integer columnNumber)
273        {
274           switch( columnNumber )
275           {
276               case 1:
277                   this.memberMetaDataType = RoleMemberMetaDataType.MEMBER_ID;
278                   break;
279               case 2:
280                   this.memberMetaDataType = RoleMemberMetaDataType.MEMBER_NAME;
281                   break;
282               case 3:
283                   this.memberMetaDataType = RoleMemberMetaDataType.FULL_MEMBER_NAME;
284                   break;
285               default:
286                   this.memberMetaDataType = RoleMemberMetaDataType.MEMBER_NAME;
287                   break;
288           }
289        }
290
291    protected RoleMemberMetaDataType memberMetaDataType = RoleMemberMetaDataType.MEMBER_NAME;
292
293    public RoleMemberMetaDataType getMemberMetaDataType() {
294        return memberMetaDataType;
295    }
296
297    public void setMemberMetaDataType(RoleMemberMetaDataType memberMetaDataType) {
298        this.memberMetaDataType = memberMetaDataType;
299    }
300
301        /**
302         * @return the members
303         */
304        public KimDocumentRoleMember getMember(String roleMemberId) {
305                if(StringUtils.isEmpty(roleMemberId)) {return null;}
306                for(KimDocumentRoleMember roleMember: getMembers()){
307                        if(roleMemberId.equals(roleMember.getRoleMemberId())) {
308                                return roleMember;
309            }
310                }
311                return null;
312        }
313
314    /**
315     * @param members the members to set
316     */
317    public void setMembers(List<KimDocumentRoleMember> members) {
318        this.members = members;
319    }
320
321    /**
322     * @return the modifiedMembers
323     */
324    public List<KimDocumentRoleMember> getModifiedMembers() {
325        return this.modifiedMembers;
326    }
327
328    /**
329     * @param modifiedMembers the modifiedMembers to set
330     */
331    public void setModifiedMembers(List<KimDocumentRoleMember> modifiedMembers) {
332        this.modifiedMembers = modifiedMembers;
333    }
334
335    /**
336     * @return the searchResultMembers
337     */
338    public List<KimDocumentRoleMember> getSearchResultMembers() {
339        return this.searchResultMembers;
340    }
341
342    /**
343     * @param searchResultMembers the searchResultMembers to set
344     */
345    public void setSearchResultMembers(List<KimDocumentRoleMember> searchResultMembers) {
346        this.searchResultMembers = searchResultMembers;
347    }
348
349        public void addResponsibility(KimDocumentRoleResponsibility roleResponsibility){
350                if(!getResponsibilityInternalService().areActionsAtAssignmentLevelById(roleResponsibility.getResponsibilityId())) {
351                        roleResponsibility.getRoleRspActions().add(getNewRespAction(roleResponsibility));
352                }
353        getResponsibilities().add(roleResponsibility);
354        }
355
356        protected KimDocumentRoleResponsibilityAction getNewRespAction(KimDocumentRoleResponsibility roleResponsibility){
357                KimDocumentRoleResponsibilityAction roleRspAction = new KimDocumentRoleResponsibilityAction();
358                roleRspAction.setKimResponsibility(roleResponsibility.getKimResponsibility());
359                roleRspAction.setRoleResponsibilityId(roleResponsibility.getRoleResponsibilityId());                    
360                return roleRspAction;
361        }
362        
363        public void addDelegationMember(RoleDocumentDelegationMember newDelegationMember){
364                getDelegationMembers().add(newDelegationMember);
365        }
366
367        /**
368         * @param member the members to set
369         */
370        public void addMember(KimDocumentRoleMember member) {
371                SequenceAccessorService sas = getSequenceAccessorService();
372                Long nextSeq = sas.getNextAvailableSequenceNumber(
373                                KimConstants.SequenceNames.KRIM_ROLE_MBR_ID_S, 
374                                KimDocumentRoleMember.class);
375                String roleMemberId = nextSeq.toString();
376                member.setRoleMemberId(roleMemberId);
377        setupMemberRspActions(member);
378        getModifiedMembers().add(member);
379        }
380
381        public KimDocumentRoleMember getBlankMember() {
382                KimDocumentRoleMember member = new KimDocumentRoleMember();
383                KimDocumentRoleQualifier qualifier;
384                if(getDefinitions()!=null){
385                        for(KimAttributeField key : getDefinitions()) {
386                        qualifier = new KimDocumentRoleQualifier();
387                        qualifier.setKimAttrDefnId(getKimAttributeDefnId(key));
388                        member.getQualifiers().add(qualifier);
389                }
390                }
391        setupMemberRspActions(member);
392        return member;
393        }
394
395        public RoleDocumentDelegationMember getBlankDelegationMember() {
396                RoleDocumentDelegationMember member = new RoleDocumentDelegationMember();
397                RoleDocumentDelegationMemberQualifier qualifier;
398                if(getDefinitions()!=null){
399                        for(KimAttributeField key : getDefinitions()) {
400                                qualifier = new RoleDocumentDelegationMemberQualifier();
401                                setAttrDefnIdForDelMemberQualifier(qualifier, key);
402                        member.getQualifiers().add(qualifier);
403                }
404                }
405        return member;
406        }
407
408    public void setupMemberRspActions(KimDocumentRoleMember member) {
409        member.getRoleRspActions().clear();
410        for (KimDocumentRoleResponsibility roleResp: getResponsibilities()) {
411                if (getResponsibilityInternalService().areActionsAtAssignmentLevelById(roleResp.getResponsibilityId())) {
412                        KimDocumentRoleResponsibilityAction action = new KimDocumentRoleResponsibilityAction();
413                        action.setRoleResponsibilityId("*");
414                        action.setRoleMemberId(member.getRoleMemberId());
415                        member.getRoleRspActions().add(action);
416                        break;
417                }               
418        }
419    }
420
421    public void updateMembers(IdentityManagementRoleDocumentForm roleDocumentForm){
422        for(KimDocumentRoleMember member: roleDocumentForm.getRoleDocument().getMembers()){
423                roleDocumentForm.getRoleDocument().setupMemberRspActions(member);
424        }
425    }
426    
427    public void updateMembers(KimDocumentRoleResponsibility newResponsibility){
428        for(KimDocumentRoleMember member: getMembers()){
429                setupMemberRspActions(newResponsibility, member);
430        }
431    }
432    
433    public void setupMemberRspActions(KimDocumentRoleResponsibility roleResp, KimDocumentRoleMember member) {
434        if ((member.getRoleRspActions()==null || member.getRoleRspActions().size()<1) && getResponsibilityInternalService().areActionsAtAssignmentLevelById(roleResp.getResponsibilityId())) {
435                KimDocumentRoleResponsibilityAction action = new KimDocumentRoleResponsibilityAction();
436                action.setRoleResponsibilityId("*");
437                action.setRoleMemberId(member.getRoleMemberId());
438                if(member.getRoleRspActions()==null) {
439                        member.setRoleRspActions(new ArrayList<KimDocumentRoleResponsibilityAction>());
440            }
441                member.getRoleRspActions().add(action);
442        }               
443    }
444    
445    protected void setAttrDefnIdForDelMemberQualifier(RoleDocumentDelegationMemberQualifier qualifier,KimAttributeField definition) {
446                qualifier.setKimAttrDefnId(definition.getId());
447    }
448    
449    /**
450     * @see org.kuali.rice.krad.document.DocumentBase#doRouteStatusChange(org.kuali.rice.kew.framework.postprocessor.DocumentRouteStatusChange)
451     */
452    @Override
453        public void doRouteStatusChange(DocumentRouteStatusChange statusChangeEvent) {
454                super.doRouteStatusChange(statusChangeEvent);
455                if (getDocumentHeader().getWorkflowDocument().isProcessed()) {
456            KIMServiceLocatorInternal.getUiDocumentService().saveRole(this);
457                }
458        }
459
460        public void initializeDocumentForNewRole() {
461                if(StringUtils.isBlank(this.roleId)){
462                        SequenceAccessorService sas = getSequenceAccessorService();
463                        Long nextSeq = sas.getNextAvailableSequenceNumber(
464                                        KimConstants.SequenceNames.KRIM_ROLE_ID_S, this.getClass());
465                        this.roleId = nextSeq.toString();
466                }
467                if(StringUtils.isBlank(this.roleTypeId)) {
468                        this.roleTypeId = "1";
469                }
470        }
471        
472        public String getRoleId(){
473                if(StringUtils.isBlank(this.roleId)){
474                        initializeDocumentForNewRole();
475                }
476                return roleId;
477        }
478        
479        @Override
480        public void prepareForSave(){
481                SequenceAccessorService sas = getSequenceAccessorService();
482                
483                String roleId;
484                if(StringUtils.isBlank(getRoleId())){
485                        Long nextSeq = sas.getNextAvailableSequenceNumber(
486                                        KimConstants.SequenceNames.KRIM_ROLE_ID_S, this.getClass()); 
487                        roleId = nextSeq.toString();
488                        setRoleId(roleId);
489                } else {
490                        roleId = getRoleId();
491        }
492
493                if(getPermissions()!=null){
494                        String rolePermissionId;
495                        for(KimDocumentRolePermission permission: getPermissions()){
496                                permission.setRoleId(roleId);
497                                if(StringUtils.isBlank(permission.getRolePermissionId())){
498                                        Long nextSeq = sas.getNextAvailableSequenceNumber(
499                                                        KimConstants.SequenceNames.KRIM_ROLE_PERM_ID_S, 
500                                                        KimDocumentRolePermission.class);
501                                        rolePermissionId = nextSeq.toString();
502                                        permission.setRolePermissionId(rolePermissionId);
503                                }
504                        }
505                }
506                if(getResponsibilities()!=null){
507                        String roleResponsibilityId;
508                        for(KimDocumentRoleResponsibility responsibility: getResponsibilities()){
509                                if(StringUtils.isBlank(responsibility.getRoleResponsibilityId())){
510                                        Long nextSeq = sas.getNextAvailableSequenceNumber(
511                                                        KimConstants.SequenceNames.KRIM_ROLE_RSP_ID_S, 
512                                                        KimDocumentRoleResponsibility.class);
513                                        roleResponsibilityId = nextSeq.toString();
514                                        responsibility.setRoleResponsibilityId(roleResponsibilityId);
515                                }
516                                responsibility.setRoleId(roleId);
517                                if(!getResponsibilityInternalService().areActionsAtAssignmentLevelById(responsibility.getResponsibilityId())){
518                                        if(StringUtils.isBlank(responsibility.getRoleRspActions().get(0).getRoleResponsibilityActionId())){
519                                                Long nextSeq = sas.getNextAvailableSequenceNumber(
520                                                                KimConstants.SequenceNames.KRIM_ROLE_RSP_ACTN_ID_S,
521                                                                KimDocumentRoleResponsibilityAction.class);
522                                                String roleResponsibilityActionId = nextSeq.toString();
523                                                responsibility.getRoleRspActions().get(0).setRoleResponsibilityActionId(roleResponsibilityActionId);
524                                        }
525                                        responsibility.getRoleRspActions().get(0).setRoleMemberId("*");
526                                        responsibility.getRoleRspActions().get(0).setDocumentNumber(getDocumentNumber());
527                                }
528                        }
529                }
530        if(getModifiedMembers()!=null){
531            String roleMemberId;
532            String roleResponsibilityActionId;
533            for(KimDocumentRoleMember member: getModifiedMembers()){
534                member.setRoleId(roleId);
535                if(StringUtils.isBlank(member.getRoleMemberId())){
536                    Long nextSeq = sas.getNextAvailableSequenceNumber(
537                            KimConstants.SequenceNames.KRIM_ROLE_MBR_ID_S,
538                            KimDocumentRoleMember.class);
539                    roleMemberId = nextSeq.toString();
540                    member.setRoleMemberId(roleMemberId);
541                }
542                for(KimDocumentRoleQualifier qualifier: member.getQualifiers()){
543                    qualifier.setKimTypId(getKimType().getId());
544                }
545                for(KimDocumentRoleResponsibilityAction roleRespAction: member.getRoleRspActions()){
546                    if(StringUtils.isBlank(roleRespAction.getRoleResponsibilityActionId())){
547                        Long nextSeq = sas.getNextAvailableSequenceNumber(
548                                KimConstants.SequenceNames.KRIM_ROLE_RSP_ACTN_ID_S,
549                                KimDocumentRoleResponsibilityAction.class);
550                        roleResponsibilityActionId = nextSeq.toString();
551                        roleRespAction.setRoleResponsibilityActionId(roleResponsibilityActionId);
552                    }
553                    roleRespAction.setRoleMemberId(member.getRoleMemberId());
554                    roleRespAction.setDocumentNumber(getDocumentNumber());
555                    if ( !StringUtils.equals( roleRespAction.getRoleResponsibilityId(), "*" ) ) {
556                        for(KimDocumentRoleResponsibility responsibility: getResponsibilities()){
557                            if( StringUtils.equals( roleRespAction.getKimResponsibility().getId(), responsibility.getResponsibilityId() ) ) {
558                                roleRespAction.setRoleResponsibilityId(responsibility.getRoleResponsibilityId());
559                            }
560                        }
561                    }
562                    if (ObjectUtils.isNull(roleRespAction.getVersionNumber())) {
563                        roleRespAction.setVersionNumber(new Long(1));
564                    }
565                }
566            }
567        }
568                if(getDelegationMembers()!=null){
569                        for(RoleDocumentDelegationMember delegationMember: getDelegationMembers()){
570                                delegationMember.setDocumentNumber(getDocumentNumber());
571                                addDelegationMemberToDelegation(delegationMember);
572                        }
573                        for(RoleDocumentDelegation delegation: getDelegations()){
574                                delegation.setDocumentNumber(getDocumentNumber());
575                                delegation.setKimTypeId(getKimType().getId());
576                List<RoleDocumentDelegationMember> membersToRemove = new AutoPopulatingList(RoleDocumentDelegationMember.class);
577                for(RoleDocumentDelegationMember member: delegation.getMembers()){
578                    if (delegation.getDelegationId().equals(member.getDelegationId()) &&
579                        delegation.getDelegationTypeCode().equals(member.getDelegationTypeCode())) {
580                            for(RoleDocumentDelegationMemberQualifier qualifier: member.getQualifiers()){
581                                                        qualifier.setKimTypId(getKimType().getId());
582                            }
583                                        } else {
584                        membersToRemove.add(member);
585                    }
586                                }
587                if (!membersToRemove.isEmpty()) {
588                    for(RoleDocumentDelegationMember member: membersToRemove) {
589                        delegation.getMembers().remove(member);
590                    }
591                }
592                                delegation.setRoleId(roleId);
593                        }
594                }
595        }
596        
597    public ResponsibilityService getResponsibilityService() {
598        if ( responsibilityService == null ) {
599                responsibilityService = KimApiServiceLocator.getResponsibilityService();
600        }
601                return responsibilityService;
602        }
603
604    public ResponsibilityInternalService getResponsibilityInternalService() {
605        if ( responsibilityInternalService == null ) {
606                responsibilityInternalService = KimImplServiceLocator.getResponsibilityInternalService();
607        }
608                return responsibilityInternalService;
609        }
610
611        /**
612         * @return the editing
613         */
614        public boolean isEditing() {
615                return this.editing;
616        }
617
618        /**
619         * @param editing the editing to set
620         */
621        public void setEditing(boolean editing) {
622                this.editing = editing;
623        }
624
625        /**
626         * @return the delegations
627         */
628        public List<RoleDocumentDelegation> getDelegations() {
629                return this.delegations;
630        }
631
632        /**
633         * @param delegations the delegations to set
634         */
635        public void setDelegations(List<RoleDocumentDelegation> delegations) {
636                this.delegations = delegations;
637        }
638        
639        public void setKimType(KimType kimType) {
640                super.setKimType(kimType);
641                if (kimType != null){
642                        setRoleTypeId(kimType.getId());
643                        setRoleTypeName(kimType.getName());
644                }
645        }
646}