001/*
002 *  Copyright 2019 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.odf.content;
017
018import java.io.IOException;
019import java.util.List;
020import java.util.Locale;
021import java.util.Optional;
022
023import org.apache.avalon.framework.service.ServiceException;
024import org.apache.avalon.framework.service.ServiceManager;
025import org.apache.cocoon.ProcessingException;
026import org.apache.cocoon.environment.ObjectModelHelper;
027import org.apache.cocoon.environment.Request;
028import org.apache.cocoon.generation.ServiceableGenerator;
029import org.apache.cocoon.xml.AttributesImpl;
030import org.apache.cocoon.xml.XMLUtils;
031import org.apache.commons.lang.StringUtils;
032import org.xml.sax.SAXException;
033
034import org.ametys.cms.contenttype.ContentTypesHelper;
035import org.ametys.cms.repository.Content;
036import org.ametys.odf.NoLiveVersionException;
037import org.ametys.odf.ODFHelper;
038import org.ametys.odf.ProgramItem;
039import org.ametys.odf.course.Course;
040import org.ametys.odf.courselist.CourseList;
041import org.ametys.odf.coursepart.CoursePart;
042import org.ametys.odf.enumeration.OdfReferenceTableEntry;
043import org.ametys.odf.enumeration.OdfReferenceTableHelper;
044import org.ametys.odf.program.AbstractProgram;
045import org.ametys.odf.program.Container;
046import org.ametys.odf.program.Program;
047import org.ametys.odf.program.SubProgram;
048import org.ametys.plugins.repository.AmetysRepositoryException;
049import org.ametys.plugins.repository.jcr.DefaultAmetysObject;
050import org.ametys.runtime.model.View;
051import org.ametys.runtime.model.exception.BadItemTypeException;
052import org.ametys.runtime.model.type.DataContext;
053
054/**
055 * SAX the structure (ie. the child program items) of a {@link ProgramItem}
056 *
057 */
058public class ProgramItemStructureGenerator extends ServiceableGenerator
059{
060    /** The ODF helper */
061    protected ODFHelper _odfHelper;
062    /** Helper for ODF reference table */
063    protected OdfReferenceTableHelper _odfReferenceTableHelper;
064    /** The content types helper */
065    protected ContentTypesHelper _cTypesHelper;
066    
067    @Override
068    public void service(ServiceManager smanager) throws ServiceException
069    {
070        _odfHelper = (ODFHelper) smanager.lookup(ODFHelper.ROLE);
071        _odfReferenceTableHelper = (OdfReferenceTableHelper) smanager.lookup(OdfReferenceTableHelper.ROLE);
072        _cTypesHelper = (ContentTypesHelper) smanager.lookup(ContentTypesHelper.ROLE);
073    }
074    
075    public void generate() throws IOException, SAXException, ProcessingException
076    {
077        Request request = ObjectModelHelper.getRequest(objectModel);
078        Content content = (Content) request.getAttribute(Content.class.getName());
079
080        String viewName = request.getParameter("viewName");
081        
082        contentHandler.startDocument();
083        
084        if ("main".equals(viewName) || StringUtils.isEmpty(viewName))
085        {
086            if (content instanceof ProgramItem)
087            {
088                XMLUtils.startElement(contentHandler, "structure");
089                saxProgramItem((ProgramItem) content); 
090                XMLUtils.endElement(contentHandler, "structure");
091            }
092            else
093            {
094                getLogger().warn("Cannot get the structure of a non program item '" + content.getId() + "'");
095            }
096        }
097        
098        contentHandler.endDocument();
099    }
100        
101    /**
102     * SAX a program item with its child program items
103     * @param programItem the program item
104     * @throws SAXException if an error occurs while saxing
105     */
106    protected void saxProgramItem(ProgramItem programItem) throws SAXException
107    {
108        if (programItem instanceof Program)
109        {
110            saxProgram((Program) programItem);
111        }
112        else if (programItem instanceof SubProgram)
113        {
114            saxSubProgram((SubProgram) programItem);
115        }
116        else if (programItem instanceof Container)
117        {
118            saxContainer((Container) programItem);
119        }
120        else if (programItem instanceof CourseList)
121        {
122            saxCourseList((CourseList) programItem);
123        }
124        else if (programItem instanceof Course)
125        {
126            saxCourse((Course) programItem);
127        }
128    }
129    
130    /**
131     * SAX the child program items
132     * @param programItem the program item
133     * @throws SAXException if an error occurs while saxing
134     */
135    protected void saxChildProgramItems(ProgramItem programItem) throws SAXException
136    {
137        List<ProgramItem> childProgramItems = _odfHelper.getChildProgramItems(programItem);
138        for (ProgramItem childProgramItem : childProgramItems)
139        {
140            try
141            {
142                _odfHelper.switchToLiveVersionIfNeeded((DefaultAmetysObject) childProgramItem);
143                saxProgramItem(childProgramItem);
144            }
145            catch (NoLiveVersionException e) 
146            {
147                // Just ignore the program item
148            }
149        }
150    }
151        
152    /**
153     * SAX a program
154     * @param program the subprogram to SAX
155     * @throws SAXException if an error occurs
156     */
157    protected void saxProgram(Program program) throws SAXException
158    {
159        AttributesImpl attrs = new AttributesImpl();
160        _saxCommonAttributes(program, attrs);
161        
162        XMLUtils.startElement(contentHandler, "program", attrs);
163        
164        XMLUtils.startElement(contentHandler, "attributes");
165        _saxStructureViewIfExists(program);
166        XMLUtils.endElement(contentHandler, "attributes");
167        
168        saxChildProgramItems(program);
169        XMLUtils.endElement(contentHandler, "program");
170    }
171    
172    /**
173     * SAX a subprogram
174     * @param subProgram the subprogram to SAX
175     * @throws SAXException if an error occurs
176     */
177    protected void saxSubProgram(SubProgram subProgram) throws SAXException
178    {
179        AttributesImpl attrs = new AttributesImpl();
180        _saxCommonAttributes(subProgram, attrs);
181        
182        XMLUtils.startElement(contentHandler, "subprogram", attrs);
183        
184        XMLUtils.startElement(contentHandler, "attributes");
185        _saxReferenceTableItem(subProgram.getEcts(), AbstractProgram.ECTS, subProgram.getLanguage());
186        _saxStructureViewIfExists(subProgram);
187        XMLUtils.endElement(contentHandler, "attributes");
188        
189        saxChildProgramItems(subProgram);
190        
191        XMLUtils.endElement(contentHandler, "subprogram");
192    }
193    
194    /**
195     * SAX a container
196     * @param container the container to SAX
197     * @throws SAXException if an error occurs while saxing
198     */
199    protected void saxContainer(Container container) throws SAXException
200    {
201        AttributesImpl attrs = new AttributesImpl();
202        _saxCommonAttributes(container, attrs);
203        
204        XMLUtils.startElement(contentHandler, "container", attrs);
205        
206        XMLUtils.startElement(contentHandler, "attributes");
207        _saxReferenceTableItem(container.getNature(), Container.NATURE, container.getLanguage());
208        _saxReferenceTableItem(container.getPeriod(), Container.PERIOD, container.getLanguage());
209        
210        _saxStructureViewIfExists(container);
211        
212        XMLUtils.endElement(contentHandler, "attributes");
213        
214        saxChildProgramItems(container);
215        
216        XMLUtils.endElement(contentHandler, "container");
217    }
218    
219    /**
220     * SAX a course list
221     * @param cl the course list to SAX
222     * @throws SAXException if an error occurs while saxing
223     */
224    protected void saxCourseList(CourseList cl) throws SAXException
225    {
226        AttributesImpl attrs = new AttributesImpl();
227        _saxCommonAttributes(cl, attrs);
228        
229        XMLUtils.startElement(contentHandler, "courselist", attrs);
230        
231        XMLUtils.startElement(contentHandler, "attributes");
232        _saxStructureViewIfExists(cl);
233        XMLUtils.endElement(contentHandler, "attributes");
234        
235        saxChildProgramItems(cl);
236        
237        XMLUtils.endElement(contentHandler, "courselist");
238    }
239    
240    /**
241     * SAX a course
242     * @param course the container to SAX
243     * @throws SAXException if an error occurs while saxing
244     */
245    protected void saxCourse(Course course) throws SAXException
246    {
247        AttributesImpl attrs = new AttributesImpl();
248        _saxCommonAttributes(course, attrs);
249        
250        XMLUtils.startElement(contentHandler, "course", attrs);
251        
252        XMLUtils.startElement(contentHandler, "attributes");
253        _saxReferenceTableItem(course.getCourseType(), Course.COURSE_TYPE, course.getLanguage());
254        _saxStructureViewIfExists(course);
255        XMLUtils.endElement(contentHandler, "attributes");
256        
257        saxChildProgramItems(course);
258        
259        saxCourseParts(course);
260        
261        XMLUtils.endElement(contentHandler, "course");
262    }
263    
264    /**
265     * SAX a course part
266     * @param course The course
267     * @throws SAXException if an error occurs
268     */
269    protected void saxCourseParts(Course course) throws SAXException
270    {
271        List<CoursePart> courseParts = course.getCourseParts();
272        
273        double totalHours = 0;
274        for (CoursePart coursePart : course.getCourseParts())
275        {
276            totalHours += coursePart.getNumberOfHours();
277        }
278        
279        AttributesImpl attrs = new AttributesImpl();
280        attrs.addCDATAAttribute("totalHours", String.valueOf(totalHours));
281        XMLUtils.startElement(contentHandler, "courseparts", attrs);
282
283        for (CoursePart coursePart : courseParts)
284        {
285            saxCoursePart(coursePart);
286        }
287        
288        XMLUtils.endElement(contentHandler, "courseparts");
289    }
290    
291    /**
292     * SAX a course part
293     * @param coursePart The course part to SAX
294     * @throws SAXException if an error occurs
295     */
296    protected void saxCoursePart(CoursePart coursePart) throws SAXException
297    {
298        AttributesImpl attrs = new AttributesImpl();
299        attrs.addCDATAAttribute("id", coursePart.getId());
300        attrs.addCDATAAttribute("title", coursePart.getTitle());
301        _addAttrIfNotEmpty(attrs, "code", coursePart.getCode());
302
303        XMLUtils.startElement(contentHandler, "coursepart", attrs);
304        
305        XMLUtils.startElement(contentHandler, "attributes");
306        _saxReferenceTableItem(coursePart.getNature(), CoursePart.NATURE, coursePart.getLanguage());
307        
308        _saxStructureViewIfExists(coursePart);
309        
310        XMLUtils.endElement(contentHandler, "attributes");
311        
312        XMLUtils.endElement(contentHandler, "coursepart");
313    }
314    
315    /**
316     * SAX the 'structure' view if exists
317     * @param content the content
318     * @throws SAXException if an error occurs
319     */
320    protected void _saxStructureViewIfExists(Content content) throws SAXException
321    {
322        View view = _cTypesHelper.getView("structure", content.getTypes(), content.getMixinTypes());
323        if (view != null)
324        {
325            try
326            {
327                content.dataToSAX(contentHandler, view, DataContext.newInstance().withLocale(new Locale(content.getLanguage())).withEmptyValues(false));
328            }
329            catch (BadItemTypeException | AmetysRepositoryException | IOException e)
330            {
331                throw new SAXException("Fail to sax the 'structure' view for content " + content.getId(), e);
332            }
333        }
334    }
335    
336    /**
337     * SAX the common attributes for program item
338     * @param programItem the program item
339     * @param attrs the attributes
340     */
341    protected void _saxCommonAttributes(ProgramItem programItem, AttributesImpl attrs)
342    {
343        attrs.addCDATAAttribute("title", ((Content) programItem).getTitle());
344        attrs.addCDATAAttribute("id", programItem.getId());
345        attrs.addCDATAAttribute("code", programItem.getCode());
346        attrs.addCDATAAttribute("name", programItem.getName());
347    }
348    
349    /**
350     * SAX the item of a reference table
351     * @param itemId the item id
352     * @param tagName the tag name
353     * @param lang the language to use
354     * @throws SAXException if an error occurs while saxing
355     */
356    protected void _saxReferenceTableItem(String itemId, String tagName, String lang) throws SAXException
357    {
358        OdfReferenceTableEntry item = Optional.ofNullable(itemId)
359                                              .filter(StringUtils::isNotEmpty)
360                                              .map(_odfReferenceTableHelper::getItem)
361                                              .orElse(null);
362        
363        if (item != null)
364        {
365            AttributesImpl attrs = new AttributesImpl();
366            attrs.addCDATAAttribute("id", item.getId());
367            _addAttrIfNotEmpty(attrs, "code", item.getCode());
368            
369            XMLUtils.createElement(contentHandler, tagName, attrs, item.getLabel(lang));
370        }
371    }
372    
373    /**
374     * Add an attribute if its not null or empty.
375     * @param attrs The attributes
376     * @param attrName The attribute name
377     * @param attrValue The attribute value
378     */
379    protected void _addAttrIfNotEmpty(AttributesImpl attrs, String attrName, String attrValue)
380    {
381        if (StringUtils.isNotEmpty(attrValue))
382        {
383            attrs.addCDATAAttribute(attrName, attrValue);
384        }
385    }
386
387}