001/*
002 *  Copyright 2021 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.duplicate.contents;
017
018import java.util.ArrayList;
019import java.util.List;
020
021import org.apache.avalon.framework.configuration.Configuration;
022import org.apache.avalon.framework.configuration.ConfigurationException;
023import org.apache.commons.lang3.tuple.Pair;
024
025import org.ametys.cms.contenttype.ContentType;
026import org.ametys.cms.duplicate.contents.attr.AbstractDuplicateAttributeConfiguration;
027import org.ametys.cms.duplicate.contents.attr.DuplicateAttributeConfiguration;
028import org.ametys.cms.duplicate.contents.attr.impl.BooleanDuplicateAttributeConfiguration;
029import org.ametys.cms.duplicate.contents.attr.impl.ContentDuplicateAttributeConfiguration;
030import org.ametys.cms.duplicate.contents.attr.impl.DateDuplicateAttributeConfiguration;
031import org.ametys.cms.duplicate.contents.attr.impl.DateTimeDuplicateAttributeConfiguration;
032import org.ametys.cms.duplicate.contents.attr.impl.DoubleDuplicateAttributeConfiguration;
033import org.ametys.cms.duplicate.contents.attr.impl.LongDuplicateAttributeConfiguration;
034import org.ametys.cms.duplicate.contents.attr.impl.StringDuplicateAttributeConfiguration;
035import org.ametys.runtime.model.ModelItemGroup;
036import org.ametys.runtime.model.type.ModelItemTypeConstants;
037
038/**
039 * Represents the configuration for a content type of content duplicate detection configuration
040 */
041public class DuplicateContentTypeConfiguration
042{
043        
044    private String _contentTypeId;
045    private List<DuplicateAttributeConfiguration> _attributeList = new ArrayList<>();
046    private List<Pair<String, List<Object>>> _errorList = new ArrayList<>();
047    private List<Pair<String, List<Object>>> _warnList = new ArrayList<>();
048    
049    /**
050     * Create a new DuplicateContentTypeConfiguration
051     * @param configuration the configuration used to create the DuplicateContentTypeConfiguration
052     * @param duplicateContentsManager the duplicate contents manager, only use this parameter to get linked components
053     * @throws ConfigurationException if the configuration is not valid.
054     */
055    public DuplicateContentTypeConfiguration(Configuration configuration, DuplicateContentsManager duplicateContentsManager) throws ConfigurationException
056    {
057        String contentTypeId;
058        contentTypeId = configuration.getAttribute("id");
059        
060        if (duplicateContentsManager._cTypeEP.hasExtension(contentTypeId))
061        {
062            _contentTypeId = contentTypeId;
063            
064            ContentType contentType = duplicateContentsManager._cTypeEP.getExtension(contentTypeId);
065
066            for (Configuration attributeConf : configuration.getChildren("attribute"))
067            {
068                DuplicateAttributeConfiguration duplicateAttributeConfiguration = _createDuplicateAttributeConfiguration(contentType, attributeConf);
069
070                if (duplicateAttributeConfiguration != null)
071                {
072                    _attributeList.add(duplicateAttributeConfiguration);
073                }
074            }
075        }
076        else
077        {
078            _errorList.add(Pair.of("Content type not found for identifier '{}'. @{}", List.of(contentTypeId, configuration.getLocation())));
079        }
080    }
081
082    private AbstractDuplicateAttributeConfiguration _createDuplicateAttributeConfiguration(ContentType contentType, Configuration attributeConfiguration) throws ConfigurationException
083    {
084
085        String path = attributeConfiguration.getAttribute("path");
086        if (contentType.hasModelItem(path))
087        {
088            //ModelItemTypeConstants.REPEATER_TYPE_ID.equals
089            boolean insideOfRepeater = false;
090
091            ModelItemGroup parent = contentType.getModelItem(path).getParent();
092            while (parent != null)
093            {
094                insideOfRepeater = insideOfRepeater || org.ametys.plugins.repository.data.type.ModelItemTypeConstants.REPEATER_TYPE_ID.equals(parent.getType().getId());
095                parent = parent.getParent();
096            }
097            
098            if (insideOfRepeater)
099            {
100                _warnList.add(Pair.of("Ignored attribute query for attribute with path: {}. Attributes inside of a repeater are not handled.", 
101                        List.of(path)));
102            }
103            else
104            {
105                String attributeType = contentType.getModelItem(path).getType().getId();
106                switch (attributeType)
107                {
108                    case ModelItemTypeConstants.STRING_TYPE_ID:
109                        return new StringDuplicateAttributeConfiguration(attributeConfiguration, attributeType);
110                    case org.ametys.cms.data.type.ModelItemTypeConstants.CONTENT_ELEMENT_TYPE_ID:
111                        return new ContentDuplicateAttributeConfiguration(attributeConfiguration, attributeType);
112                    case ModelItemTypeConstants.DATE_TYPE_ID:
113                        return new DateDuplicateAttributeConfiguration(attributeConfiguration, attributeType);
114                    case ModelItemTypeConstants.DATETIME_TYPE_ID:
115                        return new DateTimeDuplicateAttributeConfiguration(attributeConfiguration, attributeType);
116                    case ModelItemTypeConstants.LONG_TYPE_ID:
117                        return new LongDuplicateAttributeConfiguration(attributeConfiguration, attributeType);
118                    case ModelItemTypeConstants.DOUBLE_TYPE_ID:
119                        return new DoubleDuplicateAttributeConfiguration(attributeConfiguration, attributeType);
120                    case ModelItemTypeConstants.BOOLEAN_TYPE_ID:
121                        return new BooleanDuplicateAttributeConfiguration(attributeConfiguration, attributeType);
122                    default:
123                        _warnList.add(Pair.of("Ignored attribute query for attribute with path: {}. This attribute type is not handled for duplicate attributes: {}.", 
124                                List.of(path, 
125                                        contentType.getModelItem(path).getType())));
126                        break;
127                }
128            }
129        }
130        else
131        {
132            _errorList.add(Pair.of("Attribute '{}' not found for content type '{}'. @{}"
133                    , List.of(path
134                    , contentType.getId()
135                    , attributeConfiguration.getLocation())));
136        }
137        return null;
138    }
139    
140    /**
141     * Check if the content type has any attributes that require near duplicate check
142     * @return true if the content type has any attributes that require near duplicate check
143     */
144    public boolean hasAnyNearDuplicateAttributes()
145    {
146        return _attributeList.stream()
147            .anyMatch(DuplicateAttributeConfiguration::checkNearDuplicate);
148    }
149    
150    /**
151     * Get the content type id
152     * @return the content type id
153     */
154    public String getContentTypeId()
155    {
156        return _contentTypeId;
157    }
158    
159    /**
160     * Get the attribute list
161     * @return the attribute list
162     */
163    public List<DuplicateAttributeConfiguration> getAttributeList()
164    {
165        return _attributeList;
166    }
167
168    /**
169     * get the list of errors
170     * @return the error list
171     */
172    public List<Pair<String, List<Object>>> getErrors()
173    {
174        return _errorList;
175    }
176
177    /**
178     * get the list of warns
179     * @return the warn list
180     */
181    public List<Pair<String, List<Object>>> getWarns()
182    {
183        return _warnList;
184    }
185
186}