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.Comparator;
020import java.util.List;
021import java.util.stream.Collectors;
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.lang3.StringUtils;
032import org.xml.sax.SAXException;
033
034import org.ametys.cms.repository.Content;
035import org.ametys.odf.ODFHelper;
036import org.ametys.odf.ProgramItem;
037import org.ametys.odf.data.EducationalPath;
038import org.ametys.plugins.repository.AmetysObjectResolver;
039
040/**
041 * Generates additional information about a ODF content
042 *
043 */
044public class AdditionalOdfContentPropertiesGenerator extends ServiceableGenerator
045{
046    private static final ListProgramItemComparator __LIST_PROGRAM_ITEM_COMPARATOR = new ListProgramItemComparator();
047    
048    /** The AmetysObject resolver. */
049    protected AmetysObjectResolver _resolver;
050    /** The ODF helper */
051    protected ODFHelper _odfHelper;
052    
053    @Override
054    public void service(ServiceManager serviceManager) throws ServiceException
055    {
056        super.service(serviceManager);
057        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
058        _odfHelper = (ODFHelper) serviceManager.lookup(ODFHelper.ROLE);
059    }
060    
061    @Override
062    public void generate() throws IOException, SAXException, ProcessingException
063    {
064        Request request = ObjectModelHelper.getRequest(objectModel);
065        
066        String contentId = parameters.getParameter("contentId", request.getParameter("contentId"));
067        
068        Content content = null;
069        if (StringUtils.isNotEmpty(contentId))
070        {
071            content = _resolver.resolveById(contentId);
072        }
073        else
074        {
075            content = (Content) request.getAttribute(Content.class.getName());
076        }
077        
078        contentHandler.startDocument();
079        XMLUtils.startElement(contentHandler, "odf");
080        
081        if (content instanceof ProgramItem)
082        {
083            XMLUtils.startElement(contentHandler, "programItem");
084            saxProgramItemProperties((ProgramItem) content);
085            XMLUtils.endElement(contentHandler, "programItem");
086        }
087        
088        XMLUtils.endElement(contentHandler, "odf");
089        contentHandler.startDocument();
090        contentHandler.endDocument();
091    }
092    
093    /**
094     * SAX specifics properties for a program item
095     * @param programItem the program item
096     * @throws SAXException if an error occurs while saxing
097     */
098    protected void saxProgramItemProperties(ProgramItem programItem) throws SAXException
099    {
100        _saxProgramItemAncestorPaths(programItem);
101        _saxChildProgramItems(programItem);
102    }
103    
104    /**
105     * SAX all paths of program item
106     * @param programItem The program item
107     * @throws SAXException if an error occurs while saxing
108     */
109    protected void _saxProgramItemAncestorPaths(ProgramItem programItem) throws SAXException
110    {
111        if (_odfHelper.hasParentProgramItems(programItem))
112        {
113            List<EducationalPath> educationalPaths = _odfHelper.getEducationalPaths(programItem);
114            if (!educationalPaths.isEmpty())
115            {
116                List<List<ProgramItem>> ancestorPaths = educationalPaths.stream()
117                        .map(p -> p.getProgramItems(_resolver))
118                        .collect(Collectors.toList());
119                
120                if (!ancestorPaths.isEmpty())
121                {
122                    // Sort the list to display it
123                    ancestorPaths.sort(__LIST_PROGRAM_ITEM_COMPARATOR);
124                    
125                    XMLUtils.startElement(contentHandler, "ancestors");
126                    for (List<ProgramItem> ancestorPath : ancestorPaths)
127                    {
128                        XMLUtils.startElement(contentHandler, "path");
129                        for (ProgramItem ancestor : ancestorPath)
130                        {
131                            _saxProgramItem(ancestor);
132                        }
133                        XMLUtils.endElement(contentHandler, "path");
134                    }
135                        
136                    XMLUtils.endElement(contentHandler, "ancestors");
137                }
138            }
139        }
140    }
141    
142    /**
143     * SAX the child program items
144     * @param programItem the program item
145     * @throws SAXException if an error occurs while saxing
146     */
147    protected void _saxChildProgramItems(ProgramItem programItem) throws SAXException
148    {
149        List<ProgramItem> children = _odfHelper.getChildProgramItems(programItem);
150        
151        if (!children.isEmpty())
152        {
153            XMLUtils.startElement(contentHandler, "children");
154            for (ProgramItem child : children)
155            {
156                _saxProgramItem(child);
157            }
158            XMLUtils.endElement(contentHandler, "children");
159        }
160    }
161    
162    /**
163     * Sax a program item
164     * @param item the program item
165     * @throws SAXException if an error occurs while saxing
166     */
167    protected void _saxProgramItem(ProgramItem item) throws SAXException
168    {
169        Content content = (Content) item;
170        AttributesImpl attrs = new AttributesImpl();
171        attrs.addCDATAAttribute("id", content.getId());
172        attrs.addCDATAAttribute("code", item.getCode());
173        XMLUtils.createElement(contentHandler, "item", attrs, content.getTitle());
174    }
175
176    /**
177     * Comparator of {@link List} composing of {@link ProgramItem} by title.
178     */
179    private static final class ListProgramItemComparator implements Comparator<List<ProgramItem>>
180    {
181        public int compare(List<ProgramItem> l1, List<ProgramItem> l2)
182        {
183            int l1Size = l1.size();
184            int l2Size = l2.size();
185            
186            for (int i = 0; i < l1Size; i++)
187            {
188                if (l2Size <= i)
189                {
190                    // l1 is greater than l2
191                    return 1;
192                }
193                else
194                {
195                    // Compare title
196                    int comparing = ((Content) l1.get(i)).getTitle().compareTo(((Content) l2.get(i)).getTitle());
197                    if (comparing != 0)
198                    {
199                        return comparing;
200                    }
201                }
202            }
203            
204            // Lists are the same at the end of reading l1
205            // Then compare size of the lists
206            return Integer.compare(l1Size, l2Size);
207        }
208    }
209}