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.plugins.odfsync.cdmfr.extractor;
017
018import java.util.ArrayList;
019import java.util.Arrays;
020import java.util.Collection;
021import java.util.List;
022import java.util.Optional;
023
024import org.apache.commons.collections4.CollectionUtils;
025import org.w3c.dom.Element;
026
027import org.ametys.cms.repository.Content;
028import org.ametys.core.util.dom.DOMUtils;
029import org.ametys.odf.course.Course;
030import org.ametys.plugins.odfsync.cdmfr.ImportCDMFrContext;
031import org.ametys.plugins.odfsync.cdmfr.components.ImportCDMFrComponent;
032import org.ametys.plugins.odfsync.utils.ContentWorkflowDescription;
033import org.ametys.plugins.repository.data.extractor.xml.XMLValuesExtractorAdditionalDataGetter;
034import org.ametys.runtime.model.ElementDefinition;
035import org.ametys.runtime.model.Model;
036import org.ametys.runtime.model.ModelHelper;
037import org.ametys.runtime.model.ModelItem;
038import org.ametys.runtime.model.ModelItemContainer;
039
040/**
041 * This class provides methods to extract course's values from a CMD-fr import document
042 */
043public class ImportCourseValuesExtractor extends ImportCDMFrValuesExtractor
044{
045    /** Tag to identify a coursePart */
046    protected static final String _TAG_COURSEPART = "coursePart";
047    
048    /** The imported content's synchronization code */
049    protected String _synchronizationCode;
050    
051    /**
052     * Creates an import course values extractor
053     * @param element the DOM element containing the XML values
054     * @param factory the values extractor factory
055     * @param component the import CDM-fr component
056     * @param synchronizationCode the imported content's synchronization code
057     * @param context the import context
058     * @param additionalDataGetter the getter that retrieves needed additional data by types
059     * @param models the model of the extracted values
060     */
061    public ImportCourseValuesExtractor(Element element, ImportCDMFrValuesExtractorFactory factory, ImportCDMFrComponent component, String synchronizationCode, ImportCDMFrContext context, XMLValuesExtractorAdditionalDataGetter additionalDataGetter, Model... models)
062    {
063        this(element, factory, component, synchronizationCode, context, additionalDataGetter, Arrays.asList(models));
064    }
065    
066    /**
067     * Creates an import course values extractor
068     * @param element the DOM element containing the XML values
069     * @param factory the values extractor factory
070     * @param component the import CDM-fr component
071     * @param synchronizationCode the imported content's synchronization code
072     * @param context the imported content's context
073     * @param additionalDataGetter the getter that retrieves needed additional data by types
074     * @param modelItemContainers the model of the extracted values
075     */
076    public ImportCourseValuesExtractor(Element element, ImportCDMFrValuesExtractorFactory factory, ImportCDMFrComponent component, String synchronizationCode, ImportCDMFrContext context, XMLValuesExtractorAdditionalDataGetter additionalDataGetter, Collection<? extends ModelItemContainer> modelItemContainers)
077    {
078        super(element, factory, component, context, additionalDataGetter, modelItemContainers);
079        
080        _synchronizationCode = synchronizationCode;
081    }
082    
083    @Override
084    protected Optional<ModelItem> _getModelItemFromNodeName(Element parent, String nodeName, Collection< ? extends ModelItemContainer> modelItemContainers)
085    {
086        if (_TAG_COURSELIST.equals(nodeName))
087        {
088            return Optional.of(ModelHelper.getModelItem(Course.CHILD_COURSE_LISTS, modelItemContainers));
089        }
090        else if (_TAG_COURSEPART.equals(nodeName))
091        {
092            return Optional.of(ModelHelper.getModelItem(Course.CHILD_COURSE_PARTS, modelItemContainers));
093        }
094        else
095        {
096            return super._getModelItemFromNodeName(parent, nodeName, modelItemContainers);
097        }
098    }
099
100    @Override
101    protected boolean _hasChildForAttribute(Element element, String attributeName)
102    {
103        if (Course.CHILD_COURSE_LISTS.equals(attributeName))
104        {
105            return DOMUtils.hasChildElement(element, _TAG_COURSELIST);
106        }
107        else if (Course.CHILD_COURSE_PARTS.equals(attributeName))
108        {
109            return DOMUtils.hasChildElement(element, _TAG_COURSEPART);
110        }
111        else
112        {
113            return super._hasChildForAttribute(element, attributeName);
114        }
115    }
116    
117    @Override
118    protected <T> Object _extractElementValue(Element parent, ElementDefinition<T> definition, Optional<Object> additionalData) throws Exception
119    {
120        String attributePath = definition.getPath();
121        if (Course.CHILD_COURSE_LISTS.equals(attributePath))
122        {
123            List<Content> courseLists = new ArrayList<>();
124            List<Element> children = DOMUtils.getChildElementsByTagName(parent, _TAG_COURSELIST);
125            
126            int courseListPosition = 0;
127            for (Element child : children)
128            {
129                String title = _getAttributeOrDefault(child, "name", "Liste d'éléments pédagogiques");
130                // For courseList from another source than Ametys, there is no unique code, then a code is generated with the parent ID and the position in the parent : [parentId]-[position]
131                courseListPosition++;
132                String subContentSyncCode = _getAttributeOrDefault(child, "code", _synchronizationCode + "-" + courseListPosition);
133                Content subContent = _component.importOrSynchronizeContent(child, ContentWorkflowDescription.COURSELIST_WF_DESCRIPTION, title, subContentSyncCode, _context);
134                CollectionUtils.addIgnoreNull(courseLists, subContent);
135            }
136            
137            return courseLists.toArray(new Content[courseLists.size()]);
138        }
139        else if (Course.CHILD_COURSE_PARTS.equals(attributePath))
140        {
141            List<Content> courseParts = new ArrayList<>();
142            List<Element> children = DOMUtils.getChildElementsByTagName(parent, _TAG_COURSEPART);
143            
144            for (Element child : children)
145            {
146                String title = DOMUtils.getChildElementByTagName(child, "title").getTextContent();
147                Element subContentSyncCodeElement = DOMUtils.getChildElementByTagName(child, "code");
148                String subContentSyncCode = subContentSyncCodeElement != null
149                        ? subContentSyncCodeElement.getTextContent()
150                        : org.ametys.core.util.StringUtils.generateKey().toUpperCase();
151                Content subContent = _component.importOrSynchronizeContent(child, ContentWorkflowDescription.COURSEPART_WF_DESCRIPTION, title, subContentSyncCode, _context);
152                CollectionUtils.addIgnoreNull(courseParts, subContent);
153            }
154            
155            return courseParts.toArray(new Content[courseParts.size()]);
156        }
157        else
158        {
159            return super._extractElementValue(parent, definition, additionalData);
160        }
161    }
162}