001/*
002 *  Copyright 2013 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.cms.content;
017
018import java.io.IOException;
019import java.util.Comparator;
020import java.util.HashMap;
021import java.util.Locale;
022import java.util.Map;
023import java.util.Set;
024import java.util.TreeSet;
025
026import org.apache.avalon.framework.service.ServiceException;
027import org.apache.avalon.framework.service.ServiceManager;
028import org.apache.cocoon.ProcessingException;
029import org.apache.cocoon.environment.ObjectModelHelper;
030import org.apache.cocoon.environment.Request;
031import org.apache.cocoon.generation.ServiceableGenerator;
032import org.apache.cocoon.xml.AttributesImpl;
033import org.apache.cocoon.xml.XMLUtils;
034import org.apache.commons.lang3.StringUtils;
035import org.xml.sax.SAXException;
036
037import org.ametys.cms.contenttype.ContentType;
038import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
039import org.ametys.cms.repository.Content;
040import org.ametys.cms.tag.Tag;
041import org.ametys.cms.tag.TagProviderExtensionPoint;
042import org.ametys.plugins.repository.AmetysObjectResolver;
043import org.ametys.plugins.repository.version.VersionableAmetysObject;
044import org.ametys.runtime.i18n.I18nizableText;
045
046/**
047 * Generates addition information on content
048 */
049public class AdditionalContentPropertiesGenerator extends ServiceableGenerator
050{
051    /** The AmetysObject resolver. */
052    protected AmetysObjectResolver _resolver;
053    private ContentTypeExtensionPoint _contentTypeEP;
054    private TagProviderExtensionPoint _tagProviderEP;
055    
056    @Override
057    public void service(ServiceManager serviceManager) throws ServiceException
058    {
059        super.service(serviceManager);
060        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
061        _contentTypeEP = (ContentTypeExtensionPoint) serviceManager.lookup(ContentTypeExtensionPoint.ROLE);
062        _tagProviderEP = (TagProviderExtensionPoint) serviceManager.lookup(TagProviderExtensionPoint.ROLE);
063    }
064    
065    @Override
066    public void generate() throws IOException, SAXException, ProcessingException
067    {
068        Request request = ObjectModelHelper.getRequest(objectModel);
069        
070        String contentId = parameters.getParameter("contentId", request.getParameter("contentId"));
071        
072        Content content = null;
073        if (StringUtils.isNotEmpty(contentId))
074        {
075            content = _resolver.resolveById(contentId);
076        }
077        else
078        {
079            content = (Content) request.getAttribute(Content.class.getName());
080        }
081        
082        contentHandler.startDocument();
083        
084        AttributesImpl attrs = new AttributesImpl();
085        attrs.addCDATAAttribute("id", content.getId());
086        XMLUtils.startElement(contentHandler, "content", attrs);
087        
088        // Take the HEAD revision
089        String revision = null;
090        if (content instanceof VersionableAmetysObject)
091        {
092            revision = ((VersionableAmetysObject) content).getRevision();
093            ((VersionableAmetysObject) content).switchToRevision(null);
094        }
095        
096        _saxContentTypesHierarchy (content);
097        _saxMixinsHierarchy(content);
098        _saxReferencingContents(content);
099        _saxTags(content);
100        _saxOtherProperties(content);
101        
102        if (content instanceof VersionableAmetysObject && revision != null)
103        {
104            ((VersionableAmetysObject) content).switchToRevision(revision);
105        }
106        
107        XMLUtils.endElement(contentHandler, "content");
108        contentHandler.endDocument();
109    }
110    
111    /**
112     * SAX other additional properties
113     * @param content the content 
114     * @throws SAXException if an error occurred while saxing
115     */
116    protected void _saxOtherProperties (Content content) throws SAXException
117    {
118        // Nothing to do
119    }
120    
121    /**
122     * SAX content's tags
123     * @param content The content
124     * @throws SAXException if an error occurred
125     */
126    protected void _saxTags (Content content) throws SAXException
127    {
128        Set<String> tags = content.getTags();
129        
130        XMLUtils.startElement(contentHandler, "tags");
131        
132        for (String tagName : tags)
133        {
134            Tag tag = _tagProviderEP.getTag(tagName, getContextualParameters(content));
135            if (tag != null)
136            {
137                AttributesImpl attrs = new AttributesImpl();
138                attrs.addCDATAAttribute("name", tag.getName());
139                XMLUtils.startElement(contentHandler, "tag", attrs);
140                tag.getTitle().toSAX(contentHandler);
141                XMLUtils.endElement(contentHandler, "tag");
142            }
143        }
144        
145        XMLUtils.endElement(contentHandler, "tags");
146    }
147    
148    /**
149     * Get the contextual parameters for content
150     * @param content the content
151     * @return the content's contextual parameters
152     */
153    protected Map<String, Object> getContextualParameters (Content content)
154    {
155        return new HashMap<>();
156    }
157    
158    /**
159     * Sax the referencing contents
160     * @param content The content
161     * @throws SAXException if an error occurred while saxing
162     */
163    protected void _saxReferencingContents (Content content) throws SAXException
164    {
165        XMLUtils.startElement(contentHandler, "referencing-contents");
166        
167        Locale locale = content.getLanguage() != null ? new Locale(content.getLanguage()) : null;
168        for (Content referrerContent : content.getReferencingContents())
169        {
170            AttributesImpl refererAttrs = new AttributesImpl();
171            refererAttrs.addCDATAAttribute("id", referrerContent.getId());
172            refererAttrs.addCDATAAttribute("name", referrerContent.getName());
173            refererAttrs.addCDATAAttribute("title", referrerContent.getTitle(locale));
174            
175            XMLUtils.createElement(contentHandler, "referencing-content", refererAttrs);
176        }
177        
178        XMLUtils.endElement(contentHandler, "referencing-contents");
179    }
180    
181    /**
182     * SAX the content types hierarchy
183     * @param content The content type
184     * @throws SAXException if an error occurred while saxing
185     */
186    protected void _saxContentTypesHierarchy (Content content) throws SAXException
187    {
188        // Sort content types
189        TreeSet<ContentType> treeSet = new TreeSet<>(new ContentTypeComparator());
190        for (String id : content.getTypes())
191        {
192            ContentType contentType = _contentTypeEP.getExtension(id);
193            treeSet.add(contentType);
194        }
195        
196        XMLUtils.startElement(contentHandler, "content-types");
197        for (ContentType cType : treeSet)
198        {
199            _saxContentType (cType);
200        }
201        XMLUtils.endElement(contentHandler, "content-types");
202    }
203    
204    /**
205     * SAX the content types hierarchy
206     * @param content The content type
207     * @throws SAXException if an error occurred while saxing
208     */
209    protected void _saxMixinsHierarchy (Content content) throws SAXException
210    {
211        XMLUtils.startElement(contentHandler, "mixins");
212        
213        String[] contentTypes = content.getMixinTypes();
214        for (String id : contentTypes)
215        {
216            ContentType cType = _contentTypeEP.getExtension(id);
217            _saxMixin(cType);
218        }
219        
220        XMLUtils.endElement(contentHandler, "mixins");
221    }
222    
223    /**
224     * SAX the content type hierarchy
225     * @param cType The content type
226     * @throws SAXException if an error occurred while saxing
227     */
228    protected void _saxContentType (ContentType cType) throws SAXException
229    {
230        AttributesImpl attrs = new AttributesImpl();
231        
232        attrs.addCDATAAttribute("id", cType.getId());
233        
234        if (cType.getSmallIcon() != null)
235        {
236            attrs.addCDATAAttribute("icon", cType.getSmallIcon());
237        }
238        if (cType.getIconGlyph() != null)
239        {
240            attrs.addCDATAAttribute("icon-glyph", cType.getIconGlyph());
241        }
242        if (cType.getIconDecorator() != null)
243        {
244            attrs.addCDATAAttribute("icon-decorator", cType.getIconDecorator());
245        }
246        
247        XMLUtils.startElement(contentHandler, "content-type", attrs);
248        cType.getLabel().toSAX(contentHandler, "label");
249        
250        for (String id : cType.getSupertypeIds())
251        {
252            ContentType superType = _contentTypeEP.getExtension(id);
253            _saxContentType(superType);
254        }
255        XMLUtils.endElement(contentHandler, "content-type");
256    }
257    
258    /**
259     * SAX the mixin hierarchy
260     * @param mixin The mixin type
261     * @throws SAXException if an error occurred while saxing
262     */
263    protected void _saxMixin (ContentType mixin) throws SAXException
264    {
265        AttributesImpl attrs = new AttributesImpl();
266        
267        attrs.addCDATAAttribute("id", mixin.getId());
268        if (mixin.getSmallIcon() != null)
269        {
270            attrs.addCDATAAttribute("icon", mixin.getSmallIcon());
271        }
272        if (mixin.getIconGlyph() != null)
273        {
274            attrs.addCDATAAttribute("icon-glyph", mixin.getIconGlyph());
275        }
276        if (mixin.getIconDecorator() != null)
277        {
278            attrs.addCDATAAttribute("icon-decorator", mixin.getIconDecorator());
279        }
280        
281        XMLUtils.startElement(contentHandler, "mixin", attrs);
282        mixin.getLabel().toSAX(contentHandler, "label");
283        for (String id : mixin.getSupertypeIds())
284        {
285            ContentType superType = _contentTypeEP.getExtension(id);
286            _saxMixin(superType);
287        }
288        XMLUtils.endElement(contentHandler, "mixin");
289    }
290
291    class ContentTypeComparator implements Comparator<ContentType>
292    {
293        @Override
294        public int compare(ContentType c1, ContentType c2)
295        {
296            I18nizableText t1 = c1.getLabel();
297            I18nizableText t2 = c2.getLabel();
298            
299            String str1 = t1.isI18n() ? t1.getKey() : t1.getLabel();
300            String str2 = t2.isI18n() ? t2.getKey() : t2.getLabel();
301            
302            int compareTo = str1.toString().compareTo(str2.toString());
303            if (compareTo == 0)
304            {
305                // Content types have same keys but there are not equals, so do not return 0 to add it in TreeSet
306                // Indeed, in a TreeSet implementation two elements that are equal by the method compareTo are, from the standpoint of the set, equal 
307                return 1;
308            }
309            return compareTo;
310        }
311    }
312}