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.ProgramItem;
029import org.ametys.odf.course.Course;
030import org.ametys.odf.courselist.CourseList;
031import org.ametys.odf.program.Container;
032import org.ametys.odf.program.ProgramPart;
033import org.ametys.odf.program.SubProgram;
034import org.ametys.odf.program.TraversableProgramPart;
035import org.ametys.plugins.repository.AmetysObject;
036import org.ametys.plugins.repository.AmetysObjectResolver;
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 Ametys object resolver */
047    protected AmetysObjectResolver _resolver;
048
049    /**
050     * Constructor.
051     * @param odfContent the underlying.
052     * @param depth Depth to SAX.
053     * @param parent Parent of the element
054     * @param resolver AmetysObjectResolver to find some elements by ID
055     */
056    public AbstractODFElement(T odfContent, int depth, AbstractODFElement<?> parent, AmetysObjectResolver resolver)
057    {
058        super(odfContent, parent);
059        _depth = depth;
060        _resolver = resolver;
061    }
062
063    @Override
064    public boolean hasChildNodes()
065    {
066        if (_depth == 0)
067        {
068            // Stop recursion
069            return false;
070        }
071        
072        if (_object instanceof CourseList)
073        {
074            return ((CourseList) _object).hasCourses();
075        }
076        else if (_object instanceof TraversableProgramPart)
077        {
078            return ((TraversableProgramPart) _object).hasProgramPartChildren();
079        }
080        else if (_object instanceof Course)
081        {
082            return ((Course) _object).hasCourseLists();
083        }
084        else
085        {
086            throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "hasChildNodes");
087        }
088    }
089    
090    @Override
091    public Node getFirstChild()
092    {
093        if (_object instanceof CourseList)
094        {
095            List<Course> courses = ((CourseList) _object).getCourses();
096            if (courses.size() > 0)
097            {
098                return new CourseElement(courses.get(0), 1, (CourseListElement) this, _resolver);
099            }
100        }
101        else if (_object instanceof TraversableProgramPart)
102        {
103            List<ProgramPart> children = ((TraversableProgramPart) _object).getProgramPartChildren();
104            if (children.size() > 0)
105            {
106                ProgramPart ao = children.get(0);
107                if (ao instanceof SubProgram)
108                {
109                    return new SubProgramElement((SubProgram) ao, _depth - 1, this, _resolver);
110                }
111                else if (ao instanceof Container)
112                {
113                    return new ContainerElement((Container) ao, _depth - 1, this, _resolver);
114                }
115                else if (ao instanceof CourseList)
116                {
117                    return new CourseListElement((CourseList) ao, _depth - 1, this, _resolver);
118                }
119            }
120            return null;
121        }
122        else if (_object instanceof Course)
123        {
124            List<CourseList> children = ((Course) _object).getCourseLists();
125            if (children.size() > 0)
126            {
127                CourseList ao = children.get(0);
128                return new CourseListElement(ao, _depth - 1, this, _resolver);
129            }
130        }
131        else
132        {
133            throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "getFirstChild");
134        }
135        
136        return null;
137    }
138    
139    @Override
140    public Node getNextSibling()
141    {
142        if (_parent == null)
143        {
144            return null;
145        }
146        
147        AmetysObject nextSibling = null;
148        
149        AbstractODFElement<?> parentElement = (AbstractODFElement<?>) _parent;
150        Object parent = parentElement.getWrappedObject();
151        if (parent instanceof ProgramPart)
152        {
153            List<? extends AmetysObject> children = _getProgramPartChildren((ProgramPart) parent);
154            nextSibling = _getNextSibling(children);
155        }
156        else if (parent instanceof Course)
157        {
158            List<CourseList> children = ((Course) parent).getCourseLists();
159            nextSibling = _getNextSibling(children);
160        }
161        else
162        {
163            throw new DOMException(DOMException.NOT_SUPPORTED_ERR, "getNextSibling");
164        }
165        
166        if (nextSibling != null)
167        {
168            if (nextSibling instanceof SubProgram)
169            {
170                return new SubProgramElement((SubProgram) nextSibling, _depth, parentElement, _resolver);
171            }
172            else if (nextSibling instanceof Container)
173            {
174                return new ContainerElement((Container) nextSibling, _depth, parentElement, _resolver);
175            }
176            else if (nextSibling instanceof CourseList)
177            {
178                return new CourseListElement((CourseList) nextSibling, _depth, parentElement, _resolver);
179            }
180            else if (nextSibling instanceof Course)
181            {
182                return new CourseElement((Course) nextSibling, _depth, (CourseListElement) parentElement, _resolver);
183            }
184        }
185        
186        return null;
187    }
188    
189    /**
190     * Retrieves the children of the given program part
191     * @param programPart the program part
192     * @return the children of the program part
193     */
194    protected List<? extends AmetysObject> _getProgramPartChildren(ProgramPart programPart)
195    {
196        if (programPart instanceof TraversableProgramPart)
197        {
198            return ((TraversableProgramPart) programPart).getProgramPartChildren();
199        }
200        else if (programPart instanceof CourseList)
201        {
202            return ((CourseList) programPart).getCourses();
203        }
204        else
205        {
206            // Should never happen, a program part can only be traversable or a course list
207            return List.of();
208        }
209    }
210    
211    /**
212     * The element immediately following this one
213     * @param elements the elements that are at the same level as the current one
214     * @return The element immediately following this one
215     */
216    protected AmetysObject _getNextSibling(List<? extends AmetysObject> elements)
217    {
218        AmetysObject nextSibling = null;
219        boolean isNext = false;
220        int count = 0;
221        
222        while (nextSibling == null && count < elements.size())
223        {
224            if (isNext)
225            {
226                nextSibling = elements.get(count);
227            }
228            else if (_object.getId().equals(elements.get(count).getId()))
229            {
230                isNext = true;
231            }
232            count++;
233        }
234        
235        return nextSibling;
236    }
237    
238    @Override
239    protected Map<String, AmetysAttribute> _lookupAttributes()
240    {
241        Map<String, AmetysAttribute> result = new HashMap<>();
242        result.put("id", new AmetysAttribute("id", "id", null, _object.getId(), this));
243        
244        if (StringUtils.isNotEmpty(_object.getCode()))
245        {
246            result.put("code", new AmetysAttribute("code", "code", null, _object.getCode(), this));
247        }
248        if (StringUtils.isNotEmpty(_object.getCatalog()))
249        {
250            result.put("catalog", new AmetysAttribute("catalog", "catalog", null, _object.getCatalog(), this));
251        }
252        return result;
253    }
254}