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.export.pdf;
017
018import java.io.IOException;
019import java.net.MalformedURLException;
020import java.util.ArrayList;
021import java.util.List;
022import java.util.Map;
023import java.util.Optional;
024import java.util.stream.Stream;
025
026import org.apache.avalon.framework.parameters.ParameterException;
027import org.apache.avalon.framework.service.ServiceException;
028import org.apache.avalon.framework.service.ServiceManager;
029import org.apache.cocoon.ProcessingException;
030import org.apache.cocoon.components.source.impl.SitemapSource;
031import org.apache.cocoon.environment.ObjectModelHelper;
032import org.apache.cocoon.generation.ServiceableGenerator;
033import org.apache.cocoon.xml.AttributesImpl;
034import org.apache.cocoon.xml.XMLUtils;
035import org.apache.commons.lang3.StringUtils;
036import org.xml.sax.SAXException;
037
038import org.ametys.cms.contenttype.ContentAttributeDefinition;
039import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
040import org.ametys.cms.repository.Content;
041import org.ametys.cms.repository.ContentTypeExpression;
042import org.ametys.cms.repository.LanguageExpression;
043import org.ametys.core.util.IgnoreRootHandler;
044import org.ametys.core.util.LambdaUtils;
045import org.ametys.odf.ODFHelper;
046import org.ametys.odf.ProgramItem;
047import org.ametys.odf.catalog.Catalog;
048import org.ametys.odf.catalog.CatalogsManager;
049import org.ametys.odf.enumeration.OdfReferenceTableEntry;
050import org.ametys.odf.enumeration.OdfReferenceTableHelper;
051import org.ametys.odf.orgunit.OrgUnit;
052import org.ametys.odf.program.AbstractProgram;
053import org.ametys.odf.program.Program;
054import org.ametys.odf.program.ProgramFactory;
055import org.ametys.plugins.repository.AmetysObjectIterable;
056import org.ametys.plugins.repository.AmetysObjectResolver;
057import org.ametys.plugins.repository.AmetysRepositoryException;
058import org.ametys.plugins.repository.UnknownAmetysObjectException;
059import org.ametys.plugins.repository.query.QueryHelper;
060import org.ametys.plugins.repository.query.SortCriteria;
061import org.ametys.plugins.repository.query.expression.AndExpression;
062import org.ametys.plugins.repository.query.expression.Expression;
063import org.ametys.plugins.repository.query.expression.Expression.Operator;
064import org.ametys.plugins.repository.query.expression.OrExpression;
065import org.ametys.plugins.repository.query.expression.StringExpression;
066import org.ametys.runtime.model.View;
067
068/**
069 * Generator producing the SAX events for the catalogue summary 
070 */ 
071public class FOProgramsGenerator extends ServiceableGenerator
072{
073    /** The Ametys object resolver */
074    protected AmetysObjectResolver _resolver;
075    /** The content type extension point */
076    protected ContentTypeExtensionPoint _ctypeEP;
077    /** The ODf helper */
078    protected ODFHelper _odfHelper;
079    /** The ODf enumeration helper */
080    protected OdfReferenceTableHelper _odfTableRefHelper;
081    /** The catalog manager */
082    protected CatalogsManager _catalogManager;
083    
084    @Override
085    public void service(ServiceManager sManager) throws ServiceException
086    {
087        super.service(sManager);
088        _resolver = (AmetysObjectResolver) sManager.lookup(AmetysObjectResolver.ROLE);
089        _ctypeEP = (ContentTypeExtensionPoint) sManager.lookup(ContentTypeExtensionPoint.ROLE);
090        _odfHelper = (ODFHelper) sManager.lookup(ODFHelper.ROLE);
091        _odfTableRefHelper = (OdfReferenceTableHelper) sManager.lookup(OdfReferenceTableHelper.ROLE);
092        _catalogManager = (CatalogsManager) sManager.lookup(CatalogsManager.ROLE);
093    }
094    
095    public void generate() throws IOException, SAXException, ProcessingException
096    {
097        contentHandler.startDocument();
098        XMLUtils.startElement(contentHandler, "programs");
099        
100        Catalog catalog = null;
101        if ("_default".equals(source) && _catalogManager.getCatalogs().size() > 0)
102        {
103            catalog = _catalogManager.getCatalogs().get(0);
104        }
105        else
106        {
107            catalog = _catalogManager.getCatalog(source);
108        }
109        
110        if (catalog == null)
111        {
112            throw new IllegalArgumentException ("Failed to generated PDF of unknown catalog '" + source + "'");
113        }
114
115        String lang;
116        try
117        {
118            lang = parameters.getParameter("lang");
119        }
120        catch (ParameterException e)
121        {
122            throw new IllegalArgumentException ("Missing lang parameter", e);
123        }
124        
125        // Catalog
126        AttributesImpl attrs = new AttributesImpl();
127        attrs.addCDATAAttribute("name", source);
128        XMLUtils.createElement(contentHandler, "catalog", attrs, catalog.getTitle());
129
130        Map parentContext = (Map) objectModel.get(ObjectModelHelper.PARENT_CONTEXT);
131        
132        // OrgUnit
133        OrgUnit orgUnit = Optional.of("orgunit")
134                .map(parentContext::get)
135                .map(Object::toString)
136                .map(LambdaUtils.wrap(_resolver::<OrgUnit>resolveById))
137                .orElse(null);
138        if (orgUnit != null)
139        {
140            attrs.clear();
141            attrs.addCDATAAttribute("id", orgUnit.getId());
142            attrs.addCDATAAttribute("uaiCode", orgUnit.getUAICode());
143            XMLUtils.createElement(contentHandler, "orgunit", attrs, orgUnit.getTitle());
144        }
145        
146        // Degree
147        OdfReferenceTableEntry degree = Optional.of("degree")
148            .map(parentContext::get)
149            .map(Object::toString)
150            .filter(StringUtils::isNotEmpty)
151            .map(_odfTableRefHelper::getItem)
152            .orElse(null);
153        if (degree != null)
154        {
155            attrs.clear();
156            attrs.addCDATAAttribute("id", degree.getId());
157            attrs.addCDATAAttribute("code", degree.getCode());
158            attrs.addCDATAAttribute("order", String.valueOf(degree.getOrder()));
159            XMLUtils.createElement(contentHandler, "degree", attrs, degree.getLabel(lang));
160        }
161        
162        Map<String, ContentAttributeDefinition> tableRefAttributeDefs = _odfTableRefHelper.getTableRefAttributeDefinitions(ProgramFactory.PROGRAM_CONTENT_TYPE);
163        
164        _saxPrograms(_getPrograms(catalog.getName(), lang, orgUnit, degree), tableRefAttributeDefs);
165        
166        // SAX entries of table references
167        XMLUtils.startElement(contentHandler, "enumerated-metadata");
168        for (ContentAttributeDefinition attributeDef : tableRefAttributeDefs.values())
169        {
170            _odfTableRefHelper.saxItems(contentHandler, attributeDef);
171        }
172        XMLUtils.endElement(contentHandler, "enumerated-metadata");
173        
174        XMLUtils.endElement(contentHandler, "programs");
175        contentHandler.endDocument();
176    }
177    
178    /**
179     * Get programs from catalog, lang, orgunit and degree. Orgunit and degree can be null.
180     * @param catalog The catalog
181     * @param lang The content language
182     * @param orgUnit The root orgunit to display
183     * @param degree The degree
184     * @return An iterable of programs corresponding to the query with catalog, lang, orgunit and degree.
185     */
186    protected AmetysObjectIterable<Program> _getPrograms(String catalog, String lang, OrgUnit orgUnit, OdfReferenceTableEntry degree)
187    {
188        List<Expression> exprs = new ArrayList<>();
189        exprs.add(new ContentTypeExpression(Operator.EQ, ProgramFactory.PROGRAM_CONTENT_TYPE));
190        exprs.add(new StringExpression(ProgramItem.CATALOG, Operator.EQ, catalog));
191        exprs.add(new LanguageExpression(Operator.EQ, lang));
192
193        if (degree != null)
194        {
195            exprs.add(new StringExpression(AbstractProgram.DEGREE, Operator.EQ, degree.getId()));
196        }
197        
198        Expression[] orgUnitExpressions = Optional.ofNullable(orgUnit)
199            .map(_odfHelper::getSubOrgUnitIds)
200            .map(List::stream)
201            .orElseGet(Stream::empty)
202            .map(orgunitId -> new StringExpression(AbstractProgram.ORG_UNITS_REFERENCES, Operator.EQ, orgunitId))
203            .toArray(Expression[]::new);
204        if (orgUnitExpressions.length > 0)
205        {
206            exprs.add(new OrExpression(orgUnitExpressions));
207        }
208        
209        Expression programsExpression = new AndExpression(exprs.toArray(Expression[]::new));
210        
211        SortCriteria sortCriteria = new SortCriteria();
212        sortCriteria.addCriterion(Content.ATTRIBUTE_TITLE, true, true);
213  
214        String programsQuery = QueryHelper.getXPathQuery(null, ProgramFactory.PROGRAM_NODETYPE, programsExpression, sortCriteria);
215        return _resolver.query(programsQuery);
216    }
217    
218    /**
219     * SAX programs
220     * @param programs The programs to sax
221     * @param tableRefAttributeDefs The table reference attribute definitions
222     * @throws MalformedURLException if an error occurred
223     * @throws IOException if an error occurred
224     * @throws SAXException if an error occurred
225     */
226    protected void _saxPrograms(AmetysObjectIterable<Program> programs, Map<String, ContentAttributeDefinition> tableRefAttributeDefs) throws MalformedURLException, IOException, SAXException
227    {
228        for (Program program : programs)
229        {
230            AttributesImpl attrs = new AttributesImpl();
231            attrs.addCDATAAttribute("id", program.getId());
232            attrs.addCDATAAttribute("name", program.getName());
233            attrs.addCDATAAttribute("title", program.getTitle());
234            
235            XMLUtils.startElement(contentHandler, "program", attrs);
236            
237            _saxTableRefAttributeValues(program, tableRefAttributeDefs);
238            
239            XMLUtils.startElement(contentHandler, "fo");
240            SitemapSource src = null;      
241            
242            try
243            {
244                String uri = "cocoon://_plugins/odf/_content/" + program.getName() + ".fo";
245                src = (SitemapSource) resolver.resolveURI(uri);
246                src.toSAX(new IgnoreRootHandler(contentHandler));
247            }
248            catch (UnknownAmetysObjectException e)
249            {
250                // The content may be archived
251            }
252            finally
253            {
254                resolver.release(src);
255            }
256            
257            XMLUtils.endElement(contentHandler, "fo");
258            XMLUtils.endElement(contentHandler, "program");
259        }
260    }
261    
262    /**
263     * SAX enumerated values of an attribute 
264     * @param program The program
265     * @param tableRefAttributeDefs The table reference attribute definitions
266     * @throws AmetysRepositoryException if an error occurred
267     * @throws SAXException if an error occurred
268     * @throws IOException if an error occurred
269     */
270    protected void _saxTableRefAttributeValues(Program program, Map<String, ContentAttributeDefinition> tableRefAttributeDefs) throws AmetysRepositoryException, SAXException, IOException
271    {
272        // Build a view containing all the reference tables attributes
273        View view = View.of(program.getModel(), tableRefAttributeDefs.keySet().toArray(new String[tableRefAttributeDefs.size()]));
274        
275        // Generate SAX events for the built view
276        XMLUtils.startElement(contentHandler, "metadata");
277        program.dataToSAX(contentHandler, view);
278        XMLUtils.endElement(contentHandler, "metadata");
279    }
280}