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