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