001/*
002 *  Copyright 2023 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.search.ui.model.impl;
017
018import java.util.ArrayList;
019import java.util.HashMap;
020import java.util.List;
021import java.util.Map;
022import java.util.Optional;
023
024import org.ametys.cms.search.ui.model.SearchUIColumn;
025import org.ametys.cms.search.ui.model.SearchUIColumnHelper;
026import org.ametys.plugins.repository.model.CompositeDefinition;
027import org.ametys.plugins.repository.model.RepeaterDefinition;
028import org.ametys.runtime.model.DefinitionContext;
029import org.ametys.runtime.model.ModelItem;
030import org.ametys.runtime.model.ModelViewItem;
031import org.ametys.runtime.model.ModelViewItemGroup;
032import org.ametys.runtime.model.ViewItem;
033import org.ametys.runtime.model.ViewItemAccessor;
034
035/**
036 * This class represents a result column
037 */
038public class RepeaterSearchUIColumn extends ModelViewItemGroup<RepeaterDefinition> implements SearchUIColumn<RepeaterDefinition>
039{
040    private int _width = -1;
041    private boolean _hidden;
042    private boolean _editable = true;
043    private boolean _sortable = true;
044    private boolean _allowSortOnMultipleJoin;
045    private Optional<String> _defaultSorter = Optional.empty();
046    private Optional<String> _renderer = Optional.empty();
047    private Optional<String> _converter = Optional.empty();
048    
049    public int getWidth()
050    {
051        return _width > 0
052                ? _width
053                : SearchUIColumnHelper.getDefaultColumnWidth(this);
054    }
055    
056    public void setWidth (int width)
057    {
058        _width = width;
059    }
060    
061    public boolean isHidden()
062    {
063        return _hidden;
064    }
065    
066    public void setHidden (boolean hidden)
067    {
068        _hidden = hidden;
069    }
070    
071    public boolean isEditable()
072    {
073        return _editable && SearchUIColumnHelper.isEditionAllowed(this);
074    }
075    
076    public void setEditable (boolean editable)
077    {
078        _editable = editable;
079    }
080    
081    public boolean isSortable()
082    {
083        return _sortable && SearchUIColumnHelper.isSortAllowed(this);
084    }
085    
086    public void setSortable (boolean sortable)
087    {
088        _sortable = sortable;
089    }
090    
091    public boolean allowSortOnMultipleJoin()
092    {
093        return _allowSortOnMultipleJoin;
094    }
095    
096    public void setAllowSortOnMultipleJoin(boolean allowSortOnMultipleJoin)
097    {
098        _allowSortOnMultipleJoin = allowSortOnMultipleJoin;
099    }
100    
101    public String getDefaultSorter()
102    {
103        return isSortable()
104                ? _defaultSorter.orElse(null)
105                : null;
106    }
107    
108    public void setDefaultSorter(Optional<String> defaultSorter)
109    {
110        _defaultSorter = defaultSorter;
111    }
112    
113    public String getRenderer()
114    {
115        return _renderer
116                .orElse("Ametys.plugins.cms.search.SearchGridHelper.renderRepeater");
117    }
118    
119    public void setRenderer(Optional<String> renderer)
120    {
121        this._renderer = renderer;
122    }
123    
124    public String getConverter()
125    {
126        return _converter
127                .orElse("Ametys.plugins.cms.search.SearchGridHelper.convertRepeater");
128    }
129    
130    public void setConverter(Optional<String> converter)
131    {
132        this._converter = converter;
133    }
134    
135    @Override
136    public RepeaterSearchUIColumn createInstance()
137    {
138        return new RepeaterSearchUIColumn();
139    }
140
141    @Override
142    public Map<String, Object> toJSON(DefinitionContext context)
143    {
144        Map<String, Object> json = super.toJSON(context);
145
146        if (!json.isEmpty())
147        {
148            json.putAll(SearchUIColumnHelper.columnPropertiesToJSON(this));
149        }
150        return json;
151    }
152    
153    @Override
154    protected Map<String, Object> _childrenToJSON(DefinitionContext context)
155    {
156        Map<String, Object> result = new HashMap<>();
157        List<Map<String, Object>> subColumnsAsJSON = new ArrayList<>();
158        
159        ViewItemAccessor viewItemAccessor = getViewItems().isEmpty() ? _createRepeaterSearchUIColumn() : this;
160        for (ModelViewItem subColumn : _getSubColumns(viewItemAccessor))
161        {
162            subColumnsAsJSON.add(subColumn.toJSON(context));
163        }
164        
165        result.put("columns", subColumnsAsJSON);
166        return result;
167    }
168    
169    private RepeaterSearchUIColumn _createRepeaterSearchUIColumn()
170    {
171        RepeaterSearchUIColumn repeaterSearchUIColumn = new RepeaterSearchUIColumn();
172        repeaterSearchUIColumn.setDefinition(getDefinition());
173        
174        repeaterSearchUIColumn.addViewItems(_createViewItems(repeaterSearchUIColumn));
175        
176        return repeaterSearchUIColumn;
177    }
178    
179    private List<ViewItem> _createViewItems(ModelViewItemGroup group)
180    {
181        List<ViewItem> viewItems = new ArrayList<>();
182        
183        for (ModelItem modelItem : group.getDefinition().getModelItems())
184        {
185            if (modelItem instanceof CompositeDefinition compositeDefinition)
186            {
187                ModelViewItemGroup<CompositeDefinition> compositeViewItem = new ModelViewItemGroup<>();
188                compositeViewItem.setDefinition(compositeDefinition);
189                
190                compositeViewItem.addViewItems(_createViewItems(compositeViewItem));
191                
192                viewItems.add(compositeViewItem);
193            }
194            else
195            {
196                SearchUIColumn column = SearchUIColumnHelper.createModelItemColumn(modelItem);
197                viewItems.add(column);
198            }
199        }
200        
201        return viewItems;
202    }
203    
204    private List<ModelViewItem> _getSubColumns(ViewItemAccessor viewItemAccessor)
205    {
206        List<ModelViewItem> subColumns = new ArrayList<>();
207        
208        org.ametys.plugins.repository.model.ViewHelper.visitView(viewItemAccessor, 
209            (element, definition) -> {
210                // attribute
211                subColumns.add(element);
212            },
213            (group, definition) -> {
214                // composite
215                // CMS-10740 - explode composite to have all its children at first level
216                subColumns.addAll(_getSubColumns(group));
217            },
218            (group, definition) -> {
219                // repeater
220                subColumns.add(group);
221            },
222            group -> {
223                // group
224                subColumns.addAll(_getSubColumns(group));
225            }
226        );
227        
228        return subColumns;
229    }
230    
231    @Override
232    public void copyTo(ViewItem item)
233    {
234        super.copyTo(item);
235        
236        assert item instanceof RepeaterSearchUIColumn;
237        RepeaterSearchUIColumn column = (RepeaterSearchUIColumn) item;
238        
239        column.setWidth(_width);
240        column.setHidden(_hidden);
241        
242        column.setEditable(_editable);
243        column.setSortable(_sortable);
244        column.setAllowSortOnMultipleJoin(_allowSortOnMultipleJoin);
245        column.setDefaultSorter(_defaultSorter);
246        
247        column.setRenderer(_renderer);
248        column.setConverter(_converter);
249    }
250}