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;
024import org.apache.commons.lang3.NotImplementedException;
025
026import org.ametys.runtime.i18n.I18nizableText;
027import org.ametys.runtime.model.AbstractViewItemGroup;
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 NotImplementedException("Should not be called");
088    }
089
090    @Override
091    public void insertViewItem(ViewItem item, int index)
092    {
093        throw new NotImplementedException("Should not be called");
094    }
095    
096    Map<String, Object> toJSON() throws ProcessingException
097    {
098        return _wrapped.toJSON();
099    }
100    
101    // Wrap another group, to be able to wrap its own ViewElement to modify the ViewElement#toJson
102    private abstract static class AbstractCompareViewItemGroup implements ViewItemGroup
103    {
104        protected AbstractViewItemGroup _wrapped;
105        
106        @Override
107        public Map<String, Object> toJSON() throws ProcessingException
108        {
109            return _wrapped.toJSON();
110        }
111
112        @Override
113        public String getName()
114        {
115            return _wrapped.getName();
116        }
117
118        @Override
119        public List<ViewItem> getViewItems()
120        {
121            return _wrapped.getViewItems();
122        }
123
124        @Override
125        public void addViewItem(ViewItem item)
126        {
127            throw new NotImplementedException("Should not be called");
128        }
129
130        @Override
131        public void insertViewItem(ViewItem item, int index)
132        {
133            throw new NotImplementedException("Should not be called");
134        }
135
136        @Override
137        public String getRole()
138        {
139            return _wrapped.getRole();
140        }
141
142        @Override
143        public void setRole(String role)
144        {
145            _wrapped.setRole(role);
146        }
147    }
148    
149    private class CompareModelViewItemGroup extends AbstractCompareViewItemGroup
150    {
151        CompareModelViewItemGroup(ModelViewItemGroup fromGroup)
152        {
153            var wrapped = new ModelViewItemGroup(); // empty constructor and not ModelViewItemGroup.of(fromGroup.getDefinition()), in order to avoid duplication of view items
154            wrapped.setDefinition(fromGroup.getDefinition());
155            _wrapped = wrapped;
156            _populateViewItemContainerWithWrappedElements(fromGroup, _wrapped);
157        }
158    }
159    
160    private class CompareSimpleViewItemGroup extends AbstractCompareViewItemGroup
161    {
162        CompareSimpleViewItemGroup(SimpleViewItemGroup fromGroup)
163        {
164            var wrapped = new SimpleViewItemGroup();
165            wrapped.setName(fromGroup.getName());
166            wrapped.setLabel(fromGroup.getLabel());
167            wrapped.setDescription(fromGroup.getDescription());
168            wrapped.setRole(fromGroup.getRole());
169            _wrapped = wrapped;
170            _populateViewItemContainerWithWrappedElements(fromGroup, _wrapped);
171        }
172    }
173    
174    // Extends ViewElement, so as to modify the ElementDefinition#toJson
175    private class CompareViewElement extends ViewElement
176    {
177        CompareViewElement(ViewElement wrapped)
178        {
179            @SuppressWarnings("unchecked")
180            CompareElementDefinition compareElementDefinition = new CompareElementDefinition(wrapped.getDefinition());
181            setDefinition(compareElementDefinition);
182        }
183    }
184    
185    // here we are able to modify the ElementDefinition#toJson as we want
186    private class CompareElementDefinition<T> extends ElementDefinition<T>
187    {
188        CompareElementDefinition(ElementDefinition<T> definitionToCopy)
189        {
190            super(definitionToCopy);
191        }
192        
193        /*
194         * Here is the business logic for what we created all those wrappers
195         */
196        @Override
197        public Map<String, Object> toJSON() throws ProcessingException
198        {
199            Map<String, Object> elementDefinitionJson = new HashMap<>(super.toJSON());
200            
201            @SuppressWarnings("unchecked")
202            Map<String, I18nizableText> widgetParams = (Map<String, I18nizableText>) elementDefinitionJson.computeIfAbsent("widget-params", __ -> new HashMap<>());
203            String widgetToWrap = (String) elementDefinitionJson.get("widget");
204            widgetParams.put("comparator-wrapped-widget", new I18nizableText(widgetToWrap));
205            elementDefinitionJson.put("widget", "edition.comparison");
206            _handleExternal(widgetToWrap, widgetParams);
207            
208            // for a reason, type can be 'rich-text' while it expects 'rich_text'
209            elementDefinitionJson.computeIfPresent("type", (__, type) -> ((String) type).replaceAll("-", "_"));
210            
211            return elementDefinitionJson;
212        }
213        
214        private void _handleExternal(String widgetToWrap, Map<String, I18nizableText> widgetParams)
215        {
216            if (_externalAndLocalMetadata.contains(getPath()))
217            {
218                // Do as in org.ametys.cms.content.GetMetadataSetDefAction.metadataDefinition2JsonObject
219                // Should be more generic
220                widgetParams.put("comparator-wrapped-widget", new I18nizableText("edition.externalizable"));
221                widgetParams.put("wrapped-widget", new I18nizableText(widgetToWrap)); // wrapped widget for externalizable (so, it is wrapped twice)
222            }
223        }
224    }
225}