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}