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.repository;
017
018import java.io.IOException;
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.HashMap;
022import java.util.Map;
023import java.util.Optional;
024
025import org.ametys.cms.data.ContentDataHelper;
026import org.ametys.cms.data.ContentSynchronizationContext;
027import org.ametys.cms.data.ContentSynchronizationResult;
028import org.ametys.cms.data.ContentValue;
029import org.ametys.cms.data.ReferencedContents;
030import org.ametys.plugins.repository.data.holder.impl.DefaultModifiableModelAwareDataHolder;
031import org.ametys.plugins.repository.data.holder.values.SynchronizationContext;
032import org.ametys.plugins.repository.data.holder.values.SynchronizationResult;
033import org.ametys.plugins.repository.data.repositorydata.ModifiableRepositoryData;
034import org.ametys.runtime.model.ModelItemContainer;
035import org.ametys.runtime.model.ViewItemContainer;
036import org.ametys.runtime.model.exception.BadItemTypeException;
037import org.ametys.runtime.model.exception.UndefinedItemPathException;
038
039/**
040 * Implementation modifiable data holder for content
041 */
042public class ModifiableContentDataHolder extends DefaultModifiableModelAwareDataHolder
043{
044    /** the content */
045    protected ModifiableContent _content;
046    
047    /** the content data helper */
048    protected ContentDataHelper _contentDataHelper;
049    
050    /**
051     * Creates a modifiable content data holder
052     * @param content the content
053     * @param contentDataHelper the content data helper
054     * @param repositoryData the repository data to use
055     * @param itemContainers the model containers to use to get information about definitions. Must match the given repository data. A repository data can have several item containers. For example, a content can have several content types.
056     */
057    public ModifiableContentDataHolder(ModifiableContent content, ContentDataHelper contentDataHelper, ModifiableRepositoryData repositoryData, Collection<? extends ModelItemContainer> itemContainers)
058    {
059        super(repositoryData, Optional.empty(), Optional.of(content), itemContainers);
060        _content = content;
061        _contentDataHelper = contentDataHelper;
062    }
063    
064    @Override
065    public <T extends SynchronizationResult> T synchronizeValues(ViewItemContainer viewItemContainer, Map<String, Object> values, SynchronizationContext context) throws UndefinedItemPathException, BadItemTypeException, IOException
066    {
067        Collection<ReferencedContents> allReferencedContents = new ArrayList<>();
068        if (context instanceof ContentSynchronizationContext)
069        {
070            ContentSynchronizationContext contentContext = (ContentSynchronizationContext) context;
071            if (contentContext.invertRelationsEnabled())
072            {
073                allReferencedContents.addAll(contentContext.getReferencedContents()
074                        .orElseGet(() -> _contentDataHelper.collectReferencedContents(viewItemContainer, _content, values)));
075            }
076        }
077        
078        T result = super.synchronizeValues(viewItemContainer, values, context);
079        
080        if (result instanceof ContentSynchronizationResult)
081        {
082            Map<String, ModifiableContent> modifiedContents = new HashMap<>();
083            
084            for (ReferencedContents referencedContents : allReferencedContents)
085            {
086                String invertRelationPath = referencedContents.getDefinition().getInvertRelationPath();
087                modifiedContents.putAll(ContentDataHelper.manageInvertRelations(invertRelationPath, referencedContents.getAddedContents(), _content.getId(), ContentDataHelper::addInvertRelation, modifiedContents));
088                modifiedContents.putAll(ContentDataHelper.manageInvertRelations(invertRelationPath, referencedContents.getRemovedContents(), _content.getId(), ContentDataHelper::removeInvertRelation, modifiedContents));
089                
090                Map<ContentValue, Collection<String>> thirdPartyContents = new HashMap<>();
091                for (Map.Entry<ContentValue, ContentValue> thirdPartyContent : referencedContents.getThirdPartyContents().entrySet())
092                {
093                    thirdPartyContents.computeIfAbsent(thirdPartyContent.getValue(), __ -> new ArrayList<>()).add(thirdPartyContent.getKey().getContentId());
094                }
095                modifiedContents.putAll(ContentDataHelper.manageInvertRelations(referencedContents.getDefinition().getPath(), thirdPartyContents, ContentDataHelper::removeInvertRelation, modifiedContents));
096            }
097            
098            ((ContentSynchronizationResult) result).addModifiedContents(modifiedContents.values());
099        }
100        
101        return result;
102    }
103    
104    @SuppressWarnings("unchecked")
105    @Override
106    protected <T extends SynchronizationContext> T _createSynchronizationContextInstance()
107    {
108        return (T) ContentSynchronizationContext.newInstance();
109    }
110    
111    @SuppressWarnings("unchecked")
112    @Override
113    protected <T extends SynchronizationResult> T _createSetValueResultInstance()
114    {
115        return (T) new ContentSynchronizationResult();
116    }
117}