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;
017
018import java.util.Collection;
019import java.util.List;
020import java.util.Optional;
021
022import org.apache.avalon.framework.configuration.ConfigurationException;
023import org.apache.avalon.framework.configuration.DefaultConfiguration;
024
025import org.ametys.cms.contenttype.AbstractContentTypeViewParser;
026import org.ametys.cms.contenttype.ContentType;
027import org.ametys.cms.repository.Content;
028import org.ametys.cms.search.model.SystemProperty;
029import org.ametys.cms.search.ui.model.impl.RepeaterSearchUIColumn;
030import org.ametys.plugins.repository.model.CompositeDefinition;
031import org.ametys.plugins.repository.model.RepeaterDefinition;
032import org.ametys.runtime.model.ElementDefinition;
033import org.ametys.runtime.model.ItemParserHelper.ConfigurationAndPluginName;
034import org.ametys.runtime.model.Model;
035import org.ametys.runtime.model.ModelItem;
036import org.ametys.runtime.model.ModelItemAccessor;
037import org.ametys.runtime.model.ModelViewItem;
038import org.ametys.runtime.model.ModelViewItemGroup;
039import org.ametys.runtime.model.View;
040import org.ametys.runtime.model.ViewItem;
041
042/**
043 * Component that parses the columns of a {@link StaticSearchUIModel}
044 */
045public class StaticSearchUIModelColumnsParser extends AbstractContentTypeViewParser
046{
047    private static final String __VIEW_NAME = "StaticSearchUIModel-Columns";
048    
049    /** Content types containing the item available in the search model's columns */
050    protected Collection<ContentType> _contentTypes;
051
052    /**
053     * Creates a {@link StaticSearchUIModel}'s columns parser
054     * @param contentTypes The content types containing the item available in the search model's columns
055     */
056    public StaticSearchUIModelColumnsParser(Collection<ContentType> contentTypes)
057    {
058        _contentTypes = contentTypes;
059    }
060    
061    @Override
062    protected String _parseViewName(ConfigurationAndPluginName viewConfiguration) throws ConfigurationException
063    {
064        return __VIEW_NAME;
065    }
066
067    @Override
068    public View overrideView(ConfigurationAndPluginName viewConfiguration, View existingView) throws ConfigurationException
069    {
070        throw new UnsupportedOperationException("Unable to override columns of a search model");
071    }
072    
073    @Override
074    protected void _fillViewGeneralInformation(ConfigurationAndPluginName viewConfiguration, View view, View existingView, Collection< ? extends Model> model) throws ConfigurationException
075    {
076        // Do nothing - there is no general information in search model's columns
077    }
078    
079    @Override
080    protected Collection<? extends Model> _getModel()
081    {
082        return _contentTypes;
083    }
084    
085    @Override
086    protected ModelItem _getModelItem(ConfigurationAndPluginName itemConfiguration, String modelItemName, Collection< ? extends ModelItemAccessor> parents) throws ConfigurationException
087    {
088        ModelItem modelItem = parents.isEmpty() && Content.ATTRIBUTE_TITLE.equals(modelItemName)
089                ? _contentTypesHelper.getTitleAttributeDefinition()
090                : super._getModelItem(itemConfiguration, modelItemName, parents);
091        
092        if (modelItem instanceof SystemProperty systemProperty && !systemProperty.isDisplayable())
093        {
094            throw new ConfigurationException("The property '" + systemProperty.getName() + "' is not displayable.");
095        }
096        
097        return modelItem;
098    }
099    
100    @Override
101    protected ModelViewItem createModelViewItemForAllItemsReference(ConfigurationAndPluginName itemConfiguration, ModelItem modelItem, View referenceView, boolean override) throws ConfigurationException
102    {
103        if (modelItem instanceof CompositeDefinition compositeDefinition)
104        {
105            ModelViewItemGroup<CompositeDefinition> itemGroup = new ModelViewItemGroup<>();
106            itemGroup.setDefinition(compositeDefinition);
107            return itemGroup;
108        }
109        else
110        {
111            return super.createModelViewItemForAllItemsReference(itemConfiguration, modelItem, referenceView, override);
112        }
113    }
114    
115    @SuppressWarnings("unchecked")
116    @Override
117    protected ModelViewItem createModelViewItem(ConfigurationAndPluginName itemConfiguration, ModelItem modelItem, View referenceView, boolean override) throws ConfigurationException
118    {
119        ModelViewItem<? extends ModelItem> modelViewItem = super.createModelViewItem(itemConfiguration, modelItem, referenceView, override);
120        
121        if (modelViewItem instanceof SearchUIColumn searchColumn)
122        {
123            searchColumn.setWidth(itemConfiguration.configuration().getChild("width").getValueAsInteger(-1));
124            searchColumn.setHidden(itemConfiguration.configuration().getChild("hidden").getValueAsBoolean(false));
125            
126            searchColumn.setEditable(itemConfiguration.configuration().getChild("editable").getValueAsBoolean(true));
127            searchColumn.setSortable(itemConfiguration.configuration().getChild("sortable").getValueAsBoolean(true));
128            searchColumn.setAllowSortOnMultipleJoin(itemConfiguration.configuration().getChild("allow-sort-on-multiple-join").getValueAsBoolean(false));
129            searchColumn.setDefaultSorter(Optional.ofNullable(itemConfiguration.configuration().getChild("default-sorter").getValue(null)));
130            
131            searchColumn.setRenderer(Optional.ofNullable(itemConfiguration.configuration().getChild("renderer").getValue(null)));
132            searchColumn.setConverter(Optional.ofNullable(itemConfiguration.configuration().getChild("converter").getValue(null)));
133        }
134        
135        if (modelViewItem instanceof RepeaterSearchUIColumn repeaterColumn && modelViewItem.getDefinition() instanceof RepeaterDefinition repeaterDefinition
136                && !_getModelItemReference(itemConfiguration.configuration()).endsWith(ALL_ITEMS_REFERENCE)
137                && itemConfiguration.configuration().getChildren(ADD_ITEM_TAG_NAME).length == 0 && itemConfiguration.configuration().getChildren(ADD_GROUP_TAG_NAME).length == 0)
138        {
139            // Reference to a repeater with no child => add all model items in the view item
140            DefaultConfiguration fakeConfiguration = new DefaultConfiguration(ADD_ITEM_TAG_NAME);
141            fakeConfiguration.setAttribute(ITEM_REFERENCE_ATTRIBUTE_NAME, ALL_ITEMS_REFERENCE);
142            _parseModelViewItem(new ConfigurationAndPluginName(fakeConfiguration, itemConfiguration.pluginName()), repeaterColumn, List.of(repeaterDefinition), referenceView, override);
143        }
144            
145        return modelViewItem;
146    }
147    
148    @Override
149    protected ModelViewItem _createModelViewItemInstance(ModelItem modelItem)
150    {
151        return SearchUIColumnHelper.createModelItemColumn(modelItem);
152    }
153    
154    @Override
155    protected ViewItem _createViewItemCopyInstanceForReferencedView(ViewItem viewItem)
156    {
157        return Optional.of(viewItem)
158                       .filter(ModelViewItem.class::isInstance)
159                       .map(ModelViewItem.class::cast)
160                       .map(ModelViewItem::getDefinition)
161                       // Keep only items that can be represented in columns
162                       .filter(modelViewItem -> modelViewItem instanceof RepeaterDefinition || modelViewItem instanceof ElementDefinition)
163                       .map(this::_createModelViewItemInstance)
164                       .map(ViewItem.class::cast)
165                       .orElseGet(() -> super._createViewItemCopyInstanceForReferencedView(viewItem));
166    }
167}