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.util.ArrayList;
019import java.util.Arrays;
020import java.util.Collections;
021import java.util.Date;
022import java.util.HashMap;
023import java.util.HashSet;
024import java.util.LinkedHashMap;
025import java.util.List;
026import java.util.Map;
027import java.util.Set;
028
029import javax.jcr.Node;
030
031import org.apache.commons.lang3.ArrayUtils;
032import org.apache.commons.lang3.StringUtils;
033
034import org.ametys.cms.content.external.ExternalizableMetadataHelper;
035import org.ametys.cms.content.external.ExternalizableMetadataProvider.ExternalizableMetadataStatus;
036import org.ametys.odf.courselist.CourseList;
037import org.ametys.odf.courselist.CourseListContainer;
038import org.ametys.plugins.repository.AmetysObjectIterable;
039import org.ametys.plugins.repository.AmetysRepositoryException;
040import org.ametys.plugins.repository.RemovableAmetysObject;
041import org.ametys.plugins.repository.RepositoryIntegrityViolationException;
042import org.ametys.plugins.repository.UnknownAmetysObjectException;
043import org.ametys.plugins.repository.metadata.CompositeMetadata;
044import org.ametys.plugins.repository.metadata.RichText;
045import org.ametys.plugins.repository.metadata.UnknownMetadataException;
046
047/**
048 * Abstract common superclass for {@link Program} and {@link SubProgram}.
049 * @param <F> The actual type of the factory
050 */
051public abstract class AbstractProgram<F extends ProgramFactory> extends AbstractTraversableProgramPart<F> implements CourseListContainer
052{
053    /** already imported attribute */
054    public static final String ALREADY_IMPORTED_ATTR = "imported";
055
056    // ------------------------------------------------//
057    //
058    // PROGRAM METADATA
059    //
060    // -----------------------------------------------//
061    /** Constants for metadata 'degree' */
062    public static final String DEGREE = "degree";
063
064    /** Constants for metadata 'domain' */
065    public static final String DOMAIN = "domain";
066    
067    /** Constants for metadata 'mention' */
068    public static final String MENTION = "mention";
069
070    /** Constants for metadata 'speciality' */
071    public static final String SPECIALITY = "speciality";
072
073    /** Constants for metadata 'educationLevel' */
074    public static final String LEVEL = "educationLevel";
075
076    /** Constants for metadata 'duration' */
077    public static final String DURATION = "duration";
078    
079    /** Constants for metadata 'educationLanguage' */
080    public static final String EDUC_LANGUAGE = "educationLanguage";
081
082    /** Constants for metadata 'presentation' */
083    public static final String PRESENTATION = "presentation";
084
085    /** Constants for metadata 'objectives' */
086    public static final String OBJECTIVES = "objectives";
087
088    /** Constants for metadata 'qualification' */
089    public static final String QUALIFICATION = "qualification";
090
091    /** Constants for metadata 'teachingOrganization' */
092    public static final String TEACHING_ORGANIZATION = "teachingOrganization";
093
094    /** Constants for metadata 'accessCondition' */
095    public static final String ACCESS_CONDITION = "accessCondition";
096
097    /** Constants for metadata 'neededPrerequisite' */
098    public static final String NEEDED_PREREQUISITE = "neededPrerequisite";
099
100    /** Constants for metadata 'recommendedPrerequisite' */
101    public static final String RECOMMENDED_PREREQUISITE = "recommendedPrerequisite";
102
103    /** Constants for metadata 'furtherStudy' */
104    public static final String FURTHER_STUDY = "furtherStudy";
105
106    /** Constants for metadata 'studyAbroad' */
107    public static final String STUDY_ABROAD = "studyAbroad";
108
109    /** Constants for metadata 'targetGroup' */
110    public static final String TARGET_GROUP = "targetGroup";
111
112    /** Constants for metadata 'jobOpportunities' */
113    public static final String JOB_OPPORTUNITIES = "jobOpportunities";
114
115    /** Constants for metadata 'trainingStrategy' */
116    public static final String TRAINING_STRATEGY = "trainingStrategy";
117    
118    /** Constants for metadata 'knowledgeCheck' */
119    public static final String KNOWLEDGE_CHECK = "knowledgeCheck";
120
121    /** Constants for metadata 'universalAdjustment' */
122    public static final String UNIVERSAL_ADJUSTMENT = "universalAdjustment";
123
124    /** Constants for metadata 'expenses' */
125    public static final String EXPENSES = "expenses";
126
127    /** Constants for metadata 'additionalInformations' */
128    public static final String ADDITIONNAL_INFORMATIONS = "additionalInformations";
129
130    /** Constants for metadata 'jointOrgUnit' */
131    public static final String JOINT_ORGUNIT = "jointOrgUnit";
132
133    /** Constants for metadata 'place' */
134    public static final String PLACE = "place";
135
136    /** Constants for metadata 'distanceLearning' */
137    public static final String DISTANCE_LEARNING = "distanceLearning";
138
139    /** Constants for metadata 'educationKind' */
140    public static final String EDUCATION_KIND = "educationKind";
141
142    /** Constant for metadata 'formofteachingOrg' */
143    public static final String FORM_OF_TEACHING_ORG = "formofteachingOrg";
144
145    /** Constants for metadata 'ects' */
146    public static final String ECTS = "ects";
147
148    /** Constants for metadata 'internship' */
149    public static final String INTERNSHIP = "internship";
150
151    /** Constants for metadata 'internshipDuration' */
152    public static final String INTERNSHIP_DURATION = "internshipDuration";
153
154    /** Constants for metadata 'internshipAbroad' */
155    public static final String INTERNSHIP_ABROAD = "internshipAbroad";
156
157    /** Constants for metadata 'internshipAbroadDuration' */
158    public static final String INTERNSHIP_ABROAD_DURATION = "internshipAbroadDuration";
159    
160    /** Constants for metadata 'registrationStart' */
161    public static final String REGISTRATION_START = "registrationStart";
162    
163    /** Constants for metadata 'registrationDeadline' */
164    public static final String REGISTRATION_DEADLINE = "registrationDeadline";
165    
166    /** Constants for metadata 'teachingStart' */
167    public static final String TEACHING_START = "teachingStart";
168
169    /** Constants for metadata 'partnerSchools' */
170    public static final String PARTNER_SCHOOLS = "partnerSchools";
171
172    /** Constants for metadata 'partnerLaboratories' */
173    public static final String PARTNER_LABORATORIES = "partnerLaboratories";
174
175    /** Constants for metadata 'rncpCode' */
176    public static final String RNCP_CODE = "rncpCode";
177    
178    /** Constants for metadata 'rncpLevel' */
179    public static final String RNCP_LEVEL = "rncpLevel";
180
181    /** Constants for metadata 'siseCode' */
182    public static final String SISE_CODE = "siseCode";
183
184    /** Constants for metadata 'Cite 97' */
185    public static final String CITE97_CODE = "cite97Code";
186
187    /** Constants for metadata 'erasmusCode' */
188    public static final String ERASMUS_CODE = "erasmusCode";
189    
190    /** Constants for metadata 'erasmusCode' */
191    public static final String DGESIP_CODE = "dgesipCode";
192    
193    /** Constants for metadata 'romeCode' */
194    public static final String ROME_CODE = "romeCode";
195    
196    /** Constants for metadata 'romeCode' */
197    public static final String FAP_CODE = "fapCode";
198
199    /** Constants for metadata 'programWebSite' */
200    public static final String PROGRAM_WEBSITE = "programWebSite";
201    
202    /** Constants for metadata 'programWebSiteLabel' */
203    public static final String PROGRAM_WEBSITE_LABEL = "programWebSiteLabel";
204
205    /** Constants for metadata 'programWebSiteUrl' */
206    public static final String PROGRAM_WEBSITE_URL = "programWebSiteUrl";
207
208    /** Constants for metadata 'attachment' */
209    public static final String ATTACHMENTS = "attachments";
210
211    /** Constants for metadata 'numberOfStudents' */
212    public static final String NUMBER_OF_STUDENTS = "numberOfStudents";
213    
214    /** Constants for metadata 'successRate' */
215    public static final String SUCCESSRATE = "successRate";
216
217    /** Constants for metadata 'reorientation' */
218    public static final String REORIENTATION = "reorientation";
219    
220    /** Constants for metadata 'keywords' */
221    public static final String KEYWORDS = "keywords";
222    
223    /** Constants for metadata 'sharedWith' (for shared program in a co-accredited mention only) */
224    public static final String SHARED_WITH = "sharedWith";
225    
226    /** Constants for metadata 'educationEntryLevel' */
227    public static final String EDUCATION_ENTRY_LEVEL = "educationEntryLevel";
228    
229    /** Constants for metadata 'programField' */
230    public static final String PROGRAM_FIELD = "programField";
231    
232    /** Constants for metadata 'availableCertification' */
233    public static final String AVAILABLE_CERTIFICATION = "availableCertification";
234    
235    /** Constants for metadata 'disciplines' */
236    public static final String DISCIPLINES = "disciplines";
237    
238    /** Constants for metadata 'sectors' */
239    public static final String SECTORS = "sectors";
240    
241    /** Constants for metadata 'internshipOpen' */
242    public static final String INTERNSHIP_OPEN = "internshipOpen";
243
244    /** Constants for metadata 'internshipDescription' */
245    public static final String INTERNSHIP_DESCRIPTION = "internshipDescription";
246    
247    /** Constants for metadata 'internshipDescription/title' */
248    public static final String INTERNSHIP_DESCRIPTION_TITLE = "title";
249    
250    /** Constants for metadata 'internshipDescription/duration' */
251    public static final String INTERNSHIP_DESCRIPTION_DURATION = "duration";
252    
253    /** Constants for metadata 'internshipDescription/period' */
254    public static final String INTERNSHIP_DESCRIPTION_PERIOD = "period";
255    
256    /** Constants for metadata 'internshipDescription/kind' */
257    public static final String INTERNSHIP_DESCRIPTION_KIND = "kind";
258    
259    /** Constants for metadata 'apprenticeshipOpen' */
260    public static final String APPRENTICESHIP_OPEN = "apprenticeshipOpen";
261    
262    /** Constants for metadata 'apprenticeshipPeriod' */
263    public static final String APPRENTICESHIP_PERIOD = "apprenticeshipPeriod";
264    
265    /** Constants for metadata 'apprenticeshipContract' */
266    public static final String APPRENTICESHIP_CONTRACT = "apprenticeshipContract";
267    
268    /** Constants for metadata 'internationalEducation' */
269    public static final String INTERNATIONAL_EDUCATION = "internationalEducation";
270    
271    /** Constants for metadata 'internationalDimension' */
272    public static final String INTERNATIONAL_DIMENSION = "internationalDimension";
273    
274    /** Constants for metadata 'geoCode' */
275    public static final String GEOCODE = "geoCode";
276    
277    /** Constants for metadata 'otherPartners' */
278    public static final String OTHER_PARTNERS = "otherPartners";
279    
280    /** Constants for metadata 'campus' */
281    public static final String CAMPUS = "campus";
282    
283    /** Constants for metadata 'foreignPlace' */
284    public static final String FOREIGN_PLACE = "foreignPlace";
285    
286    /** Constants for metadata 'inscription' */
287    public static final String INSCRIPTION = "inscription";
288    
289    /** Constants for metadata 'furtherStudyPrograms' */
290    public static final String FURTHER_STUDY_PROGRAMS = "furtherStudyPrograms";
291    
292    /**
293     * References
294     */
295       
296    /** Constants for metadata 'orgUnits' */
297    public static final String ORG_UNITS_REFERENCES = "orgUnit";
298
299    /** Constants for metadata 'personInCharge' */
300    public static final String PERSONS_IN_CHARGE = "personInCharge";
301    
302    /** Constants for metadata 'personInCharge/role' */
303    public static final String PERSONS_IN_CHARGE_ROLE = "role";
304    
305    /** Constants for metadata 'personInCharge/persons' */
306    public static final String PERSONS_IN_CHARGE_PERSONS = "persons";
307
308    /** Constants for metadata 'contact' */
309    public static final String CONTACTS_REFERENCES = "contact";
310    
311    private String _contextPath;
312
313    // ------------------------------------------------//
314    //
315    // PROGRAM METHODS
316    //
317    // -----------------------------------------------//
318    /**
319     * Constructor
320     * @param node The JCR node
321     * @param parentPath The parent path
322     * @param factory The factory
323     */
324    public AbstractProgram(Node node, String parentPath, F factory)
325    {
326        super(node, parentPath, factory);
327    }
328    
329    @Override
330    public void remove() throws AmetysRepositoryException, RepositoryIntegrityViolationException
331    {
332        AmetysObjectIterable<RemovableAmetysObject> children = this.getChildren();
333        
334        for (RemovableAmetysObject child : children)
335        {
336            child.remove();
337        }
338        
339        super.remove();
340    }
341    
342    /**
343     * Set the parent path for links and breadcrumb
344     * @param path the parent path
345     */
346    public void setContextPath (String path)
347    {
348        _contextPath = path;
349    }
350    
351    /**
352     * Get the parent path. Can be null.
353     * @return the parent path
354     */
355    public String getContextPath ()
356    {
357        return _contextPath;
358    }
359    
360    /**
361     * Remove reference from local and remote metadata
362     * @param metadataName The metadata name
363     * @param value The value of reference to remove 
364     */
365    public void removeReference (String metadataName, String value)
366    {
367        String[] references = getMetadataHolder().getStringArray(metadataName, ArrayUtils.EMPTY_STRING_ARRAY);
368        List<String> refList = new ArrayList<>(Arrays.asList(references));
369        if (refList.contains(value))
370        {
371            refList.remove(value);
372            getMetadataHolder().setMetadata(metadataName, refList.toArray(new String[refList.size()]));
373        }
374        
375        String[] altReferences = getMetadataHolder().getStringArray(metadataName + ExternalizableMetadataHelper.ALTERNATIVE_SUFFIX, ArrayUtils.EMPTY_STRING_ARRAY);
376        List<String> altList = new ArrayList<>(Arrays.asList(altReferences));
377        if (altList.contains(value))
378        {
379            altList.remove(value);
380            getMetadataHolder().setMetadata(metadataName + ExternalizableMetadataHelper.ALTERNATIVE_SUFFIX, altList.toArray(new String[altList.size()]));
381        }
382    }
383    
384    // --------------------------------------------------------------------------------------//
385    // ORGUNITS
386    // --------------------------------------------------------------------------------------//
387
388    /**
389     * Return the list of orgUnits binded to this program 
390     * @return a list of uuid
391     */
392    public List<String> getOrgUnits()
393    {
394        String[] orgunits = getMetadataHolder().getStringArray(ORG_UNITS_REFERENCES, ArrayUtils.EMPTY_STRING_ARRAY);
395        return new ArrayList<>(Arrays.asList(orgunits));
396    }
397    
398    /**
399     * Bind a orgUnit element to this program
400     * @param orgUnitId the uuid of a orgunit element
401     */
402    public void addOrgUnit(String orgUnitId)
403    {
404        String[] orgunits = ArrayUtils.EMPTY_STRING_ARRAY;
405        try
406        {
407            orgunits = ExternalizableMetadataHelper.getStringArray(getMetadataHolder(), ORG_UNITS_REFERENCES, ExternalizableMetadataStatus.LOCAL);
408        }
409        catch (UnknownMetadataException e)
410        {
411            // Nothing
412        }
413        
414        List<String> orgunitsList = new ArrayList<>(Arrays.asList(orgunits));
415        orgunitsList.add(orgUnitId);
416        
417        ExternalizableMetadataHelper.setLocalMetadata(getMetadataHolder(), ORG_UNITS_REFERENCES, orgunitsList.toArray(new String[orgunitsList.size()]), ExternalizableMetadataStatus.LOCAL);
418        
419    }
420    
421    /**
422     * Remove a orgunit from the list
423     * @param orgUnitId the uuid of a orgunit element
424     * @throws AmetysRepositoryException if an error occurs
425     */
426    public void removeOrgUnit(String orgUnitId) throws AmetysRepositoryException
427    {
428        removeReference (ORG_UNITS_REFERENCES, orgUnitId);
429    }
430    
431    /**
432     * Return the list of remote orgUnits binded to this program 
433     * @return a list of uuid
434     */
435    public List<String> getLocalOrgUnits()
436    {
437        try
438        {
439            String[] orgunits = ExternalizableMetadataHelper.getStringArray(getMetadataHolder(), ORG_UNITS_REFERENCES, ExternalizableMetadataStatus.LOCAL);
440            return new ArrayList<>(Arrays.asList(orgunits));
441        }
442        catch (UnknownMetadataException e)
443        {
444            return Collections.EMPTY_LIST;
445        }
446    }
447    
448    /**
449     * Return the list of remote orgUnits binded to this program 
450     * @return a list of uuid
451     */
452    public List<String> getRemoteOrgUnits()
453    {
454        try
455        {
456            String[] orgunits = ExternalizableMetadataHelper.getStringArray(getMetadataHolder(), ORG_UNITS_REFERENCES, ExternalizableMetadataStatus.EXTERNAL);
457            return new ArrayList<>(Arrays.asList(orgunits));
458        }
459        catch (UnknownMetadataException e)
460        {
461            return Collections.EMPTY_LIST;
462        }
463    }
464    
465    // --------------------------------------------------------------------------------------//
466    // CONTACTS
467    // --------------------------------------------------------------------------------------//
468
469    /**
470     * Return the list of Persons in charge binded to this program 
471     * @return a list of roles and UUID
472     */
473    public Map<String, String[]> getPersonsInCharge()
474    {
475        Map<String, String[]> personsInChargeByRole = new HashMap<>();
476        try
477        {
478            CompositeMetadata metaPersonsInCharge = getMetadataHolder().getCompositeMetadata(PERSONS_IN_CHARGE);
479            for (String metadataName : metaPersonsInCharge.getMetadataNames())
480            {
481                CompositeMetadata metaPersonInCharge = metaPersonsInCharge.getCompositeMetadata(metadataName);
482                String role = metaPersonInCharge.getString(PERSONS_IN_CHARGE_ROLE, StringUtils.EMPTY);
483                String[] persons = metaPersonInCharge.getStringArray(PERSONS_IN_CHARGE_PERSONS, ArrayUtils.EMPTY_STRING_ARRAY);
484                personsInChargeByRole.put(role, persons);
485            }
486        }
487        catch (UnknownMetadataException e)
488        {
489            // Nothing to do
490        }
491        return personsInChargeByRole;
492    }
493    
494    /**
495     * Return the list of Contacts in charge binded to this program 
496     * @return a list of uuid
497     */
498    public List<String> getContacts()
499    {
500        String[] contacts = getMetadataHolder().getStringArray(CONTACTS_REFERENCES, ArrayUtils.EMPTY_STRING_ARRAY);
501        return new ArrayList<>(Arrays.asList(contacts));
502    }
503    
504    /**
505     * Return the list of Contacts in charge binded to this program 
506     * @return a list of uuid
507     */
508    public List<String> getLocalContacts()
509    {
510        try
511        {
512            String[] orgunits = ExternalizableMetadataHelper.getStringArray(getMetadataHolder(), CONTACTS_REFERENCES, ExternalizableMetadataStatus.LOCAL);
513            return new ArrayList<>(Arrays.asList(orgunits));
514        }
515        catch (UnknownMetadataException e)
516        {
517            return Collections.EMPTY_LIST;
518        }
519    }
520    
521    /**
522     * Return the list of Contacts in charge binded to this program 
523     * @return a list of uuid
524     */
525    public List<String> getRemoteContacts()
526    {
527        try
528        {
529            String[] orgunits = ExternalizableMetadataHelper.getStringArray(getMetadataHolder(), CONTACTS_REFERENCES, ExternalizableMetadataStatus.EXTERNAL);
530            return new ArrayList<>(Arrays.asList(orgunits));
531        }
532        catch (UnknownMetadataException e)
533        {
534            return Collections.EMPTY_LIST;
535        }
536    }
537
538    // --------------------------------------------------------------------------------------//
539    // GETTERS AND SETTERS
540    // --------------------------------------------------------------------------------------//
541    /**
542     * Get the degree
543     * @return the degree or null
544     */
545    public String getDegree()
546    {
547        return getMetadataHolder().getString(DEGREE, StringUtils.EMPTY);
548    }
549
550    /**
551     * Set the degree
552     * @param degree the degree to set
553     * @throws AmetysRepositoryException if an error occurred
554     */
555    public void setDegree(String degree) throws AmetysRepositoryException
556    {
557        if (_getFactory()._getSynchronizedMetadata(this).contains(DEGREE))
558        {
559            ExternalizableMetadataHelper.setLocalMetadata(getMetadataHolder(), DEGREE, degree, ExternalizableMetadataStatus.LOCAL);
560        }
561        else
562        {
563            ExternalizableMetadataHelper.setMetadata(getMetadataHolder(), DEGREE, degree);
564        }
565    }
566
567    /**
568     * Get the domain
569     * @return the domain or an empty array
570     */
571    public String[] getDomain()
572    {
573        return getMetadataHolder().getStringArray(DOMAIN, ArrayUtils.EMPTY_STRING_ARRAY);
574    }
575
576    /**
577     * Set the domain
578     * @param domain the domain to set
579     * @throws AmetysRepositoryException if an error occurred
580     */
581    public void setDomain(String domain) throws AmetysRepositoryException
582    {
583        if (_getFactory()._getSynchronizedMetadata(this).contains(DOMAIN))
584        {
585            ExternalizableMetadataHelper.setLocalMetadata(getMetadataHolder(), DOMAIN, domain, ExternalizableMetadataStatus.LOCAL);
586        }
587        else
588        {
589            ExternalizableMetadataHelper.setMetadata(getMetadataHolder(), DOMAIN, domain);
590        }
591    }
592    
593    /**
594     * Get the education presentation
595     * @return the education presentation or null if not set
596     */
597    public RichText getPresentation()
598    {
599        try
600        {
601            return getMetadataHolder().getRichText(PRESENTATION);
602        }
603        catch (UnknownMetadataException e)
604        {
605            return null;
606        }
607    }
608
609    /**
610     * Get the education objectives
611     * @return the education objectives or null if not set
612     */
613    public RichText getObjectives()
614    {
615        try
616        {
617            return getMetadataHolder().getRichText(OBJECTIVES);
618        }
619        catch (UnknownMetadataException e)
620        {
621            return null;
622        }
623    }
624
625    /**
626     * Get the education qualification
627     * @return the education qualification or null if not set
628     */
629    public RichText getQualification()
630    {
631        try
632        {
633            return getMetadataHolder().getRichText(QUALIFICATION);
634        }
635        catch (UnknownMetadataException e)
636        {
637            return null;
638        }
639    }
640
641    /**
642     * Get the education organization
643     * @return the education organization or null if not set
644     */
645    public RichText getTeachingOrganization()
646    {
647        try
648        {
649            return getMetadataHolder().getRichText(TEACHING_ORGANIZATION);
650        }
651        catch (UnknownMetadataException e)
652        {
653            return null;
654        }
655    }
656
657    /**
658     * Get the access conditions
659     * @return  the access conditions  or null if not set
660     */
661    public RichText getAccessCondition()
662    {
663        try
664        {
665            return getMetadataHolder().getRichText(ACCESS_CONDITION);
666        }
667        catch (UnknownMetadataException e)
668        {
669            return null;
670        }
671    }
672
673    /**
674     * Get the education requirements
675     * @return the education requirements or null if not set
676     */
677    public RichText getNeededPrerequisite()
678    {
679        try
680        {
681            return getMetadataHolder().getRichText(NEEDED_PREREQUISITE);
682        }
683        catch (UnknownMetadataException e)
684        {
685            return null;
686        }
687    }
688
689    /**
690     * Get the education recommended prerequisite
691     * @return the education recommended prerequisite or null if not set
692     */
693    public RichText getRecommendedPrerequisite()
694    {
695        try
696        {
697            return getMetadataHolder().getRichText(RECOMMENDED_PREREQUISITE);
698        }
699        catch (UnknownMetadataException e)
700        {
701            return null;
702        }
703    }
704
705    /**
706     * Get the education further study
707     * @return the education further study or null if not set
708     */
709    public RichText getFurtherStudy()
710    {
711        try
712        {
713            return getMetadataHolder().getRichText(FURTHER_STUDY);
714        }
715        catch (UnknownMetadataException e)
716        {
717            return null;
718        }
719    }
720
721    /**
722     * Get the education study abroard
723     * @return the education study abroard or null if not set
724     */
725    public RichText getStudyAbroad()
726    {
727        try
728        {
729            return getMetadataHolder().getRichText(STUDY_ABROAD);
730        }
731        catch (UnknownMetadataException e)
732        {
733            return null;
734        }
735    }
736
737    /**
738     * Get the education target
739     * @return the education target or null if not set
740     */
741    public RichText getTargetGroup()
742    {
743        try
744        {
745            return getMetadataHolder().getRichText(TARGET_GROUP);
746        }
747        catch (UnknownMetadataException e)
748        {
749            return null;
750        }
751    }
752
753    /**
754     * Get the job opportunities
755     * @return the job opportunities or null if not set
756     */
757    public RichText getJobOpportunities()
758    {
759        try
760        {
761            return getMetadataHolder().getRichText(JOB_OPPORTUNITIES);
762        }
763        catch (UnknownMetadataException e)
764        {
765            return null;
766        }
767    }
768    
769    /**
770     * Get the training strategy
771     * @return the training strategy or null if not set
772     */
773    public RichText getTrainingStrategy()
774    {
775        try
776        {
777            return getMetadataHolder().getRichText(TRAINING_STRATEGY);
778        }
779        catch (UnknownMetadataException e)
780        {
781            return null;
782        }
783    }
784    
785    /**
786     * Get acknowledgments
787     * @return acknowledgments or null if not set
788     */
789    public RichText getKnowledgeCheck()
790    {
791        try
792        {
793            return getMetadataHolder().getRichText(KNOWLEDGE_CHECK);
794        }
795        catch (UnknownMetadataException e)
796        {
797            return null;
798        }
799    }
800
801    /**
802     * Get universal adjustment
803     * @return universal adjustment or null if not set
804     */
805    public RichText getUniversalAdjustment()
806    {
807        try
808        {
809            return getMetadataHolder().getRichText(UNIVERSAL_ADJUSTMENT);
810        }
811        catch (UnknownMetadataException e)
812        {
813            return null;
814        }
815    }
816
817    /**
818     * Get the additional informations
819     * @return the additional informations or null if not set
820     */
821    public RichText getAdditionalInformations()
822    {
823        try
824        {
825            return getMetadataHolder().getRichText(ADDITIONNAL_INFORMATIONS);
826        }
827        catch (UnknownMetadataException e)
828        {
829            return null;
830        }
831    }
832
833    /**
834     * Get the education level
835     * @return the education level or null
836     */
837    public String getEducationLevel()
838    {
839        return getMetadataHolder().getString(LEVEL, StringUtils.EMPTY);
840    }
841
842    /**
843     * Get the RNCP code
844     * @return the RNCP code
845     */
846    public String[] getRncpCode()
847    {
848        return getMetadataHolder().getStringArray(RNCP_CODE, ArrayUtils.EMPTY_STRING_ARRAY);
849    }
850    
851    /**
852     * Get the RNCP level
853     * @return the RNCP level
854     */
855    public String[]  getRncpLevel()
856    {
857        return getMetadataHolder().getStringArray(RNCP_LEVEL, ArrayUtils.EMPTY_STRING_ARRAY);
858    }
859    
860    /**
861     * Get the SISE code
862     * @return the SISE code
863     */
864    public String[] getSiseCode()
865    {
866        return getMetadataHolder().getStringArray(SISE_CODE, ArrayUtils.EMPTY_STRING_ARRAY);
867    }
868
869    /**
870     * Get the CITE97 code
871     * @return the CITE97 code
872     */
873    public String[] getCite97Code()
874    {
875        return getMetadataHolder().getStringArray(CITE97_CODE, ArrayUtils.EMPTY_STRING_ARRAY);
876    }
877
878    /**
879     * Get the DGESIP code
880     * @return the DGESIP code
881     */
882    public String[] getDGESIPCode()
883    {
884        return getMetadataHolder().getStringArray(DGESIP_CODE, ArrayUtils.EMPTY_STRING_ARRAY);
885    }
886
887    /**
888     * Get the Erasmus code
889     * @return the Erasmus code
890     */
891    public String[] getErasmusCode()
892    {
893        return getMetadataHolder().getStringArray(ERASMUS_CODE, ArrayUtils.EMPTY_STRING_ARRAY);
894    }
895
896    /**
897     * Get the ROME code
898     * @return the ROME code
899     */
900    public String[] getRomeCode()
901    {
902        return getMetadataHolder().getStringArray(ROME_CODE, ArrayUtils.EMPTY_STRING_ARRAY);
903    }
904    
905    /**
906     * Get the ROME code
907     * @return the ROME code
908     */
909    public String[] getFapCode()
910    {
911        return getMetadataHolder().getStringArray(FAP_CODE, ArrayUtils.EMPTY_STRING_ARRAY);
912    }
913    
914    /**
915     * Get the mention
916     * @return the mention or null
917     */
918    public String getMention()
919    {
920        return getMetadataHolder().getString(MENTION, StringUtils.EMPTY);
921    }
922
923    /**
924     * Get the speciality
925     * @return the speciality or null
926     */
927    public String getSpeciality()
928    {
929        return getMetadataHolder().getString(SPECIALITY, StringUtils.EMPTY);
930    }
931
932    /**
933     * Get the org units
934     * @return the org units
935     */
936    public String[] getJointOrgUnit()
937    {
938        return getMetadataHolder().getStringArray(JOINT_ORGUNIT, ArrayUtils.EMPTY_STRING_ARRAY);
939    }
940
941    /**
942     * Get the places
943     * @return the places as an Array of String
944     */
945    public String[] getPlace()
946    {
947        return getMetadataHolder().getStringArray(PLACE, ArrayUtils.EMPTY_STRING_ARRAY);
948    }
949
950    /**
951     * Get the list of websites
952     * @return the list of website or an empty list
953     */
954    public Set<WebsiteLink> getWebsiteLinks()
955    {
956        Set<WebsiteLink> websites = new HashSet<>();
957        try
958        {
959            CompositeMetadata metaWebsites = getMetadataHolder().getCompositeMetadata(PROGRAM_WEBSITE);
960            for (String metadataName : metaWebsites.getMetadataNames())
961            {
962                CompositeMetadata metaWebsite = metaWebsites.getCompositeMetadata(metadataName);
963                WebsiteLink website = new WebsiteLink(metaWebsite.getString(PROGRAM_WEBSITE_URL, StringUtils.EMPTY), metaWebsite.getString(PROGRAM_WEBSITE_LABEL, StringUtils.EMPTY));
964                websites.add(website);
965            }
966        }
967        catch (UnknownMetadataException e)
968        {
969            // Nothing to do
970        }
971        return websites;
972    }
973
974    /**
975     * Get the ECTS credits
976     * @return the ECTS credits
977     */
978    public String getEcts()
979    {
980        return getMetadataHolder().getString(ECTS, StringUtils.EMPTY);
981    }
982
983    /**
984     * Get the kind of education
985     * @return the kind of education or null
986     */
987    public String getEducationKind()
988    {
989        return getMetadataHolder().getString(EDUCATION_KIND, StringUtils.EMPTY);
990    }
991    
992    /**
993     * Get the duration
994     * @return the duration 
995     */
996    public String getDuration()
997    {
998        return getMetadataHolder().getString(DURATION, StringUtils.EMPTY);
999    }
1000
1001    /**
1002     * Get the education languages
1003     * @return the education languages
1004     */
1005    public String[] getEducationLanguage()
1006    {
1007        return getMetadataHolder().getStringArray(EDUC_LANGUAGE, ArrayUtils.EMPTY_STRING_ARRAY);
1008    }
1009
1010    /**
1011     * Get the effectives
1012     * @return the effectives
1013     */
1014    public RichText getEffectives()
1015    {
1016        try
1017        {
1018            return getMetadataHolder().getRichText(NUMBER_OF_STUDENTS);
1019        }
1020        catch (UnknownMetadataException e)
1021        {
1022            return null;
1023        }
1024    }
1025    
1026    /**
1027     * Get the SuccessRate
1028     * @return the SuccessRate 
1029     */
1030    public String getSuccessRate()
1031    {
1032        return getMetadataHolder().getString(SUCCESSRATE, StringUtils.EMPTY);
1033    }
1034    
1035    /**
1036     * Get the expenses
1037     * @return the expenses
1038     */
1039    public RichText getExpenses()
1040    {
1041        try
1042        {
1043            return getMetadataHolder().getRichText(EXPENSES);
1044        }
1045        catch (UnknownMetadataException e)
1046        {
1047            return null;
1048        }
1049    }
1050    
1051    /**
1052     * Get the form of teaching organization
1053     * @return the form of teaching organization
1054     */
1055    public String[] getFormOfTeachingOrgs()
1056    {
1057        return getMetadataHolder().getStringArray(FORM_OF_TEACHING_ORG, ArrayUtils.EMPTY_STRING_ARRAY);
1058    }
1059    
1060    /**
1061     * Get the REORIENTATION 
1062     * @return RichText
1063     */
1064    public RichText getReorientation()
1065    {
1066        try
1067        {
1068            return getMetadataHolder().getRichText(REORIENTATION);
1069        }
1070        catch (UnknownMetadataException e)
1071        {
1072            return null;
1073        }
1074    }
1075    
1076    /**
1077     * Get the distance learning
1078     * @return the distance learning or null
1079     */
1080    public String getDistanceLearning()
1081    {
1082        return getMetadataHolder().getString(DISTANCE_LEARNING, StringUtils.EMPTY);
1083    }
1084
1085    /**
1086     * Get the internship
1087     * @return the internship or null
1088     */
1089    public String getInternship()
1090    {
1091        return getMetadataHolder().getString(INTERNSHIP, StringUtils.EMPTY);
1092    }
1093
1094    /**
1095     * Get the internship duration
1096     * @return the internship duration or null
1097     */
1098    public String getInternshipDuration()
1099    {
1100        return getMetadataHolder().getString(INTERNSHIP_DURATION, StringUtils.EMPTY);
1101    }
1102
1103    /**
1104     * Get the internship abroad
1105     * @return the internship abroad or null
1106     */
1107    public String getInternshipAbroad()
1108    {
1109        return getMetadataHolder().getString(INTERNSHIP_ABROAD, StringUtils.EMPTY);
1110    }
1111
1112    /**
1113     * Get the internship abroad duration
1114     * @return  the internship abroad duration or null
1115     */
1116    public String getAbroadInternshipDuration()
1117    {
1118        return getMetadataHolder().getString(INTERNSHIP_ABROAD_DURATION, StringUtils.EMPTY);
1119    }
1120
1121    /**
1122     * Get the registration start date.
1123     * @return The registration start date, can be null.
1124     */
1125    public Date getRegistrationStart()
1126    {
1127        return getMetadataHolder().getDate(REGISTRATION_START, null);
1128    }
1129    
1130    /**
1131     * Get the registration deadline date.
1132     * @return The registration deadline date, can be null.
1133     */
1134    public Date getRegistrationDeadline()
1135    {
1136        return getMetadataHolder().getDate(REGISTRATION_DEADLINE, null);
1137    }
1138    
1139    /**
1140     * Get the teaching start date.
1141     * @return The teaching start date, can be null.
1142     */
1143    public Date getTeachingStart()
1144    {
1145        return getMetadataHolder().getDate(TEACHING_START, null);
1146    }
1147    
1148    /**
1149     * Get the keywords
1150     * @return the keywords
1151     */
1152    public String[] getKeywords()
1153    {
1154        return getMetadataHolder().getStringArray(KEYWORDS, ArrayUtils.EMPTY_STRING_ARRAY);
1155    }
1156
1157    /**
1158     * Get the CDMfr codes of shared (sub)programs
1159     * @return the CDMfr codes
1160     */
1161    public String[] getSharedWith()
1162    {
1163        return getMetadataHolder().getStringArray(SHARED_WITH, ArrayUtils.EMPTY_STRING_ARRAY);
1164    }
1165    
1166    /**
1167     * Get the education level entry
1168     * @return the education level entry
1169     */
1170    public String[] getEducationLevelEntry()
1171    {
1172        return getMetadataHolder().getStringArray(EDUCATION_ENTRY_LEVEL, ArrayUtils.EMPTY_STRING_ARRAY);
1173    }
1174    
1175    /**
1176     * Get the program fields
1177     * @return the program fields
1178     */
1179    public String[] getProgramField()
1180    {
1181        return getMetadataHolder().getStringArray(PROGRAM_FIELD, ArrayUtils.EMPTY_STRING_ARRAY);
1182    }
1183    
1184    /**
1185     * Get the available certifications
1186     * @return the available certifications
1187     */
1188    public String[] getAvailableCertification()
1189    {
1190        return getMetadataHolder().getStringArray(AVAILABLE_CERTIFICATION, ArrayUtils.EMPTY_STRING_ARRAY);
1191    }
1192    
1193    /**
1194     * Get the disciplines
1195     * @return the disciplines
1196     */
1197    public String[] getDisciplines()
1198    {
1199        return getMetadataHolder().getStringArray(DISCIPLINES, ArrayUtils.EMPTY_STRING_ARRAY);
1200    }
1201    
1202    /**
1203     * Get the sectors
1204     * @return the sectors
1205     */
1206    public String[] getSectors()
1207    {
1208        return getMetadataHolder().getStringArray(SECTORS, ArrayUtils.EMPTY_STRING_ARRAY);
1209    }
1210
1211    /**
1212     * Is intership open
1213     * @return <code>true</code> if internship is open
1214     */
1215    public boolean isInternshipOpen()
1216    {
1217        return getMetadataHolder().getBoolean(INTERNSHIP_OPEN, false);
1218    }
1219
1220    /**
1221     * Is apprenticeship open
1222     * @return <code>true</code> if apprenticeship is open
1223     */
1224    public boolean isApprenticeshipOpen()
1225    {
1226        return getMetadataHolder().getBoolean(APPRENTICESHIP_OPEN, false);
1227    }
1228
1229    /**
1230     * Get the apprenticeship period description
1231     * @return the apprenticeship period
1232     */
1233    public RichText getApprenticeshipPeriod()
1234    {
1235        try
1236        {
1237            return getMetadataHolder().getRichText(APPRENTICESHIP_PERIOD);
1238        }
1239        catch (UnknownMetadataException e)
1240        {
1241            return null;
1242        }
1243    }
1244    
1245    /**
1246     * Get the available apprenticeship contracts
1247     * @return the apprenticeship contracts
1248     */
1249    public String[] getApprenticeshipContract()
1250    {
1251        return getMetadataHolder().getStringArray(APPRENTICESHIP_CONTRACT, ArrayUtils.EMPTY_STRING_ARRAY);
1252    }
1253
1254    /**
1255     * Get the international education
1256     * @return the international education
1257     */
1258    public String[] getInternationalEducation()
1259    {
1260        return getMetadataHolder().getStringArray(INTERNATIONAL_EDUCATION, ArrayUtils.EMPTY_STRING_ARRAY);
1261    }
1262
1263    /**
1264     * Get the international dimension
1265     * @return the international dimension
1266     */
1267    public RichText getInternationalDimension()
1268    {
1269        try
1270        {
1271            return getMetadataHolder().getRichText(INTERNATIONAL_DIMENSION);
1272        }
1273        catch (UnknownMetadataException e)
1274        {
1275            return null;
1276        }
1277    }
1278    
1279    /**
1280     * Get the geocode latitude and longitude
1281     * @return the geocode
1282     */
1283    public Map<String, Double> getGeocode()
1284    {
1285        try
1286        {
1287            // FIXME should return a GeoCode object
1288            CompositeMetadata geoMetadata = getMetadataHolder().getCompositeMetadata(GEOCODE);
1289            
1290            if (geoMetadata.hasMetadata("longitude") && geoMetadata.hasMetadata("latitude")) 
1291            {
1292                Double longitude = geoMetadata.getDouble("longitude");
1293                Double latitude = geoMetadata.getDouble("latitude");
1294                
1295                Map<String, Double> geocode = new LinkedHashMap<>();
1296                geocode.put("longitude", longitude);
1297                geocode.put("latitude", latitude);
1298                
1299                return geocode;
1300            }
1301        }
1302        catch (UnknownMetadataException e)
1303        {
1304            // Ignore, just return null.
1305        }
1306        
1307        return null;
1308    }
1309    
1310    /**
1311     * Get the other partners
1312     * @return the other partners
1313     */
1314    public RichText getOtherPartners()
1315    {
1316        try
1317        {
1318            return getMetadataHolder().getRichText(OTHER_PARTNERS);
1319        }
1320        catch (UnknownMetadataException e)
1321        {
1322            return null;
1323        }
1324    }
1325
1326    /**
1327     * Get the campus
1328     * @return the campus
1329     */
1330    public String[] getCampus()
1331    {
1332        return getMetadataHolder().getStringArray(CAMPUS, ArrayUtils.EMPTY_STRING_ARRAY);
1333    }
1334
1335    /**
1336     * Get the foreign places
1337     * @return the foreign places
1338     */
1339    public String[] getForeignPlace()
1340    {
1341        return getMetadataHolder().getStringArray(FOREIGN_PLACE, ArrayUtils.EMPTY_STRING_ARRAY);
1342    }
1343    
1344    /**
1345     * Get the inscription
1346     * @return the inscription
1347     */
1348    public RichText getInscription()
1349    {
1350        try
1351        {
1352            return getMetadataHolder().getRichText(INSCRIPTION);
1353        }
1354        catch (UnknownMetadataException e)
1355        {
1356            return null;
1357        }
1358    }
1359    
1360    /**
1361     * Get the further study programs
1362     * @return the further study programs
1363     */
1364    public String[] getFurtherStudyPrograms()
1365    {
1366        return getMetadataHolder().getStringArray(FURTHER_STUDY_PROGRAMS, ArrayUtils.EMPTY_STRING_ARRAY);
1367    }
1368    
1369    // --------------------------------------------------------------------------------------//
1370    // Methods of CourseListContainer
1371    // --------------------------------------------------------------------------------------//
1372    @Override
1373    public List<CourseList> getCourseLists()
1374    {
1375        List<CourseList> children = new ArrayList<>();
1376        
1377        String[] childIds = getMetadataHolder().getStringArray(METADATA_PARENT_PROGRAM_PARTS, ArrayUtils.EMPTY_STRING_ARRAY);
1378        for (String id : childIds)
1379        {
1380            try
1381            {
1382                ProgramPart child = _getFactory()._getResolver().resolveById(id);
1383                if (child instanceof CourseList)
1384                {
1385                    children.add((CourseList) child);
1386                }
1387            }
1388            catch (UnknownAmetysObjectException e)
1389            {
1390                // Nothing
1391            }
1392        }
1393        
1394        return children;
1395    }
1396    
1397    @Override
1398    public boolean containsCourseList(String clId)
1399    {
1400        return ArrayUtils.contains(getMetadataHolder().getStringArray(METADATA_PARENT_PROGRAM_PARTS, ArrayUtils.EMPTY_STRING_ARRAY), clId);
1401    }
1402
1403    @Override
1404    public boolean hasCourseLists()
1405    {
1406        return !getCourseLists().isEmpty();
1407    }
1408    
1409    // --------------------------------------------------------------------------------------//
1410    // CDMfr
1411    // --------------------------------------------------------------------------------------//
1412    @Override
1413    protected String getCDMType()
1414    {
1415        return "PR";
1416    }
1417    
1418    /**
1419     * Returns the surrounding tag name in the CDM-fr representation.
1420     * @return the surrounding tag name
1421     */
1422    public abstract String getCDMTagName();
1423}