001/*
002 *  Copyright 2019 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.odf.catalog;
017
018import java.util.Map;
019import java.util.Optional;
020import java.util.stream.Stream;
021
022import org.apache.commons.lang3.StringUtils;
023
024import org.ametys.cms.contenttype.ContentAttributeDefinition;
025import org.ametys.cms.data.ContentValue;
026import org.ametys.cms.repository.Content;
027import org.ametys.plugins.repository.data.holder.ModifiableModelAwareDataHolder;
028import org.ametys.plugins.repository.data.holder.group.ModifiableModelAwareComposite;
029import org.ametys.plugins.repository.data.holder.group.ModifiableModelAwareRepeater;
030import org.ametys.plugins.repository.data.holder.group.ModifiableModelAwareRepeaterEntry;
031import org.ametys.plugins.repository.model.CompositeDefinition;
032import org.ametys.plugins.repository.model.RepeaterDefinition;
033import org.ametys.runtime.model.ModelItem;
034import org.ametys.runtime.plugin.component.AbstractLogEnabled;
035
036/**
037 * The abstract class to copy content attribute of program item type.
038 */
039public abstract class AbstractProgramItemAttributeCopyUpdater extends AbstractLogEnabled implements CopyCatalogUpdater
040{
041    /**
042     * Update the program item attribute with the value of the new catalog.
043     * @param dataHolder The data holder
044     * @param definitionPath The definition path
045     * @param copiedContents The copied contents for the referenced attribute
046     */
047    protected void _updateContentAttribute(ModifiableModelAwareDataHolder dataHolder, String definitionPath, Map<Content, Content> copiedContents)
048    {
049        String[] pathSegments = StringUtils.split(definitionPath, ModelItem.ITEM_PATH_SEPARATOR);
050        String attributeName = pathSegments[0];
051        ModelItem definition = dataHolder.getDefinition(attributeName);
052        if (pathSegments.length == 1 && definition instanceof ContentAttributeDefinition)
053        {
054            if (((ContentAttributeDefinition) definition).isMultiple())
055            {
056                _updateMultipleContentAttribute(dataHolder, attributeName, copiedContents);
057            }
058            else
059            {
060                _updateSingleContentAttribute(dataHolder, attributeName, copiedContents);
061            }
062        }
063        else if (definition instanceof RepeaterDefinition)
064        {
065            ModifiableModelAwareRepeater repeater = dataHolder.getRepeater(attributeName);
066            if (repeater != null)
067            {
068                String childDefinitionPath = StringUtils.join(pathSegments, ModelItem.ITEM_PATH_SEPARATOR, 1, pathSegments.length);
069                for (ModifiableModelAwareRepeaterEntry entry : repeater.getEntries())
070                {
071                    _updateContentAttribute(entry, childDefinitionPath, copiedContents);
072                }
073            }
074        }
075        else if (definition instanceof CompositeDefinition)
076        {
077            ModifiableModelAwareComposite composite = dataHolder.getComposite(attributeName);
078            if (composite != null)
079            {
080                String childDefinitionPath = StringUtils.join(pathSegments, ModelItem.ITEM_PATH_SEPARATOR, 1, pathSegments.length);
081                _updateContentAttribute(composite, childDefinitionPath, copiedContents);
082            }
083        }
084        else
085        {
086            // This method shouldn't be called with anything else than a final content attribute
087            throw new IllegalArgumentException("The path '" + definitionPath + "' on data holder '" + definition.getPath() + "' is not a content attribute on content type '" + definition.getModel().getId() + "'.");
088        }
089    }
090
091    /**
092     * Update the single attribute with the content value.
093     * @param dataHolder The data holder
094     * @param attributeName The attribute name
095     * @param copiedContents The copied contents for the referenced attribute
096     */
097    protected void _updateSingleContentAttribute(ModifiableModelAwareDataHolder dataHolder, String attributeName, Map<Content, Content> copiedContents)
098    {
099        Content newContent = Optional.of(dataHolder)
100            // Get the attribute value
101            .map(dh -> dh.<ContentValue>getValue(attributeName))
102            // Get the content
103            .flatMap(ContentValue::getContentIfExists)
104            // Keep himself if not found in the copied contents map
105            .map(content -> copiedContents.getOrDefault(content, content))
106            // Get the retrieved value or null
107            .orElse(null);
108
109        // Set the attribute with the new content
110        // If null, the attribute value will be empty
111        dataHolder.setValue(attributeName, newContent);
112    }
113    
114    /**
115     * Update the multiple attribute with the program item value.
116     * @param dataHolder The data holder
117     * @param attributeName The attribute name
118     * @param copiedContents The copied contents for the referenced attribute
119     */
120    protected void _updateMultipleContentAttribute(ModifiableModelAwareDataHolder dataHolder, String attributeName, Map<Content, Content> copiedContents)
121    {
122        Content[] newContents = Optional.of(dataHolder)
123            // Get the attribute values
124            .map(dh -> dh.<ContentValue[]>getValue(attributeName))
125            // Build a stream from the ContentValue array
126            .map(Stream::of)
127            // Or build an empty stream if there is no values
128            .orElseGet(Stream::empty)
129            // For each element, get the content
130            .map(ContentValue::getContentIfExists)
131            .flatMap(Optional::stream)
132            // Keep himself if not found in the copied contents map
133            .map(content -> copiedContents.getOrDefault(content, content))
134            // Collect into an array
135            .toArray(Content[]::new);
136        
137        // Set the attribute with the new contents list
138        dataHolder.setValue(attributeName, newContents);
139    }
140}