001/*
002 *  Copyright 2019 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.lheo;
017
018import java.time.LocalDate;
019import java.time.ZonedDateTime;
020import java.time.format.DateTimeFormatter;
021import java.util.HashMap;
022import java.util.List;
023import java.util.Map;
024import java.util.Objects;
025import java.util.Optional;
026import java.util.Set;
027import java.util.stream.Collectors;
028import java.util.stream.Stream;
029
030import org.apache.avalon.framework.component.Component;
031import org.apache.avalon.framework.service.ServiceException;
032import org.apache.avalon.framework.service.ServiceManager;
033import org.apache.avalon.framework.service.Serviceable;
034import org.apache.cocoon.xml.AttributesImpl;
035import org.apache.cocoon.xml.XMLUtils;
036import org.apache.commons.lang3.StringUtils;
037import org.xml.sax.ContentHandler;
038import org.xml.sax.SAXException;
039
040import org.ametys.cms.data.RichText;
041import org.ametys.cms.data.RichTextHelper;
042import org.ametys.cms.repository.Content;
043import org.ametys.core.util.DateUtils;
044import org.ametys.odf.orgunit.OrgUnit;
045import org.ametys.odf.person.Person;
046import org.ametys.odf.program.AbstractProgram;
047import org.ametys.plugins.repository.AmetysObjectResolver;
048import org.ametys.runtime.plugin.component.AbstractLogEnabled;
049
050/**
051 * Manager to generate programs to LHEO XML
052 */
053public class ExportToLHEOManager extends AbstractLogEnabled implements Serviceable, Component
054{
055    /** The Avalon role */
056    public static final String ROLE = ExportToLHEOManager.class.getName();
057    
058    /** The ametys object resolver */
059    protected AmetysObjectResolver _resolver;
060    
061    /** The rich text helper */
062    protected RichTextHelper _richTextHelper;
063    
064    /** The LHEO utils */
065    protected LHEOUtils _lheoUtils;
066    
067    @Override
068    public void service(ServiceManager manager) throws ServiceException
069    {
070        _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE);
071        _richTextHelper = (RichTextHelper) manager.lookup(RichTextHelper.ROLE);
072        _lheoUtils = (LHEOUtils) manager.lookup(LHEOUtils.ROLE);
073    }
074    
075    /**
076     * Sax the LHEO xml for the list of programs
077     * @param contentHandler the content handler
078     * @param programs the list of program to sax
079     * @throws SAXException if a saxing exception occurred
080     */
081    public void saxLHEO(ContentHandler contentHandler, List<AbstractProgram> programs) throws SAXException
082    {
083        saxLHEO(contentHandler, programs, new HashMap<>());
084    }
085    
086    /**
087     * Sax the LHEO xml for the list of programs
088     * @param contentHandler the content handler
089     * @param programs the list of program to sax
090     * @param additionalParameters the additional parameters
091     * @throws SAXException if a saxing exception occurred
092     */
093    public void saxLHEO(ContentHandler contentHandler, List<AbstractProgram> programs, Map<String, Object> additionalParameters) throws SAXException
094    {
095        contentHandler.startDocument();
096        contentHandler.startPrefixMapping("", "http://www.lheo.org/2.2");
097            
098        XMLUtils.startElement(contentHandler, "lheo");
099        _saxOffers(contentHandler, programs, additionalParameters);
100        XMLUtils.endElement(contentHandler, "lheo");
101        
102        contentHandler.endDocument();
103    }
104
105    /**
106     * Sax the XML tag &lt;offres&gt;
107     * <br>Can contain the following XML tags:
108     * <br>[1,N] &lt;formation&gt;
109     * <br>[0,N] &lt;extras&gt;
110     * @param contentHandler the content handler
111     * @param programs the list of program to sax
112     * @param additionalParameters the additional parameters
113     * @throws SAXException if a saxing exception occurred
114     */
115    protected void _saxOffers(ContentHandler contentHandler, List<AbstractProgram> programs, Map<String, Object> additionalParameters) throws SAXException
116    {
117        XMLUtils.startElement(contentHandler, "offres");
118        _saxPrograms(contentHandler, programs, additionalParameters);
119        _saxOffersExtras(contentHandler, programs, additionalParameters);
120        XMLUtils.endElement(contentHandler, "offres");
121    }
122
123    /**
124     * Sax for each program a XML tag &lt;formation&gt;
125     * @param contentHandler the content handler
126     * @param programs the list of program to sax
127     * @param additionalParameters the additional parameters
128     * @throws SAXException if a saxing exception occurred
129     */
130    protected void _saxPrograms(ContentHandler contentHandler, List<AbstractProgram> programs, Map<String, Object> additionalParameters) throws SAXException
131    {
132        for (AbstractProgram program : programs)
133        {
134            _saxProgram(contentHandler, program, additionalParameters);
135        }
136    }
137    
138    /**
139     * Sax a XML tag &lt;formation&gt;
140     * <br>Can contain the following XML tags:
141     * <br>[1,1] &lt;domaine-formation&gt;
142     * <br>[1,1] &lt;intitule-formation&gt;
143     * <br>[1,1] &lt;objectif-formation&gt;
144     * <br>[1,1] &lt;resultats-attendus&gt;
145     * <br>[1,1] &lt;contenu-formation&gt;
146     * <br>[1,1] &lt;certifiante&gt;
147     * <br>[1,1] &lt;contact-formation&gt;
148     * <br>[1,1] &lt;parcours-de-formation&gt;
149     * <br>[1,1] &lt;code-niveau-entree&gt;
150     * <br>[0,1] &lt;objectif-general-formation&gt;
151     * <br>[0,5] &lt;certification&gt;
152     * <br>[0,1] &lt;code-niveau-sortie&gt;
153     * <br>[0,1] &lt;url-formation&gt;
154     * <br>[1,N] &lt;action&gt;
155     * <br>[1,1] &lt;organisme-formation-responsable&gt;
156     * <br>[0,1] &lt;identifiant-module&gt;
157     * <br>[0,1] &lt;positionnement&gt;
158     * <br>[0,1] &lt;sous-modules&gt;
159     * <br>[0,1] &lt;modules-prerequis&gt;
160     * <br>[0,N] &lt;extras&gt;
161     * @param contentHandler the content handler
162     * @param program the program to sax
163     * @param additionalParameters the additional parameters
164     * @throws SAXException if a saxing exception occurred
165     */
166    protected void _saxProgram(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
167    {
168        XMLUtils.startElement(contentHandler, "formation", _getProgramAttributes(program, additionalParameters));
169        
170        // <domaine-formation>
171        _saxProgramDomain(contentHandler, program, additionalParameters);
172        
173        // <intitule-formation>
174        _saxProgramTitle(contentHandler, program, additionalParameters);
175        
176        // <objectif-formation>
177        _saxProgramObjectives(contentHandler, program, additionalParameters);
178        
179        // <resultats-attendus>
180        _saxProgramExpectedResults(contentHandler, program, additionalParameters);
181        
182        // <contenu-formation>
183        _saxProgramPresentation(contentHandler, program, additionalParameters);
184        
185        // <certifiante>
186        _saxProgramCertifying(contentHandler, program, additionalParameters);
187        
188        // <contact-formation>
189        _saxProgramContact(contentHandler, program, additionalParameters);
190        
191        // <parcours-de-formation>
192        _saxProgramPath(contentHandler, program, additionalParameters);
193        
194        // <code-niveau-entree>
195        _saxProgramEducationEntryLevel(contentHandler, program, additionalParameters);
196        
197        // <certification>
198        _saxProgramCertification(contentHandler, program, additionalParameters);
199
200        // <action>
201        _saxProgramAction(contentHandler, program, additionalParameters);
202        
203        // <organisme-formation-responsable>
204        _saxProgramResponsibleOrgUnit(contentHandler, program, additionalParameters);
205        
206        // <extras>
207        _saxProgramsExtras(contentHandler, program, additionalParameters);
208        
209        XMLUtils.endElement(contentHandler, "formation");
210    }
211
212    /**
213     * Get attribute for XML tag &lt;formation&gt;
214     * @param program the program
215     * @param additionalParameters the additional parameters
216     * @return the attributes for XML tag &lt;formation&gt;
217     */
218    protected AttributesImpl _getProgramAttributes(AbstractProgram program, Map<String, Object> additionalParameters)
219    {
220        AttributesImpl attributesImpl = new AttributesImpl();
221        attributesImpl.addCDATAAttribute("numero", program.getCode());
222        
223        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
224        
225        ZonedDateTime creationDate = program.getCreationDate();
226        LocalDate creationDateToLocalDate = DateUtils.asLocalDate(creationDate.toInstant());
227        attributesImpl.addCDATAAttribute("datecrea", formatter.format(creationDateToLocalDate));
228        
229        ZonedDateTime lastModificationDate = program.getLastModified();
230        LocalDate lastModificationDateToLocalDate = DateUtils.asLocalDate(lastModificationDate.toInstant());
231        attributesImpl.addCDATAAttribute("datemaj", formatter.format(lastModificationDateToLocalDate));
232        
233        return attributesImpl;
234    }
235    
236    /**
237     * Sax the XML tag &lt;domaine-formation&gt;
238     * <br>Can contain the following XML tags:
239     * <br>[0,5] &lt;cpde-FORMACODE&gt;
240     * <br>[0,3] &lt;code-NSF&gt;
241     * <br>[0,5] &lt;code-ROME&gt;
242     * <br>[0,N] &lt;extras&gt;
243     * @param contentHandler the content handler
244     * @param program the program to sax
245     * @param additionalParameters the additional parameters
246     * @throws SAXException if a saxing exception occurred
247     */
248    protected void _saxProgramDomain(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
249    {
250        XMLUtils.startElement(contentHandler, "domaine-formation");
251        
252        // <code-FORMACODE>
253        _saxFORMACODE(contentHandler, program, additionalParameters);
254        
255        // <code-NSF>
256        _saxNSF(contentHandler, program, additionalParameters);
257        
258        // <code-ROME>
259        _saxROME(contentHandler, program, additionalParameters);
260        
261        // <extras>
262        _saxProgramDomainExtras(contentHandler, program, additionalParameters);
263        
264        XMLUtils.endElement(contentHandler, "domaine-formation");
265    }
266
267    /**
268     * Sax the XML tag &lt;code-FORMACODE&gt;
269     * <br>The code contains exactly 5 characters
270     * <br>The tag must contains the attribute "ref"
271     * @param contentHandler the content handler
272     * @param program the program to sax
273     * @param additionalParameters the additional parameters
274     * @throws SAXException if a saxing exception occurred
275     */
276    protected void _saxFORMACODE(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
277    {
278        AttributesImpl attrs = new AttributesImpl();
279        attrs.addCDATAAttribute("ref", "V12"); // The value is fixed for the default implementation
280        String[] formacodes = program.getFORMACODE();
281        if (formacodes.length > 5)
282        {
283            getLogger().warn("[" + program.getTitle() + " (" + program.getId() + ")] The LHEO format can have only 5 FORMACODE");
284        }
285        
286        for (String code : formacodes)
287        {
288            if (StringUtils.isNotBlank(code))
289            {
290                _lheoUtils.createLHEOElement(contentHandler, program, "code-FORMACODE", attrs, code, 5, 5);
291            }
292        }
293    }
294
295    /**
296     * Sax the XML tag &lt;code-NSF&gt;
297     * <br>The code contains exactly 3 characters
298     * @param contentHandler the content handler
299     * @param program the program to sax
300     * @param additionalParameters the additional parameters
301     * @throws SAXException if a saxing exception occurred
302     */
303    protected void _saxNSF(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
304    {
305        String nsfCodeId = program.getNSFCode();
306        if (StringUtils.isNotBlank(nsfCodeId))
307        {
308            Content nsfCodeContent = _resolver.resolveById(nsfCodeId);
309            String code = nsfCodeContent.getValue("code");
310            if (StringUtils.isNotBlank(code))
311            {
312                _lheoUtils.createLHEOElement(contentHandler, program, "code-NSF", code, 3, 3);
313            }
314        }
315    }
316
317    /**
318     * Sax the XML tag &lt;code-ROME&gt;
319     * <br>The code contains exactly 5 characters
320     * <br>The tag can have the attribute "ref". If not, the default value is "V3"
321     * @param contentHandler the content handler
322     * @param program the program to sax
323     * @param additionalParameters the additional parameters
324     * @throws SAXException if a saxing exception occurred
325     */
326    protected void _saxROME(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
327    {
328        String[] romeCodes = program.getRomeCode();
329        if (romeCodes.length > 5)
330        {
331            getLogger().warn("[" + program.getTitle() + " (" + program.getId() + ")] The LHEO format can have only 5 rome code");
332        }
333        
334        for (String romeCodeId : romeCodes)
335        {
336            Content romeCodeContent = _resolver.resolveById(romeCodeId);
337            String code = romeCodeContent.getValue("code");
338            if (StringUtils.isNotBlank(code))
339            {
340                _lheoUtils.createLHEOElement(contentHandler, program, "code-ROME", code, 5, 5);
341            }
342        }
343    }
344    
345    /**
346     * Sax for program domain the XML tag &lt;extras&gt;
347     * <br>Can contains all not LHEO normalized elements
348     * @param contentHandler the content handler
349     * @param program the program to sax
350     * @param additionalParameters the additional parameters
351     * @throws SAXException if a saxing exception occurred
352     */
353    protected void _saxProgramDomainExtras(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
354    {
355        // No extras in default implementation
356        XMLUtils.createElement(contentHandler, "extras");
357    }
358    
359    /**
360     * Sax the XML tag &lt;intitule-formation&gt;
361     * <br>The value contains between 1 to 255 characters
362     * @param contentHandler the content handler
363     * @param program the program to sax
364     * @param additionalParameters the additional parameters
365     * @throws SAXException if a saxing exception occurred
366     */
367    protected void _saxProgramTitle(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
368    {
369        _lheoUtils.createMandatoryLHEOElement(contentHandler, program, "intitule-formation", program.getTitle(), 1, 255);
370    }
371    
372    /**
373     * Sax the XML tag &lt;objectif-formation&gt;
374     * <br>The value contains between 1 to 3000 characters
375     * @param contentHandler the content handler
376     * @param program the program to sax
377     * @param additionalParameters the additional parameters
378     * @throws SAXException if a saxing exception occurred
379     */
380    protected void _saxProgramObjectives(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
381    {
382        String richText2String = _richText2String(program.getObjectives());
383        _lheoUtils.createMandatoryLHEOElement(contentHandler, program, "objectif-formation", richText2String, 1, 3000);
384    }
385    
386    /**
387     * Sax the XML tag &lt;resultats-attendus&gt;
388     * <br>The value contains between 1 to 3000 characters
389     * @param contentHandler the content handler
390     * @param program the program to sax
391     * @param additionalParameters the additional parameters
392     * @throws SAXException if a saxing exception occurred
393     */
394    protected void _saxProgramExpectedResults(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
395    {
396        String richText2String = _richText2String(program.getExpectedResults());
397        _lheoUtils.createMandatoryLHEOElement(contentHandler, program, "resultats-attendus", richText2String, 1, 3000);
398    }
399    
400    /**
401     * Sax the XML tag &lt;contenu-formation&gt;
402     * <br>The value contains between 1 to 3000 characters
403     * @param contentHandler the content handler
404     * @param program the program to sax
405     * @param additionalParameters the additional parameters
406     * @throws SAXException if a saxing exception occurred
407     */
408    protected void _saxProgramPresentation(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
409    {
410        String richText2String = _richText2String(program.getPresentation());
411        _lheoUtils.createMandatoryLHEOElement(contentHandler, program, "contenu-formation", richText2String, 1, 3000);
412    }
413    
414    /**
415     * Sax the XML tag &lt;certifiante&gt;
416     * <br>0 for false and 1 for true
417     * @param contentHandler the content handler
418     * @param program the program to sax
419     * @param additionalParameters the additional parameters
420     * @throws SAXException if a saxing exception occurred
421     */
422    protected void _saxProgramCertifying(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
423    {
424        boolean certifying = program.isCertifying();
425        String certifyingAsString = certifying ? "1" : "0";
426        _lheoUtils.createLHEOElement(contentHandler, program, "certifiante", certifyingAsString);
427    }
428    
429    /**
430     * Sax the XML tag &lt;contact-formation&gt;
431     * <br>Can contain the following XML tags:
432     * <br>[1,1] &lt;coordonnees&gt;
433     * <br>[0,N] &lt;extras&gt;
434     * @param contentHandler the content handler
435     * @param program the program to sax
436     * @param additionalParameters the additional parameters
437     * @throws SAXException if a saxing exception occurred
438     */
439    protected void _saxProgramContact(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
440    {
441        XMLUtils.startElement(contentHandler, "contact-formation");
442        
443        // <coordonnees>
444        _saxProgramContactCoordonnees(contentHandler, program, additionalParameters);
445        
446        // <extras>
447        _saxProgramContactExtras(contentHandler, program, additionalParameters);
448        
449        XMLUtils.endElement(contentHandler, "contact-formation");
450    }
451
452    /**
453     * Sax for program contact the XML tag &lt;coordonnees&gt;
454     * <br>Can contain the following XML tags:
455     * <br>[0,1] &lt;civilite&gt;
456     * <br>[0,1] &lt;nom&gt;
457     * <br>[0,1] &lt;prenom&gt;
458     * <br>[0,3] &lt;ligne&gt;
459     * <br>[0,1] &lt;adresse&gt;
460     * <br>[0,1] &lt;telfixe&gt;
461     * <br>[0,1] &lt;portable&gt;
462     * <br>[0,1] &lt;fax&gt;
463     * <br>[0,1] &lt;courriel&gt;
464     * <br>[0,1] &lt;web&gt;
465     * <br>[0,N] &lt;extras&gt;
466     * @param contentHandler the content handler
467     * @param program the program to sax
468     * @param additionalParameters the additional parameters
469     * @throws SAXException if a saxing exception occurred
470     */
471    protected void _saxProgramContactCoordonnees(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
472    {
473        XMLUtils.startElement(contentHandler, "coordonnees");
474        
475        Optional<Person> personOpt = _getFirstContact(program);
476        if (personOpt.isPresent())
477        {
478            Person person = personOpt.get();
479            _saxPersonCoordinate(contentHandler, person, additionalParameters);
480        }
481        
482        XMLUtils.endElement(contentHandler, "coordonnees");
483    }
484    
485    /**
486     * Sax for program contact the XML tag &lt;extras&gt;
487     * <br>Can contains all not LHEO normalized elements
488     * @param contentHandler the content handler
489     * @param program the program to sax
490     * @param additionalParameters the additional parameters
491     * @throws SAXException if a saxing exception occurred
492     */
493    protected void _saxProgramContactExtras(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
494    {
495        // No extras in default implementation
496        XMLUtils.createElement(contentHandler, "extras");
497    }
498    
499    /**
500     * Sax the XML tag &lt;parcours-de-formation&gt;
501     * @param contentHandler the content handler
502     * @param program the program to sax
503     * @param additionalParameters the additional parameters
504     * @throws SAXException if a saxing exception occurred
505     */
506    protected void _saxProgramPath(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
507    {
508        // In default implementation, the path value is always 1 ('En groupe')
509        _lheoUtils.createLHEOElement(contentHandler, program, "parcours-de-formation", "1"); 
510    }
511    
512    /**
513     * Sax the XML tag &lt;code-niveau-entree&gt;
514     * @param contentHandler the content handler
515     * @param program the program to sax
516     * @param additionalParameters the additional parameters
517     * @throws SAXException if a saxing exception occurred
518     */
519    protected void _saxProgramEducationEntryLevel(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
520    {
521        String[] educationLevelEntries = program.getEducationLevelEntry();
522        
523        String educationLevelEntryForLHEO = null;
524        if (educationLevelEntries.length > 0)
525        {
526            String educationLevelEntryId = educationLevelEntries[0]; // Take the first
527            Content educationLevelEntryContent = _resolver.resolveById(educationLevelEntryId);
528            
529            String code = educationLevelEntryContent.getValue("code");
530            educationLevelEntryForLHEO = _convertEducationEntryLevel2LHEO(code);
531        }
532        
533        _lheoUtils.createMandatoryLHEOElement(contentHandler, program, "code-niveau-entree", educationLevelEntryForLHEO); 
534    }
535    
536    /**
537     * Convert the Ametys education entry level code to LHEO
538     * @param code the ametys code
539     * @return the LHEO key value
540     */
541    protected String _convertEducationEntryLevel2LHEO(String code)
542    {
543        // No code or "Bac + 1" or "Inconnu" or "Inferieur ou égal au baccalauréat"
544        if (StringUtils.isBlank(code) || "1".equals(code) || "9".equals(code) || "0".equals(code))
545        {
546            // "information non communiquée"
547            return "0";
548        }
549        // "Bac + 2"
550        else if ("2".equals(code))
551        {
552            // "niveau III (BTS, DUT)"
553            return "6";
554        }
555        // "Bac + 3"
556        else if ("3".equals(code))
557        {
558            // "niveau II (licence ou maîtrise universitaire)"
559            return "7";
560        }
561        // "Bac + 4" or "Bac + 5" or "Bac + 6" or "Bac + 7" or "Bac + 8"
562        else
563        {
564            // "niveau I (supérieur à la maîtrise)"
565            return "8";
566        }
567    }
568    
569    /**
570     * Sax the XML tag &lt;certification&gt;
571     * <br>Can contain the following XML tags:
572     * <br>[0,1] &lt;code-RNCP&gt;
573     * <br>[0,1] &lt;code-CERTIFINFO&gt;
574     * <br>[0,N] &lt;extras&gt;
575     * @param contentHandler the content handler
576     * @param program the program to sax
577     * @param additionalParameters the additional parameters
578     * @throws SAXException if a saxing exception occurred
579     */
580    protected void _saxProgramCertification(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
581    {
582        XMLUtils.startElement(contentHandler, "certification");
583        
584        // <code-RNCP>
585        _saxProgramRNCPCode(contentHandler, program, additionalParameters);
586        
587        // <code-CERTIFINFO>
588        _saxProgramCERTIFINFOCode(contentHandler, program, additionalParameters);
589        
590        // <extras>
591        _saxCertificationExtras(contentHandler, program, additionalParameters);
592        
593        XMLUtils.endElement(contentHandler, "certification");
594    }
595    
596    /**
597     * Sax the XML tag &lt;action&gt;
598     * <br>Can contain the following XML tags:
599     * <br>[1,1] &lt;rythme-formation&gt;
600     * <br>[1,10] &lt;code-public-vise&gt;
601     * <br>[0,1] &lt;info-public-vise&gt;
602     * <br>[1,1] &lt;niveau-entree-obligatoire&gt;
603     * <br>[1,1] &lt;modalites-alternance&gt;
604     * <br>[1,1] &lt;modalites-enseignement&gt;
605     * <br>[1,1] &lt;conditions-specifiques&gt;
606     * <br>[1,1] &lt;prise-en-charge-frais-possible&gt;
607     * <br>[1,1] &lt;lieu-de-formation&gt;
608     * <br>[1,1] &lt;modalites-entrees-sorties&gt;
609     * <br>[0,1] &lt;url-action&gt;
610     * <br>[1,N] &lt;session&gt;
611     * <br>[0,1] &lt;adresse-information&gt;
612     * <br>[0,3] &lt;date-information&gt;
613     * <br>[0,1] &lt;restauration&gt;
614     * <br>[0,1] &lt;hebergement&gt;
615     * <br>[0,1] &lt;transport&gt;
616     * <br>[0,1] &lt;acces-handicapes&gt;
617     * <br>[0,1] &lt;langue-formation&gt;
618     * <br>[0,1] &lt;modalites-recrutement&gt;
619     * <br>[0,1] &lt;modalites-pedagogiques&gt;
620     * <br>[0,5] &lt;code-modalite-pedagogique&gt;
621     * <br>[0,1] &lt;frais-restants&gt;
622     * <br>[0,1] &lt;code-perimetre-recrutement&gt;
623     * <br>[0,1] &lt;infos-perimetre-recrutement&gt;
624     * <br>[0,1] &lt;prix-horaire-TTC&gt;
625     * <br>[0,1] &lt;prix-total-TTC&gt;
626     * <br>[0,1] &lt;duree-indicative&gt;
627     * <br>[0,1] &lt;nombre-heures-centre&gt;
628     * <br>[0,1] &lt;nombre-heures-entreprise&gt;
629     * <br>[0,1] &lt;nombre-heures-total&gt;
630     * <br>[0,1] &lt;detail-conditions-prise-en-charge&gt;
631     * <br>[0,1] &lt;conventionnement&gt;
632     * <br>[0,1] &lt;duree-conventionnee&gt;
633     * <br>[0,1] &lt;organisme-formateur&gt;
634     * <br>[0,8] &lt;organisme-financeur&gt;
635     * <br>[0,N] &lt;extras&gt;
636     * @param contentHandler the content handler
637     * @param program the program to sax
638     * @param additionalParameters the additional parameters
639     * @throws SAXException if a saxing exception occurred
640     */
641    protected void _saxProgramAction(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
642    {
643        XMLUtils.startElement(contentHandler, "action", _getActionAttributes(program, additionalParameters));
644        
645        // <rythme-formation>
646        _saxProgramTiming(contentHandler, program, additionalParameters);
647        
648        // <code-public-vise>
649        _saxProgramTargetAudience(contentHandler, program, additionalParameters);
650        
651        // <niveau-entree-obligatoire>
652        _saxProgramMandatoryEntryLevel(contentHandler, program, additionalParameters);
653        
654        // <modalites-alternance>
655        _saxProgramApprenticeshipModalities(contentHandler, program, additionalParameters);
656        
657        // <modalites-enseignement>
658        _saxProrgamDistanceLearning(contentHandler, program, additionalParameters);
659        
660        // <conditions-specifiques>
661        _saxProgramNeededPrerequisite(contentHandler, program, additionalParameters);
662        
663        // <prise-en-charge-frais-possible>
664        _saxProgramCostBearing(contentHandler, program, additionalParameters);
665        
666        // <lieu-de-formation>
667        _saxProgramPlaces(contentHandler, program, additionalParameters);
668        
669        // <modalites-entrees-sorties>
670        _saxProgramEntryExitModalities(contentHandler, program, additionalParameters);
671        
672        // <session>
673        _saxProgramSession(contentHandler, program, additionalParameters);
674        
675        // <langue-formation>
676        _saxProgramEducationLanguage(contentHandler, program, additionalParameters);
677
678        // <modalites-recrutement>
679        _saxProgramAccessCondition(contentHandler, program, additionalParameters);
680        
681        // <code-modalite-pedagogique>
682        _saxProgramEducationalModalities(contentHandler, program, additionalParameters);
683        
684        // <organisme-formateur>
685        _saxProgramOrgUnitFormer(contentHandler, program, additionalParameters);
686        
687        // <organisme-financeur>
688        _saxProgramOrgUnitFunder(contentHandler, program, additionalParameters);
689        
690        // <extras>
691        _saxActionExtras(contentHandler, program, additionalParameters);
692        
693        XMLUtils.endElement(contentHandler, "action");
694    }
695    
696    /**
697     * Get attribute for XML tag &lt;action&gt;
698     * @param program the program
699     * @param additionalParameters the additional parameters
700     * @return the attributes for XML tag &lt;action&gt;
701     */
702    protected AttributesImpl _getActionAttributes(AbstractProgram program, Map<String, Object> additionalParameters)
703    {
704        AttributesImpl attributesImpl = new AttributesImpl();
705        attributesImpl.addCDATAAttribute("numero", program.getCode());
706        
707        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
708        
709        ZonedDateTime creationDate = program.getCreationDate();
710        LocalDate creationDateToLocalDate = DateUtils.asLocalDate(creationDate.toInstant());
711        attributesImpl.addCDATAAttribute("datecrea", formatter.format(creationDateToLocalDate));
712        
713        ZonedDateTime lastModificationDate = program.getLastModified();
714        LocalDate lastModificationDateToLocalDate = DateUtils.asLocalDate(lastModificationDate.toInstant());
715        attributesImpl.addCDATAAttribute("datemaj", formatter.format(lastModificationDateToLocalDate));
716        
717        return attributesImpl;
718    }
719
720    /**
721     * Sax the XML tag &lt;rythme-formation&gt;
722     * <br>The value contains between 1 to 3000 characters
723     * @param contentHandler the content handler
724     * @param program the program to sax
725     * @param additionalParameters the additional parameters
726     * @throws SAXException if a saxing exception occurred
727     */
728    protected void _saxProgramTiming(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
729    {
730        // In default implementation, the program timing value is always "Temps plein"
731        _lheoUtils.createLHEOElement(contentHandler, program, "rythme-formation", "Temps plein");
732    }
733
734    /**
735     * Sax the XML tag &lt;code-public-vise&gt;
736     * <br>The code contains exactly 5 characters
737     * <br>The tag must contains the attribute "ref"
738     * @param contentHandler the content handler
739     * @param program the program to sax
740     * @param additionalParameters the additional parameters
741     * @throws SAXException if a saxing exception occurred
742     */
743    protected void _saxProgramTargetAudience(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
744    {
745        AttributesImpl attrs = new AttributesImpl();
746        attrs.addCDATAAttribute("ref", "V12"); // The value is fixed for the default implementation
747        String[] formacodes = program.getFORMACODE();
748        if (formacodes.length > 10)
749        {
750            getLogger().warn("[" + program.getTitle() + " (" + program.getId() + ")] The LHEO format can have only 10 FORMACODE for XML tag 'code-public-vise'");
751        }
752        if (formacodes.length == 0)
753        {
754            getLogger().warn("[" + program.getTitle() + " (" + program.getId() + ")] The LHEO format must have at least one FORMACODE for XML tag 'code-public-vise'");
755        }
756        
757        for (String code : formacodes)
758        {
759            if (StringUtils.isNotBlank(code))
760            {
761                _lheoUtils.createLHEOElement(contentHandler, program, "code-public-vise", attrs, code, 5, 5);
762            }
763        }
764    }
765
766    /**
767     * Sax the XML tag &lt;niveau-entree-obligatoire&gt;
768     * <br>0 for false and 1 for true
769     * @param contentHandler the content handler
770     * @param program the program to sax
771     * @param additionalParameters the additional parameters
772     * @throws SAXException if a saxing exception occurred
773     */
774    protected void _saxProgramMandatoryEntryLevel(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
775    {
776        boolean mandatoryEntryLevel = program.isMandatoryEntryLevel();
777        String mandatoryEntryLevelAsString = mandatoryEntryLevel ? "1" : "0";
778        _lheoUtils.createLHEOElement(contentHandler, program, "niveau-entree-obligatoire", mandatoryEntryLevelAsString);
779    }
780
781    /**
782     * Sax the XML tag &lt;modalites-alternance&gt;
783     * <br>The value contains between 1 to 3000 characters
784     * @param contentHandler the content handler
785     * @param program the program to sax
786     * @param additionalParameters the additional parameters
787     * @throws SAXException if a saxing exception occurred
788     */
789    protected void _saxProgramApprenticeshipModalities(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
790    {
791        String richText2String = _richText2String(program.getApprenticeshipModalities());
792        _lheoUtils.createMandatoryLHEOElement(contentHandler, program, "modalites-alternance", richText2String, 1, 3000);
793    }
794
795    /**
796     * Sax the XML tag &lt;modalites-enseignement&gt;
797     * @param contentHandler the content handler
798     * @param program the program to sax
799     * @param additionalParameters the additional parameters
800     * @throws SAXException if a saxing exception occurred
801     */
802    protected void _saxProrgamDistanceLearning(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
803    {
804        String distanceLearningId = program.getDistanceLearning();
805        String distanceLearningForLHEO = null;
806        if (StringUtils.isNotBlank(distanceLearningId))
807        {
808            Content distanceLearningContent = _resolver.resolveById(distanceLearningId);
809            
810            String code = distanceLearningContent.getValue("code");
811            distanceLearningForLHEO = _convertDistanceLearning2LHEO(code);
812        }
813        _lheoUtils.createMandatoryLHEOElement(contentHandler, program, "modalites-enseignement", distanceLearningForLHEO);
814    }
815
816    /**
817     * Convert the ametys code in LHEO code
818     * @param code the ametys code
819     * @return the LHEO code
820     */
821    protected String _convertDistanceLearning2LHEO(String code)
822    {
823        // "A distance"
824        if ("distanceLearningModalities_mandatory".equals(code))
825        {
826            // "formation entièrement à distance"
827            return "2";
828        }
829        // "Hybride"
830        else if ("distanceLearningModalities_possible".equals(code))
831        {
832            // "formation mixte"
833            return "1";
834        }
835        // "En présence"
836        else if ("distanceLearningModalities_no".equals(code))
837        {
838            // "formation entièrement présentielle"
839            return "0";
840        }
841        
842        return null;
843    }
844
845    /**
846     * Sax the XML tag &lt;conditions-specifiques&gt;
847     * <br>The value contains between 1 to 3000 characters
848     * @param contentHandler the content handler
849     * @param program the program to sax
850     * @param additionalParameters the additional parameters
851     * @throws SAXException if a saxing exception occurred
852     */
853    protected void _saxProgramNeededPrerequisite(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
854    {
855        String richText2String = _richText2String(program.getNeededPrerequisite());
856        _lheoUtils.createMandatoryLHEOElement(contentHandler, program, "conditions-specifiques", richText2String, 1, 3000);
857    }
858
859    /**
860     * Sax the XML tag &lt;prise-en-charge-frais-possible&gt;
861     * <br>0 for false and 1 for true
862     * @param contentHandler the content handler
863     * @param program the program to sax
864     * @param additionalParameters the additional parameters
865     * @throws SAXException if a saxing exception occurred
866     */
867    protected void _saxProgramCostBearing(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
868    {
869        // In default implementation, the program cost bearing value is always 0 (false)
870        _lheoUtils.createLHEOElement(contentHandler, program, "prise-en-charge-frais-possible", "0");
871    }
872
873    /**
874     * Sax the XML tag &lt;lieu-de-formation&gt;
875     * <br>Can contain the following XML tags:
876     * <br>[1,1] &lt;coordonnees&gt;
877     * <br>[0,N] &lt;extras&gt;
878     * @param contentHandler the content handler
879     * @param program the program to sax
880     * @param additionalParameters the additional parameters
881     * @throws SAXException if a saxing exception occurred
882     */
883    protected void _saxProgramPlaces(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
884    {
885        XMLUtils.startElement(contentHandler, "lieu-de-formation");
886        
887        // <coordonnees>
888        _saxPlacesCoordonnees(contentHandler, program, additionalParameters);
889        
890        // <extras>
891        _saxPlacesExtras(contentHandler, program, additionalParameters);
892        
893        XMLUtils.endElement(contentHandler, "lieu-de-formation");
894    }
895    
896    /**
897     * Sax for places the XML tag &lt;coordonnees&gt;
898     * <br>Can contain the following XML tags:
899     * <br>[0,1] &lt;civilite&gt;
900     * <br>[0,1] &lt;nom&gt;
901     * <br>[0,1] &lt;prenom&gt;
902     * <br>[0,3] &lt;ligne&gt;
903     * <br>[0,1] &lt;adresse&gt;
904     * <br>[0,1] &lt;telfixe&gt;
905     * <br>[0,1] &lt;portable&gt;
906     * <br>[0,1] &lt;fax&gt;
907     * <br>[0,1] &lt;courriel&gt;
908     * <br>[0,1] &lt;web&gt;
909     * <br>[0,N] &lt;extras&gt;
910     * @param contentHandler the content handler
911     * @param program the program to sax
912     * @param additionalParameters the additional parameters
913     * @throws SAXException if a saxing exception occurred
914     */
915    protected void _saxPlacesCoordonnees(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
916    {
917        XMLUtils.startElement(contentHandler, "coordonnees");
918        
919        List<String> places = Stream.of(program.getPlace())
920            .filter(StringUtils::isNotBlank)
921            .map(id -> _getRefContent(id))
922            .filter(Objects::nonNull)
923            .map(c -> c.getTitle())
924            .collect(Collectors.toList());
925        
926        _lheoUtils.createCoordinateLHEOElementsPart1(
927                contentHandler, 
928                program,
929                null, // civilite 
930                null, // nom
931                null, // prenom
932                places // ligne
933        );
934        
935        XMLUtils.endElement(contentHandler, "coordonnees");
936    }
937    
938    /**
939     * Sax for places the XML tag &lt;extras&gt;
940     * <br>Can contains all not LHEO normalized elements
941     * @param contentHandler the content handler
942     * @param program the program to sax
943     * @param additionalParameters the additional parameters
944     * @throws SAXException if a saxing exception occurred
945     */
946    protected void _saxPlacesExtras(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
947    {
948        // No extras in default implementation
949        XMLUtils.createElement(contentHandler, "extras");
950    }
951
952    /**
953     * Sax the XML tag &lt;modalites-entrees-sorties&gt;
954     * @param contentHandler the content handler
955     * @param program the program to sax
956     * @param additionalParameters the additional parameters
957     * @throws SAXException if a saxing exception occurred
958     */
959    protected void _saxProgramEntryExitModalities(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
960    {
961        // In default implementation, the program entry exit modalities value is always 0 ('entrées/sorties à dates fixes')
962        _lheoUtils.createLHEOElement(contentHandler, program, "modalites-entrees-sorties", "1");
963    }
964
965    /**
966     * Sax the XML tag &lt;session&gt;
967     * <br>Can contain the following XML tags:
968     * <br>[1,1] &lt;periode&gt;
969     * <br>[1,1] &lt;adresse-inscription&gt;
970     * <br>[0,1] &lt;modalites-inscription&gt;
971     * <br>[0,1] &lt;periode-inscription&gt;
972     * <br>[0,1] &lt;etat-recrutement&gt;
973     * <br>[0,N] &lt;extras&gt;
974     * @param contentHandler the content handler
975     * @param program the program to sax
976     * @param additionalParameters the additional parameters
977     * @throws SAXException if a saxing exception occurred
978     */
979    protected void _saxProgramSession(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
980    {
981        XMLUtils.startElement(contentHandler, "session", _getSessionAttributes(program, additionalParameters));
982        
983        // <periode>
984        _saxProgramPeriod(contentHandler, program, additionalParameters);
985        
986        // <adresse-inscription>
987        _saxProgramRegistrationAddress(contentHandler, program, additionalParameters);
988        
989        // <periode-inscription>
990        _saxProgramInscriptionPeriod(contentHandler, program, additionalParameters);
991        
992        // <extras>
993        _saxSessionExtras(contentHandler, program, additionalParameters);
994        
995        XMLUtils.endElement(contentHandler, "session");
996    }
997
998    /**
999     * Get attribute for XML tag &lt;session&gt;
1000     * @param program the program
1001     * @param additionalParameters the additional parameters
1002     * @return the attributes for XML tag &lt;session&gt;
1003     */
1004    protected AttributesImpl _getSessionAttributes(AbstractProgram program, Map<String, Object> additionalParameters)
1005    {
1006        AttributesImpl attributesImpl = new AttributesImpl();
1007        attributesImpl.addCDATAAttribute("numero", program.getCode());
1008        
1009        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
1010        
1011        ZonedDateTime creationDate = program.getCreationDate();
1012        LocalDate creationDateToLocalDate = DateUtils.asLocalDate(creationDate.toInstant());
1013        attributesImpl.addCDATAAttribute("datecrea", formatter.format(creationDateToLocalDate));
1014        
1015        ZonedDateTime lastModificationDate = program.getLastModified();
1016        LocalDate lastModificationDateToLocalDate = DateUtils.asLocalDate(lastModificationDate.toInstant());
1017        attributesImpl.addCDATAAttribute("datemaj", formatter.format(lastModificationDateToLocalDate));
1018        
1019        return attributesImpl;
1020    }
1021    
1022    /**
1023     * Sax the XML tag &lt;periode&gt;
1024     * <br>Can contain the following XML tags:
1025     * <br>[1,1] &lt;debut&gt;
1026     * <br>[1,1] &lt;fin&gt;
1027     * <br>[0,N] &lt;extras&gt;
1028     * @param contentHandler the content handler
1029     * @param program the program to sax
1030     * @param additionalParameters the additional parameters
1031     * @throws SAXException if a saxing exception occurred
1032     */
1033    protected void _saxProgramPeriod(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1034    {
1035        XMLUtils.startElement(contentHandler, "periode");
1036        
1037        _lheoUtils.createPeriodLHEOElments(contentHandler, program, program.getTeachingStart(), program.getTeachingEnd());
1038        
1039        // <extras>
1040        _saxPeriodExtras(contentHandler, program, additionalParameters);
1041        
1042        XMLUtils.endElement(contentHandler, "periode");
1043    }
1044
1045    /**
1046     * Sax the XML tag &lt;periode-inscription&gt;
1047     * <br>Can contain the following XML tags:
1048     * <br>[1,1] &lt;periode&gt;
1049     * @param contentHandler the content handler
1050     * @param program the program to sax
1051     * @param additionalParameters the additional parameters
1052     * @throws SAXException if a saxing exception occurred
1053     */
1054    protected void _saxProgramInscriptionPeriod(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1055    {
1056        XMLUtils.startElement(contentHandler, "periode-inscription");
1057        
1058        XMLUtils.startElement(contentHandler, "periode");
1059        
1060        _lheoUtils.createPeriodLHEOElments(contentHandler, program, program.getRegistrationStart(), program.getRegistrationDeadline());
1061        
1062        _saxProgramInscriptionPeriodExtras(contentHandler, program, additionalParameters);
1063        
1064        XMLUtils.endElement(contentHandler, "periode");
1065        
1066        XMLUtils.endElement(contentHandler, "periode-inscription");
1067    }
1068    
1069    /**
1070     * Sax for period the XML tag &lt;extras&gt;
1071     * <br>Can contains all not LHEO normalized elements
1072     * @param contentHandler the content handler
1073     * @param program the program to sax
1074     * @param additionalParameters the additional parameters
1075     * @throws SAXException if a saxing exception occurred
1076     */
1077    protected void _saxPeriodExtras(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1078    {
1079        // No extras in default implementation
1080        XMLUtils.createElement(contentHandler, "extras");
1081    }
1082    
1083    /**
1084     * Sax for inscription period the XML tag &lt;extras&gt;
1085     * <br>Can contains all not LHEO normalized elements
1086     * @param contentHandler the content handler
1087     * @param program the program to sax
1088     * @param additionalParameters the additional parameters
1089     * @throws SAXException if a saxing exception occurred
1090     */
1091    protected void _saxProgramInscriptionPeriodExtras(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1092    {
1093        // No extras in default implementation
1094        XMLUtils.createElement(contentHandler, "extras");
1095    }
1096
1097    /**
1098     * Sax the XML tag &lt;adresse-inscription&gt;
1099     * <br>Can contain the following XML tags:
1100     * <br>[1,1] &lt;adresse&gt;
1101     * <br>[0,N] &lt;extras&gt;
1102     * @param contentHandler the content handler
1103     * @param program the program to sax
1104     * @param additionalParameters the additional parameters
1105     * @throws SAXException if a saxing exception occurred
1106     */
1107    protected void _saxProgramRegistrationAddress(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1108    {
1109        XMLUtils.startElement(contentHandler, "adresse-inscription");
1110        
1111        // <adresse>
1112        _saxRegistrationAddress(contentHandler, program, additionalParameters);
1113        
1114        // <extras>
1115        _saxRegistrationAddressExtras(contentHandler, program, additionalParameters);
1116        
1117        XMLUtils.endElement(contentHandler, "adresse-inscription");
1118    }
1119
1120    /**
1121     * Sax the XML tag &lt;adresse&gt;
1122     * <br>Can contain the following XML tags:
1123     * <br>[1,4] &lt;ligne&gt;
1124     * <br>[1,1] &lt;codepostal&gt;
1125     * <br>[1,1] &lt;ville&gt;
1126     * <br>[0,1] &lt;departement&gt;
1127     * <br>[0,1] &lt;code-INSEE-commune&gt;
1128     * <br>[0,1] &lt;code-INSEE-canton&gt;
1129     * <br>[0,1] &lt;region&gt;
1130     * <br>[0,1] &lt;pays&gt;
1131     * <br>[0,1] &lt;geolocalisation&gt;
1132     * <br>[0,N] &lt;extras&gt;
1133     * @param contentHandler the content handler
1134     * @param program the program to sax
1135     * @param additionalParameters the additional parameters
1136     * @throws SAXException if a saxing exception occurred
1137     */
1138    protected void _saxRegistrationAddress(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1139    {
1140        Optional<Person> contactOpt = _getFirstContact(program);
1141        XMLUtils.startElement(contentHandler, "adresse");
1142        
1143        Content contentSaxed = contactOpt.isPresent() ? contactOpt.get() : program;
1144        String address = contactOpt.isPresent() ? contactOpt.get().getValue("address") : null;
1145        String zipCode = contactOpt.isPresent() ? contactOpt.get().getValue("zipCode") : null;
1146        String town = contactOpt.isPresent() ? contactOpt.get().getValue("town") : null;
1147        
1148        _lheoUtils.createAddressLHEOElements(
1149                contentHandler,
1150                contentSaxed,
1151                address, // ligne
1152                zipCode, // codepostal
1153                town, // ville
1154                null, // departement
1155                null, // code-INSEE-commune
1156                null, // code-INSEE-canton
1157                null, // region
1158                null, // pays
1159                null, // geolocalisation/latitude
1160                null // geolocalisation/longitude
1161        );
1162        XMLUtils.endElement(contentHandler, "adresse");
1163    }
1164
1165    /**
1166     * Sax for registration address the XML tag &lt;extras&gt;
1167     * <br>Can contains all not LHEO normalized elements
1168     * @param contentHandler the content handler
1169     * @param program the program to sax
1170     * @param additionalParameters the additional parameters
1171     * @throws SAXException if a saxing exception occurred
1172     */
1173    protected void _saxRegistrationAddressExtras(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1174    {
1175        // No extras in default implementation
1176        XMLUtils.createElement(contentHandler, "extras");
1177    }
1178    
1179    /**
1180     * Sax for action the XML tag &lt;extras&gt;
1181     * <br>Can contains all not LHEO normalized elements
1182     * @param contentHandler the content handler
1183     * @param program the program to sax
1184     * @param additionalParameters the additional parameters
1185     * @throws SAXException if a saxing exception occurred
1186     */
1187    protected void _saxSessionExtras(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1188    {
1189        // No extras in default implementation
1190        XMLUtils.createElement(contentHandler, "extras");
1191    }
1192    
1193    /**
1194     * Sax the XML tag &lt;langue-formation&gt;
1195     * <br>The value contains exactly 2 characters
1196     * @param contentHandler the content handler
1197     * @param program the program to sax
1198     * @param additionalParameters the additional parameters
1199     * @throws SAXException if a saxing exception occurred
1200     */
1201    protected void _saxProgramEducationLanguage(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1202    {
1203        Optional<String> languageOpt = Stream.of(program.getEducationLanguage())
1204                .filter(StringUtils::isNotBlank)
1205                .map(id -> _getRefContent(id))
1206                .filter(Objects::nonNull)
1207                .map(c -> (String) c.getValue("code"))
1208                .filter(StringUtils::isNotBlank)
1209                .findFirst();
1210                
1211        _lheoUtils.createLHEOElement(contentHandler, program, "langue-formation", languageOpt.isPresent() ? languageOpt.get() : null, 2, 2);
1212    }
1213    
1214    /**
1215     * Sax the XML tag &lt;modalites-recrutement&gt;
1216     * <br>The value contains between 1 to 3000 characters
1217     * @param contentHandler the content handler
1218     * @param program the program to sax
1219     * @param additionalParameters the additional parameters
1220     * @throws SAXException if a saxing exception occurred
1221     */
1222    protected void _saxProgramAccessCondition(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1223    {
1224        String richText2String = _richText2String(program.getAccessCondition());
1225        _lheoUtils.createLHEOElement(contentHandler, program, "modalites-recrutement", richText2String, 0, 3000);
1226    }
1227    
1228    /**
1229     * Sax the XML tag &lt;code-modalite-pedagogique&gt;
1230     * <br>The code contains exactly 5 characters
1231     * <br>The tag must contains the attribute "ref"
1232     * @param contentHandler the content handler
1233     * @param program the program to sax
1234     * @param additionalParameters the additional parameters
1235     * @throws SAXException if a saxing exception occurred
1236     */
1237    protected void _saxProgramEducationalModalities(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1238    {
1239        // Nothing in default implementation
1240    }
1241    
1242    /**
1243     * Sax the XML tag &lt;organisme-formateur&gt;
1244     * <br>Can contain the following XML tags:
1245     * <br>[1,4] &lt;SIRET-formateur&gt;
1246     * <br>[1,1] &lt;raison-sociale-formateur&gt;
1247     * <br>[1,1] &lt;contact-formateur&gt;
1248     * <br>[0,1] &lt;potentiel&gt;
1249     * <br>[0,1] &lt;code-UAI-formateur&gt;
1250     * <br>[0,1] &lt;reference-certification&gt;
1251     * <br>[0,N] &lt;extras&gt;
1252     * @param contentHandler the content handler
1253     * @param program the program to sax
1254     * @param additionalParameters the additional parameters
1255     * @throws SAXException if a saxing exception occurred
1256     */
1257    protected void _saxProgramOrgUnitFormer(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1258    {
1259        Optional<OrgUnit> orgunit = _getFirstOrgUnit(program);
1260        if (orgunit.isPresent())
1261        {
1262            XMLUtils.startElement(contentHandler, "organisme-formateur");
1263            
1264            // <SIRET-formateur>
1265            _saxProgramOrgUnitFormerSIRETInformation(contentHandler, program, additionalParameters);
1266            
1267            // <raison-sociale-formateur>
1268            _saxProgramOrgUnitFormerSocialReason(contentHandler, program, additionalParameters);
1269            
1270            // <contact-formateur>
1271            _saxProgramOrgUnitFormerContact(contentHandler, program, additionalParameters);
1272            
1273            // <extras>
1274            _saxProgramOrgUnitFormerExtras(contentHandler, program, additionalParameters);
1275            
1276            XMLUtils.endElement(contentHandler, "organisme-formateur");
1277        }
1278    }
1279    
1280    /**
1281     * Sax the XML tag &lt;SIRET-formateur&gt;
1282     * <br>Can contain the following XML tags:
1283     * <br>[1,1] &lt;SIRET&gt;
1284     * <br>[0,N] &lt;extras&gt;
1285     * @param contentHandler the content handler
1286     * @param program the program to sax
1287     * @param additionalParameters the additional parameters
1288     * @throws SAXException if a saxing exception occurred
1289     */
1290    protected void _saxProgramOrgUnitFormerSIRETInformation(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1291    {
1292        XMLUtils.startElement(contentHandler, "SIRET-formateur");
1293        
1294        // <SIRET>
1295        _saxProgramOrgUnitFormerSIRET(contentHandler, program, additionalParameters);
1296        
1297        // <extras>
1298        _saxProgramOrgUnitFormerSIRETExtras(contentHandler, program, additionalParameters);
1299        
1300        XMLUtils.endElement(contentHandler, "SIRET-formateur");
1301    }
1302    
1303    /**
1304     * Sax the XML tag &lt;SIRET&gt;
1305     * <br>The value contains exactly 14 characters
1306     * @param contentHandler the content handler
1307     * @param program the program to sax
1308     * @param additionalParameters the additional parameters
1309     * @throws SAXException if a saxing exception occurred
1310     */
1311    protected void _saxProgramOrgUnitFormerSIRET(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1312    {
1313        Optional<OrgUnit> orgUnit = _getFirstOrgUnit(program);
1314        _lheoUtils.createMandatoryLHEOElement(contentHandler, orgUnit.isPresent() ? orgUnit.get() : program, "SIRET", orgUnit.isPresent() ? orgUnit.get().getSIRET() : null, 14, 14); 
1315    }
1316
1317    /**
1318     * Sax for SIRET information the XML tag &lt;extras&gt;
1319     * <br>Can contains all not LHEO normalized elements
1320     * @param contentHandler the content handler
1321     * @param program the program to sax
1322     * @param additionalParameters the additional parameters
1323     * @throws SAXException if a saxing exception occurred
1324     */
1325    protected void _saxProgramOrgUnitFormerSIRETExtras(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1326    {
1327        // No extras in default implementation
1328        XMLUtils.createElement(contentHandler, "extras");
1329    }
1330    
1331    /**
1332     * Sax the XML tag &lt;raison-sociale-formateur&gt;
1333     * <br>The value contains between 1 to 255 characters
1334     * @param contentHandler the content handler
1335     * @param program the program to sax
1336     * @param additionalParameters the additional parameters
1337     * @throws SAXException if a saxing exception occurred
1338     */
1339    protected void _saxProgramOrgUnitFormerSocialReason(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1340    {
1341        Optional<OrgUnit> orgUnit = _getFirstOrgUnit(program);
1342        _lheoUtils.createMandatoryLHEOElement(contentHandler, orgUnit.isPresent() ? orgUnit.get() : program, "raison-sociale-formateur", orgUnit.isPresent() ? orgUnit.get().getTitle() : null, 1, 255);
1343    }
1344    
1345    /**
1346     * Sax the XML tag &lt;contact-formateur&gt;
1347     * <br>Can contain the following XML tags:
1348     * <br>[1,1] &lt;coordonnees&gt;
1349     * <br>[0,N] &lt;extras&gt;
1350     * @param contentHandler the content handler
1351     * @param program the program to sax
1352     * @param additionalParameters the additional parameters
1353     * @throws SAXException if a saxing exception occurred
1354     */
1355    protected void _saxProgramOrgUnitFormerContact(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1356    {
1357        XMLUtils.startElement(contentHandler, "contact-formateur");
1358        
1359        // <coordonnees>
1360        _saxProgramOrgUnitFormerContactCoordinates(contentHandler, program, additionalParameters);
1361        
1362        // <extras>
1363        _saxProgramOrgUnitFormerContactExtras(contentHandler, program, additionalParameters);
1364        
1365        XMLUtils.endElement(contentHandler, "contact-formateur");
1366    }
1367    
1368    /**
1369     * Sax for contact former orgUnit the XML tag &lt;coordonnees&gt;
1370     * <br>Can contain the following XML tags:
1371     * <br>[0,1] &lt;civilite&gt;
1372     * <br>[0,1] &lt;nom&gt;
1373     * <br>[0,1] &lt;prenom&gt;
1374     * <br>[0,3] &lt;ligne&gt;
1375     * <br>[0,1] &lt;adresse&gt;
1376     * <br>[0,1] &lt;telfixe&gt;
1377     * <br>[0,1] &lt;portable&gt;
1378     * <br>[0,1] &lt;fax&gt;
1379     * <br>[0,1] &lt;courriel&gt;
1380     * <br>[0,1] &lt;web&gt;
1381     * <br>[0,N] &lt;extras&gt;
1382     * @param contentHandler the content handler
1383     * @param program the program to sax
1384     * @param additionalParameters the additional parameters
1385     * @throws SAXException if a saxing exception occurred
1386     */
1387    protected void _saxProgramOrgUnitFormerContactCoordinates(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1388    {
1389        XMLUtils.startElement(contentHandler, "coordonnees");
1390        
1391        Optional<Person> personOpt = _getFirstOrgUnit(program).stream()
1392            .map(OrgUnit::getContacts)
1393            .flatMap(List::stream)
1394            .filter(StringUtils::isNotBlank)
1395            .map(this::_getPerson)
1396            .filter(Objects::nonNull)
1397            .findFirst();
1398            
1399        if (personOpt.isPresent())
1400        {
1401            Person person = personOpt.get();
1402            _saxPersonCoordinate(contentHandler, person, additionalParameters);
1403        }
1404        
1405        XMLUtils.endElement(contentHandler, "coordonnees");
1406    }
1407    
1408    /**
1409     * Sax for contact former orgUnit the XML tag &lt;extras&gt;
1410     * <br>Can contains all not LHEO normalized elements
1411     * @param contentHandler the content handler
1412     * @param program the program to sax
1413     * @param additionalParameters the additional parameters
1414     * @throws SAXException if a saxing exception occurred
1415     */
1416    protected void _saxProgramOrgUnitFormerContactExtras(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1417    {
1418        // No extras in default implementation
1419        XMLUtils.createElement(contentHandler, "extras");
1420    }
1421    
1422    /**
1423     * Sax for former orgUnit the XML tag &lt;extras&gt;
1424     * <br>Can contains all not LHEO normalized elements
1425     * @param contentHandler the content handler
1426     * @param program the program to sax
1427     * @param additionalParameters the additional parameters
1428     * @throws SAXException if a saxing exception occurred
1429     */
1430    protected void _saxProgramOrgUnitFormerExtras(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1431    {
1432        // No extras in default implementation
1433        XMLUtils.createElement(contentHandler, "extras");
1434    }
1435    
1436    /**
1437     * Sax the XML tag &lt;organisme-financeur&gt;
1438     * <br>Can contain the following XML tags:
1439     * <br>[1,1] &lt;code-financeur&gt;
1440     * <br>[0,1] &lt;nb-places-financees&gt;
1441     * <br>[0,N] &lt;extras&gt;
1442     * @param contentHandler the content handler
1443     * @param program the program to sax
1444     * @param additionalParameters the additional parameters
1445     * @throws SAXException if a saxing exception occurred
1446     */
1447    protected void _saxProgramOrgUnitFunder(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1448    {
1449        // Nothing in default implementation
1450    }
1451    
1452    /**
1453     * Sax for action the XML tag &lt;extras&gt;
1454     * <br>Can contains all not LHEO normalized elements
1455     * @param contentHandler the content handler
1456     * @param program the program to sax
1457     * @param additionalParameters the additional parameters
1458     * @throws SAXException if a saxing exception occurred
1459     */
1460    protected void _saxActionExtras(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1461    {
1462        // No extras in default implementation
1463        XMLUtils.createElement(contentHandler, "extras");
1464    }
1465    
1466    /**
1467     * Sax the XML tag &lt;organisme-formation-responsable&gt;
1468     * <br>Can contain the following XML tags:
1469     * <br>[1,1] &lt;numero-activite&gt;
1470     * <br>[1,1] &lt;SIRET-organisme-formation&gt;
1471     * <br>[1,1] &lt;nom-organisme&gt;
1472     * <br>[1,1] &lt;raison-sociale&gt;
1473     * <br>[1,1] &lt;coordonnees-organisme&gt;
1474     * <br>[1,1] &lt;contact-organisme&gt;
1475     * <br>[0,1] &lt;renseignements-specifiques&gt;
1476     * <br>[0,1] &lt;potentiel&gt;
1477     * <br>[0,N] &lt;extras&gt;
1478     * @param contentHandler the content handler
1479     * @param program the program to sax
1480     * @param additionalParameters the additional parameters
1481     * @throws SAXException if a saxing exception occurred
1482     */
1483    protected void _saxProgramResponsibleOrgUnit(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1484    {
1485        XMLUtils.startElement(contentHandler, "organisme-formation-responsable", _getResponsibleOrgUnitAttribut(program, additionalParameters));
1486        
1487        // <numero-activite>
1488        _saxProgramActivityNumber(contentHandler, program, additionalParameters);
1489        
1490        // <SIRET-organisme-formation>
1491        _saxProgramSIRETInformation(contentHandler, program, additionalParameters);
1492        
1493        // <nom-organisme>
1494        _saxProgramOrgUnitName(contentHandler, program, additionalParameters);
1495        
1496        // <raison-sociale>
1497        _saxProgramCorporateName(contentHandler, program, additionalParameters);
1498        
1499        // <coordonnees-organisme>
1500        _saxProgramOrgUnitDetails(contentHandler, program, additionalParameters);
1501        
1502        // <contact-organisme>
1503        _saxProgramContactOrgUnit(contentHandler, program, additionalParameters);
1504        
1505        // <extras>
1506        _saxResponsibleOrgUnitExtras(contentHandler, program, additionalParameters);
1507        
1508        XMLUtils.endElement(contentHandler, "organisme-formation-responsable");
1509    }
1510    
1511    /**
1512     * Get attribute for XML tag &lt;organisme-formation-responsable&gt;
1513     * @param program the program
1514     * @param additionalParameters the additional parameters
1515     * @return the attributes for XML tag &lt;session&gt;
1516     */
1517    protected AttributesImpl _getResponsibleOrgUnitAttribut(AbstractProgram program, Map<String, Object> additionalParameters)
1518    {
1519        return new AttributesImpl();
1520    }
1521
1522    /**
1523     * Sax the XML tag &lt;numero-activite&gt;
1524     * <br>The value contains exactly 11 characters
1525     * @param contentHandler the content handler
1526     * @param program the program to sax
1527     * @param additionalParameters the additional parameters
1528     * @throws SAXException if a saxing exception occurred
1529     */
1530    protected void _saxProgramActivityNumber(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1531    {
1532        Optional<OrgUnit> orgUnit = _getFirstOrgUnit(program);
1533        
1534        // In default implementation, the program activity number value is always '00000000000'
1535        String activityNumber = orgUnit
1536            .map(c -> c.<String>getValue(OrgUnit.ACTIVITY_NUMBER))
1537            .orElseGet(() -> "00000000000");
1538        
1539        _lheoUtils.createMandatoryLHEOElement(contentHandler, orgUnit.isPresent() ? orgUnit.get() : program, "numero-activite", activityNumber, 11, 11);
1540    }
1541
1542    /**
1543     * Sax the XML tag &lt;SIRET-organisme-formation&gt;
1544     * <br>Can contain the following XML tags:
1545     * <br>[1,1] &lt;SIRET&gt;
1546     * <br>[0,N] &lt;extras&gt;
1547     * @param contentHandler the content handler
1548     * @param program the program to sax
1549     * @param additionalParameters the additional parameters
1550     * @throws SAXException if a saxing exception occurred
1551     */
1552    protected void _saxProgramSIRETInformation(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1553    {
1554        XMLUtils.startElement(contentHandler, "SIRET-organisme-formation");
1555        
1556        // <SIRET>
1557        _saxProgramSIRET(contentHandler, program, additionalParameters);
1558        
1559        // <extras>
1560        _saxSIRETInformationExtras(contentHandler, program, additionalParameters);
1561        
1562        XMLUtils.endElement(contentHandler, "SIRET-organisme-formation");
1563    }
1564
1565    /**
1566     * Sax the XML tag &lt;SIRET&gt;
1567     * <br>The value contains exactly 14 characters
1568     * @param contentHandler the content handler
1569     * @param program the program to sax
1570     * @param additionalParameters the additional parameters
1571     * @throws SAXException if a saxing exception occurred
1572     */
1573    protected void _saxProgramSIRET(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1574    {
1575        Optional<OrgUnit> orgUnit = _getFirstOrgUnit(program);
1576        
1577        _lheoUtils.createMandatoryLHEOElement(contentHandler, orgUnit.isPresent() ? orgUnit.get() : program, "SIRET", orgUnit.isPresent() ? orgUnit.get().getSIRET() : null, 14, 14); 
1578    }
1579
1580    /**
1581     * Sax for SIRET information the XML tag &lt;extras&gt;
1582     * <br>Can contains all not LHEO normalized elements
1583     * @param contentHandler the content handler
1584     * @param program the program to sax
1585     * @param additionalParameters the additional parameters
1586     * @throws SAXException if a saxing exception occurred
1587     */
1588    protected void _saxSIRETInformationExtras(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1589    {
1590        // No extras in default implementation
1591        XMLUtils.createElement(contentHandler, "extras");
1592    }
1593    
1594    /**
1595     * Sax the XML tag &lt;nom-organisme&gt;
1596     * <br>The value contains between 1 to 255 characters
1597     * @param contentHandler the content handler
1598     * @param program the program to sax
1599     * @param additionalParameters the additional parameters
1600     * @throws SAXException if a saxing exception occurred
1601     */
1602    protected void _saxProgramOrgUnitName(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1603    {
1604        Optional<OrgUnit> orgUnit = _getFirstOrgUnit(program);
1605        
1606        _lheoUtils.createMandatoryLHEOElement(contentHandler, orgUnit.isPresent() ? orgUnit.get() : program, "nom-organisme", orgUnit.isPresent() ? orgUnit.get().getTitle() : null, 1, 255);
1607    }
1608
1609    /**
1610     * Sax the XML tag &lt;raison-sociale&gt;
1611     * <br>The value contains between 1 to 255 characters
1612     * @param contentHandler the content handler
1613     * @param program the program to sax
1614     * @param additionalParameters the additional parameters
1615     * @throws SAXException if a saxing exception occurred
1616     */
1617    protected void _saxProgramCorporateName(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1618    {
1619        Optional<OrgUnit> orgUnit = _getFirstOrgUnit(program);
1620        _lheoUtils.createMandatoryLHEOElement(contentHandler, orgUnit.isPresent() ? orgUnit.get() : program, "raison-sociale", orgUnit.isPresent() ? orgUnit.get().getTitle() : null, 1, 255);
1621    }
1622
1623    /**
1624     * Sax the XML tag &lt;coordonnees-organisme&gt;
1625     * <br>Can contain the following XML tags:
1626     * <br>[1,1] &lt;coordonnees&gt;
1627     * <br>[0,N] &lt;extras&gt;
1628     * @param contentHandler the content handler
1629     * @param program the program to sax
1630     * @param additionalParameters the additional parameters
1631     * @throws SAXException if a saxing exception occurred
1632     */
1633    protected void _saxProgramOrgUnitDetails(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1634    {
1635        XMLUtils.startElement(contentHandler, "coordonnees-organisme");
1636        
1637        // <coordonnees>
1638        _saxOrgUnitDetailsCoordinates(contentHandler, program, additionalParameters);
1639        
1640        // <extras>
1641        _saxOrgUnitDetailsExtras(contentHandler, program, additionalParameters);
1642        
1643        XMLUtils.endElement(contentHandler, "coordonnees-organisme");
1644    }
1645
1646    /**
1647     * Sax for orgUnit details the XML tag &lt;coordonnees&gt;
1648     * <br>Can contain the following XML tags:
1649     * <br>[0,1] &lt;civilite&gt;
1650     * <br>[0,1] &lt;nom&gt;
1651     * <br>[0,1] &lt;prenom&gt;
1652     * <br>[0,3] &lt;ligne&gt;
1653     * <br>[0,1] &lt;adresse&gt;
1654     * <br>[0,1] &lt;telfixe&gt;
1655     * <br>[0,1] &lt;portable&gt;
1656     * <br>[0,1] &lt;fax&gt;
1657     * <br>[0,1] &lt;courriel&gt;
1658     * <br>[0,1] &lt;web&gt;
1659     * <br>[0,N] &lt;extras&gt;
1660     * @param contentHandler the content handler
1661     * @param program the program to sax
1662     * @param additionalParameters the additional parameters
1663     * @throws SAXException if a saxing exception occurred
1664     */
1665    protected void _saxOrgUnitDetailsCoordinates(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1666    {
1667        XMLUtils.startElement(contentHandler, "coordonnees");
1668        
1669        Optional<Person> personOpt = _getFirstContactFromOrgUnits(program);
1670        if (personOpt.isPresent())
1671        {
1672            Person person = personOpt.get();
1673            _saxPersonCoordinate(contentHandler, person, additionalParameters);
1674        }
1675        
1676        XMLUtils.endElement(contentHandler, "coordonnees");
1677    }
1678    
1679    /**
1680     * Sax for orgUnit details the XML tag &lt;extras&gt;
1681     * <br>Can contains all not LHEO normalized elements
1682     * @param contentHandler the content handler
1683     * @param program the program to sax
1684     * @param additionalParameters the additional parameters
1685     * @throws SAXException if a saxing exception occurred
1686     */
1687    protected void _saxOrgUnitDetailsExtras(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1688    {
1689        // No extras in default implementation
1690        XMLUtils.createElement(contentHandler, "extras");
1691    }
1692    
1693    /**
1694     * Sax the XML tag &lt;contact-organisme&gt;
1695     * <br>Can contain the following XML tags:
1696     * <br>[1,1] &lt;coordonnees&gt;
1697     * <br>[0,N] &lt;extras&gt;
1698     * @param contentHandler the content handler
1699     * @param program the program to sax
1700     * @param additionalParameters the additional parameters
1701     * @throws SAXException if a saxing exception occurred
1702     */
1703    protected void _saxProgramContactOrgUnit(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1704    {
1705        XMLUtils.startElement(contentHandler, "contact-organisme");
1706        
1707        // <coordonnees>
1708        _saxContactOrgUnitCoordinates(contentHandler, program, additionalParameters);
1709        
1710        // <extras>
1711        _saxContactOrgUnitExtras(contentHandler, program, additionalParameters);
1712        
1713        XMLUtils.endElement(contentHandler, "contact-organisme");
1714    }
1715    
1716    /**
1717     * Sax for contact orgUnit the XML tag &lt;coordonnees&gt;
1718     * <br>Can contain the following XML tags:
1719     * <br>[0,1] &lt;civilite&gt;
1720     * <br>[0,1] &lt;nom&gt;
1721     * <br>[0,1] &lt;prenom&gt;
1722     * <br>[0,3] &lt;ligne&gt;
1723     * <br>[0,1] &lt;adresse&gt;
1724     * <br>[0,1] &lt;telfixe&gt;
1725     * <br>[0,1] &lt;portable&gt;
1726     * <br>[0,1] &lt;fax&gt;
1727     * <br>[0,1] &lt;courriel&gt;
1728     * <br>[0,1] &lt;web&gt;
1729     * <br>[0,N] &lt;extras&gt;
1730     * @param contentHandler the content handler
1731     * @param program the program to sax
1732     * @param additionalParameters the additional parameters
1733     * @throws SAXException if a saxing exception occurred
1734     */
1735    protected void _saxContactOrgUnitCoordinates(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1736    {
1737        XMLUtils.startElement(contentHandler, "coordonnees");
1738        
1739        Optional<Person> personOpt = _getFirstContact(program);
1740        if (personOpt.isPresent())
1741        {
1742            Person person = personOpt.get();
1743            _saxPersonCoordinate(contentHandler, person, additionalParameters);
1744        }
1745        
1746        XMLUtils.endElement(contentHandler, "coordonnees");
1747    }
1748    
1749    /**
1750     * Sax for contact orgUnit the XML tag &lt;extras&gt;
1751     * <br>Can contains all not LHEO normalized elements
1752     * @param contentHandler the content handler
1753     * @param program the program to sax
1754     * @param additionalParameters the additional parameters
1755     * @throws SAXException if a saxing exception occurred
1756     */
1757    protected void _saxContactOrgUnitExtras(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1758    {
1759        // No extras in default implementation
1760        XMLUtils.createElement(contentHandler, "extras");
1761    }
1762    
1763    /**
1764     * Sax for responsible orgUnit the XML tag &lt;extras&gt;
1765     * <br>Can contains all not LHEO normalized elements
1766     * @param contentHandler the content handler
1767     * @param program the program to sax
1768     * @param additionalParameters the additional parameters
1769     * @throws SAXException if a saxing exception occurred
1770     */
1771    protected void _saxResponsibleOrgUnitExtras(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1772    {
1773        // No extras in default implementation
1774        XMLUtils.createElement(contentHandler, "extras");
1775    }
1776    
1777    /**
1778     * Sax the XML tag &lt;code-RNCP&gt;
1779     * <br>The value contains between 1 and 6 characters
1780     * @param contentHandler the content handler
1781     * @param program the program to sax
1782     * @param additionalParameters the additional parameters
1783     * @throws SAXException if a saxing exception occurred
1784     */
1785    protected void _saxProgramRNCPCode(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1786    {
1787        Optional<String> rncpCode = Stream.of(program.getRncpCode())
1788                .filter(StringUtils::isNotBlank)
1789                .findFirst();
1790        
1791        _lheoUtils.createLHEOElement(contentHandler, program, "code-RNCP", rncpCode.isPresent() ? rncpCode.get() : null, 1, 6);
1792    }
1793    
1794    /**
1795     * Sax the XML tag &lt;code-CERTIFINFO&gt;
1796     * <br>The value contains between 1 and 6 characters
1797     * @param contentHandler the content handler
1798     * @param program the program to sax
1799     * @param additionalParameters the additional parameters
1800     * @throws SAXException if a saxing exception occurred
1801     */
1802    protected void _saxProgramCERTIFINFOCode(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1803    {
1804        // No CERTIFINFO code in default implementation
1805    }
1806    
1807    /**
1808     * Sax for certification the XML tag &lt;extras&gt;
1809     * <br>Can contains all not LHEO normalized elements
1810     * @param contentHandler the content handler
1811     * @param program the program to sax
1812     * @param additionalParameters the additional parameters
1813     * @throws SAXException if a saxing exception occurred
1814     */
1815    protected void _saxCertificationExtras(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1816    {
1817        // No extras in default implementation
1818        XMLUtils.createElement(contentHandler, "extras");
1819    }
1820    
1821    /**
1822     * Sax for programs the XML tag &lt;extras&gt;
1823     * <br>Can contains all not LHEO normalized elements
1824     * @param contentHandler the content handler
1825     * @param program the program to sax
1826     * @param additionalParameters the additional parameters
1827     * @throws SAXException if a saxing exception occurred
1828     */
1829    protected void _saxProgramsExtras(ContentHandler contentHandler, AbstractProgram program, Map<String, Object> additionalParameters) throws SAXException
1830    {
1831        // No extras in default implementation
1832        XMLUtils.createElement(contentHandler, "extras");
1833    }
1834    
1835    /**
1836     * Sax for offers the XML tag &lt;extras&gt;
1837     * <br>Can contains all not LHEO normalized elements
1838     * @param contentHandler the content handler
1839     * @param programs the list of program to sax
1840     * @param additionalParameters the additional parameters
1841     * @throws SAXException if a saxing exception occurred
1842     */
1843    protected void _saxOffersExtras(ContentHandler contentHandler, List<AbstractProgram> programs, Map<String, Object> additionalParameters) throws SAXException
1844    {
1845        // No extras in default implementation
1846        XMLUtils.createElement(contentHandler, "extras");
1847    }
1848    
1849    /**
1850     * Sax LHEO elements of a ametys person for the XML tag &gt;coordonnees&lt;
1851     * @param contentHandler the content handler
1852     * @param contact the person contact
1853     * @param additionalParameters the additional parameters
1854     * @throws SAXException if a saxing exception occurred
1855     */
1856    protected void _saxPersonCoordinate(ContentHandler contentHandler, Person contact, Map<String, Object> additionalParameters) throws SAXException
1857    {
1858        _lheoUtils.createCoordinateLHEOElementsPart1(
1859                contentHandler,
1860                contact,
1861                contact.getValue("personTitle"), // civilite 
1862                contact.getValue("lastName"), // nom
1863                contact.getValue("givenName"), // prenom
1864                (String) contact.getValue("address") // ligne
1865        );
1866        
1867        XMLUtils.startElement(contentHandler, "adresse");
1868        _lheoUtils.createAddressLHEOElements(
1869                contentHandler,
1870                contact,
1871                (String) contact.getValue("address"), // ligne
1872                contact.getValue("zipCode"), // codepostal
1873                contact.getValue("town"), // ville
1874                null, // departement
1875                null, // code-INSEE-commune
1876                null, // code-INSEE-canton
1877                null, // region
1878                null, // pays
1879                null, // geolocalisation/latitude
1880                null // geolocalisation/longitude
1881        );
1882        XMLUtils.endElement(contentHandler, "adresse");
1883        
1884        _lheoUtils.createCoordinateLHEOElementsPart2(
1885                contentHandler,
1886                contact,
1887                contact.getValue("phone"), // telfix
1888                null, // portable
1889                contact.getValue("fax"), //fax
1890                contact.getValue("mail"), // courriel
1891                contact.getValue("webLinkUrl") // web
1892        );
1893    }
1894    
1895    /**
1896     * Get ref content from id
1897     * @param id the ref content id
1898     * @return the ref content. Null if no exist
1899     */
1900    protected Content _getRefContent(String id)
1901    {
1902        try
1903        {
1904            return _resolver.resolveById(id);
1905        }
1906        catch (Exception e) 
1907        {
1908            getLogger().warn("Can't find person with id " + id, e);
1909            return null;
1910        }
1911    }
1912    
1913    /**
1914     * Get first contact from abstract program
1915     * @param program the abstract program
1916     * @return the first contact if exist
1917     */
1918    protected Optional<Person> _getFirstContact(AbstractProgram program)
1919    {
1920        Set<String> contacts = program.getContacts();
1921        return contacts
1922                .stream()
1923                .filter(StringUtils::isNotBlank)
1924                .map(id -> _getPerson(id))
1925                .filter(Objects::nonNull)
1926                .findFirst();
1927    }
1928    
1929    /**
1930     * Get first contact from orgunits of the abstract program
1931     * @param program the abstract program
1932     * @return the first contact if exist
1933     */
1934    protected Optional<Person> _getFirstContactFromOrgUnits(AbstractProgram program)
1935    {
1936        List<String> orgUnits = program.getOrgUnits();
1937        return orgUnits
1938            .stream()
1939            .filter(StringUtils::isNotBlank)
1940            .map(id -> _getOrgUnit(id))
1941            .filter(Objects::nonNull)
1942            .map(OrgUnit::getContacts)
1943            .flatMap(List::stream)
1944            .filter(StringUtils::isNotBlank)
1945            .map(id -> _getPerson(id))
1946            .filter(Objects::nonNull)
1947            .findFirst();
1948    }
1949    
1950    /**
1951     * Get first orgunit of the abstract program
1952     * @param program the abstract program
1953     * @return the first orgunit
1954     */
1955    protected Optional<OrgUnit> _getFirstOrgUnit(AbstractProgram program)
1956    {
1957        List<String> orgUnits = program.getOrgUnits();
1958        return orgUnits
1959                .stream()
1960                .filter(StringUtils::isNotBlank)
1961                .map(id -> _getOrgUnit(id))
1962                .filter(Objects::nonNull)
1963                .findFirst();
1964    }
1965    
1966    /**
1967     * Get person from id
1968     * @param id the person id
1969     * @return the person content. Null if no exist
1970     */
1971    protected Person _getPerson(String id)
1972    {
1973        try
1974        {
1975            return _resolver.resolveById(id);
1976        }
1977        catch (Exception e) 
1978        {
1979            getLogger().warn("Can't find person with id " + id, e);
1980            return null;
1981        }
1982    }
1983    
1984    /**
1985     * Get orgUnit from id
1986     * @param id the orgUnit id
1987     * @return the orgUnit content. Null if no exist
1988     */
1989    protected OrgUnit _getOrgUnit(String id)
1990    {
1991        try
1992        {
1993            return _resolver.resolveById(id);
1994        }
1995        catch (Exception e) 
1996        {
1997            getLogger().warn("Can't find orgUnit with id " + id, e);
1998            return null;
1999        }
2000    }
2001    
2002    /**
2003     * Convert rich-text to string for LHEO
2004     * @param richText the rich-text
2005     * @return the transformed rich-text
2006     */
2007    protected String _richText2String(RichText richText)
2008    {
2009        return richText != null ? _richTextHelper.richTextToString(richText) : null;
2010    }
2011}