001/* 002 * Copyright 2010 Anyware Services 003 * 004 * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0 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.ametys.odf.program; 017 018import java.time.LocalDate; 019import java.util.ArrayList; 020import java.util.Arrays; 021import java.util.HashSet; 022import java.util.LinkedHashMap; 023import java.util.List; 024import java.util.Map; 025import java.util.Optional; 026import java.util.Set; 027import java.util.stream.Collectors; 028 029import javax.jcr.Node; 030 031import org.apache.commons.lang3.ArrayUtils; 032import org.apache.commons.lang3.StringUtils; 033 034import org.ametys.cms.data.ContentDataHelper; 035import org.ametys.cms.data.ContentValue; 036import org.ametys.cms.data.Geocode; 037import org.ametys.cms.data.RichText; 038import org.ametys.odf.courselist.CourseList; 039import org.ametys.odf.courselist.CourseListContainer; 040import org.ametys.odf.data.EducationalPath; 041import org.ametys.plugins.repository.AmetysObjectIterable; 042import org.ametys.plugins.repository.AmetysRepositoryException; 043import org.ametys.plugins.repository.RemovableAmetysObject; 044import org.ametys.plugins.repository.RepositoryIntegrityViolationException; 045import org.ametys.plugins.repository.data.holder.group.ModelAwareRepeater; 046import org.ametys.plugins.repository.data.holder.group.ModelAwareRepeaterEntry; 047import org.ametys.runtime.model.ElementDefinition; 048import org.ametys.runtime.model.exception.UndefinedItemPathException; 049 050/** 051 * Abstract common superclass for {@link Program} and {@link SubProgram}. 052 * @param <F> The actual type of the factory 053 */ 054public abstract class AbstractProgram<F extends ProgramFactory> extends AbstractTraversableProgramPart<F> implements CourseListContainer 055{ 056 // ------------------------------------------------// 057 // 058 // PROGRAM ATTRIBUTES 059 // 060 // -----------------------------------------------// 061 /** Constants for attribute 'degree' */ 062 public static final String DEGREE = "degree"; 063 064 /** Constants for attribute 'certified' */ 065 public static final String CERTIFIED = "certified"; 066 067 /** Constants for attribute 'domain' */ 068 public static final String DOMAIN = "domain"; 069 070 /** Constants for attribute 'mention' */ 071 public static final String MENTION = "mention"; 072 073 /** Constants for attribute 'speciality' */ 074 public static final String SPECIALITY = "speciality"; 075 076 /** Constants for attribute 'educationLevel' */ 077 public static final String LEVEL = "educationLevel"; 078 079 /** Constants for attribute 'duration' */ 080 public static final String DURATION = "duration"; 081 082 /** Constants for attribute 'educationLanguage' */ 083 public static final String EDUC_LANGUAGE = "educationLanguage"; 084 085 /** Constants for attribute 'presentation' */ 086 public static final String PRESENTATION = "presentation"; 087 088 /** Constants for attribute 'objectives' */ 089 public static final String OBJECTIVES = "objectives"; 090 091 /** Constants for attribute 'qualification' */ 092 public static final String QUALIFICATION = "qualification"; 093 094 /** Constants for attribute 'teachingOrganization' */ 095 public static final String TEACHING_ORGANIZATION = "teachingOrganization"; 096 097 /** Constants for attribute 'apprenticeshipModalities' */ 098 public static final String APPRENTICESHIP_MODALITIES = "apprenticeshipModalities"; 099 100 /** Constants for attribute 'accessCondition' */ 101 public static final String ACCESS_CONDITION = "accessCondition"; 102 103 /** Constants for attribute 'neededPrerequisite' */ 104 public static final String NEEDED_PREREQUISITE = "neededPrerequisite"; 105 106 /** Constants for attribute 'recommendedPrerequisite' */ 107 public static final String RECOMMENDED_PREREQUISITE = "recommendedPrerequisite"; 108 109 /** Constants for attribute 'expectedResults' */ 110 public static final String EXPECTED_RESULTS = "expectedResults"; 111 112 /** Constants for attribute 'furtherStudy' */ 113 public static final String FURTHER_STUDY = "furtherStudy"; 114 115 /** Constants for attribute 'studyAbroad' */ 116 public static final String STUDY_ABROAD = "studyAbroad"; 117 118 /** Constants for attribute 'targetGroup' */ 119 public static final String TARGET_GROUP = "targetGroup"; 120 121 /** Constants for attribute 'jobOpportunities' */ 122 public static final String JOB_OPPORTUNITIES = "jobOpportunities"; 123 124 /** Constants for attribute 'trainingStrategy' */ 125 public static final String TRAINING_STRATEGY = "trainingStrategy"; 126 127 /** Constants for attribute 'knowledgeCheck' */ 128 public static final String KNOWLEDGE_CHECK = "knowledgeCheck"; 129 130 /** Constants for attribute 'universalAdjustment' */ 131 public static final String UNIVERSAL_ADJUSTMENT = "universalAdjustment"; 132 133 /** Constants for attribute 'certifying' */ 134 public static final String CERTIFYING = "certifying"; 135 136 /** Constants for attribute 'expenses' */ 137 public static final String EXPENSES = "expenses"; 138 139 /** Constants for attribute 'additionalInformations' */ 140 public static final String ADDITIONNAL_INFORMATIONS = "additionalInformations"; 141 142 /** Constants for attribute 'jointOrgUnit' */ 143 public static final String JOINT_ORGUNIT = "jointOrgUnit"; 144 145 /** Constants for attribute 'place' */ 146 public static final String PLACE = "place"; 147 148 /** Constants for attribute 'distanceLearning' */ 149 public static final String DISTANCE_LEARNING = "distanceLearning"; 150 151 /** Constants for attribute 'educationKind' */ 152 public static final String EDUCATION_KIND = "educationKind"; 153 154 /** Constant for attribute 'formofteachingOrg' */ 155 public static final String FORM_OF_TEACHING_ORG = "formofteachingOrg"; 156 157 /** Constants for attribute 'ects' */ 158 public static final String ECTS = "ects"; 159 160 /** Constants for attribute 'internship' */ 161 public static final String INTERNSHIP = "internship"; 162 163 /** Constants for attribute 'internshipDuration' */ 164 public static final String INTERNSHIP_DURATION = "internshipDuration"; 165 166 /** Constants for attribute 'internshipAbroad' */ 167 public static final String INTERNSHIP_ABROAD = "internshipAbroad"; 168 169 /** Constants for attribute 'internshipAbroadDuration' */ 170 public static final String INTERNSHIP_ABROAD_DURATION = "internshipAbroadDuration"; 171 172 /** Constants for attribute 'registrationStart' */ 173 public static final String REGISTRATION_START = "registrationStart"; 174 175 /** Constants for attribute 'registrationDeadline' */ 176 public static final String REGISTRATION_DEADLINE = "registrationDeadline"; 177 178 /** Constants for attribute 'teachingStart' */ 179 public static final String TEACHING_START = "teachingStart"; 180 181 /** Constants for attribute 'teachingEnd' */ 182 public static final String TEACHING_END = "teachingEnd"; 183 184 /** Constants for attribute 'partnerSchools' */ 185 public static final String PARTNER_SCHOOLS = "partnerSchools"; 186 187 /** Constants for attribute 'partnerSchools/linkUrl' */ 188 public static final String PARTNER_SCHOOLS_LINK_URL = "linkUrl"; 189 190 /** Constants for attribute 'partnerSchools/linkLabel' */ 191 public static final String PARTNER_SCHOOLS_LINK_LABEL = "linkLabel"; 192 193 /** Constants for attribute 'partnerLaboratories' */ 194 public static final String PARTNER_LABORATORIES = "partnerLaboratories"; 195 196 /** Constants for attribute 'partnerLaboratories/linkUrl' */ 197 public static final String PARTNER_LABORATORIES_LINK_URL = "linkUrl"; 198 199 /** Constants for attribute 'partnerLaboratories/linkLabel' */ 200 public static final String PARTNER_LABORATORIES_LINK_LABEL = "linkLabel"; 201 202 /** Constants for attribute 'rncpCode' */ 203 public static final String RNCP_CODE = "rncpCode"; 204 205 /** Constants for attribute 'rncpLevel' */ 206 public static final String RNCP_LEVEL = "rncpLevel"; 207 208 /** Constants for attribute 'siseCode' */ 209 public static final String SISE_CODE = "siseCode"; 210 211 /** Constants for attribute 'Cite 97' */ 212 public static final String CITE97_CODE = "cite97Code"; 213 214 /** Constants for attribute 'erasmusCode' */ 215 public static final String ERASMUS_CODE = "erasmusCode"; 216 217 /** Constants for attribute 'erasmusCode' */ 218 public static final String DGESIP_CODE = "dgesipCode"; 219 220 /** Constants for attribute 'formacode' */ 221 public static final String FORMACODE = "formacode"; 222 223 /** Constants for attribute 'romeCode' */ 224 public static final String ROME_CODE = "romeCode"; 225 226 /** Constants for attribute 'fapCode' */ 227 public static final String FAP_CODE = "fapCode"; 228 229 /** Constants for attribute 'nsfCode' */ 230 public static final String NSF_CODE = "nsfCode"; 231 232 /** Constants for attribute 'programWebSite' */ 233 public static final String PROGRAM_WEBSITE = "programWebSite"; 234 235 /** Constants for attribute 'programWebSiteLabel' */ 236 public static final String PROGRAM_WEBSITE_LABEL = "programWebSiteLabel"; 237 238 /** Constants for attribute 'programWebSiteUrl' */ 239 public static final String PROGRAM_WEBSITE_URL = "programWebSiteUrl"; 240 241 /** Constants for attribute 'attachments' */ 242 public static final String ATTACHMENTS = "attachments"; 243 244 /** Constants for attribute 'attachments/attachment' */ 245 public static final String ATTACHMENTS_ATTACHMENT = "attachment"; 246 247 /** Constants for attribute 'attachments/attachment-text' */ 248 public static final String ATTACHMENTS_ATTACHMENT_TEXT = "attachment-text"; 249 250 /** Constants for attribute 'numberOfStudents' */ 251 public static final String NUMBER_OF_STUDENTS = "numberOfStudents"; 252 253 /** Constants for attribute 'successRate' */ 254 public static final String SUCCESSRATE = "successRate"; 255 256 /** Constants for attribute 'reorientation' */ 257 public static final String REORIENTATION = "reorientation"; 258 259 /** Constants for attribute 'keywords' */ 260 public static final String KEYWORDS = "keywords"; 261 262 /** Constants for attribute 'educationEntryLevel' */ 263 public static final String EDUCATION_ENTRY_LEVEL = "educationEntryLevel"; 264 265 /** Constants for attribute 'mandatoryEntryLevel' */ 266 public static final String MANDATORY_ENTRY_LEVEL = "mandatoryEntryLevel"; 267 268 /** Constants for attribute 'programField' */ 269 public static final String PROGRAM_FIELD = "programField"; 270 271 /** Constants for attribute 'availableCertification' */ 272 public static final String AVAILABLE_CERTIFICATION = "availableCertification"; 273 274 /** Constants for attribute 'disciplines' */ 275 public static final String DISCIPLINES = "disciplines"; 276 277 /** Constants for attribute 'sectors' */ 278 public static final String SECTORS = "sectors"; 279 280 /** Constants for attribute 'internshipOpen' */ 281 public static final String INTERNSHIP_OPEN = "internshipOpen"; 282 283 /** Constants for attribute 'internshipDescription' */ 284 public static final String INTERNSHIP_DESCRIPTION = "internshipDescription"; 285 286 /** Constants for attribute 'internshipDescription/title' */ 287 public static final String INTERNSHIP_DESCRIPTION_TITLE = "title"; 288 289 /** Constants for attribute 'internshipDescription/duration' */ 290 public static final String INTERNSHIP_DESCRIPTION_DURATION = "duration"; 291 292 /** Constants for attribute 'internshipDescription/period' */ 293 public static final String INTERNSHIP_DESCRIPTION_PERIOD = "period"; 294 295 /** Constants for attribute 'internshipDescription/kind' */ 296 public static final String INTERNSHIP_DESCRIPTION_KIND = "kind"; 297 298 /** Constants for attribute 'apprenticeshipOpen' */ 299 public static final String APPRENTICESHIP_OPEN = "apprenticeshipOpen"; 300 301 /** Constants for attribute 'apprenticeshipPeriod' */ 302 public static final String APPRENTICESHIP_PERIOD = "apprenticeshipPeriod"; 303 304 /** Constants for attribute 'apprenticeshipContract' */ 305 public static final String APPRENTICESHIP_CONTRACT = "apprenticeshipContract"; 306 307 /** Constants for attribute 'internationalEducation' */ 308 public static final String INTERNATIONAL_EDUCATION = "internationalEducation"; 309 310 /** Constants for attribute 'internationalDimension' */ 311 public static final String INTERNATIONAL_DIMENSION = "internationalDimension"; 312 313 /** Constants for attribute 'geoCode' */ 314 public static final String GEOCODE = "geoCode"; 315 316 /** Constants for attribute 'otherPartners' */ 317 public static final String OTHER_PARTNERS = "otherPartners"; 318 319 /** Constants for attribute 'otherPartners' */ 320 public static final String OTHER_CONTACT = "otherContact"; 321 322 /** Constants for attribute 'campus' */ 323 public static final String CAMPUS = "campus"; 324 325 /** Constants for attribute 'foreignPlace' */ 326 public static final String FOREIGN_PLACE = "foreignPlace"; 327 328 /** Constants for attribute 'inscription' */ 329 public static final String INSCRIPTION = "inscription"; 330 331 /** Constants for attribute 'furtherStudyPrograms' */ 332 public static final String FURTHER_STUDY_PROGRAMS = "furtherStudyPrograms"; 333 334 /** 335 * References 336 */ 337 338 /** Constants for attribute 'contacts' */ 339 public static final String CONTACTS = "contacts"; 340 341 /** Constants for attribute 'contacts/role' */ 342 public static final String CONTACTS_ROLE = "role"; 343 344 /** Constants for attribute 'contacts/persons' */ 345 public static final String CONTACTS_PERSONS = "persons"; 346 347 private String _contextPath; 348 349 private List<EducationalPath> _currentEducationalPaths; 350 351 // ------------------------------------------------// 352 // 353 // PROGRAM METHODS 354 // 355 // -----------------------------------------------// 356 /** 357 * Constructor 358 * @param node The JCR node 359 * @param parentPath The parent path 360 * @param factory The factory 361 */ 362 public AbstractProgram(Node node, String parentPath, F factory) 363 { 364 super(node, parentPath, factory); 365 } 366 367 @Override 368 public void remove() throws AmetysRepositoryException, RepositoryIntegrityViolationException 369 { 370 AmetysObjectIterable<RemovableAmetysObject> children = this.getChildren(); 371 372 for (RemovableAmetysObject child : children) 373 { 374 child.remove(); 375 } 376 377 super.remove(); 378 } 379 380 // --------------------------------------------------------------------------------------// 381 // CONTEXT 382 // --------------------------------------------------------------------------------------// 383 384 // Méthodes utilisées lors du parcours d'une maquette uniquement, afin de contextualiser l'élément 385 // A ne pas utiliser n'importe ou ni n'importe comment 386 387 /** 388 * Set the parent path for links and breadcrumb 389 * @param path the parent path 390 */ 391 public void setContextPath (String path) 392 { 393 _contextPath = path; 394 } 395 396 /** 397 * Get the parent path. Can be null. 398 * @return the parent path 399 */ 400 public String getContextPath () 401 { 402 return _contextPath; 403 } 404 405 /** 406 * Set the current educational path of this abstract program 407 * @param paths the current educational path 408 */ 409 public void setCurrentEducationalPaths(List<EducationalPath> paths) 410 { 411 _currentEducationalPaths = paths; 412 } 413 414 /** 415 * Get the current educational path of this abstract program 416 * @return the current educational path 417 */ 418 public List<EducationalPath> getCurrentEducationalPaths() 419 { 420 return _currentEducationalPaths; 421 } 422 423 // --------------------------------------------------------------------------------------// 424 // CONTACTS 425 // --------------------------------------------------------------------------------------// 426 427 /** 428 * Return the list of Persons in charge binded to this program 429 * @return a list of roles and UUID 430 */ 431 public Set<String> getContacts() 432 { 433 Set<String> contactIds = new HashSet<>(); 434 435 ModelAwareRepeater contacts = getRepeater(CONTACTS); 436 if (contacts != null) 437 { 438 for (ModelAwareRepeaterEntry contactEntry : contacts.getEntries()) 439 { 440 // Remove empty values 441 contactIds.addAll(ContentDataHelper.getContentIdsStreamFromMultipleContentData(contactEntry, CONTACTS_PERSONS) 442 .filter(contentId -> !contentId.isEmpty()) 443 .collect(Collectors.toSet())); 444 } 445 } 446 447 return contactIds; 448 } 449 450 /** 451 * Return the list of Persons in charge binded to this program 452 * @return a list of roles and UUID 453 */ 454 public Map<String, List<String>> getContactsByRole() 455 { 456 Map<String, List<String>> contactsByRole = new LinkedHashMap<>(); 457 458 ModelAwareRepeater contacts = getRepeater(CONTACTS); 459 if (contacts != null) 460 { 461 for (ModelAwareRepeaterEntry contactEntry : contacts.getEntries()) 462 { 463 // Remove empty values 464 List<String> persons = ContentDataHelper.getContentIdsStreamFromMultipleContentData(contactEntry, CONTACTS_PERSONS) 465 .filter(contentId -> !contentId.isEmpty()) 466 .collect(Collectors.toList()); 467 if (!persons.isEmpty()) 468 { 469 String role = ContentDataHelper.getContentIdFromContentData(contactEntry, CONTACTS_ROLE); 470 List<String> personList = contactsByRole.getOrDefault(role, new ArrayList<>()); 471 personList.addAll(persons); 472 contactsByRole.put(role, personList); 473 } 474 } 475 } 476 477 return contactsByRole; 478 } 479 480 // --------------------------------------------------------------------------------------// 481 // GETTERS AND SETTERS 482 // --------------------------------------------------------------------------------------// 483 /** 484 * Returns <code>true</code> if the {@link AbstractProgram} is certified 485 * @return <code>true</code> if certified or default value (true in the kernel) if not defined 486 */ 487 public boolean isCertified() 488 { 489 return getValue(CERTIFIED, true, true); 490 } 491 492 /** 493 * Get the domain 494 * @return the domain or an empty array 495 */ 496 public String[] getDomain() 497 { 498 try 499 { 500 ElementDefinition definition = (ElementDefinition) getDefinition(DOMAIN); 501 if (definition.isMultiple()) 502 { 503 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, DOMAIN); 504 } 505 else 506 { 507 return new String[] {ContentDataHelper.getContentIdFromContentData(this, DOMAIN)}; 508 } 509 } 510 catch (UndefinedItemPathException e) 511 { 512 return null; // this attribute is not part of model 513 } 514 515 } 516 517 /** 518 * Get the education presentation 519 * @return the education presentation or null if not set 520 */ 521 public RichText getPresentation() 522 { 523 try 524 { 525 return getValue(PRESENTATION); 526 } 527 catch (UndefinedItemPathException e) 528 { 529 return null; // this attribute is not part of model 530 } 531 } 532 533 /** 534 * Get the education objectives 535 * @return the education objectives or null if not set 536 */ 537 public RichText getObjectives() 538 { 539 try 540 { 541 return getValue(OBJECTIVES); 542 } 543 catch (UndefinedItemPathException e) 544 { 545 return null; // this attribute is not part of model 546 } 547 } 548 549 /** 550 * Get the education qualification 551 * @return the education qualification or null if not set 552 */ 553 public RichText getQualification() 554 { 555 try 556 { 557 return getValue(QUALIFICATION); 558 } 559 catch (UndefinedItemPathException e) 560 { 561 return null; // this attribute is not part of model 562 } 563 } 564 565 /** 566 * Get the education organization 567 * @return the education organization or null if not set 568 */ 569 public RichText getTeachingOrganization() 570 { 571 try 572 { 573 return getValue(TEACHING_ORGANIZATION); 574 } 575 catch (UndefinedItemPathException e) 576 { 577 return null; // this attribute is not part of model 578 } 579 } 580 581 /** 582 * Get the apprenticeship modalities 583 * @return the apprenticeship modalities or null if not set 584 */ 585 public RichText getApprenticeshipModalities() 586 { 587 try 588 { 589 return getValue(APPRENTICESHIP_MODALITIES); 590 } 591 catch (UndefinedItemPathException e) 592 { 593 return null; // this attribute is not part of model 594 } 595 } 596 597 /** 598 * Get the access conditions 599 * @return the access conditions or null if not set 600 */ 601 public RichText getAccessCondition() 602 { 603 try 604 { 605 return getValue(ACCESS_CONDITION); 606 } 607 catch (UndefinedItemPathException e) 608 { 609 return null; // this attribute is not part of model 610 } 611 } 612 613 /** 614 * Get the education requirements 615 * @return the education requirements or null if not set 616 */ 617 public RichText getNeededPrerequisite() 618 { 619 try 620 { 621 return getValue(NEEDED_PREREQUISITE); 622 } 623 catch (UndefinedItemPathException e) 624 { 625 return null; // this attribute is not part of model 626 } 627 } 628 629 /** 630 * Get the education recommended prerequisite 631 * @return the education recommended prerequisite or null if not set 632 */ 633 public RichText getRecommendedPrerequisite() 634 { 635 try 636 { 637 return getValue(RECOMMENDED_PREREQUISITE); 638 } 639 catch (UndefinedItemPathException e) 640 { 641 return null; // this attribute is not part of model 642 } 643 } 644 645 /** 646 * Get the education expected results 647 * @return the education expected results or null if not set 648 */ 649 public RichText getExpectedResults() 650 { 651 try 652 { 653 return getValue(EXPECTED_RESULTS); 654 } 655 catch (UndefinedItemPathException e) 656 { 657 return null; // this attribute is not part of model 658 } 659 } 660 661 /** 662 * Get the education further study 663 * @return the education further study or null if not set 664 */ 665 public RichText getFurtherStudy() 666 { 667 try 668 { 669 return getValue(FURTHER_STUDY); 670 } 671 catch (UndefinedItemPathException e) 672 { 673 return null; // this attribute is not part of model 674 } 675 } 676 677 /** 678 * Get the education study abroard 679 * @return the education study abroard or null if not set 680 */ 681 public RichText getStudyAbroad() 682 { 683 try 684 { 685 return getValue(STUDY_ABROAD); 686 } 687 catch (UndefinedItemPathException e) 688 { 689 return null; // this attribute is not part of model 690 } 691 } 692 693 /** 694 * Get the education target 695 * @return the education target or null if not set 696 */ 697 public RichText getTargetGroup() 698 { 699 try 700 { 701 return getValue(TARGET_GROUP); 702 } 703 catch (UndefinedItemPathException e) 704 { 705 return null; // this attribute is not part of model 706 } 707 } 708 709 /** 710 * Get the job opportunities 711 * @return the job opportunities or null if not set 712 */ 713 public RichText getJobOpportunities() 714 { 715 try 716 { 717 return getValue(JOB_OPPORTUNITIES); 718 } 719 catch (UndefinedItemPathException e) 720 { 721 return null; // this attribute is not part of model 722 } 723 } 724 725 /** 726 * Get the training strategy 727 * @return the training strategy or null if not set 728 */ 729 public RichText getTrainingStrategy() 730 { 731 try 732 { 733 return getValue(TRAINING_STRATEGY); 734 } 735 catch (UndefinedItemPathException e) 736 { 737 return null; // this attribute is not part of model 738 } 739 } 740 741 /** 742 * Get acknowledgments 743 * @return acknowledgments or null if not set 744 */ 745 public RichText getKnowledgeCheck() 746 { 747 try 748 { 749 return getValue(KNOWLEDGE_CHECK); 750 } 751 catch (UndefinedItemPathException e) 752 { 753 return null; // this attribute is not part of model 754 } 755 } 756 757 /** 758 * Get universal adjustment 759 * @return universal adjustment or null if not set 760 */ 761 public RichText getUniversalAdjustment() 762 { 763 try 764 { 765 return getValue(UNIVERSAL_ADJUSTMENT); 766 } 767 catch (UndefinedItemPathException e) 768 { 769 return null; // this attribute is not part of model 770 } 771 } 772 773 /** 774 * Is certifying 775 * @return <code>true</code> if the progam is certifying 776 */ 777 public boolean isCertifying() 778 { 779 try 780 { 781 return getValue(CERTIFYING, false, false); 782 } 783 catch (UndefinedItemPathException e) 784 { 785 return false; // this attribute is not part of model 786 } 787 } 788 789 /** 790 * Get the additional informations 791 * @return the additional informations or null if not set 792 */ 793 public RichText getAdditionalInformations() 794 { 795 try 796 { 797 return getValue(ADDITIONNAL_INFORMATIONS); 798 } 799 catch (UndefinedItemPathException e) 800 { 801 return null; // this attribute is not part of model 802 } 803 } 804 805 /** 806 * Get the education level 807 * @return the education level or null 808 */ 809 public String getEducationLevel() 810 { 811 try 812 { 813 return ContentDataHelper.getContentIdFromContentData(this, LEVEL); 814 } 815 catch (UndefinedItemPathException e) 816 { 817 return null; // this attribute is not part of model 818 } 819 } 820 821 /** 822 * Get the RNCP code 823 * @return the RNCP code 824 */ 825 public String[] getRncpCode() 826 { 827 try 828 { 829 return getValue(RNCP_CODE, false, ArrayUtils.EMPTY_STRING_ARRAY); 830 } 831 catch (UndefinedItemPathException e) 832 { 833 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 834 } 835 } 836 837 /** 838 * Get the RNCP level 839 * @return the RNCP level 840 */ 841 public String[] getRncpLevel() 842 { 843 try 844 { 845 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, RNCP_LEVEL); 846 } 847 catch (UndefinedItemPathException e) 848 { 849 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 850 } 851 } 852 853 /** 854 * Get the SISE code 855 * @return the SISE code 856 */ 857 public String[] getSiseCode() 858 { 859 try 860 { 861 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, SISE_CODE); 862 } 863 catch (UndefinedItemPathException e) 864 { 865 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 866 } 867 } 868 869 /** 870 * Get the CITE97 code 871 * @return the CITE97 code 872 */ 873 public String[] getCite97Code() 874 { 875 try 876 { 877 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, CITE97_CODE); 878 } 879 catch (UndefinedItemPathException e) 880 { 881 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 882 } 883 } 884 885 /** 886 * Get the DGESIP code 887 * @return the DGESIP code 888 */ 889 public String[] getDGESIPCode() 890 { 891 try 892 { 893 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, DGESIP_CODE); 894 } 895 catch (UndefinedItemPathException e) 896 { 897 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 898 } 899 } 900 901 /** 902 * Get the Erasmus code 903 * @return the Erasmus code 904 */ 905 public String[] getErasmusCode() 906 { 907 try 908 { 909 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, ERASMUS_CODE); 910 } 911 catch (UndefinedItemPathException e) 912 { 913 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 914 } 915 } 916 917 /** 918 * Get the FORMACODE 919 * @return the FORMACODE 920 */ 921 public String[] getFORMACODE() 922 { 923 try 924 { 925 return getValue(FORMACODE, false, ArrayUtils.EMPTY_STRING_ARRAY); 926 } 927 catch (UndefinedItemPathException e) 928 { 929 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 930 } 931 } 932 933 /** 934 * Get the ROME code 935 * @return the ROME code 936 */ 937 public String[] getRomeCode() 938 { 939 try 940 { 941 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, ROME_CODE); 942 } 943 catch (UndefinedItemPathException e) 944 { 945 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 946 } 947 } 948 949 /** 950 * Get the FAP code 951 * @return the FAP code 952 */ 953 public String[] getFapCode() 954 { 955 try 956 { 957 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, FAP_CODE); 958 } 959 catch (UndefinedItemPathException e) 960 { 961 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 962 } 963 } 964 965 /** 966 * Get the NSF code 967 * @return the NSF code 968 */ 969 public String getNSFCode() 970 { 971 try 972 { 973 return ContentDataHelper.getContentIdFromContentData(this, NSF_CODE); 974 } 975 catch (UndefinedItemPathException e) 976 { 977 return null; // this attribute is not part of model 978 } 979 } 980 981 /** 982 * Get the mention 983 * @return the mention or null 984 */ 985 public String getMention() 986 { 987 try 988 { 989 return ContentDataHelper.getContentIdFromContentData(this, MENTION); 990 } 991 catch (UndefinedItemPathException e) 992 { 993 return null; // this attribute is not part of model 994 } 995 } 996 997 /** 998 * Get the speciality 999 * @return the speciality or null 1000 */ 1001 public String getSpeciality() 1002 { 1003 try 1004 { 1005 return getValue(SPECIALITY, false, StringUtils.EMPTY); 1006 } 1007 catch (UndefinedItemPathException e) 1008 { 1009 return null; // this attribute is not part of model 1010 } 1011 } 1012 1013 /** 1014 * Get the org units 1015 * @return the org units 1016 */ 1017 public String[] getJointOrgUnit() 1018 { 1019 try 1020 { 1021 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, JOINT_ORGUNIT); 1022 } 1023 catch (UndefinedItemPathException e) 1024 { 1025 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 1026 } 1027 } 1028 1029 /** 1030 * Get the places 1031 * @return the places as an Array of String 1032 */ 1033 public String[] getPlace() 1034 { 1035 try 1036 { 1037 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, PLACE); 1038 } 1039 catch (UndefinedItemPathException e) 1040 { 1041 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 1042 } 1043 } 1044 1045 /** 1046 * Get the list of websites 1047 * @return the list of website or an empty list 1048 */ 1049 public Set<WebsiteLink> getWebsiteLinks() 1050 { 1051 Set<WebsiteLink> websites = new HashSet<>(); 1052 1053 try 1054 { 1055 ModelAwareRepeater webSites = getRepeater(PROGRAM_WEBSITE); 1056 if (webSites != null) 1057 { 1058 for (ModelAwareRepeaterEntry entry : webSites.getEntries()) 1059 { 1060 WebsiteLink website = new WebsiteLink(entry.getValue(PROGRAM_WEBSITE_URL, false, StringUtils.EMPTY), entry.getValue(PROGRAM_WEBSITE_LABEL, false, StringUtils.EMPTY)); 1061 websites.add(website); 1062 } 1063 } 1064 } 1065 catch (UndefinedItemPathException e) 1066 { 1067 // this attribute is not part of model 1068 } 1069 1070 return websites; 1071 } 1072 1073 /** 1074 * Get the ECTS credits 1075 * @return the ECTS credits 1076 */ 1077 public String getEcts() 1078 { 1079 try 1080 { 1081 return ContentDataHelper.getContentIdFromContentData(this, ECTS); 1082 } 1083 catch (UndefinedItemPathException e) 1084 { 1085 return null; // this attribute is not part of model 1086 } 1087 } 1088 1089 /** 1090 * Get the kind of education 1091 * @return the kind of education or null 1092 */ 1093 public String getEducationKind() 1094 { 1095 try 1096 { 1097 return ContentDataHelper.getContentIdFromContentData(this, EDUCATION_KIND); 1098 } 1099 catch (UndefinedItemPathException e) 1100 { 1101 return null; // this attribute is not part of model 1102 } 1103 } 1104 1105 /** 1106 * Get the duration 1107 * @return the duration 1108 */ 1109 public String getDuration() 1110 { 1111 try 1112 { 1113 return ContentDataHelper.getContentIdFromContentData(this, DURATION); 1114 } 1115 catch (UndefinedItemPathException e) 1116 { 1117 return null; // this attribute is not part of model 1118 } 1119 } 1120 1121 /** 1122 * Get the education languages 1123 * @return the education languages 1124 */ 1125 public String[] getEducationLanguage() 1126 { 1127 try 1128 { 1129 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, EDUC_LANGUAGE); 1130 } 1131 catch (UndefinedItemPathException e) 1132 { 1133 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 1134 } 1135 } 1136 1137 /** 1138 * Get the effectives 1139 * @return the effectives 1140 */ 1141 public RichText getEffectives() 1142 { 1143 try 1144 { 1145 return getValue(NUMBER_OF_STUDENTS); 1146 } 1147 catch (UndefinedItemPathException e) 1148 { 1149 return null; // this attribute is not part of model 1150 } 1151 } 1152 1153 /** 1154 * Get the SuccessRate 1155 * @return the SuccessRate 1156 */ 1157 public String getSuccessRate() 1158 { 1159 try 1160 { 1161 return getValue(SUCCESSRATE, false, StringUtils.EMPTY); 1162 } 1163 catch (UndefinedItemPathException e) 1164 { 1165 return null; // this attribute is not part of model 1166 } 1167 } 1168 1169 /** 1170 * Get the expenses 1171 * @return the expenses 1172 */ 1173 public RichText getExpenses() 1174 { 1175 try 1176 { 1177 return getValue(EXPENSES); 1178 } 1179 catch (UndefinedItemPathException e) 1180 { 1181 return null; // this attribute is not part of model 1182 } 1183 } 1184 1185 /** 1186 * Get the form of teaching organization 1187 * @return the form of teaching organization 1188 */ 1189 public String[] getFormOfTeachingOrgs() 1190 { 1191 try 1192 { 1193 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, FORM_OF_TEACHING_ORG); 1194 } 1195 catch (UndefinedItemPathException e) 1196 { 1197 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 1198 } 1199 } 1200 1201 /** 1202 * Get the REORIENTATION 1203 * @return RichText 1204 */ 1205 public RichText getReorientation() 1206 { 1207 try 1208 { 1209 return getValue(REORIENTATION); 1210 } 1211 catch (UndefinedItemPathException e) 1212 { 1213 return null; // this attribute is not part of model 1214 } 1215 } 1216 1217 /** 1218 * Get the distance learning 1219 * @return the distance learning or null 1220 */ 1221 public String getDistanceLearning() 1222 { 1223 try 1224 { 1225 return ContentDataHelper.getContentIdFromContentData(this, DISTANCE_LEARNING); 1226 } 1227 catch (UndefinedItemPathException e) 1228 { 1229 return null; // this attribute is not part of model 1230 } 1231 } 1232 1233 /** 1234 * Get the internship 1235 * @return the internship or null 1236 */ 1237 public String getInternship() 1238 { 1239 try 1240 { 1241 return ContentDataHelper.getContentIdFromContentData(this, INTERNSHIP); 1242 } 1243 catch (UndefinedItemPathException e) 1244 { 1245 return null; // this attribute is not part of model 1246 } 1247 } 1248 1249 /** 1250 * Get the internship duration 1251 * @return the internship duration or null 1252 */ 1253 public String getInternshipDuration() 1254 { 1255 try 1256 { 1257 return getValue(INTERNSHIP_DURATION, false, StringUtils.EMPTY); 1258 } 1259 catch (UndefinedItemPathException e) 1260 { 1261 return null; // this attribute is not part of model 1262 } 1263 } 1264 1265 /** 1266 * Get the internship abroad 1267 * @return the internship abroad or null 1268 */ 1269 public String getInternshipAbroad() 1270 { 1271 try 1272 { 1273 return ContentDataHelper.getContentIdFromContentData(this, INTERNSHIP_ABROAD); 1274 } 1275 catch (UndefinedItemPathException e) 1276 { 1277 return null; // this attribute is not part of model 1278 } 1279 } 1280 1281 /** 1282 * Get the internship abroad duration 1283 * @return the internship abroad duration or null 1284 */ 1285 public String getAbroadInternshipDuration() 1286 { 1287 try 1288 { 1289 return getValue(INTERNSHIP_ABROAD_DURATION, false, StringUtils.EMPTY); 1290 } 1291 catch (UndefinedItemPathException e) 1292 { 1293 return null; // this attribute is not part of model 1294 } 1295 } 1296 1297 /** 1298 * Get the registration start date. 1299 * @return The registration start date, can be null. 1300 */ 1301 public LocalDate getRegistrationStart() 1302 { 1303 try 1304 { 1305 return getValue(REGISTRATION_START); 1306 } 1307 catch (UndefinedItemPathException e) 1308 { 1309 return null; // this attribute is not part of model 1310 } 1311 } 1312 1313 /** 1314 * Get the registration deadline date. 1315 * @return The registration deadline date, can be null. 1316 */ 1317 public LocalDate getRegistrationDeadline() 1318 { 1319 try 1320 { 1321 return getValue(REGISTRATION_DEADLINE); 1322 } 1323 catch (UndefinedItemPathException e) 1324 { 1325 return null; // this attribute is not part of model 1326 } 1327 } 1328 1329 /** 1330 * Get the teaching start date. 1331 * @return The teaching start date, can be null. 1332 */ 1333 public LocalDate getTeachingStart() 1334 { 1335 try 1336 { 1337 return getValue(TEACHING_START); 1338 } 1339 catch (UndefinedItemPathException e) 1340 { 1341 return null; // this attribute is not part of model 1342 } 1343 } 1344 1345 /** 1346 * Get the teaching end date. 1347 * @return The teaching end date, can be null. 1348 */ 1349 public LocalDate getTeachingEnd() 1350 { 1351 try 1352 { 1353 return getValue(TEACHING_END); 1354 } 1355 catch (UndefinedItemPathException e) 1356 { 1357 return null; // this attribute is not part of model 1358 } 1359 } 1360 1361 /** 1362 * Get the keywords 1363 * @return the keywords 1364 */ 1365 public String[] getKeywords() 1366 { 1367 try 1368 { 1369 return getValue(KEYWORDS, false, ArrayUtils.EMPTY_STRING_ARRAY); 1370 } 1371 catch (UndefinedItemPathException e) 1372 { 1373 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 1374 } 1375 } 1376 1377 /** 1378 * Get the education level entry 1379 * @return the education level entry 1380 */ 1381 public String[] getEducationLevelEntry() 1382 { 1383 try 1384 { 1385 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, EDUCATION_ENTRY_LEVEL); 1386 } 1387 catch (UndefinedItemPathException e) 1388 { 1389 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 1390 } 1391 } 1392 1393 /** 1394 * Is the entry level mandatory 1395 * @return <code>true</code> if the entry level is mandatory 1396 */ 1397 public boolean isMandatoryEntryLevel() 1398 { 1399 try 1400 { 1401 return getValue(MANDATORY_ENTRY_LEVEL, false, false); 1402 } 1403 catch (UndefinedItemPathException e) 1404 { 1405 return false; // this attribute is not part of model 1406 } 1407 } 1408 1409 /** 1410 * Get the program fields 1411 * @return the program fields 1412 */ 1413 public String[] getProgramField() 1414 { 1415 try 1416 { 1417 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, PROGRAM_FIELD); 1418 } 1419 catch (UndefinedItemPathException e) 1420 { 1421 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 1422 } 1423 } 1424 1425 /** 1426 * Get the available certifications 1427 * @return the available certifications 1428 */ 1429 public String[] getAvailableCertification() 1430 { 1431 try 1432 { 1433 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, AVAILABLE_CERTIFICATION); 1434 } 1435 catch (UndefinedItemPathException e) 1436 { 1437 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 1438 } 1439 } 1440 1441 /** 1442 * Get the disciplines 1443 * @return the disciplines 1444 */ 1445 public String[] getDisciplines() 1446 { 1447 try 1448 { 1449 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, DISCIPLINES); 1450 } 1451 catch (UndefinedItemPathException e) 1452 { 1453 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 1454 } 1455 } 1456 1457 /** 1458 * Get the sectors 1459 * @return the sectors 1460 */ 1461 public String[] getSectors() 1462 { 1463 try 1464 { 1465 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, SECTORS); 1466 } 1467 catch (UndefinedItemPathException e) 1468 { 1469 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 1470 } 1471 } 1472 1473 /** 1474 * Is intership open 1475 * @return <code>true</code> if internship is open 1476 */ 1477 public boolean isInternshipOpen() 1478 { 1479 try 1480 { 1481 return getValue(INTERNSHIP_OPEN, false, false); 1482 } 1483 catch (UndefinedItemPathException e) 1484 { 1485 return false; // this attribute is not part of model 1486 } 1487 } 1488 1489 /** 1490 * Is apprenticeship open 1491 * @return <code>true</code> if apprenticeship is open 1492 */ 1493 public boolean isApprenticeshipOpen() 1494 { 1495 try 1496 { 1497 return getValue(APPRENTICESHIP_OPEN, false, false); 1498 } 1499 catch (UndefinedItemPathException e) 1500 { 1501 return false; // this attribute is not part of model 1502 } 1503 } 1504 1505 /** 1506 * Get the apprenticeship period description 1507 * @return the apprenticeship period 1508 */ 1509 public RichText getApprenticeshipPeriod() 1510 { 1511 try 1512 { 1513 return getValue(APPRENTICESHIP_PERIOD); 1514 } 1515 catch (UndefinedItemPathException e) 1516 { 1517 return null; // this attribute is not part of model 1518 } 1519 } 1520 1521 /** 1522 * Get the available apprenticeship contracts 1523 * @return the apprenticeship contracts 1524 */ 1525 public String[] getApprenticeshipContract() 1526 { 1527 try 1528 { 1529 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, APPRENTICESHIP_CONTRACT); 1530 } 1531 catch (UndefinedItemPathException e) 1532 { 1533 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 1534 } 1535 } 1536 1537 /** 1538 * Get the international education 1539 * @return the international education 1540 */ 1541 public String[] getInternationalEducation() 1542 { 1543 try 1544 { 1545 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, INTERNATIONAL_EDUCATION); 1546 } 1547 catch (UndefinedItemPathException e) 1548 { 1549 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 1550 } 1551 } 1552 1553 /** 1554 * Get the international dimension 1555 * @return the international dimension 1556 */ 1557 public RichText getInternationalDimension() 1558 { 1559 try 1560 { 1561 return getValue(INTERNATIONAL_DIMENSION); 1562 } 1563 catch (UndefinedItemPathException e) 1564 { 1565 return null; // this attribute is not part of model 1566 } 1567 } 1568 1569 /** 1570 * Get the geocode latitude and longitude 1571 * @return the geocode 1572 */ 1573 public Geocode getGeocode() 1574 { 1575 try 1576 { 1577 return getValue(GEOCODE); 1578 } 1579 catch (UndefinedItemPathException e) 1580 { 1581 return null; // this attribute is not part of model 1582 } 1583 } 1584 1585 /** 1586 * Get the other partners 1587 * @return the other partners 1588 */ 1589 public RichText getOtherPartners() 1590 { 1591 try 1592 { 1593 return getValue(OTHER_PARTNERS); 1594 } 1595 catch (UndefinedItemPathException e) 1596 { 1597 return null; // this attribute is not part of model 1598 } 1599 } 1600 1601 /** 1602 * Get the campus 1603 * @return the campus 1604 */ 1605 public String[] getCampus() 1606 { 1607 try 1608 { 1609 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, CAMPUS); 1610 } 1611 catch (UndefinedItemPathException e) 1612 { 1613 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 1614 } 1615 } 1616 1617 /** 1618 * Get the foreign places 1619 * @return the foreign places 1620 */ 1621 public String[] getForeignPlace() 1622 { 1623 try 1624 { 1625 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, FOREIGN_PLACE); 1626 } 1627 catch (UndefinedItemPathException e) 1628 { 1629 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 1630 } 1631 } 1632 1633 /** 1634 * Get the inscription 1635 * @return the inscription 1636 */ 1637 public RichText getInscription() 1638 { 1639 try 1640 { 1641 return getValue(INSCRIPTION); 1642 } 1643 catch (UndefinedItemPathException e) 1644 { 1645 return null; // this attribute is not part of model 1646 } 1647 } 1648 1649 /** 1650 * Get the further study programs 1651 * @return the further study programs 1652 */ 1653 public String[] getFurtherStudyPrograms() 1654 { 1655 try 1656 { 1657 return ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, FURTHER_STUDY_PROGRAMS); 1658 } 1659 catch (UndefinedItemPathException e) 1660 { 1661 return ArrayUtils.EMPTY_STRING_ARRAY; // this attribute is not part of model 1662 } 1663 } 1664 1665 // --------------------------------------------------------------------------------------// 1666 // Methods of CourseListContainer 1667 // --------------------------------------------------------------------------------------// 1668 @Override 1669 public List<CourseList> getCourseLists() 1670 { 1671 return Arrays.stream(getValue(CHILD_PROGRAM_PARTS, false, new ContentValue[0])) 1672 .map(ContentValue::getContentIfExists) 1673 .flatMap(Optional::stream) 1674 // This cast is not checked because an exception must be thrown if the retrieved content is not a program part 1675 // TODO: change this behavior to throw our own exception and not a CassCastException 1676 .map(ProgramPart.class::cast) 1677 // Program parts that are not course lists are simply ignored 1678 .filter(CourseList.class::isInstance) 1679 .map(CourseList.class::cast) 1680 .collect(Collectors.toList()); 1681 } 1682 1683 @Override 1684 public boolean containsCourseList(String clId) 1685 { 1686 return ArrayUtils.contains(ContentDataHelper.getContentIdsArrayFromMultipleContentData(this, CHILD_PROGRAM_PARTS), clId); 1687 } 1688 1689 @Override 1690 public boolean hasCourseLists() 1691 { 1692 return !getCourseLists().isEmpty(); 1693 } 1694 1695 // --------------------------------------------------------------------------------------// 1696 // CDMfr 1697 // --------------------------------------------------------------------------------------// 1698 @Override 1699 protected String getCDMType() 1700 { 1701 return "PR"; 1702 } 1703 1704 /** 1705 * Returns the surrounding tag name in the CDM-fr representation. 1706 * @return the surrounding tag name 1707 */ 1708 public abstract String getCDMTagName(); 1709}