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.apache.commons.lang3.StringUtils;
026import org.apache.xpath.XPathAPI;
027import org.w3c.dom.Document;
028import org.w3c.dom.Element;
029import org.w3c.dom.NamedNodeMap;
030import org.w3c.dom.Node;
031
032import org.ametys.cms.repository.Content;
033import org.ametys.core.util.dom.DOMUtils;
034import org.ametys.odf.courselist.CourseList;
035import org.ametys.plugins.odfsync.cdmfr.ImportCDMFrContext;
036import org.ametys.plugins.odfsync.cdmfr.components.ImportCDMFrComponent;
037import org.ametys.plugins.odfsync.utils.ContentWorkflowDescription;
038import org.ametys.runtime.model.ElementDefinition;
039import org.ametys.runtime.model.Model;
040import org.ametys.runtime.model.ModelHelper;
041import org.ametys.runtime.model.ModelItem;
042import org.ametys.runtime.model.ModelItemContainer;
043import org.ametys.runtime.model.ModelViewItem;
044import org.ametys.runtime.model.ViewElement;
045import org.ametys.runtime.model.ViewItemContainer;
046
047/**
048 * This class provides methods to extract course list's values from a CMD-fr import document
049 */
050public class ImportCourseListValuesExtractor extends ImportCDMFrValuesExtractor
051{
052    /**
053     * Creates an import course list values extractor
054     * @param element the DOM element containing the XML values
055     * @param factory the values extractor factory
056     * @param component the import CDM-fr component
057     * @param context the import context
058     * @param models the model of the extracted values
059     */
060    public ImportCourseListValuesExtractor(Element element, ImportCDMFrValuesExtractorFactory factory, ImportCDMFrComponent component, ImportCDMFrContext context, Model... models)
061    {
062        this(element, factory, component, context, Arrays.asList(models));
063    }
064    
065    /**
066     * Creates an import course list values extractor
067     * @param element the DOM element containing the XML values
068     * @param factory the values extractor factory
069     * @param component the import CDM-fr component
070     * @param context the imported content's context
071     * @param modelItemContainers the model of the extracted values
072     */
073    public ImportCourseListValuesExtractor(Element element, ImportCDMFrValuesExtractorFactory factory, ImportCDMFrComponent component, ImportCDMFrContext context, Collection<? extends ModelItemContainer> modelItemContainers)
074    {
075        super(element, factory, component, context, modelItemContainers);
076    }
077    
078    @SuppressWarnings("unchecked")
079    @Override
080    protected void _fillViewItemContainerFromXML(Element element, ViewItemContainer viewItemContainer, Collection<? extends ModelItemContainer> modelItemContainers) throws Exception
081    {
082        NamedNodeMap attributes = element.getAttributes();
083        for (int i = 0; i < attributes.getLength(); i++)
084        {
085            Node attribute = attributes.item(i);
086
087            Optional<ModelItem> optionalModelItem = _getModelItemFromNodeName(element, attribute.getNodeName(), modelItemContainers);
088            if (optionalModelItem.isPresent())
089            {
090                ModelItem modelItem = optionalModelItem.get();
091
092                ModelViewItem modelViewItem = new ViewElement();
093                modelViewItem.setDefinition(modelItem);
094
095                viewItemContainer.addViewItem(modelViewItem);
096            }
097        }
098        
099        if (DOMUtils.hasChildElement(element, _MULTIPLE_DATA_ITEM_TAG))
100        {
101            ModelItem childCoursesModelItem = ModelHelper.getModelItem(CourseList.CHILD_COURSES, modelItemContainers);
102            
103            ModelViewItem modelViewItem = new ViewElement();
104            modelViewItem.setDefinition(childCoursesModelItem);
105
106            viewItemContainer.addViewItem(modelViewItem);
107        }
108    }
109
110    @Override
111    protected boolean _hasChildForAttribute(Element element, String attributeName)
112    {
113        return CourseList.CHILD_COURSES.equals(attributeName)
114                ? DOMUtils.hasChildElement(element, _MULTIPLE_DATA_ITEM_TAG)
115                : element.hasAttribute(attributeName);
116    }
117    
118    @Override
119    protected <T> Object _extractElementValue(Element parent, ElementDefinition<T> definition, Optional<Object> additionalData) throws Exception
120    {
121        String attributePath = definition.getPath();
122        if (CourseList.CHILD_COURSES.equals(attributePath))
123        {
124            Document doc = _context.getDocument();
125            String lang = _context.getLang();
126            List<Content> courses = new ArrayList<>();
127            List<Element> children = DOMUtils.getChildElementsByTagName(parent, _MULTIPLE_DATA_ITEM_TAG);
128            for (Element child : children)
129            {
130                String courseSyncCode = child.getTextContent().trim();
131                
132                Element courseElement = (Element) XPathAPI.selectSingleNode(doc.getFirstChild(), "course[@CDMid = '" + courseSyncCode + "' and @language = '" + lang + "']");
133                if (courseElement == null)
134                {
135                    courseElement = (Element) XPathAPI.selectSingleNode(doc.getFirstChild(), "course[@CDMid = '" + courseSyncCode + "']");
136                }
137                
138                if (courseElement != null)
139                {
140                    String courseCatalog = _component.getCatalogName(courseElement);
141                    if (courseCatalog != null && !courseCatalog.equals(_context.getCatalog()))
142                    {
143                        String cdmCode = DOMUtils.getChildElementByTagName(courseElement, "cdmCode").getTextContent();
144                        _context.getLogger().error("The course '{}' belongs to a different catalog than the one from the imported/synchronized program : '{}' vs '{}'. No synchronization will be done on this course.", cdmCode, courseCatalog, _context.getCatalog());
145                    }
146                    else
147                    {
148                        ImportCDMFrContext courseContext = new ImportCDMFrContext(_context);
149                        String elpLang = courseElement.getAttribute("language");
150                        if (StringUtils.isNotEmpty(elpLang))
151                        {
152                            courseContext.setLang(elpLang);
153                        }
154                        
155                        String title = DOMUtils.getChildElementByTagName(courseElement, "title").getTextContent();
156                        Content course = _component.importOrSynchronizeContent(courseElement, ContentWorkflowDescription.COURSE_WF_DESCRIPTION, title, courseSyncCode, courseContext);
157                        CollectionUtils.addIgnoreNull(courses, course);
158                    }
159                }
160            }
161            return courses.toArray(new Content[courses.size()]);
162        }
163        else
164        {
165            String attribute = parent.getAttribute(attributePath);
166            if (StringUtils.isNotEmpty(attribute))
167            {
168                return CourseList.CHOICE_TYPE.equals(attributePath)
169                        ? attribute.toUpperCase()
170                        : definition.getType().castValue(attribute); 
171            }
172            else
173            {
174                return null;
175            }
176        }
177    }
178}