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