001/*
002 *  Copyright 2014 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.cdmfr;
017
018import java.text.SimpleDateFormat;
019import java.util.Date;
020import java.util.HashMap;
021import java.util.HashSet;
022import java.util.List;
023import java.util.Locale;
024import java.util.Map;
025import java.util.Set;
026
027import org.apache.avalon.framework.component.Component;
028import org.apache.avalon.framework.context.Context;
029import org.apache.avalon.framework.context.ContextException;
030import org.apache.avalon.framework.context.Contextualizable;
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.components.ContextHelper;
035import org.apache.cocoon.environment.Request;
036import org.apache.cocoon.xml.AttributesImpl;
037import org.apache.cocoon.xml.XMLUtils;
038import org.apache.commons.collections4.MapUtils;
039import org.apache.commons.lang3.ArrayUtils;
040import org.apache.commons.lang3.StringUtils;
041import org.apache.excalibur.source.SourceResolver;
042import org.xml.sax.ContentHandler;
043import org.xml.sax.SAXException;
044
045import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
046import org.ametys.cms.contenttype.ContentTypesHelper;
047import org.ametys.cms.contenttype.MetadataDefinition;
048import org.ametys.cms.contenttype.MetadataType;
049import org.ametys.cms.repository.Content;
050import org.ametys.odf.course.Course;
051import org.ametys.odf.course.LOMSheet;
052import org.ametys.odf.courselist.CourseList;
053import org.ametys.odf.courselist.CourseList.ChoiceType;
054import org.ametys.odf.coursepart.CoursePart;
055import org.ametys.odf.enumeration.OdfReferenceTableEntry;
056import org.ametys.odf.enumeration.OdfReferenceTableHelper;
057import org.ametys.odf.orgunit.OrgUnit;
058import org.ametys.odf.orgunit.RootOrgUnitProvider;
059import org.ametys.odf.person.ContactData;
060import org.ametys.odf.person.Person;
061import org.ametys.odf.program.AbstractProgram;
062import org.ametys.odf.program.Container;
063import org.ametys.odf.program.Program;
064import org.ametys.odf.program.ProgramFactory;
065import org.ametys.odf.program.ProgramPart;
066import org.ametys.odf.program.TraversableProgramPart;
067import org.ametys.odf.program.WebsiteLink;
068import org.ametys.plugins.repository.AmetysObject;
069import org.ametys.plugins.repository.AmetysObjectResolver;
070import org.ametys.plugins.repository.UnknownAmetysObjectException;
071import org.ametys.plugins.repository.metadata.RichText;
072import org.ametys.runtime.config.Config;
073
074/**
075 * Component able to generate CDM-fr for a {@link CDMEntity}.
076 */
077public class ExportCDMfrManager implements Component, Serviceable, Contextualizable
078{
079    /** The Avalon role */
080    public static final String ROLE = ExportCDMfrManager.class.getName();
081    
082    private static final String[] __FIXED_DOMAIN_NAMES = new String[]{"ALL", "DEG", "SHS", "STS", "STA"};
083    
084    /** The Ametys object resolver */
085    protected AmetysObjectResolver _resolver;
086    /** The source resolver */
087    protected SourceResolver _sourceResolver;
088    /** The ODF enumeration helper */
089    protected OdfReferenceTableHelper _refTableHelper;
090    /** The root orgunit provider */
091    protected RootOrgUnitProvider _rootOrgUnitProvider;
092    /** The avalon context */
093    protected Context _context;
094    /** The CDMfr extension point */
095    protected CDMfrExtensionPoint _cdmFrExtensionPoint;
096    /** The content type extension point */
097    protected ContentTypeExtensionPoint _cTypeEP;
098    /** Helper for content types */
099    protected ContentTypesHelper _cTypeHelper;
100
101    
102    @Override
103    public void service(ServiceManager smanager) throws ServiceException
104    {
105        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
106        _sourceResolver = (SourceResolver) smanager.lookup(SourceResolver.ROLE);
107        _refTableHelper = (OdfReferenceTableHelper) smanager.lookup(OdfReferenceTableHelper.ROLE);
108        _rootOrgUnitProvider = (RootOrgUnitProvider) smanager.lookup(RootOrgUnitProvider.ROLE);
109        _cdmFrExtensionPoint = (CDMfrExtensionPoint) smanager.lookup(CDMfrExtensionPoint.ROLE);
110        _cTypeEP = (ContentTypeExtensionPoint) smanager.lookup(ContentTypeExtensionPoint.ROLE);
111        _cTypeHelper = (ContentTypesHelper) smanager.lookup(ContentTypesHelper.ROLE);
112    }
113    
114    @Override
115    public void contextualize(Context context) throws ContextException
116    {
117        _context = context;
118    }
119    
120    /**
121     * Entry point to generate the CDM for a program
122     * @param contentHandler The content handler to sax into
123     * @param program The program
124     * @throws SAXException if failed to generate CDM
125     */
126    public void generateCDM(ContentHandler contentHandler, Program program) throws SAXException
127    {
128        contentHandler.startDocument();
129        contentHandler.startPrefixMapping("", "http://cdm-fr.fr/2013/CDM");
130        contentHandler.startPrefixMapping("xsi", "http://www.w3.org/2001/XMLSchema-instance");
131        contentHandler.startPrefixMapping("cdmfr", "http://cdm-fr.fr/2013/CDM-frSchema");
132        contentHandler.startPrefixMapping("xhtml", "http://www.w3.org/1999/xhtml");
133        contentHandler.startPrefixMapping("ametys-cdm", "http://www.ametys.org/cdm/1.0");
134            
135        AttributesImpl attrs = new AttributesImpl();
136        attrs.addCDATAAttribute("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation", "xsi:schemaLocation", "http://cdm-fr.fr/2013/CDM http://cdm-fr.fr/2013/schemas/CDMFR.xsd");
137        attrs.addCDATAAttribute("profile", "CDM-fr");
138        attrs.addCDATAAttribute("language", program.getLanguage());
139        XMLUtils.startElement(contentHandler, "CDM", attrs);
140        
141        saxHabilitation(contentHandler, program);
142        
143        XMLUtils.startElement(contentHandler, "properties");
144        XMLUtils.createElement(contentHandler, "datasource", "Ametys v4.2");
145        attrs = new AttributesImpl();
146        attrs.addCDATAAttribute("date", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
147        XMLUtils.createElement(contentHandler, "datetime", attrs);
148        XMLUtils.endElement(contentHandler, "properties");
149        
150        program2CDM(contentHandler, program);
151        
152        XMLUtils.endElement(contentHandler, "CDM");
153        
154        contentHandler.endDocument();
155    }
156    
157    /**
158     * SAX CDMfr events for program's habilitation
159     * @param contentHandler the content handler to sax into
160     * @param program the program
161     * @throws SAXException if an error occurred
162     */
163    public void saxHabilitation(ContentHandler contentHandler, Program program) throws SAXException
164    {
165        /*
166         * <cdmfr:habilitation>
167         *      <cdmfr:habiliId>ID</cdmfr:habiliId>
168         *      <cdmfr:cohabilitation>
169         *          <cdmfr:exists>true|false</cdmfr:exists> // Habilité ou non
170         *          <cdmfr:listOfOrgUnit>
171         *              <cdmfr:habOrgUnit>
172         *                  <cdmfr:refOrgUnit> // Etablisssement cohabilité
173         *                  <cdmfr:domainName> 
174         *                      <cdmfr:fixedDomain> // Domaine concerné par cette habilitation
175         *                  </cdmfr:domainName>
176         *              </cdmfr:habOrgUnit>
177         *          </cdmfr:listOfOrgUnit>
178         *      </cdmfr:cohabilitation>
179         *      <cdmfr:field>
180         *          <fieldname>
181         *              <free>MENTION</free>
182         *          </fieldname>
183         *          <speciality>
184         *              <specialityId/>
185         *              <specialityName/>
186         *          </speciality>
187         *      </cdmfr:field>
188         *      <cdmfr:partnership>
189         *          <cdmfr:training>
190         *              <cdmfr:trainingStrategy /> // Stages
191         *          </cdmfr:training>
192         *      </cdmfr:partnership>
193         * </cdmfr:habilitation>
194         * 
195         */
196        Request request = ContextHelper.getRequest(_context);
197        Content currentContent = (Content) request.getAttribute(Content.class.getName());
198        
199        try
200        {
201            request.setAttribute(Content.class.getName(), program);
202            
203            AttributesImpl attrs = new AttributesImpl();
204            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID, "");
205            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_DIPLOMA_TYPE, getDiplomaType(program));
206            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_HABILITATION);
207            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_HABILITATION_ID, program.getCDMId());
208            
209            _saxCohabilitation(contentHandler, program);
210            _saxHabilitationJointOrgUnit(contentHandler, program);
211            _saxHabilitationDiploma(contentHandler, program);
212            _saxHabilitationPartnerShip(contentHandler, program);
213
214            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_HABILITATION);
215        }
216        finally
217        {
218            request.setAttribute(Content.class.getName(), currentContent);
219        }
220    }
221    
222    /**
223     * Sax habilitation informations of the diploma.
224     * @param contentHandler The content handler
225     * @param program The program to sax
226     * @throws SAXException if an error occurs
227     */
228    protected void _saxHabilitationDiploma(ContentHandler contentHandler, Program program) throws SAXException
229    {
230        String speciality = program.getSpeciality();
231        String mention = program.getMention();
232        
233        if (StringUtils.isNotEmpty(mention) || StringUtils.isNotEmpty(speciality))
234        {
235            // Formation LMD : Mention - Speciality
236            /*
237             * <cdmfr:field>
238             *      <cdmfr:fieldName>
239             *          <cdmfr:controlled fieldNameCode="code_mention">
240             *              <cdmfr:registeredName>Libelle mention</cdmfr:registeredName>
241             *          </cdmfr:controlled>
242             *      </cdmfr:fieldName>
243             *      <cdmfr:speciality>
244             *          <cdmfr:specialityName/>
245             *          <cdmfr:specialityId/>
246             *          <cdmfr:refProgram/>
247             *      </cdmfr:speciality>
248             * </cdmfr:field>
249             */
250            
251            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "field");
252            _saxHabilitationMention(contentHandler, program, mention);
253            _saxHabilitationSpeciality(contentHandler, program, speciality);
254            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "field");
255        }
256        else if (Config.getInstance().getValue("odf.programs.degree.licensepro").equals(program.getDegree()))
257        {
258            _saxHabilitationLicPro(contentHandler, program);
259        }
260        else
261        {
262            _saxHabilitationOtherDiploma(contentHandler, program);
263        }
264    }
265    
266    /**
267     * Sax habilitation informations of professional license.
268     * @param contentHandler The content handler
269     * @param program The program to sax
270     * @throws SAXException if an error occurs
271     */
272    protected void _saxHabilitationLicPro(ContentHandler contentHandler, Program program) throws SAXException
273    {
274        // Licence professionelle
275        /*
276         * <cdmfr:licPro>
277         *      <cdmfr:specialite>
278         *          <cdm:text/>
279         *      </cdmfr:specialite>
280         * </cdmfr:licPro>
281         */
282        
283        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "licPro");
284        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "specialite");
285        XMLUtils.createElement(contentHandler, "text", program.getSpeciality());
286        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "specialite");
287        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "licPro");
288    }
289
290    /**
291     * Sax habilitation informations for another diploma : no mention, not a license.
292     * @param contentHandler The content handler
293     * @param program The program to sax
294     * @throws SAXException if an error occurs
295     */
296    protected void _saxHabilitationOtherDiploma(ContentHandler contentHandler, Program program) throws SAXException
297    {
298        // Autre diplôme habilité ne s'inscrivant pas dans une structure mention-spécialité
299        /*
300         * <cdmfr:otherDiploma>
301         *      <cdmfr:refProgram/>
302         * </cdmfr:otherDiploma>
303         */
304        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "otherDiploma");
305        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "refProgram", program.getCDMId());
306        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "otherDiploma");
307    }
308
309    /**
310     * Sax habilitation informations of mention.
311     * @param contentHandler The content handler
312     * @param program The program to sax
313     * @param mention The mention to sax
314     * @throws SAXException if an error occurs
315     */
316    protected void _saxHabilitationMention(ContentHandler contentHandler, Program program, String mention) throws SAXException
317    {
318        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "fieldName");
319        
320        MetadataDefinition mentionDef = _cTypeHelper.getMetadataDefinition("mention", program.getTypes(), program.getMixinTypes());
321        if (mentionDef.getType() == MetadataType.CONTENT)
322        {
323            Content mentionContent = _resolver.resolveById(mention);
324            String mentionLabel = mentionContent.getTitle(new Locale(program.getLanguage()));
325            
326            AttributesImpl attrs = new AttributesImpl();
327            attrs.addCDATAAttribute("fieldNameCode", mentionContent.getMetadataHolder().getString("code", mention));
328            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "controlled", attrs);
329            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "registeredName", mentionLabel);
330            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "controlled");
331        }
332        else
333        {
334            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "free", mention);
335        }
336       
337       
338        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "fieldName");
339    }
340
341    /**
342     * Sax habilitation informations of speciality.
343     * @param contentHandler The content handler
344     * @param program The program to sax
345     * @param speciality The speciality to sax
346     * @throws SAXException if an error occurs
347     */
348    protected void _saxHabilitationSpeciality(ContentHandler contentHandler, Program program, String speciality) throws SAXException
349    {
350        if (StringUtils.isNotEmpty(speciality))
351        {
352            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "speciality");
353            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "specialityName", speciality);
354            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "specialityId", program.getCDMId() + "SP01"); // FIXME "SP + n° ordre de la spécialité"
355            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "refProgram", program.getCDMId());
356            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "speciality");
357        }
358    }
359
360    /**
361     * Sax cohabilitation informations.
362     * @param contentHandler The content handler
363     * @param program The program to sax
364     * @throws SAXException if an error occurs
365     */
366    protected void _saxCohabilitation(ContentHandler contentHandler, Program program) throws SAXException
367    {
368        String[] domains = program.getDomain();
369        boolean isHabilited = false;
370        for (String domain : domains)
371        {
372            isHabilited |= !"HD".equals(_refTableHelper.getItemCode(domain)); 
373        }
374        
375        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "cohabilitation");
376        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "exists", String.valueOf(isHabilited));
377        
378        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "listOfOrgUnit");
379        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "habOrgUnit");
380        
381        String rneCode = _getRootOrgUnitId(program);
382        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "refOrgUnit", rneCode);
383        
384        _saxHabilitationDomains(contentHandler, domains);
385        
386        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "habOrgUnit");
387        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "listOfOrgUnit");
388        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "cohabilitation");
389    }
390
391    /**
392     * Sax habilitation informations of domains.
393     * @param contentHandler The content handler
394     * @param domains The domains to SAX
395     * @throws SAXException if an error occurs
396     */
397    protected void _saxHabilitationDomains(ContentHandler contentHandler, String[] domains) throws SAXException
398    {
399        for (String domain : domains)
400        {
401            Content content = _resolver.resolveById(domain);
402            OdfReferenceTableEntry entry = new OdfReferenceTableEntry(content);
403            String domainName = entry.getCdmValue();
404            String domainCode = entry.getCode();
405            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "domainName");
406            if (ArrayUtils.contains(__FIXED_DOMAIN_NAMES, domainCode))
407            {
408                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "fixedDomain", domainName);
409            }
410            else
411            {
412                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "openDomain", domainName);
413            }
414            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "domainName");
415        }
416    }
417
418    /**
419     * Sax habilitation informations of joint orgunits.
420     * @param contentHandler The content handler
421     * @param program The program to sax
422     * @throws SAXException if an error occurs
423     */
424    protected void _saxHabilitationJointOrgUnit(ContentHandler contentHandler, Program program) throws SAXException
425    {
426        // Etablissement cohabilité
427        String[] jointOrgUnit = program.getJointOrgUnit();
428        if (jointOrgUnit.length > 0)
429        {
430            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "cohabilitation");
431            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "exists", String.valueOf(true));
432            
433            AttributesImpl attrs = new AttributesImpl();
434            attrs.addCDATAAttribute("cohabilitationRole", "cohabilité");
435            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "listOfOrgUnit", attrs);
436            for (String id : jointOrgUnit)
437            {
438                if (StringUtils.isNotEmpty(id))
439                {
440                    String uaiCode = _refTableHelper.getItemCode(id);
441                    XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "habOrgUnit");
442                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "refOrgUnit", uaiCode);
443                    String ouLabel = _refTableHelper.getItemLabel(id, program.getLanguage());
444                    if (ouLabel != null)
445                    {
446                        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, ouLabel);
447                    }
448                    XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "habOrgUnit");
449                }
450            }
451            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "listOfOrgUnit");
452            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "cohabilitation");
453        }
454    }
455
456    /**
457     * Sax habilitation informations of partner ship.
458     * @param contentHandler The content handler
459     * @param program The program to sax
460     * @throws SAXException if an error occurs
461     */
462    protected void _saxHabilitationPartnerShip(ContentHandler contentHandler, Program program) throws SAXException
463    {
464        /*
465         * <cdmfr:partnership>
466         *     <cdmfr:training>
467         *          <cdmfr:trainingStrategy />
468         *     </cdmfr:training>
469         * </cdmfr:partnership>
470         */      
471        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "partnership");
472        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "training");
473        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "trainingStrategy", program.getTrainingStrategy(), _sourceResolver);
474        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "training");
475        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + "partnership");
476    }
477    
478    /**
479     * Get the root organization unit uai code
480     * @param program the exported program
481     * @return the uai code of the root organization unit corresponding to the exported program
482     */
483    protected String _getRootOrgUnitId(Program program)
484    {
485        return _rootOrgUnitProvider.getRoot().getUAICode();
486    }
487    
488    /**
489     * Get the type of diploma
490     * @param program the program
491     * @return diploma type
492     */
493    protected String getDiplomaType (Program program)
494    {
495        String degree = program.getDegree();
496        if (Config.getInstance().getValue("odf.programs.degree.license").equals(degree))
497        {
498            return "L";
499        }
500        else if (Config.getInstance().getValue("odf.programs.degree.master").equals(degree))
501        {
502            return "M";
503        }
504        else if (Config.getInstance().getValue("odf.programs.degree.licensepro").equals(degree))
505        {
506            return "Lpro";
507        }
508        return "autre";
509    }
510
511    //***********************************************************************//
512    //                              PROGRAM                                  //
513    //***********************************************************************//
514    
515    /**
516     * Export this entity in CDM-fr format.
517     * @param contentHandler the target handler
518     * @param program the program
519     * @throws SAXException if an error occurs during export.
520     */
521    public void program2CDM(ContentHandler contentHandler, Program program) throws SAXException
522    {
523        boolean rootOrgUnitSaxed = false;
524        
525        Set<String> persons = new HashSet<>();
526        Set<String> orgUnits = new HashSet<>();
527        Set<String> coursesToSax = new HashSet<>();
528        
529        abstractProgram2CDM(contentHandler, program, persons, orgUnits, coursesToSax);
530        
531        Set<String> coursesAlreadySax = new HashSet<>();
532        
533        while (!coursesToSax.isEmpty())
534        {
535            Set<String> courses = new HashSet<>();
536            for (String courseId : coursesToSax)
537            {
538                coursesAlreadySax.add(courseId);
539                Course course = _resolver.resolveById(courseId);
540                courses.addAll(course2CDM(contentHandler, course, orgUnits, persons));
541            }
542            
543            coursesToSax.clear();
544            coursesToSax.addAll(courses);
545            coursesToSax.removeAll(coursesAlreadySax);
546        }
547
548        for (String orgUnitId : orgUnits)
549        {
550            OrgUnit orgUnit = _resolver.resolveById(orgUnitId);
551            if (orgUnitId.equals(_rootOrgUnitProvider.getRootId()))
552            {
553                rootOrgUnitSaxed = true;
554            }
555            
556            _orgunit2CDM(contentHandler, orgUnit, persons);
557        }
558        
559        if (!rootOrgUnitSaxed)
560        {
561            _orgunit2CDM(contentHandler, _rootOrgUnitProvider.getRoot(), persons);
562        }
563        
564        for (String personId : persons)
565        {
566            Person person = _resolver.resolveById(personId);
567            person2CDM(contentHandler, person);
568        }
569    }
570    
571    /**
572     * Send the CDM-fr representation of their ProgramPart to the given Contenthandler.<br>
573     * Also collects referenced orgUnits, persons and courses, so that they could be sent afterwards.
574     * @param contentHandler the receiving contentHandler.
575     * @param program the program
576     * @param persons collected {@link Person} ids.
577     * @param orgUnits collected {@link OrgUnit} ids.
578     * @param courses collected {@link Course} ids.
579     * @throws SAXException if an error occurs during CDM processing.
580     */
581    public void abstractProgram2CDM(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program, Set<String> persons, Set<String> orgUnits, Set<String> courses) throws SAXException
582    {
583        Request request = ContextHelper.getRequest(_context);
584        Content currentContent = (Content) request.getAttribute(Content.class.getName());
585        
586        try
587        {
588            request.setAttribute(Content.class.getName(), program);
589            
590            String tagName = program.getCDMTagName();
591        
592            AttributesImpl attrs = new AttributesImpl();
593            
594            // <program id="FRUAI{rootOrgUnitId}PR{code}" language="...">
595            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID, program.getCDMId());
596            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LANGUAGE, program.getLanguage());
597            XMLUtils.startElement(contentHandler, tagName, attrs);
598            
599            // <programID>code</programID>
600            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_PROGRAM_ID, program.getCDMId());
601    
602            // <programName><text>name</text></programName>
603            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_NAME);
604            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_TEXT, program.getTitle());
605            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_NAME);
606            
607            _abstractProgram2CDMCode(contentHandler, program);
608            
609            // <webLink>
610            //      <href>href</href>
611            //      <linkName>linkName</linkName>
612            // </webLink>
613            for (WebsiteLink website : program.getWebsiteLinks())
614            {
615                website.toCDM(contentHandler);
616            }
617            
618            // <programDescription>...</programDescription>
619            attrs = new AttributesImpl();
620            String programType = program.getEducationKind();
621            if (StringUtils.isNotEmpty(programType))
622            {
623                programType = _refTableHelper.getItemCDMfrValue(programType, false);
624                _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_NATURE, programType);
625            }
626            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_DESCRIPTION, attrs);
627            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, program.getPresentation(), _sourceResolver);
628            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_DESCRIPTION);
629            
630            // <qualification>...</qualification>
631            _qualification2CDM(contentHandler, program);
632            
633            // <levelCode @codeSet="bac|bac+1|bac+2|bac+3|..."/>
634            // <level @level="L|M|Doc"/>
635            _level2CDM(contentHandler, program);
636            
637            // <learningObjectives>...</learningObjectives>
638            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_LEARNING_OBJECTIVES, program.getObjectives(), _sourceResolver);
639            
640            // <admissionInfo>
641            //      <cdmfr:admissionDescription>...</cdmfr:admissionDescription>
642            // </admissionInfo>
643            _admissionInfo2CDM(contentHandler, program);
644            
645            // <recommendedPrerequisites>...</recommendedPrerequisites>
646            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_RECOMMANDED_PREREQUISITES, program.getRecommendedPrerequisite(), _sourceResolver);
647    
648            // <formalPrerequisites>...</formalPrerequisites>
649            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_FORMAL_PREREQUISITES, program.getNeededPrerequisite(), _sourceResolver, true);
650            
651            // <teachingPlace>...</teachingPlace>
652            _place2CDM(contentHandler, program);
653            
654            // <targetGroup>...</targetGroup>
655            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_TARGET_GROUP, program.getTargetGroup(), _sourceResolver);
656    
657            // <formOfTeaching org="" method=""/>
658            String formOfTeachingMethod = _refTableHelper.getItemCDMfrValue(program.getDistanceLearning(), false);
659            for (String formOfTeachingOrg: program.getFormOfTeachingOrgs())
660            {
661                attrs = new AttributesImpl();
662                String formOfTeachingOrgValue = _refTableHelper.getItemCDMfrValue(formOfTeachingOrg, false);
663                _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_METHOD, formOfTeachingMethod);
664                _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_ORG, formOfTeachingOrgValue);
665                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_FORM_OF_TEACHING, attrs);
666            }
667
668            // <instructionLanguage teachingLang="fr"/>
669            String[] langs = program.getEducationLanguage();
670            for (String lang : langs)
671            {
672                String teachingLanguageValue = _refTableHelper.getItemCDMfrValue(lang, true);
673                attrs = new AttributesImpl();
674                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_TEACHING_LANG, teachingLanguageValue);
675                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INSTRUCTION_LANGUAGE, attrs);   
676            }
677            
678            // <studyAbroad>...</studoAbroad>
679            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_STUDY_ABROAD, program.getStudyAbroad(), _sourceResolver);
680            
681            // <programDuration>...</programDuration>
682            String duration = program.getDuration();
683            if (StringUtils.isNotBlank(duration))
684            {
685                String durationValue = _refTableHelper.getItemCDMfrValue(duration, true);
686                if (durationValue != null)
687                {
688                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_PROGRAM_DURATION, durationValue);
689                }
690            }
691            
692            // <cdmfr:programStructure>...</cdmfr:programStructure>
693            RichText teachingOrganization = program.getTeachingOrganization();
694            if (teachingOrganization != null && teachingOrganization.getLength() > 0)
695            {
696                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_STRUCTURE);
697                CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, program.getTeachingOrganization(), _sourceResolver);
698                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_STRUCTURE);
699            }
700            
701            // Sax courses list
702            String[] children = program.getMetadataHolder().getStringArray(TraversableProgramPart.METADATA_CHILD_PROGRAM_PARTS, new String[0]);
703            for (String child : children)
704            {
705                AmetysObject ao = _resolver.resolveById(child);
706                if (ao instanceof CourseList)
707                {
708                    courselist2CDM(contentHandler, (CourseList) ao, courses);
709                }
710            }
711            
712            // <regulations/>
713            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_REGULATIONS);
714    
715            // <expenses>...</expenses>
716            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_EXPENSES, program.getExpenses(), _sourceResolver);
717            
718            // <universalAjustment>...</universalAjustment>
719            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_UNIVERSAL_ADJUSTMENT, program.getUniversalAdjustment(), _sourceResolver);
720    
721            // <contacts>
722            //      <refPerson idRef="ID"/>
723            //      <refOrgUnit idRef="ID"/>
724            // </contacts>
725            _abstractProgram2CDMContacts(contentHandler, program, persons, orgUnits);
726            
727            // <infoBlock>...</infoBlock>
728            attrs = new AttributesImpl();
729            if (tagName.equals(CDMFRTagsConstants.TAG_SUB_PROGRAM))
730            {
731                attrs.addCDATAAttribute("userDefined", "subProgram");
732            }
733            
734            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, program.getAdditionalInformations(), _sourceResolver, attrs);
735    
736            // <infoBlock userDefined="ametys-extension>...</infoBlock>
737            attrs = new AttributesImpl();
738            attrs.addCDATAAttribute("userDefined", "ametys-extension");
739            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs);
740            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
741            for (String id : _cdmFrExtensionPoint.getExtensionsIds())
742            {
743                CDMfrExtension cdmFrExtension = _cdmFrExtensionPoint.getExtension(id);
744                
745                if (program instanceof Program)
746                {
747                    cdmFrExtension.program2CDM(contentHandler, (Program) program, persons, orgUnits);
748                }
749                
750                cdmFrExtension.abstractProgram2CDM(contentHandler, program, persons, orgUnits);
751            }
752            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
753            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK);
754            
755            // <programStructure>...</programStructure>
756            _programPart2CDMSubPrograms(contentHandler, program, persons, orgUnits, courses);
757            
758            // <searchword>keyword</searchword>
759            for (String keyword : program.getKeywords())
760            {
761                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_SEARCH_WORD, keyword);
762            }
763            
764            // </program>
765            XMLUtils.endElement(contentHandler, tagName); 
766        }
767        finally
768        {
769            request.setAttribute(Content.class.getName(), currentContent);
770        }
771    }
772
773    /**
774     * SAX CDM-fr teaching places
775     * @param contentHandler the content handler to sax into
776     * @param program the program or subprogram
777     * @throws SAXException if an error occurred
778     */
779    protected void _place2CDM(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program) throws SAXException
780    {
781        // <teachingPlace>
782        //      <cdmfr:refOrgUnit />
783        //      <adr>
784        //          <pcode></pcode>
785        //      </adr>
786        // </teachingPlace>
787        String[] places = program.getPlace();
788        for (String id : places)
789        {
790            if (StringUtils.isNotEmpty(id))
791            {
792                String postalCode = _refTableHelper.getItemCode(id);
793                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_TEACHING_PLACE);
794                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REF_ORG_UNIT);
795                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ADDRESS);
796                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_PCODE, postalCode);
797                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ADDRESS);
798                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_TEACHING_PLACE);
799            }
800        }
801    }
802
803    /**
804     * SAX CDM-fr qualification
805     * @param contentHandler the content handler to sax into
806     * @param program the program or subprogram
807     * @throws SAXException if an error occurred
808     */
809    protected void _qualification2CDM(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program) throws SAXException
810    {
811        // <qualification>
812        //      <qualificatioName><text></text></qualificationName>
813        //      <qualificationDescription>
814        //          <infoBlock>...</infoBlock>
815        //          <statistics>success rate</statistics>
816        //      </qualificationDescription>
817        //      <degree degree=""/>
818        //      <profession>...</profession>
819        //      <studyQualification>...</studyQualification>
820        // </qualification>
821        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_QUALIFICATION);
822        
823        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_QUALIFICATION_NAME);
824        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_TEXT);
825        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_QUALIFICATION_NAME);
826        
827        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_QUALIFICATION_DESCRIPTION);
828        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, program.getQualification(), _sourceResolver, true);
829        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_STATISTICS, program.getSuccessRate());
830        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_QUALIFICATION_DESCRIPTION);
831        
832        AttributesImpl attrs = new AttributesImpl();
833        String ects = _refTableHelper.getItemCDMfrValue(program.getEcts(), true);
834        _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_ECTS_CREDITS, ects);
835        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_CREDITS, attrs);
836        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, program.getKnowledgeCheck(), _sourceResolver, true);
837        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_CREDITS);
838
839        _degree2CDM(contentHandler, program);
840
841        // <profession>
842        //      <infoBlock>...</infoBlock>
843        //      <romeData romeCode="code" romeLibel=""/>
844        // </profession>
845        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROFESSION);
846        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, program.getJobOpportunities(), _sourceResolver, true);
847        _romeCodes2CDM(contentHandler, program);
848        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROFESSION);
849        
850        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_STUDY_QUALIFICATION);
851        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, program.getFurtherStudy(), _sourceResolver, true);
852        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_COMPETENCE_DESC);
853        attrs = new AttributesImpl();
854        attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LIMITED, "false");
855        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_COMPETENCE_VALIDITY, attrs);
856        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_STUDY_QUALIFICATION);
857        
858        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_QUALIFICATION);
859    }
860    
861    /**
862     * SAX CDMfr degree
863     * @param contentHandler the content handler to sax into
864     * @param program the program or subprogram
865     * @throws SAXException if an error occurred
866     */
867    protected void _degree2CDM(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program) throws SAXException
868    {
869        AttributesImpl attrs = new AttributesImpl();
870        String degreeId = program.getDegree();
871        if (StringUtils.isNotEmpty(degreeId))
872        {
873            String cdmCode = _refTableHelper.getItemCDMfrValue(degreeId, false);
874            if (StringUtils.isNotEmpty(cdmCode))
875            {
876                _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_DEGREE, cdmCode);
877            }
878            else
879            {
880                _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_DEGREE_NOT_LMD, _refTableHelper.getItemCode(degreeId));
881            }
882            
883            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_DEGREE, attrs);
884        }
885    }
886
887    /**
888     * SAX CDMfr rome codes
889     * @param contentHandler the content handler to sax into
890     * @param program the program or subprogram
891     * @throws SAXException if an error occurred
892     */
893    protected void _romeCodes2CDM(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program) throws SAXException
894    {
895        String[] romeCode = program.getRomeCode();
896        for (String id : romeCode)
897        {
898            AttributesImpl attrs = new AttributesImpl();
899            String code = _refTableHelper.getItemCode(id);
900            attrs.addCDATAAttribute("romeCode", code);
901            String label = _refTableHelper.getItemCDMfrValue(id, false);
902            if (StringUtils.isNotBlank(label))
903            {
904                attrs.addCDATAAttribute("romeLibel", label);
905            }
906            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_ROME_DATA, attrs);
907        }
908    }
909
910    /**
911     * SAX CDMfr level
912     * @param contentHandler the content handler to sax into
913     * @param program the program or subprogram
914     * @throws SAXException if an error occurred
915     */
916    protected void _level2CDM(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program) throws SAXException
917    {
918        AttributesImpl attrs = new AttributesImpl();
919        String educationLevel = program.getEducationLevel();
920        _addNotNullAttribute(attrs, "codeSet", _refTableHelper.getItemCDMfrValue(educationLevel, true));
921        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_LEVEL_CODE, attrs);
922        
923        attrs = new AttributesImpl();
924        String educationLevelCode = _refTableHelper.getItemCode(educationLevel);
925        if (educationLevelCode.startsWith("licence"))
926        {
927            _addNotNullAttribute(attrs, "level", "L");
928            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_LEVEL, attrs);
929        }
930        else if (educationLevelCode.startsWith("master"))
931        {
932            _addNotNullAttribute(attrs, "level", "M");
933            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_LEVEL, attrs);
934        }
935        else if (educationLevelCode.startsWith("doctorat"))
936        {
937            _addNotNullAttribute(attrs, "level", "Doc");
938            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_LEVEL, attrs);
939        }
940    }
941
942    /**
943     * SAX CDMfr admission info
944     * @param contentHandler the content handler to sax into
945     * @param program the program or subprogram
946     * @throws SAXException if an error occurred
947     */
948    protected void _admissionInfo2CDM(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program) throws SAXException
949    {
950        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ADMISSION_INFO);
951        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_ADMISSION_DESCRIPTION, program.getAccessCondition(), _sourceResolver);
952        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_STUDENT_STATUS, "");
953        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_ECTS_REQUIRED, "");
954        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_STUDENT_PLACES, program.getEffectives(), _sourceResolver);
955        
956        Date teachingStart = program.getTeachingStart();
957        if (teachingStart != null)
958        {
959            CDMHelper.date2CDM(contentHandler, CDMFRTagsConstants.TAG_TEACHING_START, teachingStart);
960        }
961        
962        Date registrationDeadline = program.getRegistrationDeadline();
963        if (registrationDeadline != null)
964        {
965            CDMHelper.date2CDM(contentHandler, CDMFRTagsConstants.TAG_REGISTRATION_DEADLINE, registrationDeadline);
966        }
967        
968        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REGISTRATION_DETAIL);
969        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REGISTRATION_PROCESS);
970        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REGISTRATION_MODALITIES, program.getInscription(), _sourceResolver);
971        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REGISTRATION_PROCESS);
972        Date registrationStart = program.getRegistrationStart();
973        if (registrationStart != null)
974        {
975            CDMHelper.date2CDM(contentHandler, CDMFRTagsConstants.TAG_REGISTRATION_START, registrationStart);
976        }
977        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REGISTRATION_DETAIL);
978        
979        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ADMISSION_INFO);
980    }
981    
982    /**
983     * SAX CDMfr events for a {@link AbstractProgram} codes
984     * @param contentHandler the content handler to sax into
985     * @param program the program or subprogram
986     * @throws SAXException if an error occurred
987     */
988    protected void _abstractProgram2CDMCode(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program) throws SAXException
989    {
990        AttributesImpl attrs = new AttributesImpl();
991        
992        // <programCode codeSet="userDefined">code</programCode>
993        String code = program.getCode();
994        if (StringUtils.isNotBlank(code))
995        {
996            attrs = new AttributesImpl();
997            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "userDefined");
998            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, code);
999        }
1000        
1001        // <programCode codeSet="codeCNIS-NSF">code</programCode>
1002        // <programCode libSet="codeCNIS-NSF">label</programCode>
1003        String nsfId = program.getMetadataHolder().getString("nsfCode", "");
1004        if (StringUtils.isNotBlank(nsfId))
1005        {
1006            attrs = new AttributesImpl();
1007            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "codeCNIS-NSF");
1008            
1009            String nsfCode = _refTableHelper.getItemCode(nsfId);
1010            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, nsfCode);
1011            
1012            String codeLib = _refTableHelper.getItemCDMfrValue(nsfId, false);
1013            if (StringUtils.isNotEmpty(codeLib))
1014            {
1015                attrs = new AttributesImpl();
1016                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LIB_SET, "codeCNIS-NSF");
1017                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, codeLib);
1018            }
1019        }
1020        
1021        // <programCode codeSet="sectDGESIP">code</programCode>
1022        // <programCode libSet="sectDGESIP">label</programCode>
1023        for (String id : program.getDGESIPCode())
1024        {
1025            attrs = new AttributesImpl();
1026            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "sectDGESIP");
1027            
1028            String dgesipCode = _refTableHelper.getItemCode(id);
1029            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, dgesipCode);
1030            
1031            String codeLib = _refTableHelper.getItemCDMfrValue(id, false);
1032            if (StringUtils.isNotEmpty(codeLib))
1033            {
1034                attrs = new AttributesImpl();
1035                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LIB_SET, "sectDGESIP");
1036                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, codeLib);
1037            }
1038        }
1039        
1040        // <programCode codeSet="ERASMUS">code</programCode>
1041        // <programCode libSet="ERASMUS">label</programCode>
1042        for (String id : program.getErasmusCode())
1043        {
1044            attrs = new AttributesImpl();
1045            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "ERASMUS");
1046            
1047            String erasmusCode = _refTableHelper.getItemCode(id);
1048            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, erasmusCode);
1049            
1050            String codeLib = _refTableHelper.getItemCDMfrValue(id, false);
1051            if (StringUtils.isNotEmpty(codeLib))
1052            {
1053                attrs = new AttributesImpl();
1054                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LIB_SET, "ERASMUS");
1055                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, codeLib);
1056            }
1057        }
1058        
1059        // <programCode codeSet="cite97">code</programCode>
1060        // <programCode libSet="cite97">label</programCode>
1061        for (String id : program.getCite97Code())
1062        {
1063            attrs = new AttributesImpl();
1064            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "cite97");
1065            String cite97Code = _refTableHelper.getItemCode(id);
1066            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, cite97Code);
1067            
1068            String codeLib = _refTableHelper.getItemCDMfrValue(id, false);
1069            if (StringUtils.isNotEmpty(codeLib))
1070            {
1071                attrs = new AttributesImpl();
1072                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LIB_SET, "cite97");
1073                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, codeLib);
1074            }
1075        }
1076        
1077        // <programCode codeSet="FAP">code</programCode>
1078        // <programCode libSet="FAP">label</programCode>
1079        for (String id : program.getFapCode())
1080        {
1081            attrs = new AttributesImpl();
1082            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "FAP");
1083            String fapCode = _refTableHelper.getItemCode(id);
1084            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, fapCode);
1085            
1086            String codeLib = _refTableHelper.getItemCDMfrValue(id, false);
1087            if (StringUtils.isNotEmpty(codeLib))
1088            {
1089                attrs = new AttributesImpl();
1090                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LIB_SET, "FAP");
1091                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, codeLib);
1092            }
1093        }
1094        
1095        // <programCode codeSet="codeSectDiscipSISE">code</programCode>
1096        // <programCode libSet="codeSectDiscipSISE">label</programCode>
1097        for (String id : program.getSiseCode())
1098        {
1099            attrs = new AttributesImpl();
1100            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "sectDiscipSISE");
1101            
1102            String siseCode = _refTableHelper.getItemCode(id);
1103            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, siseCode);
1104            
1105            String codeLib = _refTableHelper.getItemCDMfrValue(id, false);
1106            if (StringUtils.isNotEmpty(codeLib))
1107            {
1108                attrs = new AttributesImpl();
1109                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LIB_SET, "sectDiscipSISE");
1110                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, codeLib);
1111            }
1112        }
1113    }
1114    
1115    /**
1116     * SAX CDMfr events for a {@link AbstractProgram} contacts
1117     * @param contentHandler the content handler to sax into
1118     * @param program the program or subprogram
1119     * @param persons the contacts
1120     * @param orgUnits the orgunits
1121     * @throws SAXException if an error occurred
1122     */
1123    protected void _abstractProgram2CDMContacts(ContentHandler contentHandler, AbstractProgram<? extends ProgramFactory> program, Set<String> persons, Set<String> orgUnits) throws SAXException
1124    {
1125        AttributesImpl attrs = new AttributesImpl();
1126        
1127        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_CONTACTS);
1128        
1129        Map<String, String[]> personsInChargeByRole = program.getPersonsInCharge();
1130        for (String role : personsInChargeByRole.keySet())
1131        {
1132            String roleCode = null;
1133            if (StringUtils.isNotEmpty(role))
1134            {
1135                roleCode = _refTableHelper.getItemCode(role);
1136            }
1137            String[] personsInCharge = personsInChargeByRole.get(role);
1138            for (String personID : personsInCharge)
1139            {
1140                Person person = _resolver.resolveById(personID);
1141                persons.add(person.getId());
1142                attrs = new AttributesImpl();
1143                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, person.getCDMId());
1144                if (roleCode != null)
1145                {
1146                    attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ROLE, roleCode);
1147                }
1148                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_REF_PERSON, attrs);
1149            }
1150        }
1151        
1152        for (String contactID : program.getContacts())
1153        {
1154            if (!"".equals(contactID))
1155            {
1156                try
1157                {
1158                    Person person = _resolver.resolveById(contactID);
1159                    persons.add(person.getId());
1160                    attrs = new AttributesImpl();
1161                    attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, person.getCDMId());
1162                    attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ROLE, "contact");
1163                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_REF_PERSON, attrs);
1164                }
1165                catch (UnknownAmetysObjectException e)
1166                {
1167                    // Contact does exist anymore, ignore it
1168                }
1169                
1170            }
1171        }
1172
1173        for (String orgUnitId : program.getOrgUnits())
1174        {
1175            if (!"".equals(orgUnitId))
1176            {
1177                try
1178                {
1179                    OrgUnit orgUnit = _resolver.resolveById(orgUnitId);
1180                    orgUnits.add(orgUnit.getId());
1181                    attrs = new AttributesImpl();
1182                    attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, orgUnit.getCDMId());
1183                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_REF_ORG_UNIT, attrs);
1184                }
1185                catch (UnknownAmetysObjectException e)
1186                {
1187                    // OrgUnit does exist anymore, ignore it
1188                }
1189            }
1190        }
1191        
1192        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_CONTACTS);
1193    }
1194    
1195    /**
1196     * SAX CDMfr events for a {@link ProgramPart} strcuture
1197     * @param contentHandler the content handler to sax into
1198     * @param programPart the program part
1199     * @param persons the contacts
1200     * @param orgUnits the orgunits
1201     * @param courses the courses
1202     * @throws SAXException if an error occurred
1203     */
1204    protected void _programPart2CDMSubPrograms(ContentHandler contentHandler, TraversableProgramPart programPart, Set<String> persons, Set<String> orgUnits, Set<String> courses) throws SAXException
1205    {
1206        String[] children = programPart.getMetadataHolder().getStringArray(TraversableProgramPart.METADATA_CHILD_PROGRAM_PARTS, new String[0]);
1207        for (String childId : children)
1208        {
1209            AmetysObject child = _resolver.resolveById(childId);
1210            if (child instanceof AbstractProgram)
1211            {
1212                abstractProgram2CDM(contentHandler, (AbstractProgram<?>) child, persons, orgUnits, courses);
1213            }
1214            else if (child instanceof Container)
1215            {
1216                container2CDM(contentHandler, (Container) child, persons, orgUnits, courses);
1217            }
1218        }
1219    }
1220    
1221    
1222    //***********************************************************************//
1223    //                             CONTAINER                                 //
1224    //***********************************************************************//
1225    
1226    /**
1227     * Send the CDM-fr representation of their ProgramPart to the given Contenthandler.<br>
1228     * Also collects referenced orgUnits, persons and courses, so that they could be sent afterwards.
1229     * @param contentHandler the receiving contentHandler.
1230     * @param container The container
1231     * @param persons collected {@link Person} ids.
1232     * @param orgUnits collected {@link OrgUnit} ids.
1233     * @param courses collected {@link Course} ids.
1234     * @throws SAXException if an error occurs during CDM processing.
1235     */
1236    public void container2CDM(ContentHandler contentHandler, Container container, Set<String> persons, Set<String> orgUnits, Set<String> courses) throws SAXException
1237    {
1238        Request request = ContextHelper.getRequest(_context);
1239        Content currentContent = (Content) request.getAttribute(Content.class.getName());
1240        
1241        try
1242        {
1243            request.setAttribute(Content.class.getName(), container);
1244            
1245            AttributesImpl attrs = new AttributesImpl();
1246            
1247            // <program id="FR_RNE_{rootOrgUnitId}_PR_{code}">
1248            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID, container.getCDMId());
1249            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_SUB_PROGRAM, attrs);
1250            
1251            // <programID>code</programID>
1252            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_PROGRAM_ID, container.getCDMId());
1253    
1254            // <programName><text>name</text></programName>
1255            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_NAME);
1256            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_TEXT, container.getTitle());
1257            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_NAME);
1258            
1259            // <programCode codeSet="userDefined">code</programCode>
1260            String code = container.getCode();
1261            if (StringUtils.isNotBlank(code))
1262            {
1263                attrs = new AttributesImpl();
1264                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "userDefined");
1265                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_CODE, attrs, code);
1266            }
1267            
1268            // <programDescription>...</programDescription>
1269            attrs = new AttributesImpl();
1270            String nature = container.getNature();
1271            if (StringUtils.isNotEmpty(nature))
1272            {
1273                nature = _refTableHelper.getItemCDMfrValue(nature, true);
1274                _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_NATURE, nature);
1275            }
1276            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_DESCRIPTION, attrs);
1277            
1278            // <qualification>...</qualification>
1279            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_QUALIFICATION);
1280            _addPositiveAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_ECTS_CREDITS, container.getEcts());
1281            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_CREDITS, attrs);
1282            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, null, null, true);
1283            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_CREDITS);
1284            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_QUALIFICATION);
1285    
1286            // <programStructure>...</programStructure>
1287            String[] children = container.getMetadataHolder().getStringArray(TraversableProgramPart.METADATA_CHILD_PROGRAM_PARTS, new String[0]);
1288            for (String childId : children)
1289            {
1290                AmetysObject child = _resolver.resolveById(childId);
1291                if (child instanceof CourseList)
1292                {
1293                    courselist2CDM(contentHandler, (CourseList) child, courses);
1294                }
1295            }
1296    
1297            // <infoBlock>...</infoBlock>
1298            attrs = new AttributesImpl();
1299            attrs.addCDATAAttribute("userDefined", "container");
1300            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs);
1301
1302            // <infoBlock userDefined="ametys-extension>...</infoBlock>
1303            attrs = new AttributesImpl();
1304            attrs.addCDATAAttribute("userDefined", "ametys-extension");
1305            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs);
1306            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
1307            for (String id : _cdmFrExtensionPoint.getExtensionsIds())
1308            {
1309                CDMfrExtension cdmFrExtension = _cdmFrExtensionPoint.getExtension(id);
1310                cdmFrExtension.container2CDM(contentHandler, container, persons, orgUnits);
1311            }
1312            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
1313            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK);
1314            
1315            // <subProgram>...</subProgram>
1316            _programPart2CDMSubPrograms(contentHandler, container, persons, orgUnits, courses);
1317    
1318            // </program>
1319            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_SUB_PROGRAM);
1320        }
1321        finally
1322        {
1323            request.setAttribute(Content.class.getName(), currentContent);
1324        }
1325    }
1326    
1327    /**
1328     * SAX the course list structure
1329     * @param contentHandler The content handler to SAX into
1330     * @param cl The course list
1331     * @param courses References of saxed courses 
1332     * @throws SAXException if an error occurs
1333     */
1334    public void courselist2CDM (ContentHandler contentHandler, CourseList cl, Set<String> courses) throws SAXException
1335    {
1336        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_STRUCTURE);
1337        
1338        _courselist2CDM (contentHandler, cl);
1339                
1340        for (Course course  : cl.getCourses())
1341        {
1342            courses.add(course.getId());
1343            AttributesImpl attrs = new AttributesImpl();
1344            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, course.getCDMId());
1345            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REF_COURSE, attrs);
1346        }
1347        
1348        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROGRAM_STRUCTURE);
1349    }
1350    
1351    /**
1352     * Convert course list info to CDM (without child courses)
1353     * @param contentHandler The content handler to sax into
1354     * @param cl The course list
1355     * @throws SAXException if an error occurs
1356     */
1357    protected void _courselist2CDM (ContentHandler contentHandler, CourseList cl) throws SAXException
1358    {
1359        AttributesImpl attrs = new AttributesImpl();
1360        attrs.addCDATAAttribute("userDefined", "listCode");
1361        String code = cl.getCode();
1362        if (StringUtils.isEmpty(code))
1363        {
1364            code = org.ametys.core.util.StringUtils.generateKey().toUpperCase();
1365        }
1366        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs, code);
1367        
1368        attrs = new AttributesImpl();
1369        attrs.addCDATAAttribute("userDefined", "listName");
1370        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs, cl.getTitle());
1371        
1372        ChoiceType type = cl.getType();
1373        if (type != null)
1374        {
1375            attrs = new AttributesImpl();
1376            attrs.addCDATAAttribute("userDefined", "listChoiceType");
1377            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs, cl.getType().toString().toLowerCase());
1378        }
1379        
1380        if (type != null && type.equals(ChoiceType.CHOICE))
1381        {
1382            attrs = new AttributesImpl();
1383            attrs.addCDATAAttribute("userDefined", "listMinNumberOfCourses");
1384            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs, String.valueOf(cl.getMinNumberOfCourses()));
1385            
1386            attrs = new AttributesImpl();
1387            attrs.addCDATAAttribute("userDefined", "listMaxNumberOfCourses");
1388            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs, String.valueOf(cl.getMaxNumberOfCourses()));
1389        }
1390    }
1391    
1392    //***********************************************************************//
1393    //                               COURSE                                  //
1394    //***********************************************************************//
1395    
1396    /**
1397     * Export this entity in CDM-fr format.
1398     * @param contentHandler the target handler
1399     * @param course The course
1400     * @throws SAXException if an error occurs during export.
1401     */
1402    public void course2CDM(ContentHandler contentHandler, Course course) throws SAXException
1403    {
1404        boolean rootOrgUnitSaxed = false;
1405        
1406        Set<String> orgUnits = new HashSet<>();
1407        Set<String> persons = new HashSet<>();
1408        
1409        Set<String> coursesAlreadySax = new HashSet<>();
1410        Set<String> coursesToSax = new HashSet<>();
1411        coursesToSax.add(course.getId());
1412        
1413        while (!coursesToSax.isEmpty())
1414        {
1415            Set<String> courses = new HashSet<>();
1416            for (String courseId : coursesToSax)
1417            {
1418                coursesAlreadySax.add(courseId);
1419                Course courseToSax = _resolver.resolveById(courseId);
1420                courses.addAll(course2CDM(contentHandler, courseToSax, orgUnits, persons));
1421            }
1422            coursesToSax.clear();
1423            coursesToSax.addAll(courses);
1424            coursesToSax.removeAll(coursesAlreadySax);
1425        }
1426        
1427        for (String orgUnitId : orgUnits)
1428        {
1429            OrgUnit orgUnit = _resolver.resolveById(orgUnitId);
1430            if (orgUnitId.equals(_rootOrgUnitProvider.getRootId()))
1431            {
1432                rootOrgUnitSaxed = true;
1433            }
1434            _orgunit2CDM(contentHandler, orgUnit, persons);
1435        }
1436        
1437        if (!rootOrgUnitSaxed)
1438        {
1439            OrgUnit orgUnit = _rootOrgUnitProvider.getRoot();
1440            _orgunit2CDM(contentHandler, orgUnit, persons);
1441        }
1442        
1443        for (String personId : persons)
1444        {
1445            Person person = _resolver.resolveById(personId);
1446            person2CDM(contentHandler, person);
1447        }
1448        
1449        // Set content in request
1450        ContextHelper.getRequest(_context).setAttribute(Content.class.getName(), course);
1451    }
1452    
1453    /**
1454     * Export this entity in CDM-fr format.
1455     * @param contentHandler the content handler to SAX into
1456     * @param course the course
1457     * @param orgUnits the org units
1458     * @param persons the persons
1459     * @return the saxed coursed
1460     * @throws SAXException if failed to generate CDM
1461     */
1462    public Set<String> course2CDM(ContentHandler contentHandler, Course course, Set<String> orgUnits, Set<String> persons) throws SAXException
1463    {
1464        Request request = ContextHelper.getRequest(_context);
1465        Content currentContent = (Content) request.getAttribute(Content.class.getName());
1466        
1467        try
1468        {
1469            request.setAttribute(Content.class.getName(), course);
1470            
1471            AttributesImpl attrs = new AttributesImpl();
1472            
1473            // <course id="FR_RNE_{rootOrgUnitId}_CO_{code}" language="...">
1474            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID, course.getCDMId());
1475            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_LANGUAGE, course.getLanguage());
1476            String erasmusCode = _refTableHelper.getItemCode(course.getErasmusCode());
1477            _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_IDENT, erasmusCode);
1478            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_COURSE, attrs);
1479            
1480            // <courseID>code</courseID>
1481            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_COURSE_ID, course.getCDMId());
1482            
1483            // <courseName><text>name</text></courseName>
1484            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_COURSE_NAME);
1485            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_TEXT, course.getTitle());
1486            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_COURSE_NAME);
1487    
1488            // <courseCode codeSet="UE">code</couseCode>
1489            String courseType = _refTableHelper.getItemCode(course.getCourseType());
1490            attrs = new AttributesImpl();
1491            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, courseType);
1492            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_COURSE_CODE, attrs, course.getCdmCode());
1493            
1494            // <webLink>
1495            //      <href>href</href>
1496            //      <linkName>linkName</linkName>
1497            // </webLink>
1498            String webLinkURL = course.getWebLinkUrl();
1499            if (StringUtils.isNotEmpty(webLinkURL))
1500            {
1501                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_WEB_LINK);
1502                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_HREF, webLinkURL);
1503                String webLinkLabel = course.getWebLinkLabel();
1504                if (StringUtils.isNotEmpty(webLinkLabel))
1505                {
1506                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_LINK_NAME, course.getWebLinkLabel());
1507                }
1508                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_WEB_LINK);
1509            }
1510    
1511            // <level>level</level>
1512            attrs = new AttributesImpl();
1513            String level = _refTableHelper.getItemCDMfrValue(course.getLevel(), true);
1514            _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_LEVEL, level);
1515            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_LEVEL, attrs);
1516            
1517            // <courseDescription>...</courseDescription>
1518            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_COURSE_DESCRIPTION);
1519            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_DIGITAL_USE);
1520            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PERCENTAGE, "0");
1521            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_LCMS);
1522            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_EXISTS, "false");
1523            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_RESOURCE_MGT);
1524            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PERC_OF_USE, "0");
1525            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_LCMS);
1526            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROD_PEDA);
1527            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_DIGIT_PROD_PERC, "0");
1528            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_DIGIT_PROD_UNT_PERC, "0");
1529            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROD_PEDA);
1530            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_DIGITAL_USE);
1531            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_INFO_BLOCK, course.getDescription(), _sourceResolver, true);
1532            
1533            Set<String> courses = _courseLists2CDM(contentHandler, course);
1534            
1535            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_COURSE_DESCRIPTION);
1536            
1537            // <teachingTerm term="" timeOfDay="" start=""/>
1538            attrs = new AttributesImpl();
1539            Date startDate = course.getStartDate();
1540            if (startDate != null)
1541            {
1542                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_START, new SimpleDateFormat("yyyy-MM-dd").format(startDate));
1543            }
1544            
1545            String teachingTerm = _refTableHelper.getItemCDMfrValue(course.getTeachingTerm(), true);
1546            _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_TERM, teachingTerm);
1547            String timeSlot = _refTableHelper.getItemCDMfrValue(course.getTimeSlot(), true);
1548            _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_TIME_OF_DAY, timeSlot);
1549            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_TEACHING_TERM, attrs);
1550    
1551            // <credits ECTScredits="" totalWorkLoad="">
1552            //      <infoBlock>...</infoBlock>
1553            //      <globalVolume teachingType="CM/TP/TD">...</globalVolume>
1554            // </credits>
1555            _credits2CDM(contentHandler, course);
1556            
1557            // <learningObjectives>...</learningObjectives>
1558            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_LEARNING_OBJECTIVES);
1559            CDMHelper.richText2CDM(contentHandler, course.getObjectives(), _sourceResolver);
1560            // <ametys-cdm:skills>...</ametys-cdm:skills>
1561            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + "skills", course.getSkills(), _sourceResolver);
1562            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_LEARNING_OBJECTIVES);
1563            
1564            // <admissionInfo>
1565            //      <studentPlaces places=""/>
1566            //      <teachingStart date=""/>
1567            // </admissionInfo>
1568            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ADMISSION_INFO);
1569    
1570            String maxNumberOfStudents = course.getMaxNumberOfStudents();
1571            if (StringUtils.isNotEmpty(maxNumberOfStudents))
1572            {
1573                attrs = new AttributesImpl();
1574                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_PLACES, maxNumberOfStudents);
1575                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_STUDENT_PLACES, attrs);
1576            }
1577            
1578            if (startDate != null)
1579            {
1580                attrs = new AttributesImpl();
1581                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_DATE, new SimpleDateFormat("yyyy-MM-dd").format(startDate));
1582                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_TEACHING_START, attrs);
1583            }
1584            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ADMISSION_INFO);
1585            
1586            // <formalPrerequisites>...</formalPrerequisites>
1587            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_FORMAL_PREREQUISITES, course.getNeededPrerequisite(), _sourceResolver, true);
1588    
1589            // <teachingPlace>
1590            //      <cdmfr:refOrgUnit />
1591            //      <adr>
1592            //          <pcode></pcode>
1593            //      </adr>
1594            // </teachingPlace>
1595            _teachingLocation2CDM(contentHandler, course);
1596            
1597            // <formOfTeaching method="" org=""/>
1598            String formOfTeachingMethod = _refTableHelper.getItemCDMfrValue(course.getFormOfTeachingMethod(), false);
1599            for (String formOfTeachingOrg: course.getFormOfTeachingOrgs())
1600            {
1601                attrs = new AttributesImpl();
1602                String formOfTeachingOrgValue = _refTableHelper.getItemCDMfrValue(formOfTeachingOrg, false);
1603                _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_METHOD, formOfTeachingMethod);
1604                _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_ORG, formOfTeachingOrgValue);
1605                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_FORM_OF_TEACHING, attrs);
1606            }
1607            
1608            // <formOfAssessment>...</formOfAssessment>
1609            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_FORM_OF_ASSESSMENT, course.getFormOfAssessment(), _sourceResolver);
1610            
1611            // <instructionLanguage teachingLang=""/>
1612            String[] teachingLanguages = course.getTeachingLanguage();
1613            for (String teachingLanguage : teachingLanguages)
1614            {
1615                String teachingLanguageValue = _refTableHelper.getItemCDMfrValue(teachingLanguage, true);
1616                attrs = new AttributesImpl();
1617                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_TEACHING_LANG, teachingLanguageValue);
1618                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_INSTRUCTION_LANGUAGE, attrs);
1619            }
1620            
1621            // <syllabus>
1622            // ...
1623            //   <webLink role="learningObjectMetadata">
1624            //      <href>...</href>
1625            //      <linkName>...</linkName>
1626            //   </webLink>
1627            // </syllabus>
1628            _syllabus2CDM(contentHandler, course);
1629            
1630            // <teachingActivity method=""/>
1631            attrs = new AttributesImpl();
1632            String teachingActivity = _refTableHelper.getItemCDMfrValue(course.getTeachingActivity(), true);
1633            _addNotNullAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_METHOD, teachingActivity);
1634            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_TEACHING_ACTIVITY, attrs);
1635            
1636            // <contacts>
1637            //      <refPerson idRef=ID/>
1638            // </contacts>
1639            _course2CDMContacts(contentHandler, course, persons, orgUnits);
1640            
1641            // <infoBlock>...</infoBlock>
1642            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, course.getAdditionalInformations(), _sourceResolver);
1643            
1644            // <infoBlock userDefined="ametys-extension>...</infoBlock>
1645            attrs = new AttributesImpl();
1646            attrs.addCDATAAttribute("userDefined", "ametys-extension");
1647            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs);
1648            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
1649            for (String id : _cdmFrExtensionPoint.getExtensionsIds())
1650            {
1651                CDMfrExtension cdmFrExtension = _cdmFrExtensionPoint.getExtension(id);
1652                cdmFrExtension.course2CDM(contentHandler, course, persons, orgUnits);
1653            }
1654            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
1655            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK);
1656            
1657            // <searchword>keyword</searchword>
1658            for (String keyword : course.getKeywords())
1659            {
1660                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_SEARCH_WORD, keyword);
1661            }
1662           
1663            // </course>
1664            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_COURSE);
1665            
1666            return courses;
1667        }
1668        finally
1669        {
1670            request.setAttribute(Content.class.getName(), currentContent);
1671        }
1672    }
1673    
1674    /**
1675     * Sax the courses lists for a course.
1676     * @param contentHandler the content handler to SAX into
1677     * @param course The courselist container.
1678     * @return The list of courses into the course list.
1679     * @throws SAXException if failed to generate CDM
1680     */
1681    protected Set<String> _courseLists2CDM(ContentHandler contentHandler, Course course) throws SAXException
1682    {
1683        Set<String> courses = new HashSet<>();
1684        
1685        for (CourseList coursesList : course.getCourseLists())
1686        {
1687            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_COURSE_CONTENTS);
1688            
1689            // SAX infos on course list. This DO NOT respect the CDM-fr schema
1690            _courselist2CDM(contentHandler, coursesList);
1691            
1692            for (Course childCourse : coursesList.getCourses())
1693            {
1694                courses.add(childCourse.getId());
1695                AttributesImpl attrs = new AttributesImpl();
1696                attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, childCourse.getCDMId());
1697                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REF_COURSE, attrs);
1698            }
1699            
1700            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_COURSE_CONTENTS);
1701        }
1702        
1703        return courses;
1704    }
1705    
1706    /**
1707     * Sax the course credits.
1708     * @param contentHandler the content handler to SAX into
1709     * @param course The course to sax
1710     * @throws SAXException if failed to generate CDM
1711     */
1712    protected void _credits2CDM(ContentHandler contentHandler, Course course) throws SAXException
1713    {
1714        AttributesImpl attrs = new AttributesImpl();
1715        _addPositiveAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_TOTAL_WORK_LOAD, course.getNumberOfHours());
1716        _addPositiveAttribute(attrs, CDMFRTagsConstants.ATTRIBUTE_ECTS_CREDITS, course.getEcts());
1717        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_CREDITS, attrs);
1718        CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, null, _sourceResolver, true);
1719        
1720        Map<String, Double> hoursByNature = new HashMap<>();
1721        List<CoursePart> courseParts = course.getCourseParts();
1722        for (CoursePart coursePart : courseParts)
1723        {
1724            String nature = coursePart.getNature();
1725            Double hours = MapUtils.getDouble(hoursByNature, nature, 0d) + coursePart.getNumberOfHours();
1726            if (hours > 0)
1727            {
1728                hoursByNature.put(nature, hours);
1729            }
1730        }
1731        
1732        for (String nature : hoursByNature.keySet())
1733        {
1734            attrs = new AttributesImpl();
1735            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_TEACHING_TYPE, _refTableHelper.getItemCDMfrValue(nature, true));
1736            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_GLOBAL_VOLUME, attrs, String.valueOf(hoursByNature.get(nature)));
1737        }
1738        
1739        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_CREDITS);
1740    }
1741
1742    /**
1743     * SAX the syllabus elements of the course.
1744     * @param contentHandler The content handler
1745     * @param course The course to SAX
1746     * @throws SAXException if an error occurs
1747     */
1748    protected void _syllabus2CDM(ContentHandler contentHandler, Course course) throws SAXException
1749    {
1750        RichText syllabus = course.getSyllabus();
1751        RichText bibliography = course.getBibliography();
1752        Set<LOMSheet> lomSheets = course.getLOMSheets();
1753        if (syllabus != null || bibliography != null || !lomSheets.isEmpty())
1754        {
1755            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_SYLLABUS);
1756            
1757            // Syllabus
1758            if (syllabus != null)
1759            {
1760                CDMHelper.richText2CDM(contentHandler, syllabus, _sourceResolver);
1761            }
1762            
1763            // Bibliography
1764            if (bibliography != null)
1765            {
1766                CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_AMETYS_CDM + CDMFRTagsConstants.TAG_BIBLIOGRAPHY, bibliography, _sourceResolver);
1767            }
1768            
1769            // LOM sheets
1770            for (LOMSheet lomSheet : lomSheets)
1771            {
1772                lomSheet.toCDM(contentHandler);
1773            }
1774            
1775            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_SYLLABUS);
1776        }
1777    }
1778    
1779    /**
1780     * Sax the course teaching locations.
1781     * @param contentHandler the content handler to SAX into
1782     * @param course The course to sax
1783     * @throws SAXException if failed to generate CDM
1784     */
1785    protected void _teachingLocation2CDM(ContentHandler contentHandler, Course course) throws SAXException
1786    {
1787        for (String id : course.getTeachingLocation())
1788        {
1789            if (StringUtils.isNotEmpty(id))
1790            {
1791                String postalCode = _refTableHelper.getItemCode(id);
1792                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_TEACHING_PLACE);
1793                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REF_ORG_UNIT);
1794                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ADDRESS);
1795                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_PCODE, postalCode);
1796                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ADDRESS);
1797                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_TEACHING_PLACE);
1798            }
1799        }
1800    }
1801    
1802    /**
1803     * SAX CDMfr events for a {@link Course} contacts
1804     * @param contentHandler the content handler to sax into
1805     * @param course the course
1806     * @param persons the contacts
1807     * @param orgUnits the orgunits
1808     * @throws SAXException if an error occurred
1809     */
1810    protected void _course2CDMContacts(ContentHandler contentHandler, Course course, Set<String> persons, Set<String> orgUnits) throws SAXException
1811    {
1812        AttributesImpl attrs = new AttributesImpl();
1813        
1814        XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_CONTACTS);
1815        
1816        for (String contactID : course.getPersonsInCharge())
1817        {
1818            if (!"".equals(contactID))
1819            {
1820                try
1821                {
1822                    Person person = _resolver.resolveById(contactID);
1823                    persons.add(person.getId());
1824                    attrs = new AttributesImpl();
1825                    attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, person.getCDMId());
1826                    attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ROLE, "responsible");
1827                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_REF_PERSON, attrs);
1828                }
1829                catch (UnknownAmetysObjectException e)
1830                {
1831                    // Contact does exist anymore, ignore it
1832                }
1833                
1834            }
1835        }
1836        
1837        for (String contactID : course.getContacts())
1838        {
1839            if (!"".equals(contactID))
1840            {
1841                try
1842                {
1843                    Person person = _resolver.resolveById(contactID);
1844                    persons.add(person.getId());
1845                    attrs = new AttributesImpl();
1846                    attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, person.getCDMId());
1847                    attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ROLE, "contact");
1848                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_REF_PERSON, attrs);
1849                }
1850                catch (UnknownAmetysObjectException e)
1851                {
1852                    // Contact does exist anymore, ignore it
1853                }
1854            }
1855        }
1856        
1857        for (String orgUnitID : course.getOrgUnits())
1858        {
1859            if (!"".equals(orgUnitID))
1860            {
1861                try
1862                {
1863                    OrgUnit orgUnit = _resolver.resolveById(orgUnitID);
1864                    orgUnits.add(orgUnit.getId());
1865                    attrs = new AttributesImpl();
1866                    attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, orgUnit.getCDMId());
1867                    XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_REF_ORG_UNIT, attrs);
1868                }
1869                catch (UnknownAmetysObjectException e)
1870                {
1871                    // OrgUnit does exist anymore, ignore it
1872                }
1873            }
1874        }
1875        
1876        XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_CONTACTS);
1877    }
1878    
1879    
1880    //***********************************************************************//
1881    //                              ORGUNIT                                  //
1882    //***********************************************************************//
1883    
1884    /**
1885     * Export this entity in CDM-fr format.
1886     * @param contentHandler the target handler
1887     * @param orgunit the orgunit
1888     * @throws SAXException if an error occurs during export.
1889     */
1890    public void orgunit2CDM(ContentHandler contentHandler, OrgUnit orgunit) throws SAXException
1891    {
1892        Set<String> persons = new HashSet<>();
1893        
1894        _orgunit2CDM(contentHandler, orgunit, persons, true);
1895        
1896        for (String personId : persons)
1897        {
1898            Person person = _resolver.resolveById(personId);
1899            person2CDM(contentHandler, person);
1900        }
1901        
1902        if (!orgunit.getId().equals(_rootOrgUnitProvider.getRootId()))
1903        {
1904            OrgUnit rootOrgunit = _resolver.resolveById(_rootOrgUnitProvider.getRootId());
1905            _orgunit2CDM(contentHandler, rootOrgunit, persons);
1906        }
1907        
1908        // Set content in request
1909        ContextHelper.getRequest(_context).setAttribute(Content.class.getName(), orgunit);
1910    }
1911    
1912    /**
1913     * SAX CDMfr event for a {@link OrgUnit}
1914     * @param contentHandler the content handler to sax into
1915     * @param orgunit the orgunit
1916     * @param persons the contacts of orgunit
1917     * @throws SAXException if an error occurred
1918     */
1919    protected void _orgunit2CDM(ContentHandler contentHandler, OrgUnit orgunit, Set<String> persons) throws SAXException
1920    {
1921        _orgunit2CDM(contentHandler, orgunit, persons, false);
1922    }
1923    
1924    /**
1925     * SAX CDMfr event for a {@link OrgUnit}
1926     * @param contentHandler the content handler to sax into
1927     * @param orgunit the orgunit
1928     * @param persons the contacts of orgunit
1929     * @param showChildren true to sax child orgunits
1930     * @throws SAXException if an error occurred
1931     */
1932    protected void _orgunit2CDM(ContentHandler contentHandler, OrgUnit orgunit, Set<String> persons, boolean showChildren) throws SAXException
1933    {
1934        Request request = ContextHelper.getRequest(_context);
1935        Content currentContent = (Content) request.getAttribute(Content.class.getName());
1936        
1937        try
1938        {
1939            request.setAttribute(Content.class.getName(), orgunit);
1940        
1941            AttributesImpl attrs = new AttributesImpl();
1942            
1943            // <orgunit id="FR_RNE_{rootOrgUnitId}_OR_{login}">
1944            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID, orgunit.getCDMId());
1945            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT, attrs);
1946            
1947            // <orgUnitID>RNE</orgUnitID>
1948            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT_ID, orgunit.getCDMId());
1949            
1950            // <orgUnitName><text>name</text></orgUnitID>
1951            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT_NAME);
1952            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_TEXT, orgunit.getTitle());
1953            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT_NAME);
1954            
1955            // <orgUnitAcronym>acronyme</orgUnitAcronym>
1956            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT_ACRONYM, orgunit.getAcronym());
1957            
1958            // <orgUnitCode codeSet="codeUAI">code</orgUnitCode>
1959            attrs = new AttributesImpl();
1960            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_CODE_SET, "codeUAI");
1961            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT_CODE, attrs, orgunit.getUAICode());
1962    
1963            // <orgUnitKind orgUnitKindCodeValueSet=""/>
1964            attrs = new AttributesImpl();
1965            String orgunitType = _refTableHelper.getItemCDMfrValue(orgunit.getType(), false);
1966            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ORG_UNIT_KIND_CODE_VALUESET, orgunitType);
1967            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT_KIND);
1968            
1969            // <webLink>
1970            //      <href>href</href>
1971            //      <linkName>linkName</linkName>
1972            // </webLink>
1973            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_WEB_LINK);
1974            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_HREF, orgunit.getWebLinkURL());
1975            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_LINK_NAME, orgunit.getWebLinkLabel());
1976            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_WEB_LINK);
1977            
1978            // <orgUnitDescription>...</orgUnitDescription>
1979            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT_DESCRIPTION, orgunit.getDescription(), _sourceResolver);
1980            
1981            // <admissionInfo>
1982            //      <admissionDescription>...</admissionDescription>
1983            // </admissionInfo>
1984            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ADMISSION_INFO);
1985            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_ADMISSION_DESCRIPTION, orgunit.getAdmissionInfo(), _sourceResolver);
1986            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ADMISSION_INFO);
1987            
1988            // <regulations>...</regulations>
1989            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_REGULATIONS, orgunit.getRegulations(), _sourceResolver);
1990            
1991            // <expenses>...</expenses>
1992            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_EXPENSES, orgunit.getExpenses(), _sourceResolver);
1993            
1994            // <studentFacilities>...</studentFacilities>
1995            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_STUDENT_FACILITIES, orgunit.getStudentFacilities(), _sourceResolver);
1996            
1997            // <universalAjustment>...</universalAjustment>
1998            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_UNIVERSAL_ADJUSTMENT, orgunit.getUniversalAdjustment(), _sourceResolver);
1999            
2000            // <contacts>
2001            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_CONTACTS);
2002            
2003            for (String contactID : orgunit.getContacts())
2004            {
2005                if (StringUtils.isNotEmpty(contactID))
2006                {
2007                    try
2008                    {
2009                        Person person = _resolver.resolveById(contactID);
2010                        persons.add(person.getId());
2011                        attrs = new AttributesImpl();
2012                        attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, person.getCDMId());
2013                        attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ROLE, "contact");
2014                        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_REF_PERSON, attrs);
2015                    }
2016                    catch (UnknownAmetysObjectException e)
2017                    {
2018                        // Contact does exist anymore, ignore it
2019                    }
2020                }
2021            }
2022            
2023            // </contacts>
2024            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_CONTACTS);
2025            
2026            // <infoBlock>...</infoBlock>
2027            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, orgunit.getAdditionnalInfos(), _sourceResolver);
2028    
2029            Set<OrgUnit> orgUnits = new HashSet<>();
2030            if (showChildren)
2031            {
2032                // <refOrgUnit IDref=""/>
2033                for (String orgUnitID : orgunit.getSubOrgUnits())
2034                {
2035                    try
2036                    {
2037                        OrgUnit orgUnit = _resolver.resolveById(orgUnitID);
2038                        orgUnits.add(orgUnit);
2039                        attrs = new AttributesImpl();
2040                        attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID_REF, orgUnit.getCDMId());
2041                        XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_REF_ORG_UNIT, attrs);
2042                    }
2043                    catch (UnknownAmetysObjectException e)
2044                    {
2045                        // OrgUnit does exist anymore, ignore it
2046                    }
2047                }
2048            }
2049            
2050            // <infoBlock userDefined="ametys-extension>...</infoBlock>
2051            attrs = new AttributesImpl();
2052            attrs.addCDATAAttribute("userDefined", "ametys-extension");
2053            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs);
2054            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
2055            for (String id : _cdmFrExtensionPoint.getExtensionsIds())
2056            {
2057                CDMfrExtension cdmFrExtension = _cdmFrExtensionPoint.getExtension(id);
2058                cdmFrExtension.orgunit2CDM(contentHandler, orgunit);
2059            }
2060            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
2061            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK);
2062                    
2063            // </orgunit>
2064            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ORG_UNIT);
2065    
2066            for (OrgUnit childOrgunit : orgUnits)
2067            {
2068                _orgunit2CDM(contentHandler, childOrgunit, persons);
2069            }
2070        }
2071        finally
2072        {
2073            request.setAttribute(Content.class.getName(), currentContent);
2074        }
2075    }
2076    
2077    
2078    //***********************************************************************//
2079    //                              PERSON                                   //
2080    //***********************************************************************//
2081    
2082    /**
2083     * Export this entity in CDM-fr format.
2084     * @param contentHandler the target handler
2085     * @param person the person
2086     * @throws SAXException if an error occurs during export.
2087     */
2088    public void person2CDM(ContentHandler contentHandler, Person person) throws SAXException
2089    {
2090        Request request = ContextHelper.getRequest(_context);
2091        Content currentContent = (Content) request.getAttribute(Content.class.getName());
2092        
2093        try
2094        {
2095            request.setAttribute(Content.class.getName(), person);
2096            
2097            AttributesImpl attrs = new AttributesImpl();
2098            
2099            // <person id="FRUAI{rootOrgUnitId}PE{login}">
2100            attrs.addCDATAAttribute(CDMFRTagsConstants.ATTRIBUTE_ID, person.getCDMId());
2101            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_PERSON, attrs);
2102            
2103            // <personID>login</personID>
2104            String login = person.getLogin();
2105            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_PERSON_ID, StringUtils.isNotEmpty(login) ? login : person.getName());
2106            
2107            // <name>
2108            //      <given>prénom</given>
2109            //      <family>nom</family>
2110            // </name>
2111            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_NAME);
2112            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_GIVEN_NAME, person.getGivenName());
2113            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_FAMILY_NAME, person.getLastName());
2114            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_NAME);
2115    
2116            // <title><text>titre</text></title>
2117            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_TITLE);
2118            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_TEXT, person.getPersonTitle());
2119            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_TITLE);
2120            
2121            // <role><text>role</text></role>
2122            for (String role : person.getRole())
2123            {
2124                XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ROLE);
2125                XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_TEXT, role);
2126                XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ROLE);
2127            }
2128            
2129            // <affiliation>
2130            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_AFFILIATION);
2131            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_REF_ORG_UNIT);
2132            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_PROFESS_FIELD);
2133            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_SECT_CNU);
2134            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.NAMESPACE_CDMFR + CDMFRTagsConstants.TAG_AFFILIATION);
2135            
2136            
2137            // <contactData>
2138            ContactData contactData = person.getContactData();
2139            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_CONTACT_DATA);
2140            
2141            // <adr>
2142            //      <extadr>extadr</extadr>
2143            //      <street>address</street>
2144            //      <locality>locality</locality>
2145            //      <pcode>pcode</pcode>
2146            // </adr>
2147            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_ADDRESS);
2148            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_EXTADR, contactData.getAdditionalAddress());
2149            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_STREET, contactData.getAddress());
2150            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_LOCALITY, contactData.getTown());
2151            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_PCODE, contactData.getZipCode());
2152            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_ADDRESS);
2153            
2154            // <telephone>telephone</telephone>
2155            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_PHONE, contactData.getPhone());
2156            
2157            // <fax>fax</fax>
2158            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_FAX, contactData.getFax());
2159            
2160            // <email>email</email>
2161            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_EMAIL, contactData.getMail());
2162            
2163            // <webLink>
2164            //      <href>href</href>
2165            //      <linkName>linkName</linkName>
2166            // </webLink>
2167            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_WEB_LINK);
2168            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_HREF, contactData.getWebLinkUrl());
2169            XMLUtils.createElement(contentHandler, CDMFRTagsConstants.TAG_LINK_NAME, contactData.getWebLinkLabel());
2170            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_WEB_LINK);
2171            
2172            // </contactData>
2173            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_CONTACT_DATA);
2174            
2175            // <infoBlock>...</infoBlock>
2176            CDMHelper.richText2CDM(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, person.getAdditionalInformations(), _sourceResolver);
2177            
2178            // <infoBlock userDefined="ametys-extension>...</infoBlock>
2179            attrs = new AttributesImpl();
2180            attrs.addCDATAAttribute("userDefined", "ametys-extension");
2181            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK, attrs);
2182            XMLUtils.startElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
2183            for (String id : _cdmFrExtensionPoint.getExtensionsIds())
2184            {
2185                CDMfrExtension cdmFrExtension = _cdmFrExtensionPoint.getExtension(id);
2186                cdmFrExtension.person2CDM(contentHandler, person);
2187            }
2188            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_EXTENSION_BLOCK);
2189            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_INFO_BLOCK);
2190    
2191            // </person>
2192            XMLUtils.endElement(contentHandler, CDMFRTagsConstants.TAG_PERSON);
2193        }
2194        finally
2195        {
2196            request.setAttribute(Content.class.getName(), currentContent);
2197        }
2198    }
2199    
2200    
2201    //***********************************************************************//
2202    //                               MISC                                    //
2203    //***********************************************************************//
2204
2205    /**
2206     * SAX value as attributes if not null nor empty
2207     * @param attrs the XML attributes
2208     * @param localName the local name
2209     * @param value the value
2210     */
2211    protected void _addNotNullAttribute(AttributesImpl attrs, String localName, String value)
2212    {
2213        if (StringUtils.isNotEmpty(value))
2214        {
2215            attrs.addCDATAAttribute(localName, value);
2216        }
2217    }
2218    
2219    /**
2220     * SAX value as attributes if not null nor empty
2221     * @param attrs the XML attributes
2222     * @param localName the local name
2223     * @param value the value
2224     */
2225    protected void _addPositiveAttribute(AttributesImpl attrs, String localName, double value)
2226    {
2227        if (value > 0)
2228        {
2229            attrs.addCDATAAttribute(localName, String.valueOf(value));
2230        }
2231    }
2232}