View Javadoc
1   /*
2   * Copyright 2012-2025 Christophe Friederich
3   *
4   * Licensed under the Apache License, Version 2.0 (the "License");
5   * you may not use this file except in compliance with the License.
6   * You may obtain a copy of the License at
7   *
8   * http://www.apache.org/licenses/LICENSE-2.0
9   *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16  package org.devacfr.maven.skins.reflow.model;
17  
18  import com.google.common.collect.Sets;
19  import java.util.List;
20  import java.util.Set;
21  import javax.annotation.Nonnull;
22  import javax.annotation.Nullable;
23  import org.apache.commons.lang3.builder.ToStringBuilder;
24  import org.devacfr.maven.skins.reflow.HtmlTool;
25  import org.devacfr.maven.skins.reflow.HtmlTool.IdElement;
26  import org.devacfr.maven.skins.reflow.ISkinConfig;
27  import org.devacfr.maven.skins.reflow.Xpp3Utils;
28  import org.slf4j.Logger;
29  import org.slf4j.LoggerFactory;
30  
31  /**
32   * Represents the base of Table of content component.
33   *
34   * @author devacfr
35   * @since 2.0
36   * @param <T>
37   *          the type of inherit of {@link Toc}.
38   */
39  public abstract class Toc<T extends Toc<?>> extends BsComponent {
40  
41    private static final Set<String> TOC_TYPES = Sets.newHashSet("sidebar", "top", "false");
42  
43    /** */
44    public static final String COMPONENT = "toc";
45  
46    /** */
47    private static final Logger LOGGER = LoggerFactory.getLogger(Toc.class);
48  
49    /** enable by default */
50    private boolean enabled = true;
51  
52    /** */
53    private final String type;
54  
55    /**
56     * @param config
57     *          a config (can <b>not</b> be {@code null}).
58     * @param preferredType
59     *          the default type of Toc to use.
60     * @return Returns new instance corresponding {@link Toc} to configuration.
61     */
62    public static Toc<?> createToc(@Nonnull final ISkinConfig config, @Nullable final String preferredType) {
63      Toc<?> toc = null;
64      String type = config.getPropertyValue(COMPONENT, String.class, preferredType);
65      if (LOGGER.isTraceEnabled()) {
66        LOGGER.trace("Page '{}' Find Toc: {}", config.getFileId(), type);
67      }
68      if (!TOC_TYPES.contains(type)) {
69        type = preferredType;
70      }
71      if (type == null) {
72        type = "";
73      }
74      switch (type) {
75        case "sidebar":
76          toc = createSidebar(config);
77          break;
78        case "top":
79          toc = createTopBar(config);
80          break;
81        default:
82          // create a disabled empty toc
83          toc = new Toc<Toc<?>>(config, "", "") {};
84  
85          toc.withEnabled(false);
86          break;
87      }
88  
89      return toc;
90    }
91  
92    /**
93     * @param config
94     *          a config (can <b>not</b> be {@code null}).
95     * @return Returns new instance of Toc sidebar.
96     */
97    public static Toc<?> createSidebar(@Nonnull final ISkinConfig config) {
98      return new TocSidebar(config);
99    }
100 
101   /**
102    * @param config
103    *          a config (can <b>not</b> be {@code null}).
104    * @return Returns new instance Toc top bar.
105    */
106   public static Toc<?> createTopBar(@Nonnull final ISkinConfig config) {
107     return new TocTopBar(config);
108   }
109 
110   /**
111    * @param config
112    *          a config (can <b>not</b> be {@code null}
113    * @param type
114    *          the {@link String} representation of Toc.
115    * @param component
116    *          the bootstrap component name.
117    */
118   protected Toc(@Nonnull final ISkinConfig config, final String type, final String component) {
119     super(config, component);
120     this.type = type;
121   }
122 
123   /**
124    * @return Returns the fluent instance.
125    */
126   @SuppressWarnings("unchecked")
127   protected T self() {
128     return (T) this;
129   }
130 
131   /**
132    * @return Returns the {@link String} reprensenting the type of {@link Toc}.
133    */
134   public String getType() {
135     return type;
136   }
137 
138   /**
139    * Gets the indicating whether is enable.
140    *
141    * @return Returns {@code true} if is enable, otherwise {@code false}.
142    */
143   public boolean isEnabled() {
144     return enabled;
145   }
146 
147   /**
148    * @return Returns a list of {@link IdElement} representing the heading tree containing in current page.
149    * @since 2.1
150    */
151   public List<? extends IdElement> getTocItems() {
152     final HtmlTool htmlTool = config.getHtmlTool();
153     final String bodyContent = config.getBodyContent();
154     if (LOGGER.isTraceEnabled()) {
155       LOGGER.trace("Generating TOC items for page '{}', for content:{}", config.getFileId(), bodyContent);
156     }
157     final List<? extends IdElement> tocItems = htmlTool.headingTree(bodyContent,
158       Xpp3Utils.getChildren(config.get("sections")));
159     return tocItems;
160   }
161 
162   /**
163    * Sets the indicating whether is enable.
164    *
165    * @param enabled
166    *          status to use.
167    * @return Returns the fluent instance.
168    */
169   protected T withEnabled(final boolean enabled) {
170     this.enabled = enabled;
171     return self();
172   }
173 
174   @Override
175   protected String onPreRender(final @Nonnull String bodyContent) {
176     if (this.enabled) {
177       final HtmlTool htmlTool = config.getHtmlTool();
178       return htmlTool.ensureHeadingIds(config.getContext().getType(),
179         config.getFileId(),
180         bodyContent,
181         HtmlTool.DEFAULT_SLUG_SEPARATOR);
182     }
183     return bodyContent;
184   }
185 
186   /**
187    * {@inheritDoc}
188    */
189   @Override
190   public String toString() {
191     return ToStringBuilder.reflectionToString(this);
192   }
193 }