001package ball.tools.javadoc; 002/*- 003 * ########################################################################## 004 * Utilities 005 * $Id: JavadocHTMLTemplates.html 5431 2020-02-12 19:03:17Z ball $ 006 * $HeadURL: svn+ssh://svn.hcf.dev/var/spool/scm/repository.svn/hcf-dev/blog/2019-03-30-java-interface-facades/src/main/resources/javadoc/src-html/ball/tools/javadoc/JavadocHTMLTemplates.html $ 007 * %% 008 * Copyright (C) 2008 - 2020 Allen D. Ball 009 * %% 010 * Licensed under the Apache License, Version 2.0 (the "License"); 011 * you may not use this file except in compliance with the License. 012 * You may obtain a copy of the License at 013 * 014 * http://www.apache.org/licenses/LICENSE-2.0 015 * 016 * Unless required by applicable law or agreed to in writing, software 017 * distributed under the License is distributed on an "AS IS" BASIS, 018 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 019 * See the License for the specific language governing permissions and 020 * limitations under the License. 021 * ########################################################################## 022 */ 023import ball.xml.FluentNode; 024import ball.xml.HTMLTemplates; 025import com.sun.javadoc.Tag; 026import com.sun.tools.doclets.Taglet; 027import java.lang.annotation.Annotation; 028/* import java.lang.reflect.AnnotatedElement; */ 029import java.lang.reflect.Constructor; 030import java.lang.reflect.Field; 031import java.lang.reflect.Member; 032import java.lang.reflect.Method; 033import java.lang.reflect.Modifier; 034import java.lang.reflect.Parameter; 035import java.lang.reflect.ParameterizedType; 036import java.lang.reflect.Type; 037import java.util.Arrays; 038import java.util.Collection; 039import java.util.List; 040import java.util.stream.Collectors; 041import java.util.stream.IntStream; 042import java.util.stream.Stream; 043import javax.swing.table.TableModel; 044import org.apache.commons.lang3.ArrayUtils; 045import org.apache.commons.lang3.exception.ExceptionUtils; 046import org.w3c.dom.Node; 047 048import static org.apache.commons.lang3.StringUtils.SPACE; 049import static org.apache.commons.lang3.StringUtils.isAllBlank; 050import static org.apache.commons.lang3.StringUtils.isNotEmpty; 051 052/** 053 * Javadoc {@link HTMLTemplates}. 054 * 055 * @author {@link.uri mailto:ball@hcf.dev Allen D. Ball} 056 * @version $Revision: 5431 $ 057 */ 058public interface JavadocHTMLTemplates extends HTMLTemplates { 059 FluentNode a(Tag tag, Class<?> type, Node node); 060 FluentNode a(Tag tag, Member member, Node node); 061 FluentNode a(Tag tag, String name, Node node); 062 063 /** 064 * {@code <}{@code p><b><u>}{@link Tag tag}{@code </u></b></p}{@code >} 065 * {@code <!}{@code -- }{@link Throwable stack trace}{@code --}{@code >} 066 * 067 * @param tag The offending {@link Tag}. 068 * @param throwable The {@link Throwable}. 069 * 070 * @return {@link org.w3c.dom.DocumentFragment} 071 */ 072 default FluentNode warning(Tag tag, Throwable throwable) { 073 System.err.println(tag.position() + ": " + throwable); 074 075 String string = "@" + ((Taglet) this).getName(); 076 077 if (isNotEmpty(tag.text())) { 078 string += SPACE + tag.text(); 079 } 080 081 if (((Taglet) this).isInlineTag()) { 082 string = "{" + string + "}"; 083 } 084 085 return fragment(p(b(u(string))), 086 comment(ExceptionUtils.getStackTrace(throwable))); 087 } 088 089 /** 090 * {@code <a href="}{@link com.sun.javadoc.ClassDoc type}{@code ">}{@link com.sun.javadoc.ClassDoc#name() ClassDoc.name()}{@code </a>} 091 * 092 * @param tag The {@link Tag}. 093 * @param type The target {@link Class}. 094 * 095 * @return {@link org.w3c.dom.Element} 096 */ 097 default FluentNode a(Tag tag, Class<?> type) { 098 return a(tag, type, (String) null); 099 } 100 101 /** 102 * {@code <a href="}{@link com.sun.javadoc.ClassDoc type}{@code ">}{@link #code(String) code(name)}{@code </a>} 103 * 104 * @param tag The {@link Tag}. 105 * @param type The target {@link Class}. 106 * @param name The link name. 107 * 108 * @return {@link org.w3c.dom.Element} 109 */ 110 default FluentNode a(Tag tag, Class<?> type, String name) { 111 return a(tag, type, (name != null) ? code(name) : null); 112 } 113 114 /** 115 * {@code <a href="}{@link com.sun.javadoc.MemberDoc member}{@code ">}{@link com.sun.javadoc.MemberDoc#name() MemberDoc.name()}{@code </a>} 116 * 117 * @param tag The {@link Tag}. 118 * @param member The target {@link Member}. 119 * 120 * @return {@link org.w3c.dom.Element} 121 */ 122 default FluentNode a(Tag tag, Member member) { 123 return a(tag, member, (String) null); 124 } 125 126 /** 127 * {@code <a href="}{@link com.sun.javadoc.MemberDoc member}{@code ">}{@link #code(String) code(name)}{@code </a>} 128 * 129 * @param tag The {@link Tag}. 130 * @param member The target {@link Member}. 131 * @param name The link name. 132 * 133 * @return {@link org.w3c.dom.Element} 134 */ 135 default FluentNode a(Tag tag, Member member, String name) { 136 return a(tag, member, (name != null) ? code(name) : null); 137 } 138 139 /** 140 * {@code <a href="}{@link com.sun.javadoc.ClassDoc constant}{@code ">}{@link Enum#name() constant.name()}{@code </a>} 141 * 142 * @param tag The {@link Tag}. 143 * @param constant The target {@link Enum}. 144 * 145 * @return {@link org.w3c.dom.Element} 146 */ 147 default FluentNode a(Tag tag, Enum<?> constant) { 148 return a(tag, constant.getDeclaringClass(), constant.name()); 149 } 150 151 /** 152 * Dispatches call to {@link #declaration(Tag,Field)} or 153 * {@link #declaration(Tag,Method)} as appropriate. 154 * 155 * @param tag The {@link Tag}. 156 * @param member The target {@link Member}. 157 * 158 * @return {@link org.w3c.dom.DocumentFragment} 159 */ 160 default FluentNode declaration(Tag tag, Member member) { 161 FluentNode node = null; 162 163 if (member instanceof Field) { 164 node = declaration(tag, (Field) member); 165 } else if (member instanceof Method) { 166 node = declaration(tag, (Method) member); 167 } else { 168 throw new IllegalArgumentException(String.valueOf(member)); 169 } 170 171 return node; 172 } 173 174 /** 175 * Method to generate a {@link Field} declaration with javadoc 176 * hyperlinks. 177 * 178 * @param tag The {@link Tag}. 179 * @param field The target {@link Field}. 180 * 181 * @return {@link org.w3c.dom.DocumentFragment} 182 */ 183 default FluentNode declaration(Tag tag, Field field) { 184 return fragment(modifiers(field.getModifiers()), 185 type(tag, field.getGenericType()), 186 code(SPACE), 187 a(tag, field, (String) null)); 188 } 189 190 /** 191 * Method to generate a {@link Method} declaration with javadoc 192 * hyperlinks. 193 * 194 * @param tag The {@link Tag}. 195 * @param method The target {@link Method}. 196 * 197 * @return {@link org.w3c.dom.DocumentFragment} 198 */ 199 default FluentNode declaration(Tag tag, Method method) { 200 FluentNode node = 201 fragment(modifiers(method.getModifiers()), 202 type(tag, method.getGenericReturnType()), 203 code(SPACE), 204 a(tag, method, (String) null)); 205 206 Parameter[] parameters = method.getParameters(); 207 208 node.add(code("(")); 209 210 for (int i = 0; i < parameters.length; i += 1) { 211 if (i > 0) { 212 node.add(code(", ")); 213 } 214 215 node.add(declaration(tag, parameters[i])); 216 } 217 218 node.add(code(")")); 219 220 return node; 221 } 222 223 /** 224 * Method to generate a {@link Parameter} declaration with javadoc 225 * hyperlinks. 226 * 227 * @param tag The {@link Tag}. 228 * @param parameter The target {@link Parameter}. 229 * 230 * @return {@link org.w3c.dom.DocumentFragment} 231 */ 232 default FluentNode declaration(Tag tag, Parameter parameter) { 233 return fragment(modifiers(parameter.getModifiers()), 234 type(tag, parameter.getParameterizedType()), 235 code(SPACE), 236 code(parameter.getName())); 237 } 238 /* 239 * default FluentNode annotations(Tag tag, AnnotatedElement element) { 240 * return annotations(tag, element.getDeclaredAnnotations()); 241 * } 242 * 243 * default FluentNode annotations(Tag tag, Annotation... annotations) { 244 * return fragment().add(Stream.of(annotations) 245 * .map(t -> annotation(tag, t))); 246 * } 247 */ 248 /** 249 * {@code <a href="}{@link com.sun.javadoc.ClassDoc annotation}{@code ">}{@link #code(String) code(String.valueOf(annotation))}{@code </a>} 250 * 251 * @param tag The {@link Tag}. 252 * @param annotation The target {@link Annotation}. 253 * 254 * @return {@link org.w3c.dom.Element} 255 */ 256 default FluentNode annotation(Tag tag, Annotation annotation) { 257 Class<?> type = annotation.annotationType(); 258 String string = 259 String.valueOf(annotation) 260 .replace(type.getCanonicalName(), type.getSimpleName()); 261 262 return fragment().add(a(tag, type, code(string))); 263 } 264 265 /** 266 * Method to generate modifiers for {@code declaration()} methods. 267 * 268 * @param modifiers See {@link Modifier}. 269 * 270 * @return {@link org.w3c.dom.DocumentFragment} 271 */ 272 default FluentNode modifiers(int modifiers) { 273 FluentNode node = fragment(); 274 String string = Modifier.toString(modifiers); 275 276 if (isNotEmpty(string)) { 277 node.add(code(string + SPACE)); 278 } 279 280 return node; 281 } 282 283 /** 284 * Method to generate types for {@code declaration()} methods. 285 * 286 * @param tag The {@link Tag}. 287 * @param type The target {@link Type}. 288 * 289 * @return {@link org.w3c.dom.DocumentFragment} 290 */ 291 default FluentNode type(Tag tag, Type type) { 292 FluentNode node = null; 293 294 if (type instanceof ParameterizedType) { 295 node = 296 fragment(type(tag, ((ParameterizedType) type).getRawType())); 297 298 Type[] types = ((ParameterizedType) type).getActualTypeArguments(); 299 300 node = node.add(code("<")); 301 302 for (int i = 0; i < types.length; i += 1) { 303 if (i > 0) { 304 node.add(code(",")); 305 } 306 307 node.add(type(tag, types[i])); 308 } 309 310 node.add(code(">")); 311 } else if (type instanceof Class<?>) { 312 node = a(tag, (Class<?>) type); 313 } else { 314 node = code(type.getTypeName()); 315 } 316 317 return node; 318 } 319 320 /** 321 * {@code <table>}{@link TableModel model}{@code </table>} 322 * 323 * @param tag The {@link Tag}. 324 * @param model The {@link TableModel} to use to create the 325 * new table {@link org.w3c.dom.Element}. 326 * @param stream The {@link Stream} of {@link Node}s to 327 * append to the newly created 328 * {@link org.w3c.dom.Element}. 329 * 330 * @return {@link org.w3c.dom.Element} 331 */ 332 default FluentNode table(Tag tag, TableModel model, Stream<Node> stream) { 333 return table(tag, model, stream.toArray(Node[]::new)); 334 } 335 336 /** 337 * {@code <table>}{@link TableModel model}{@code </table>} 338 * 339 * @param tag The {@link Tag}. 340 * @param model The {@link TableModel} to use to create the 341 * new table {@link org.w3c.dom.Element}. 342 * @param nodes The {@link Node}s to append to the newly 343 * created 344 * {@link org.w3c.dom.Element}. 345 * 346 * @return {@link org.w3c.dom.Element} 347 */ 348 default FluentNode table(Tag tag, TableModel model, Node... nodes) { 349 FluentNode table = table(); 350 String[] names = 351 IntStream.range(0, model.getColumnCount()) 352 .boxed() 353 .map(model::getColumnName) 354 .toArray(String[]::new); 355 356 if (! isAllBlank(names)) { 357 table.add(thead(tr(Stream.of(names).map(this::th)))); 358 } 359 360 table 361 .add(tbody(IntStream.range(0, model.getRowCount()) 362 .boxed() 363 .map(y -> tr(IntStream.range(0, names.length) 364 .boxed() 365 .map(x -> td(toHTML(tag, 366 model.getValueAt(y, x)))))))); 367 368 return table.add(nodes); 369 } 370 371 /** 372 * Method to get a Javadoc HTML representation of an {@link Object}. 373 * 374 * @param tag The {@link Tag}. 375 * @param object The target {@link Object}. 376 * 377 * @return {@link org.w3c.dom.Node} 378 */ 379 default FluentNode toHTML(Tag tag, Object object) { 380 FluentNode node = null; 381 382 if (object instanceof byte[]) { 383 node = 384 text(Stream.of(ArrayUtils.toObject((byte[]) object)) 385 .map (t -> String.format("0x%02X", t)) 386 .collect(Collectors.joining(", ", "[", "]"))); 387 } else if (object instanceof boolean[]) { 388 node = text(Arrays.toString((boolean[]) object)); 389 } else if (object instanceof double[]) { 390 node = text(Arrays.toString((double[]) object)); 391 } else if (object instanceof float[]) { 392 node = text(Arrays.toString((float[]) object)); 393 } else if (object instanceof int[]) { 394 node = text(Arrays.toString((int[]) object)); 395 } else if (object instanceof long[]) { 396 node = text(Arrays.toString((long[]) object)); 397 } else if (object instanceof Object[]) { 398 node = toHTML(tag, Arrays.asList((Object[]) object)); 399 } else if (object instanceof Type) { 400 node = type(tag, (Type) object); 401 } else if (object instanceof Enum<?>) { 402 node = a(tag, (Enum<?>) object); 403 } else if (object instanceof Field) { 404 node = a(tag, (Field) object); 405 } else if (object instanceof Constructor) { 406 node = a(tag, (Constructor) object); 407 } else if (object instanceof Method) { 408 node = a(tag, (Method) object); 409 } else if (object instanceof Collection<?>) { 410 List<Node> nodes = 411 ((Collection<?>) object) 412 .stream() 413 .map(t -> toHTML(tag, t)) 414 .collect(Collectors.toList()); 415 416 for (int i = nodes.size() - 1; i > 0; i -= 1) { 417 nodes.add(i, text(", ")); 418 } 419 420 node = 421 fragment() 422 .add(text("[")) 423 .add(nodes.stream()) 424 .add(text("]")); 425 } else { 426 node = text(String.valueOf(object)); 427 } 428 429 return node; 430 } 431}