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 mixed content (elements and 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 HtmlElementMixed<SELF extends HtmlElementMixed<SELF>> extends HtmlElement<SELF> {
042
043   private List<Object> children;
044
045   /**
046    * Adds a child element to this element.
047    *
048    * @param value
049    *    The child to add as a child element.
050    *    Can be a string or {@link HtmlElement}.
051    *    Can also be a container of strings and elements.
052    *    Can be <jk>null</jk> (a <jk>null</jk> entry is added to the children list).
053    * @return This object.
054    */
055   public SELF child(Object value) {
056      if (this.children == null)
057         this.children = new LinkedList<>();
058      if (value instanceof Collection<?> value2)
059         this.children.addAll(value2);
060      else
061         this.children.add(value);
062      return self();
063   }
064
065   /**
066    * Adds one or more child elements to this element.
067    *
068    * @param value
069    *    The children to add as child elements.
070    *    Can be a mixture of strings and {@link HtmlElement} objects.
071    *    Can also be containers of strings and elements.
072    *    Must not be <jk>null</jk>.
073    * @return This object.
074    */
075   public SELF children(Object...value) {
076      if (value.length != 0)
077         for (var c : value)
078            child(c);
079      return self();
080   }
081
082   /**
083    * Returns the child node at the specified index.
084    *
085    * @param <T> he class type of the node.
086    * @param type The class type of the node.
087    * @param index The index of the node in the list of children.
088    * @return The child node, or <jk>null</jk> if it doesn't exist.
089    * @throws InvalidConversionException If node is not the expected type.
090    */
091   public <T> T getChild(Class<T> type, int index) {
092      return (children == null || children.size() <= index || index < 0 ? null : BasicConverter.INSTANCE.to(children.get(index), type));
093   }
094
095   /**
096    * Returns the child node at the specified index.
097    *
098    * @param index The index of the node in the list of children.
099    * @return The child node, or <jk>null</jk> if it doesn't exist.
100    */
101   public Object getChild(int index) {
102      return (children == null || children.size() <= index || index < 0 ? null : children.get(index));
103   }
104
105   /**
106    * Returns the child node at the specified address.
107    *
108    * <p>
109    * Indexes are zero-indexed.
110    *
111    * <p>
112    * For example, calling <c>getChild(1,2,3);</c> will return the 4th child of the 3rd child of the 2nd child.
113    *
114    * @param index The child indexes.
115    * @return The child node, or <jk>null</jk> if it doesn't point to a valid child.
116    */
117   public Object getChild(int...index) {
118      if (index.length == 0)
119         return null;
120      if (index.length == 1)
121         return getChild(index[0]);
122      Object c = this;
123      for (var element : index) {
124         if (c instanceof HtmlElementMixed<?> c2)
125            c = c2.getChild(element);
126         else if (c instanceof HtmlElementContainer<?> c2)
127            c = c2.getChild(element);
128         else
129            return null;
130      }
131      return c;
132   }
133
134   /**
135    * The children of this element.
136    *
137    * @return The children of this element, or <jk>null</jk> if no children are set.
138    */
139   @Xml(format = MIXED)
140   @BeanProp(name="c") @MarshalledProp(dictionary=HtmlBeanDictionary.class)
141   public List<Object> getChildren() { return u(children); }
142
143   /**
144    * Sets the children of this element.
145    *
146    * @param children The new children of this element. Can be <jk>null</jk> to clear all children.
147    * @return This object.
148    */
149   @BeanProp("c")
150   public SELF setChildren(List<Object> children) {
151      this.children = children;
152      return self();
153   }
154}