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