001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.bean.html5;
018
019import static org.apache.juneau.marshall.xml.XmlFormat.*;
020import static org.apache.juneau.commons.utils.Shorts.*;
021
022import java.util.*;
023
024import org.apache.juneau.commons.bean.*;
025import org.apache.juneau.commons.conversion.*;
026import org.apache.juneau.marshall.*;
027import org.apache.juneau.marshall.xml.*;
028
029/**
030 * A subclass of HTML elements that contain only other elements, not text.
031 *
032 * <h5 class='section'>See Also:</h5><ul>
033 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauBeanHtml5">juneau-bean-html5</a>
034 * </ul>
035 *
036 * @param <SELF> The self type for fluent setters.
037 */
038@SuppressWarnings({
039   "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name.
040})
041public abstract class HtmlElementContainer<SELF extends HtmlElementContainer<SELF>> extends HtmlElement<SELF> {
042
043   private List<Object> children;
044
045   /**
046    * Adds a child element to this element.
047    *
048    * @param value The child to add as a child element. Can be <jk>null</jk> (a <jk>null</jk> entry is added to the children list).
049    * @return This object.
050    */
051   public SELF child(Object value) {
052      if (this.children == null)
053         this.children = new LinkedList<>();
054      this.children.add(value);
055      return self();
056   }
057
058   /**
059    * Adds one or more child elements to this element.
060    *
061    * @param value The children to add as child elements. Must not be <jk>null</jk>.
062    * @return This object.
063    */
064   public SELF children(Object...value) {
065      if (value.length > 0) {
066         if (this.children == null)
067            this.children = new LinkedList<>();
068         for (var c : value)
069            this.children.add(c);
070      }
071      return self();
072   }
073
074   /**
075    * Returns the child node at the specified index.
076    *
077    * @param <T> The class type of the node.
078    * @param type The class type of the node.
079    * @param index The index of the node in the list of children.
080    * @return The child node, or <jk>null</jk> if it doesn't exist.
081    * @throws InvalidConversionException If node is not the expected type.
082    */
083   public <T> T getChild(Class<T> type, int index) {
084      return (children == null || children.size() <= index || index < 0 ? null : BasicConverter.INSTANCE.to(children.get(index), type));
085   }
086
087   /**
088    * Returns the child node at the specified index.
089    *
090    * @param index The index of the node in the list of children.
091    * @return The child node, or <jk>null</jk> if it doesn't exist.
092    */
093   public Object getChild(int index) {
094      return (children == null || children.size() <= index || index < 0 ? null : children.get(index));
095   }
096
097   /**
098    * Returns the child node at the specified address.
099    *
100    * <p>
101    * Indexes are zero-indexed.
102    *
103    * <p>
104    * For example, calling <c>getChild(1,2,3);</c> will return the 4th child of the 3rd child of the 2nd child.
105    *
106    * @param index The child indexes.
107    * @return The child node, or <jk>null</jk> if it doesn't point to a valid child.
108    */
109   public Object getChild(int...index) {
110      if (index.length == 0)
111         return null;
112      if (index.length == 1)
113         return getChild(index[0]);
114      Object c = this;
115      for (var element : index) {
116         if (c instanceof HtmlElementMixed<?> c2)
117            c = c2.getChild(element);
118         else if (c instanceof HtmlElementContainer<?> c2)
119            c = c2.getChild(element);
120         else
121            return null;
122      }
123      return c;
124   }
125
126   /**
127    * The children of this element.
128    *
129    * @return The children of this element, or <jk>null</jk> if no children are set.
130    */
131   @Xml(format = FREEFORM)
132   @BeanProp(name="c") @MarshalledProp(dictionary=HtmlBeanDictionary.class)
133   public List<Object> getChildren() { return u(children); }
134
135   /**
136    * Sets the children for this container.
137    *
138    * @param children The new children for this container. Can be <jk>null</jk> to clear all children.
139    * @return This object.
140    */
141   @BeanProp("c")
142   public SELF setChildren(List<Object> children) {
143      this.children = children;
144      return self();
145   }
146}