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