001/*
002 *  Copyright 2018 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.contenttype;
017
018import java.io.IOException;
019import java.util.Map;
020
021import org.apache.avalon.framework.parameters.Parameters;
022import org.apache.avalon.framework.service.ServiceException;
023import org.apache.avalon.framework.service.ServiceManager;
024import org.apache.avalon.framework.service.Serviceable;
025import org.apache.cocoon.ProcessingException;
026import org.apache.cocoon.environment.ObjectModelHelper;
027import org.apache.cocoon.environment.Request;
028import org.apache.cocoon.environment.SourceResolver;
029import org.apache.cocoon.generation.AbstractGenerator;
030import org.apache.cocoon.xml.AttributesImpl;
031import org.apache.cocoon.xml.XMLUtils;
032import org.apache.commons.lang3.ArrayUtils;
033import org.xml.sax.SAXException;
034
035import org.ametys.cms.repository.Content;
036import org.ametys.runtime.i18n.I18nizableText;
037
038/**
039 * Generates content type's definition.
040 */
041public class ContentTypeGenerator extends AbstractGenerator implements Serviceable
042{
043    private ContentTypeExtensionPoint _cTypeEp;
044    private ContentTypesHelper _cTypeHelper;
045    
046    public void service(ServiceManager manager) throws ServiceException
047    {
048        _cTypeEp = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE);
049        _cTypeHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE);
050    }
051    
052    @Override
053    public void setup(SourceResolver resolver, Map objectModel, String src, Parameters par) throws ProcessingException, SAXException, IOException
054    {
055        super.setup(resolver, objectModel, src, par);
056    }
057    
058    public void generate() throws IOException, SAXException, ProcessingException
059    {
060        Request request = ObjectModelHelper.getRequest(objectModel);
061        Content content = (Content) request.getAttribute(Content.class.getName());
062        
063        if (content == null)
064        {
065            throw new IllegalArgumentException("Content not present in request. Cannot compute rendering from model.");
066        }
067        
068        String viewName = parameters.getParameter("metadataSet", "main");
069        
070        // TODO NEWATTRIBUTEAPI_CONTENT: Use the new API method (cTypeHelper.getView()) when the API is ready
071//        View view = _cTypeHelper.getView(viewName, content.getTypes(), content.getMixinTypes());
072        MetadataSet metadataSet = _cTypeHelper.getMetadataSetForView(viewName, content.getTypes(), content.getMixinTypes());
073        
074        contentHandler.startDocument();
075        
076        AttributesImpl atts = new AttributesImpl();
077        atts.addCDATAAttribute("id", _cTypeHelper.getContentTypeIdForRendering(content));
078        atts.addCDATAAttribute("metadataSet", viewName);
079        XMLUtils.startElement(contentHandler, "contentType", atts);
080        
081        I18nizableText label = _cTypeHelper.getContentTypeLabel(content);
082        if (label != null)
083        {
084            XMLUtils.startElement(contentHandler, "label");
085            label.toSAX(contentHandler);
086            XMLUtils.endElement(contentHandler, "label");
087        }
088        
089        I18nizableText description = _cTypeHelper.getContentTypeDescription(content);
090        if (description != null)
091        {
092            XMLUtils.startElement(contentHandler, "description");
093            description.toSAX(contentHandler);
094            XMLUtils.endElement(contentHandler, "description");
095        }
096        
097        // TODO NEWATTRIBUTEAPI_CONTENT: Use the new API method (_saxView()) when the API is ready
098//        _saxView(view, null);
099        if (metadataSet != null)
100        {
101            _saxMetadataSetElement(metadataSet, null, content.getTypes(), content.getMixinTypes());
102        }
103        
104        XMLUtils.endElement(contentHandler, "contentType");
105        contentHandler.endDocument();
106    }
107    
108    // TODO NEWATTRIBUTEAPI_CONTENT: Look what is wrong in this new API method
109//    private void _saxView(ViewItemContainer view, String prefix) throws SAXException
110//    {
111//        for (ViewItem item : view.getViewItems())
112//        {
113//            if (item instanceof ModelViewItem)
114//            {
115//                ModelViewItem modelViewItem = (ModelViewItem) item;
116//                ModelItem modelItem = modelViewItem.getDefinition();
117//                String name = modelItem.getName();
118//                
119//                AttributesImpl atts = new AttributesImpl();
120//                atts.addCDATAAttribute("name", name);
121//                atts.addCDATAAttribute("path", prefix == null ? name : prefix + name);
122//
123//                if (modelItem instanceof ElementDefinition)
124//                {
125//                    ElementDefinition definition = (ElementDefinition) modelItem;
126//                    
127//                    atts.addCDATAAttribute("type", definition.getType().getId());
128//                    atts.addCDATAAttribute("multiple", String.valueOf(definition.isMultiple()));
129//                }
130//                else if (modelItem instanceof CompositeDefinition)
131//                {
132//                    atts.addCDATAAttribute("type", "composite");
133//                }
134//                else if (modelItem instanceof org.ametys.plugins.repository.model.RepeaterDefinition)
135//                {
136//                    atts.addCDATAAttribute("type", "repeater");
137//                }
138//                
139//                XMLUtils.startElement(contentHandler, "metadata", atts);
140//
141//                I18nizableText label = modelItem.getLabel();
142//                if (label != null)
143//                {
144//                    XMLUtils.startElement(contentHandler, "label");
145//                    label.toSAX(contentHandler);
146//                    XMLUtils.endElement(contentHandler, "label");
147//                }
148//                
149//                I18nizableText description = modelItem.getDescription();
150//                if (description != null)
151//                {
152//                    XMLUtils.startElement(contentHandler, "description");
153//                    description.toSAX(contentHandler);
154//                    XMLUtils.endElement(contentHandler, "description");
155//                }
156//                
157//                if (item instanceof ViewItemContainer)
158//                {
159//                    _saxView((ViewItemContainer) item, prefix == null ? name  + "/" : prefix + name + "/");
160//                }
161//
162//                XMLUtils.endElement(contentHandler, "metadata");
163//            }
164//            else if (item instanceof ViewItemGroup)
165//            {
166//                ViewItemGroup group = (ViewItemGroup) item;
167//                AttributesImpl atts = new AttributesImpl();
168//                
169//                if (group.getRole() != null)
170//                {
171//                    atts.addCDATAAttribute("role", group.getRole());
172//                }
173//                
174//                XMLUtils.startElement(contentHandler, "fieldset", atts);
175//                
176//                if (item instanceof Labelable)
177//                {
178//                    I18nizableText label = ((Labelable) item).getLabel();
179//                    if (label != null)
180//                    {
181//                        XMLUtils.startElement(contentHandler, "label");
182//                        label.toSAX(contentHandler);
183//                        XMLUtils.endElement(contentHandler, "label");
184//                    }
185//                }
186//                
187//                _saxView(group, prefix);
188//                
189//                XMLUtils.endElement(contentHandler, "fieldset");
190//            }
191//        }
192//    }
193    
194    private void _saxMetadataSetElement(AbstractMetadataSetElement container, String prefix, String[] contentTypes, String[] mixinTypes) throws SAXException
195    {
196        for (AbstractMetadataSetElement element : container.getElements())
197        {
198            if (element instanceof Fieldset)
199            {
200                AttributesImpl atts = new AttributesImpl();
201                
202                if (((Fieldset) element).getRole() != null)
203                {
204                    atts.addCDATAAttribute("role", ((Fieldset) element).getRole());
205                }
206                
207                XMLUtils.startElement(contentHandler, "fieldset", atts);
208                
209                I18nizableText label = ((Fieldset) element).getLabel();
210                if (label != null)
211                {
212                    XMLUtils.startElement(contentHandler, "label");
213                    label.toSAX(contentHandler);
214                    XMLUtils.endElement(contentHandler, "label");
215                }
216                
217                _saxMetadataSetElement(element, prefix, contentTypes, mixinTypes);
218                
219                XMLUtils.endElement(contentHandler, "fieldset");
220            }
221            else if (element instanceof MetadataDefinitionReference)
222            {
223                MetadataDefinitionReference metadataDefRef = (MetadataDefinitionReference) element;
224                String metadataName = metadataDefRef.getMetadataName();
225//                MetadataDefinition metadataDefinition = contentType.getMetadataDefinitionByPath(prefix == null ? metadataName : prefix + metadataName);
226                MetadataDefinition metadataDefinition = _getDefinition(prefix == null ? metadataName : prefix + metadataName, contentTypes, mixinTypes);
227                MetadataType metadataType = metadataDefinition.getType();
228                String type = (metadataType == MetadataType.COMPOSITE && metadataDefinition instanceof RepeaterDefinition) ? "repeater" : metadataType.toString().toLowerCase();
229                
230                AttributesImpl atts = new AttributesImpl();
231                atts.addCDATAAttribute("name", metadataName);
232                atts.addCDATAAttribute("path", prefix == null ? metadataName : prefix + metadataName);
233                atts.addCDATAAttribute("type", type);
234                atts.addCDATAAttribute("multiple", String.valueOf(metadataDefinition.isMultiple()));
235                XMLUtils.startElement(contentHandler, "metadata", atts);
236                
237                I18nizableText label = metadataDefinition.getLabel();
238                if (label != null)
239                {
240                    XMLUtils.startElement(contentHandler, "label");
241                    label.toSAX(contentHandler);
242                    XMLUtils.endElement(contentHandler, "label");
243                }
244                
245                I18nizableText description = metadataDefinition.getDescription();
246                if (description != null)
247                {
248                    XMLUtils.startElement(contentHandler, "description");
249                    description.toSAX(contentHandler);
250                    XMLUtils.endElement(contentHandler, "description");
251                }
252                
253                switch (metadataType)
254                {
255                    case COMPOSITE:
256                        _saxMetadataSetElement(element, prefix == null ? metadataName  + "/" : prefix + metadataName + "/", contentTypes, mixinTypes);
257                        break;
258                    default:
259                        break;
260                }
261                
262                XMLUtils.endElement(contentHandler, "metadata");
263            }
264            else
265            {
266                throw new IllegalArgumentException("A metadataSet may only contain Fieldset and MetadataDefinitionReference");
267            }
268        }
269    }
270    
271    private MetadataDefinition _getDefinition(String path, String[] contentTypes, String[] mixins)
272    {
273        String[] allContentTypes = ArrayUtils.addAll(contentTypes, mixins);
274        
275        for (String cTypeId : allContentTypes)
276        {
277            ContentType cType = _cTypeEp.getExtension(cTypeId);
278            MetadataDefinition definition = cType.getMetadataDefinitionByPath(path);
279            if (definition != null)
280            {
281                return definition;
282            }
283        }
284        
285        throw new IllegalArgumentException("Unknown metadata " + path);
286    }
287}