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                
483                String[] rawPersons = metaPersonInCharge.getStringArray(PERSONS_IN_CHARGE_PERSONS, ArrayUtils.EMPTY_STRING_ARRAY);
484                // Remove empty values
485                String[] persons = Arrays.stream(rawPersons).filter(x -> !x.isEmpty()).toArray(String[]::new);
486                if (persons.length > 0)
487                {
488                    String role = metaPersonInCharge.getString(PERSONS_IN_CHARGE_ROLE, StringUtils.EMPTY);
489                    personsInChargeByRole.put(role, persons);
490                }
491            }
492        }
493        catch (UnknownMetadataException e)
494        {
495            // Nothing to do
496        }
497        return personsInChargeByRole;
498    }
499    
500    /**
501     * Return the list of Contacts in charge binded to this program 
502     * @return a list of uuid
503     */
504    public List<String> getContacts()
505    {
506        String[] contacts = getMetadataHolder().getStringArray(CONTACTS_REFERENCES, ArrayUtils.EMPTY_STRING_ARRAY);
507        return new ArrayList<>(Arrays.asList(contacts));
508    }
509    
510    /**
511     * Return the list of Contacts in charge binded to this program 
512     * @return a list of uuid
513     */
514    public List<String> getLocalContacts()
515    {
516        try
517        {
518            String[] orgunits = ExternalizableMetadataHelper.getStringArray(getMetadataHolder(), CONTACTS_REFERENCES, ExternalizableMetadataStatus.LOCAL);
519            return new ArrayList<>(Arrays.asList(orgunits));
520        }
521        catch (UnknownMetadataException e)
522        {
523            return Collections.EMPTY_LIST;
524        }
525    }
526    
527    /**
528     * Return the list of Contacts in charge binded to this program 
529     * @return a list of uuid
530     */
531    public List<String> getRemoteContacts()
532    {
533        try
534        {
535            String[] orgunits = ExternalizableMetadataHelper.getStringArray(getMetadataHolder(), CONTACTS_REFERENCES, ExternalizableMetadataStatus.EXTERNAL);
536            return new ArrayList<>(Arrays.asList(orgunits));
537        }
538        catch (UnknownMetadataException e)
539        {
540            return Collections.EMPTY_LIST;
541        }
542    }
543
544    // --------------------------------------------------------------------------------------//
545    // GETTERS AND SETTERS
546    // --------------------------------------------------------------------------------------//
547    /**
548     * Get the degree
549     * @return the degree or null
550     */
551    public String getDegree()
552    {
553        return getMetadataHolder().getString(DEGREE, StringUtils.EMPTY);
554    }
555
556    /**
557     * Set the degree
558     * @param degree the degree to set
559     * @throws AmetysRepositoryException if an error occurred
560     */
561    public void setDegree(String degree) throws AmetysRepositoryException
562    {
563        if (_getFactory()._getSynchronizedMetadata(this).contains(DEGREE))
564        {
565            ExternalizableMetadataHelper.setLocalMetadata(getMetadataHolder(), DEGREE, degree, ExternalizableMetadataStatus.LOCAL);
566        }
567        else
568        {
569            ExternalizableMetadataHelper.setMetadata(getMetadataHolder(), DEGREE, degree);
570        }
571    }
572
573    /**
574     * Get the domain
575     * @return the domain or an empty array
576     */
577    public String[] getDomain()
578    {
579        return getMetadataHolder().getStringArray(DOMAIN, ArrayUtils.EMPTY_STRING_ARRAY);
580    }
581
582    /**
583     * Set the domain
584     * @param domain the domain to set
585     * @throws AmetysRepositoryException if an error occurred
586     */
587    public void setDomain(String domain) throws AmetysRepositoryException
588    {
589        if (_getFactory()._getSynchronizedMetadata(this).contains(DOMAIN))
590        {
591            ExternalizableMetadataHelper.setLocalMetadata(getMetadataHolder(), DOMAIN, domain, ExternalizableMetadataStatus.LOCAL);
592        }
593        else
594        {
595            ExternalizableMetadataHelper.setMetadata(getMetadataHolder(), DOMAIN, domain);
596        }
597    }
598    
599    /**
600     * Get the education presentation
601     * @return the education presentation or null if not set
602     */
603    public RichText getPresentation()
604    {
605        try
606        {
607            return getMetadataHolder().getRichText(PRESENTATION);
608        }
609        catch (UnknownMetadataException e)
610        {
611            return null;
612        }
613    }
614
615    /**
616     * Get the education objectives
617     * @return the education objectives or null if not set
618     */
619    public RichText getObjectives()
620    {
621        try
622        {
623            return getMetadataHolder().getRichText(OBJECTIVES);
624        }
625        catch (UnknownMetadataException e)
626        {
627            return null;
628        }
629    }
630
631    /**
632     * Get the education qualification
633     * @return the education qualification or null if not set
634     */
635    public RichText getQualification()
636    {
637        try
638        {
639            return getMetadataHolder().getRichText(QUALIFICATION);
640        }
641        catch (UnknownMetadataException e)
642        {
643            return null;
644        }
645    }
646
647    /**
648     * Get the education organization
649     * @return the education organization or null if not set
650     */
651    public RichText getTeachingOrganization()
652    {
653        try
654        {
655            return getMetadataHolder().getRichText(TEACHING_ORGANIZATION);
656        }
657        catch (UnknownMetadataException e)
658        {
659            return null;
660        }
661    }
662
663    /**
664     * Get the access conditions
665     * @return  the access conditions  or null if not set
666     */
667    public RichText getAccessCondition()
668    {
669        try
670        {
671            return getMetadataHolder().getRichText(ACCESS_CONDITION);
672        }
673        catch (UnknownMetadataException e)
674        {
675            return null;
676        }
677    }
678
679    /**
680     * Get the education requirements
681     * @return the education requirements or null if not set
682     */
683    public RichText getNeededPrerequisite()
684    {
685        try
686        {
687            return getMetadataHolder().getRichText(NEEDED_PREREQUISITE);
688        }
689        catch (UnknownMetadataException e)
690        {
691            return null;
692        }
693    }
694
695    /**
696     * Get the education recommended prerequisite
697     * @return the education recommended prerequisite or null if not set
698     */
699    public RichText getRecommendedPrerequisite()
700    {
701        try
702        {
703            return getMetadataHolder().getRichText(RECOMMENDED_PREREQUISITE);
704        }
705        catch (UnknownMetadataException e)
706        {
707            return null;
708        }
709    }
710
711    /**
712     * Get the education further study
713     * @return the education further study or null if not set
714     */
715    public RichText getFurtherStudy()
716    {
717        try
718        {
719            return getMetadataHolder().getRichText(FURTHER_STUDY);
720        }
721        catch (UnknownMetadataException e)
722        {
723            return null;
724        }
725    }
726
727    /**
728     * Get the education study abroard
729     * @return the education study abroard or null if not set
730     */
731    public RichText getStudyAbroad()
732    {
733        try
734        {
735            return getMetadataHolder().getRichText(STUDY_ABROAD);
736        }
737        catch (UnknownMetadataException e)
738        {
739            return null;
740        }
741    }
742
743    /**
744     * Get the education target
745     * @return the education target or null if not set
746     */
747    public RichText getTargetGroup()
748    {
749        try
750        {
751            return getMetadataHolder().getRichText(TARGET_GROUP);
752        }
753        catch (UnknownMetadataException e)
754        {
755            return null;
756        }
757    }
758
759    /**
760     * Get the job opportunities
761     * @return the job opportunities or null if not set
762     */
763    public RichText getJobOpportunities()
764    {
765        try
766        {
767            return getMetadataHolder().getRichText(JOB_OPPORTUNITIES);
768        }
769        catch (UnknownMetadataException e)
770        {
771            return null;
772        }
773    }
774    
775    /**
776     * Get the training strategy
777     * @return the training strategy or null if not set
778     */
779    public RichText getTrainingStrategy()
780    {
781        try
782        {
783            return getMetadataHolder().getRichText(TRAINING_STRATEGY);
784        }
785        catch (UnknownMetadataException e)
786        {
787            return null;
788        }
789    }
790    
791    /**
792     * Get acknowledgments
793     * @return acknowledgments or null if not set
794     */
795    public RichText getKnowledgeCheck()
796    {
797        try
798        {
799            return getMetadataHolder().getRichText(KNOWLEDGE_CHECK);
800        }
801        catch (UnknownMetadataException e)
802        {
803            return null;
804        }
805    }
806
807    /**
808     * Get universal adjustment
809     * @return universal adjustment or null if not set
810     */
811    public RichText getUniversalAdjustment()
812    {
813        try
814        {
815            return getMetadataHolder().getRichText(UNIVERSAL_ADJUSTMENT);
816        }
817        catch (UnknownMetadataException e)
818        {
819            return null;
820        }
821    }
822
823    /**
824     * Get the additional informations
825     * @return the additional informations or null if not set
826     */
827    public RichText getAdditionalInformations()
828    {
829        try
830        {
831            return getMetadataHolder().getRichText(ADDITIONNAL_INFORMATIONS);
832        }
833        catch (UnknownMetadataException e)
834        {
835            return null;
836        }
837    }
838
839    /**
840     * Get the education level
841     * @return the education level or null
842     */
843    public String getEducationLevel()
844    {
845        return getMetadataHolder().getString(LEVEL, StringUtils.EMPTY);
846    }
847
848    /**
849     * Get the RNCP code
850     * @return the RNCP code
851     */
852    public String[] getRncpCode()
853    {
854        return getMetadataHolder().getStringArray(RNCP_CODE, ArrayUtils.EMPTY_STRING_ARRAY);
855    }
856    
857    /**
858     * Get the RNCP level
859     * @return the RNCP level
860     */
861    public String[]  getRncpLevel()
862    {
863        return getMetadataHolder().getStringArray(RNCP_LEVEL, ArrayUtils.EMPTY_STRING_ARRAY);
864    }
865    
866    /**
867     * Get the SISE code
868     * @return the SISE code
869     */
870    public String[] getSiseCode()
871    {
872        return getMetadataHolder().getStringArray(SISE_CODE, ArrayUtils.EMPTY_STRING_ARRAY);
873    }
874
875    /**
876     * Get the CITE97 code
877     * @return the CITE97 code
878     */
879    public String[] getCite97Code()
880    {
881        return getMetadataHolder().getStringArray(CITE97_CODE, ArrayUtils.EMPTY_STRING_ARRAY);
882    }
883
884    /**
885     * Get the DGESIP code
886     * @return the DGESIP code
887     */
888    public String[] getDGESIPCode()
889    {
890        return getMetadataHolder().getStringArray(DGESIP_CODE, ArrayUtils.EMPTY_STRING_ARRAY);
891    }
892
893    /**
894     * Get the Erasmus code
895     * @return the Erasmus code
896     */
897    public String[] getErasmusCode()
898    {
899        return getMetadataHolder().getStringArray(ERASMUS_CODE, ArrayUtils.EMPTY_STRING_ARRAY);
900    }
901
902    /**
903     * Get the ROME code
904     * @return the ROME code
905     */
906    public String[] getRomeCode()
907    {
908        return getMetadataHolder().getStringArray(ROME_CODE, ArrayUtils.EMPTY_STRING_ARRAY);
909    }
910    
911    /**
912     * Get the ROME code
913     * @return the ROME code
914     */
915    public String[] getFapCode()
916    {
917        return getMetadataHolder().getStringArray(FAP_CODE, ArrayUtils.EMPTY_STRING_ARRAY);
918    }
919    
920    /**
921     * Get the mention
922     * @return the mention or null
923     */
924    public String getMention()
925    {
926        return getMetadataHolder().getString(MENTION, StringUtils.EMPTY);
927    }
928
929    /**
930     * Get the speciality
931     * @return the speciality or null
932     */
933    public String getSpeciality()
934    {
935        return getMetadataHolder().getString(SPECIALITY, StringUtils.EMPTY);
936    }
937
938    /**
939     * Get the org units
940     * @return the org units
941     */
942    public String[] getJointOrgUnit()
943    {
944        return getMetadataHolder().getStringArray(JOINT_ORGUNIT, ArrayUtils.EMPTY_STRING_ARRAY);
945    }
946
947    /**
948     * Get the places
949     * @return the places as an Array of String
950     */
951    public String[] getPlace()
952    {
953        return getMetadataHolder().getStringArray(PLACE, ArrayUtils.EMPTY_STRING_ARRAY);
954    }
955
956    /**
957     * Get the list of websites
958     * @return the list of website or an empty list
959     */
960    public Set<WebsiteLink> getWebsiteLinks()
961    {
962        Set<WebsiteLink> websites = new HashSet<>();
963        try
964        {
965            CompositeMetadata metaWebsites = getMetadataHolder().getCompositeMetadata(PROGRAM_WEBSITE);
966            for (String metadataName : metaWebsites.getMetadataNames())
967            {
968                CompositeMetadata metaWebsite = metaWebsites.getCompositeMetadata(metadataName);
969                WebsiteLink website = new WebsiteLink(metaWebsite.getString(PROGRAM_WEBSITE_URL, StringUtils.EMPTY), metaWebsite.getString(PROGRAM_WEBSITE_LABEL, StringUtils.EMPTY));
970                websites.add(website);
971            }
972        }
973        catch (UnknownMetadataException e)
974        {
975            // Nothing to do
976        }
977        return websites;
978    }
979
980    /**
981     * Get the ECTS credits
982     * @return the ECTS credits
983     */
984    public String getEcts()
985    {
986        return getMetadataHolder().getString(ECTS, StringUtils.EMPTY);
987    }
988
989    /**
990     * Get the kind of education
991     * @return the kind of education or null
992     */
993    public String getEducationKind()
994    {
995        return getMetadataHolder().getString(EDUCATION_KIND, StringUtils.EMPTY);
996    }
997    
998    /**
999     * Get the duration
1000     * @return the duration 
1001     */
1002    public String getDuration()
1003    {
1004        return getMetadataHolder().getString(DURATION, StringUtils.EMPTY);
1005    }
1006
1007    /**
1008     * Get the education languages
1009     * @return the education languages
1010     */
1011    public String[] getEducationLanguage()
1012    {
1013        return getMetadataHolder().getStringArray(EDUC_LANGUAGE, ArrayUtils.EMPTY_STRING_ARRAY);
1014    }
1015
1016    /**
1017     * Get the effectives
1018     * @return the effectives
1019     */
1020    public RichText getEffectives()
1021    {
1022        try
1023        {
1024            return getMetadataHolder().getRichText(NUMBER_OF_STUDENTS);
1025        }
1026        catch (UnknownMetadataException e)
1027        {
1028            return null;
1029        }
1030    }
1031    
1032    /**
1033     * Get the SuccessRate
1034     * @return the SuccessRate 
1035     */
1036    public String getSuccessRate()
1037    {
1038        return getMetadataHolder().getString(SUCCESSRATE, StringUtils.EMPTY);
1039    }
1040    
1041    /**
1042     * Get the expenses
1043     * @return the expenses
1044     */
1045    public RichText getExpenses()
1046    {
1047        try
1048        {
1049            return getMetadataHolder().getRichText(EXPENSES);
1050        }
1051        catch (UnknownMetadataException e)
1052        {
1053            return null;
1054        }
1055    }
1056    
1057    /**
1058     * Get the form of teaching organization
1059     * @return the form of teaching organization
1060     */
1061    public String[] getFormOfTeachingOrgs()
1062    {
1063        return getMetadataHolder().getStringArray(FORM_OF_TEACHING_ORG, ArrayUtils.EMPTY_STRING_ARRAY);
1064    }
1065    
1066    /**
1067     * Get the REORIENTATION 
1068     * @return RichText
1069     */
1070    public RichText getReorientation()
1071    {
1072        try
1073        {
1074            return getMetadataHolder().getRichText(REORIENTATION);
1075        }
1076        catch (UnknownMetadataException e)
1077        {
1078            return null;
1079        }
1080    }
1081    
1082    /**
1083     * Get the distance learning
1084     * @return the distance learning or null
1085     */
1086    public String getDistanceLearning()
1087    {
1088        return getMetadataHolder().getString(DISTANCE_LEARNING, StringUtils.EMPTY);
1089    }
1090
1091    /**
1092     * Get the internship
1093     * @return the internship or null
1094     */
1095    public String getInternship()
1096    {
1097        return getMetadataHolder().getString(INTERNSHIP, StringUtils.EMPTY);
1098    }
1099
1100    /**
1101     * Get the internship duration
1102     * @return the internship duration or null
1103     */
1104    public String getInternshipDuration()
1105    {
1106        return getMetadataHolder().getString(INTERNSHIP_DURATION, StringUtils.EMPTY);
1107    }
1108
1109    /**
1110     * Get the internship abroad
1111     * @return the internship abroad or null
1112     */
1113    public String getInternshipAbroad()
1114    {
1115        return getMetadataHolder().getString(INTERNSHIP_ABROAD, StringUtils.EMPTY);
1116    }
1117
1118    /**
1119     * Get the internship abroad duration
1120     * @return  the internship abroad duration or null
1121     */
1122    public String getAbroadInternshipDuration()
1123    {
1124        return getMetadataHolder().getString(INTERNSHIP_ABROAD_DURATION, StringUtils.EMPTY);
1125    }
1126
1127    /**
1128     * Get the registration start date.
1129     * @return The registration start date, can be null.
1130     */
1131    public Date getRegistrationStart()
1132    {
1133        return getMetadataHolder().getDate(REGISTRATION_START, null);
1134    }
1135    
1136    /**
1137     * Get the registration deadline date.
1138     * @return The registration deadline date, can be null.
1139     */
1140    public Date getRegistrationDeadline()
1141    {
1142        return getMetadataHolder().getDate(REGISTRATION_DEADLINE, null);
1143    }
1144    
1145    /**
1146     * Get the teaching start date.
1147     * @return The teaching start date, can be null.
1148     */
1149    public Date getTeachingStart()
1150    {
1151        return getMetadataHolder().getDate(TEACHING_START, null);
1152    }
1153    
1154    /**
1155     * Get the keywords
1156     * @return the keywords
1157     */
1158    public String[] getKeywords()
1159    {
1160        return getMetadataHolder().getStringArray(KEYWORDS, ArrayUtils.EMPTY_STRING_ARRAY);
1161    }
1162
1163    /**
1164     * Get the CDMfr codes of shared (sub)programs
1165     * @return the CDMfr codes
1166     */
1167    public String[] getSharedWith()
1168    {
1169        return getMetadataHolder().getStringArray(SHARED_WITH, ArrayUtils.EMPTY_STRING_ARRAY);
1170    }
1171    
1172    /**
1173     * Get the education level entry
1174     * @return the education level entry
1175     */
1176    public String[] getEducationLevelEntry()
1177    {
1178        return getMetadataHolder().getStringArray(EDUCATION_ENTRY_LEVEL, ArrayUtils.EMPTY_STRING_ARRAY);
1179    }
1180    
1181    /**
1182     * Get the program fields
1183     * @return the program fields
1184     */
1185    public String[] getProgramField()
1186    {
1187        return getMetadataHolder().getStringArray(PROGRAM_FIELD, ArrayUtils.EMPTY_STRING_ARRAY);
1188    }
1189    
1190    /**
1191     * Get the available certifications
1192     * @return the available certifications
1193     */
1194    public String[] getAvailableCertification()
1195    {
1196        return getMetadataHolder().getStringArray(AVAILABLE_CERTIFICATION, ArrayUtils.EMPTY_STRING_ARRAY);
1197    }
1198    
1199    /**
1200     * Get the disciplines
1201     * @return the disciplines
1202     */
1203    public String[] getDisciplines()
1204    {
1205        return getMetadataHolder().getStringArray(DISCIPLINES, ArrayUtils.EMPTY_STRING_ARRAY);
1206    }
1207    
1208    /**
1209     * Get the sectors
1210     * @return the sectors
1211     */
1212    public String[] getSectors()
1213    {
1214        return getMetadataHolder().getStringArray(SECTORS, ArrayUtils.EMPTY_STRING_ARRAY);
1215    }
1216
1217    /**
1218     * Is intership open
1219     * @return <code>true</code> if internship is open
1220     */
1221    public boolean isInternshipOpen()
1222    {
1223        return getMetadataHolder().getBoolean(INTERNSHIP_OPEN, false);
1224    }
1225
1226    /**
1227     * Is apprenticeship open
1228     * @return <code>true</code> if apprenticeship is open
1229     */
1230    public boolean isApprenticeshipOpen()
1231    {
1232        return getMetadataHolder().getBoolean(APPRENTICESHIP_OPEN, false);
1233    }
1234
1235    /**
1236     * Get the apprenticeship period description
1237     * @return the apprenticeship period
1238     */
1239    public RichText getApprenticeshipPeriod()
1240    {
1241        try
1242        {
1243            return getMetadataHolder().getRichText(APPRENTICESHIP_PERIOD);
1244        }
1245        catch (UnknownMetadataException e)
1246        {
1247            return null;
1248        }
1249    }
1250    
1251    /**
1252     * Get the available apprenticeship contracts
1253     * @return the apprenticeship contracts
1254     */
1255    public String[] getApprenticeshipContract()
1256    {
1257        return getMetadataHolder().getStringArray(APPRENTICESHIP_CONTRACT, ArrayUtils.EMPTY_STRING_ARRAY);
1258    }
1259
1260    /**
1261     * Get the international education
1262     * @return the international education
1263     */
1264    public String[] getInternationalEducation()
1265    {
1266        return getMetadataHolder().getStringArray(INTERNATIONAL_EDUCATION, ArrayUtils.EMPTY_STRING_ARRAY);
1267    }
1268
1269    /**
1270     * Get the international dimension
1271     * @return the international dimension
1272     */
1273    public RichText getInternationalDimension()
1274    {
1275        try
1276        {
1277            return getMetadataHolder().getRichText(INTERNATIONAL_DIMENSION);
1278        }
1279        catch (UnknownMetadataException e)
1280        {
1281            return null;
1282        }
1283    }
1284    
1285    /**
1286     * Get the geocode latitude and longitude
1287     * @return the geocode
1288     */
1289    public Map<String, Double> getGeocode()
1290    {
1291        try
1292        {
1293            // FIXME should return a GeoCode object
1294            CompositeMetadata geoMetadata = getMetadataHolder().getCompositeMetadata(GEOCODE);
1295            
1296            if (geoMetadata.hasMetadata("longitude") && geoMetadata.hasMetadata("latitude")) 
1297            {
1298                Double longitude = geoMetadata.getDouble("longitude");
1299                Double latitude = geoMetadata.getDouble("latitude");
1300                
1301                Map<String, Double> geocode = new LinkedHashMap<>();
1302                geocode.put("longitude", longitude);
1303                geocode.put("latitude", latitude);
1304                
1305                return geocode;
1306            }
1307        }
1308        catch (UnknownMetadataException e)
1309        {
1310            // Ignore, just return null.
1311        }
1312        
1313        return null;
1314    }
1315    
1316    /**
1317     * Get the other partners
1318     * @return the other partners
1319     */
1320    public RichText getOtherPartners()
1321    {
1322        try
1323        {
1324            return getMetadataHolder().getRichText(OTHER_PARTNERS);
1325        }
1326        catch (UnknownMetadataException e)
1327        {
1328            return null;
1329        }
1330    }
1331
1332    /**
1333     * Get the campus
1334     * @return the campus
1335     */
1336    public String[] getCampus()
1337    {
1338        return getMetadataHolder().getStringArray(CAMPUS, ArrayUtils.EMPTY_STRING_ARRAY);
1339    }
1340
1341    /**
1342     * Get the foreign places
1343     * @return the foreign places
1344     */
1345    public String[] getForeignPlace()
1346    {
1347        return getMetadataHolder().getStringArray(FOREIGN_PLACE, ArrayUtils.EMPTY_STRING_ARRAY);
1348    }
1349    
1350    /**
1351     * Get the inscription
1352     * @return the inscription
1353     */
1354    public RichText getInscription()
1355    {
1356        try
1357        {
1358            return getMetadataHolder().getRichText(INSCRIPTION);
1359        }
1360        catch (UnknownMetadataException e)
1361        {
1362            return null;
1363        }
1364    }
1365    
1366    /**
1367     * Get the further study programs
1368     * @return the further study programs
1369     */
1370    public String[] getFurtherStudyPrograms()
1371    {
1372        return getMetadataHolder().getStringArray(FURTHER_STUDY_PROGRAMS, ArrayUtils.EMPTY_STRING_ARRAY);
1373    }
1374    
1375    // --------------------------------------------------------------------------------------//
1376    // Methods of CourseListContainer
1377    // --------------------------------------------------------------------------------------//
1378    @Override
1379    public List<CourseList> getCourseLists()
1380    {
1381        List<CourseList> children = new ArrayList<>();
1382        
1383        String[] childIds = getMetadataHolder().getStringArray(METADATA_PARENT_PROGRAM_PARTS, ArrayUtils.EMPTY_STRING_ARRAY);
1384        for (String id : childIds)
1385        {
1386            try
1387            {
1388                ProgramPart child = _getFactory()._getResolver().resolveById(id);
1389                if (child instanceof CourseList)
1390                {
1391                    children.add((CourseList) child);
1392                }
1393            }
1394            catch (UnknownAmetysObjectException e)
1395            {
1396                // Nothing
1397            }
1398        }
1399        
1400        return children;
1401    }
1402    
1403    @Override
1404    public boolean containsCourseList(String clId)
1405    {
1406        return ArrayUtils.contains(getMetadataHolder().getStringArray(METADATA_PARENT_PROGRAM_PARTS, ArrayUtils.EMPTY_STRING_ARRAY), clId);
1407    }
1408
1409    @Override
1410    public boolean hasCourseLists()
1411    {
1412        return !getCourseLists().isEmpty();
1413    }
1414    
1415    // --------------------------------------------------------------------------------------//
1416    // CDMfr
1417    // --------------------------------------------------------------------------------------//
1418    @Override
1419    protected String getCDMType()
1420    {
1421        return "PR";
1422    }
1423    
1424    /**
1425     * Returns the surrounding tag name in the CDM-fr representation.
1426     * @return the surrounding tag name
1427     */
1428    public abstract String getCDMTagName();
1429}