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}