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;
17  
18  import static java.util.Objects.requireNonNull;
19  
20  import java.nio.charset.StandardCharsets;
21  import javax.annotation.Nonnull;
22  import org.jsoup.Jsoup;
23  import org.jsoup.nodes.Document;
24  import org.jsoup.nodes.Element;
25  import org.jsoup.nodes.Node;
26  import org.jsoup.parser.Parser;
27  import org.jsoup.parser.Tag;
28  
29  /*
30   * Licensed to the Apache Software Foundation (ASF) under one
31   * or more contributor license agreements.  See the NOTICE file
32   * distributed with this work for additional information
33   * regarding copyright ownership.  The ASF licenses this file
34   * to you under the Apache License, Version 2.0 (the
35   * "License"); you may not use this file except in compliance
36   * with the License.  You may obtain a copy of the License at
37   *
38   *   http://www.apache.org/licenses/LICENSE-2.0
39   *
40   * Unless required by applicable law or agreed to in writing,
41   * software distributed under the License is distributed on an
42   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
43   * KIND, either express or implied.  See the License for the
44   * specific language governing permissions and limitations
45   * under the License.
46   */
47  public final class JsoupUtils {
48  
49    private JsoupUtils() {
50      // util
51    }
52  
53    /**
54     * Parse HTML string into a Jsoup Document with XML output settings.
55     *
56     * @param html
57     *          HTML string
58     * @return Jsoup Document
59     */
60    @Nonnull
61    public static Document createHtmlDocument(@Nonnull String html) {
62      requireNonNull(html);
63      Document doc = Jsoup.parse(html, "", createHtmlParser());
64      // doc.outputSettings()
65      // .outline(false)
66      // .syntax(Document.OutputSettings.Syntax.xml)
67      // .prettyPrint(false)
68      // .escapeMode(Entities.EscapeMode.xhtml)
69      // .charset(StandardCharsets.UTF_8);
70      return doc;
71    }
72  
73    @Nonnull
74    public static Document createXmlDocument(@Nonnull String html) {
75      requireNonNull(html);
76      Document doc = Jsoup.parse(html, "", createXmlParser());
77      doc.outputSettings()
78          .outline(false)
79          .syntax(Document.OutputSettings.Syntax.xml)
80          .prettyPrint(false)
81          .escapeMode(org.jsoup.nodes.Entities.EscapeMode.xhtml)
82          .charset(StandardCharsets.UTF_8);
83      return doc;
84    }
85  
86    @Nonnull
87    public static Parser createHtmlParser() {
88      Parser parser = Parser.htmlParser();
89      parser.tagSet().onNewTag(tag -> {
90        if (!tag.isKnownTag())
91          tag.set(Tag.SelfClose);
92      });
93      return parser;
94    }
95  
96    @Nonnull
97    public static Parser createXmlParser() {
98      Parser parser = Parser.xmlParser();
99      return parser;
100   }
101 
102   public static Element link(Document doc,
103     String href,
104     String name,
105     String target,
106     String img,
107     String icon,
108     String className) {
109     Element a = doc.createElement("a");
110     if (href != null && !href.isEmpty()) {
111       a.attr("href", href);
112     }
113     if (name != null && !name.isEmpty()) {
114       a.attr("title", name);
115     }
116     if (target != null && !target.isEmpty()) {
117       a.attr("target", target);
118     }
119     if (className != null && !className.isEmpty()) {
120       a.addClass(className);
121     }
122     if (img != null && !img.isEmpty()) {
123       // Image on the left side of the name
124       Element image = image(doc, img, "", "", "", "");
125       a.appendChild(image);
126       a.appendText(name);
127     } else if (icon != null && !icon.isEmpty()) {
128       // No image - optional icon though on the right of the name
129       a.appendText(name);
130       Element i = doc.createElement("i");
131       i.addClass(icon);
132       a.appendChild(i);
133     } else {
134       // No image - no icon
135       a.appendText(name);
136     }
137     return a;
138   }
139 
140   public static Element image(Document doc, String src, String alt, String border, String width, String height) {
141     Element image = doc.createElement("img");
142     image.addClass("image-link");
143     image.attr("src", src);
144     if (alt != null && !alt.isEmpty()) {
145       image.attr("alt", alt);
146     }
147     if (border != null && !border.isEmpty()) {
148       image.attr("border", border);
149     }
150     if (width != null && !width.isEmpty()) {
151       image.attr("width", width);
152     }
153     if (height != null && !height.isEmpty()) {
154       image.attr("height", height);
155     }
156     return image;
157   }
158 
159   public static String getNodeName(Node node) {
160     if (node instanceof Element) {
161       return ((Element) node).tagName();
162     } else {
163       return node.nodeName();
164     }
165   }
166 
167   public static boolean hasTextNode(Node node) {
168     return node.childNodes().stream().anyMatch(n -> {
169       boolean match = n.nodeName().equals("#text") && !n.toString().trim().isEmpty();
170       return match;
171     });
172   }
173 
174 }