001/*
002 *  Copyright 2020 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.version;
017
018import java.util.HashMap;
019import java.util.List;
020import java.util.Map;
021import java.util.Set;
022
023import org.apache.cocoon.ProcessingException;
024
025import org.ametys.runtime.i18n.I18nizableText;
026import org.ametys.runtime.model.AbstractViewItemGroup;
027import org.ametys.runtime.model.DefinitionContext;
028import org.ametys.runtime.model.ElementDefinition;
029import org.ametys.runtime.model.ModelViewItemGroup;
030import org.ametys.runtime.model.SimpleViewItemGroup;
031import org.ametys.runtime.model.View;
032import org.ametys.runtime.model.ViewElement;
033import org.ametys.runtime.model.ViewItem;
034import org.ametys.runtime.model.ViewItemContainer;
035import org.ametys.runtime.model.ViewItemGroup;
036
037// Wrap another view, to be able to wrap its own ViewElement to modify the ViewElement#toJson
038class CompareView implements ViewItemContainer
039{
040    private final View _wrapped;
041    private final Set<String> _externalAndLocalMetadata;
042    
043    CompareView(View fromView, Set<String> externalAndLocalMetadata)
044    {
045        _wrapped = new View();
046        _populateViewItemContainerWithWrappedElements(fromView, _wrapped);
047        _externalAndLocalMetadata = externalAndLocalMetadata;
048    }
049    
050    private void _populateViewItemContainerWithWrappedElements(ViewItemContainer sourceOfCopy, ViewItemContainer toPopulate)
051    {
052        for (ViewItem viewItem : sourceOfCopy.getViewItems())
053        {
054            toPopulate.addViewItem(_wrapViewElement(viewItem));
055        }
056    }
057    
058    private ViewItem _wrapViewElement(ViewItem item)
059    {
060        if (item instanceof ViewElement)
061        {
062            return new CompareViewElement((ViewElement) item);
063        }
064        else if (item instanceof ModelViewItemGroup)
065        {
066            return new CompareModelViewItemGroup((ModelViewItemGroup) item);
067        }
068        else if (item instanceof SimpleViewItemGroup)
069        {
070            return new CompareSimpleViewItemGroup((SimpleViewItemGroup) item);
071        }
072        else
073        {
074            throw new IllegalArgumentException("Unexpected and unhandled ViewItem class '" + item.getClass() + "' to wrap it into a CompareView. Please contact your administrator.");
075        }
076    }
077
078    @Override
079    public List<ViewItem> getViewItems()
080    {
081        return _wrapped.getViewItems();
082    }
083
084    @Override
085    public void addViewItem(ViewItem item)
086    {
087        throw new UnsupportedOperationException("Should not be called");
088    }
089
090    @Override
091    public void insertViewItem(ViewItem item, int index)
092    {
093        throw new UnsupportedOperationException("Should not be called");
094    }
095    
096    public boolean removeViewItem(ViewItem item)
097    {
098        throw new UnsupportedOperationException("Should not be called");
099    }
100    
101    public void clear()
102    {
103        throw new UnsupportedOperationException("Should not be called");
104    }
105    
106    Map<String, Object> toJSON() throws ProcessingException
107    {
108        return _wrapped.toJSON(DefinitionContext.newInstance());
109    }
110    
111    // Wrap another group, to be able to wrap its own ViewElement to modify the ViewElement#toJson
112    private abstract static class AbstractCompareViewItemGroup implements ViewItemGroup
113    {
114        protected AbstractViewItemGroup _wrapped;
115        
116        @Override
117        public Map<String, Object> toJSON(DefinitionContext context) throws ProcessingException
118        {
119            return _wrapped.toJSON(context);
120        }
121
122        @Override
123        public String getName()
124        {
125            return _wrapped.getName();
126        }
127        
128        public I18nizableText getLabel()
129        {
130            return _wrapped.getLabel();
131        }
132        
133        public I18nizableText getDescription()
134        {
135            return _wrapped.getDescription();
136        }
137
138        @Override
139        public List<ViewItem> getViewItems()
140        {
141            return _wrapped.getViewItems();
142        }
143        
144        public void setName(String name)
145        {
146            throw new UnsupportedOperationException("Should not be called");
147        }
148        
149        public void setLabel(I18nizableText label)
150        {
151            throw new UnsupportedOperationException("Should not be called");
152        }
153        
154        public void setDescription(I18nizableText description)
155        {
156            throw new UnsupportedOperationException("Should not be called");
157        }
158
159        @Override
160        public void addViewItem(ViewItem item)
161        {
162            throw new UnsupportedOperationException("Should not be called");
163        }
164
165        @Override
166        public void insertViewItem(ViewItem item, int index)
167        {
168            throw new UnsupportedOperationException("Should not be called");
169        }
170        
171        public boolean removeViewItem(ViewItem item)
172        {
173            throw new UnsupportedOperationException("Should not be called");
174        }
175        
176        public void clear()
177        {
178            throw new UnsupportedOperationException("Should not be called");
179        }
180        
181        @Override
182        public void copyTo(ViewItem item)
183        {
184            throw new UnsupportedOperationException("Should not be called");
185        }
186        
187        @Override
188        public ViewItem createInstance()
189        {
190            throw new UnsupportedOperationException("Should not be called");
191        }
192        
193        public boolean equals(Object obj, boolean checkDetails)
194        {
195            throw new UnsupportedOperationException("Should not be called");
196        }
197
198        @Override
199        public String getRole()
200        {
201            return _wrapped.getRole();
202        }
203
204        @Override
205        public void setRole(String role)
206        {
207            _wrapped.setRole(role);
208        }
209    }
210    
211    private class CompareModelViewItemGroup extends AbstractCompareViewItemGroup
212    {
213        CompareModelViewItemGroup(ModelViewItemGroup fromGroup)
214        {
215            var wrapped = new ModelViewItemGroup(); // empty constructor and not ModelViewItemGroup.of(fromGroup.getDefinition()), in order to avoid duplication of view items
216            wrapped.setDefinition(fromGroup.getDefinition());
217            _wrapped = wrapped;
218            _populateViewItemContainerWithWrappedElements(fromGroup, _wrapped);
219        }
220    }
221    
222    private class CompareSimpleViewItemGroup extends AbstractCompareViewItemGroup
223    {
224        CompareSimpleViewItemGroup(SimpleViewItemGroup fromGroup)
225        {
226            var wrapped = new SimpleViewItemGroup();
227            wrapped.setName(fromGroup.getName());
228            wrapped.setLabel(fromGroup.getLabel());
229            wrapped.setDescription(fromGroup.getDescription());
230            wrapped.setRole(fromGroup.getRole());
231            _wrapped = wrapped;
232            _populateViewItemContainerWithWrappedElements(fromGroup, _wrapped);
233        }
234    }
235    
236    // Extends ViewElement, so as to modify the ElementDefinition#toJson
237    private class CompareViewElement extends ViewElement
238    {
239        CompareViewElement(ViewElement wrapped)
240        {
241            @SuppressWarnings("unchecked")
242            CompareElementDefinition compareElementDefinition = new CompareElementDefinition(wrapped.getDefinition());
243            setDefinition(compareElementDefinition);
244        }
245    }
246    
247    // here we are able to modify the ElementDefinition#toJson as we want
248    private class CompareElementDefinition<T> extends ElementDefinition<T>
249    {
250        CompareElementDefinition(ElementDefinition<T> definitionToCopy)
251        {
252            super(definitionToCopy);
253        }
254        
255        /*
256         * Here is the business logic for what we created all those wrappers
257         */
258        @Override
259        protected Map<String, Object> _toJSON(DefinitionContext context) throws ProcessingException
260        {
261            Map<String, Object> elementDefinitionJson = new HashMap<>(super._toJSON(context));
262            
263            @SuppressWarnings("unchecked")
264            Map<String, I18nizableText> widgetParams = (Map<String, I18nizableText>) elementDefinitionJson.computeIfAbsent("widget-params", __ -> new HashMap<>());
265            String widgetToWrap = (String) elementDefinitionJson.get("widget");
266            widgetParams.put("comparator-wrapped-widget", new I18nizableText(widgetToWrap));
267            elementDefinitionJson.put("widget", "edition.comparison");
268            _handleExternal(widgetToWrap, widgetParams);
269            
270            return elementDefinitionJson;
271        }
272        
273        private void _handleExternal(String widgetToWrap, Map<String, I18nizableText> widgetParams)
274        {
275            if (_externalAndLocalMetadata.contains(getPath()))
276            {
277                // Wrap the widget as in org.ametys.cms.contenttype.AttributeDefinition#toJSON
278                // Should be more generic
279                widgetParams.put("comparator-wrapped-widget", new I18nizableText("edition.externalizable"));
280                widgetParams.put("wrapped-widget", new I18nizableText(widgetToWrap)); // wrapped widget for externalizable (so, it is wrapped twice)
281            }
282        }
283    }
284}