001/*
002 *  Copyright 2015 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.xslt;
017
018import java.util.HashMap;
019import java.util.List;
020import java.util.Map;
021
022import org.apache.commons.lang.StringUtils;
023import org.w3c.dom.DOMException;
024import org.w3c.dom.Node;
025
026import org.ametys.core.util.dom.AbstractWrappingAmetysElement;
027import org.ametys.core.util.dom.AmetysAttribute;
028import org.ametys.odf.ODFHelper;
029import org.ametys.odf.ProgramItem;
030import org.ametys.odf.course.Course;
031import org.ametys.odf.courselist.CourseList;
032import org.ametys.odf.program.Container;
033import org.ametys.odf.program.ProgramPart;
034import org.ametys.odf.program.SubProgram;
035import org.ametys.odf.program.TraversableProgramPart;
036import org.ametys.plugins.repository.AmetysObject;
037
038/**
039 * DOM layer on structure of an ODF content.
040 * @param <T> The type of wrapped object
041 */
042public abstract class AbstractODFElement<T extends ProgramItem> extends AbstractWrappingAmetysElement<T>
043{
044    /** The children depth */
045    protected int _depth;
046    /** The ODF helper */
047    protected ODFHelper _odfHelper;
048
049    /**
050     * Constructor.
051     * @param odfContent the underlying.
052     * @param depth Depth to SAX.
053     * @param parent Parent of the element
054     * @param odfHelper ODFHelper to resolve children easily
055     */
056    public AbstractODFElement(T odfContent, int depth, AbstractODFElement<?> parent, ODFHelper odfHelper)
057    {
058        super(odfContent, parent);
059        _depth = depth;
060        _odfHelper = odfHelper;
061    }
062
063    @Override
064    public boolean hasChildNodes()
065    {
066        if (_depth == 0)
067        {
068            // Stop recursion
069            return false;
070        }
071        
072        return !_odfHelper.getChildProgramItems(_object).isEmpty();
073    }
074    
075    @Override
076    public Node getFirstChild()
077    {
078        List<ProgramItem> children = _odfHelper.getChildProgramItems(_object);
079        if (!children.isEmpty())
080        {
081            return _convertAmetysObjectToElement(children.get(0), this);
082        }
083        
084        return null;
085    }
086    
087    @Override
088    public Node getNextSibling()
089    {
090        if (_parent == null)
091        {
092            return null;
093        }
094        
095        AmetysObject nextSibling = null;
096        
097        AbstractODFElement<?> parentElement = (AbstractODFElement<?>) _parent;
098        Object parent = parentElement.getWrappedObject();
099        if (parent instanceof ProgramPart)
100        {
101            List<? extends AmetysObject> children = _getProgramPartChildren((ProgramPart) parent);
102            nextSibling = _getNextSibling(children);
103        }
104        else if (parent instanceof Course)
105        {
106            List<CourseList> children = ((Course) parent).getCourseLists();
107            nextSibling = _getNextSibling(children);
108        }
109        else
110        {
111            throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "getNextSibling");
112        }
113        
114        if (nextSibling != null)
115        {
116            return _convertAmetysObjectToElement(nextSibling, parentElement);
117        }
118        
119        return null;
120    }
121    
122    /**
123     * Retrieves the children of the given program part
124     * @param programPart the program part
125     * @return the children of the program part
126     */
127    protected List<? extends AmetysObject> _getProgramPartChildren(ProgramPart programPart)
128    {
129        if (programPart instanceof TraversableProgramPart)
130        {
131            return ((TraversableProgramPart) programPart).getProgramPartChildren();
132        }
133        else if (programPart instanceof CourseList)
134        {
135            return ((CourseList) programPart).getCourses();
136        }
137        else
138        {
139            // Should never happen, a program part can only be traversable or a course list
140            return List.of();
141        }
142    }
143    
144    /**
145     * The element immediately following this one
146     * @param elements the elements that are at the same level as the current one
147     * @return The element immediately following this one
148     */
149    protected AmetysObject _getNextSibling(List<? extends AmetysObject> elements)
150    {
151        AmetysObject nextSibling = null;
152        boolean isNext = false;
153        int count = 0;
154        
155        while (nextSibling == null && count < elements.size())
156        {
157            if (isNext)
158            {
159                nextSibling = elements.get(count);
160            }
161            else if (_object.getId().equals(elements.get(count).getId()))
162            {
163                isNext = true;
164            }
165            count++;
166        }
167        
168        return nextSibling;
169    }
170    
171    @Override
172    protected Map<String, AmetysAttribute> _lookupAttributes()
173    {
174        Map<String, AmetysAttribute> result = new HashMap<>();
175        result.put("id", new AmetysAttribute("id", "id", null, _object.getId(), this));
176        
177        if (StringUtils.isNotEmpty(_object.getCode()))
178        {
179            result.put("code", new AmetysAttribute("code", "code", null, _object.getCode(), this));
180        }
181        if (StringUtils.isNotEmpty(_object.getCatalog()))
182        {
183            result.put("catalog", new AmetysAttribute("catalog", "catalog", null, _object.getCatalog(), this));
184        }
185        return result;
186    }
187    
188    private Node _convertAmetysObjectToElement(AmetysObject ao, AbstractODFElement<?> parentElement)
189    {
190        if (ao instanceof SubProgram subProgram)
191        {
192            return new SubProgramElement(subProgram, _depth, parentElement, _odfHelper);
193        }
194        
195        if (ao instanceof Container container)
196        {
197            return new ContainerElement(container, _depth, parentElement, _odfHelper);
198        }
199        
200        if (ao instanceof CourseList courseList)
201        {
202            return new CourseListElement(courseList, _depth, parentElement, _odfHelper);
203        }
204        
205        if (ao instanceof Course course)
206        {
207            return new CourseElement(course, _depth, (CourseListElement) parentElement, _odfHelper);
208        }
209        
210        return null;
211    }
212}