TldParser.java
/*
* The MIT License
* Copyright © 2004-2014 Fabrizio Giustina
* Copyright © 2022-2026 Web-Legacy
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package io.github.weblegacy.maven.plugin.taglib.checker;
import io.github.weblegacy.maven.plugin.taglib.util.XmlHelper;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Strings;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Reads a tld file and generates a Tld object. This "manual" parser takes in account different
* versions of the tlds
*
* @author Fabrizio Giustina
*/
public final class TldParser {
/**
* Don't instantiate.
*/
private TldParser() {
// unused
}
/**
* Parse a Tld object from a document.
*
* @param tldDoc Document for the parsed tld
* @param tldName of the tld file
*
* @return Tld instance
*/
public static Tld parse(Document tldDoc, String tldName) {
Tld tld = new Tld();
tld.setFilename(tldName);
Set<Tag> tags = new TreeSet<>();
Set<ElFunction> functions = new TreeSet<>();
Set<TagFile> tagfiles = new TreeSet<>();
NodeList tagList = tldDoc.getElementsByTagName("taglib").item(0).getChildNodes();
for (int i = 0; i < tagList.getLength(); i++) {
Node tagNode = tagList.item(i);
if (tagNode != null && tagNode.getNodeName() != null) {
switch (tagNode.getNodeName()) {
case "shortname":
case "short-name":
{
Node child = tagNode.getFirstChild();
if (child != null) {
tld.setShortname(child.getNodeValue());
}
break;
}
case "display-name":
{
Node child = tagNode.getFirstChild();
if (child != null) {
tld.setName(child.getNodeValue());
}
break;
}
case "info":
case "description":
tld.setInfo(XmlHelper.getTextContent(tagNode));
break;
case "tlib-version":
case "tlibversion":
{
Node child = tagNode.getFirstChild();
if (child != null) {
tld.setTlibversion(child.getNodeValue());
}
break;
}
case "uri":
{
Node child = tagNode.getFirstChild();
if (child != null) {
tld.setUri(child.getNodeValue());
}
break;
}
case "tag":
{
Tag tag = parseTag(tagNode);
tags.add(tag);
break;
}
case "function":
{
ElFunction tag = parseFunction(tagNode);
functions.add(tag);
break;
}
case "tag-file":
{
TagFile tag = parseTagFile(tagNode);
tagfiles.add(tag);
break;
}
default:
break;
}
}
tld.setTags(tags.toArray(Tag[]::new));
tld.setFunctions(functions.toArray(ElFunction[]::new));
tld.setTagfiles(tagfiles.toArray(TagFile[]::new));
}
return tld;
}
/**
* Parse a {@code tag-file} element.
*
* @param tagNode Node
*
* @return a Tag-File instance
*/
private static TagFile parseTagFile(Node tagNode) {
TagFile tag = new TagFile();
NodeList tagAttributes = tagNode.getChildNodes();
for (int k = 0; k < tagAttributes.getLength(); k++) {
Node tagAttribute = tagAttributes.item(k);
String nodeName = tagAttribute.getNodeName();
if (nodeName != null) {
switch (nodeName) {
case "name":
// tag class name
tag.setName(tagAttribute.getFirstChild().getNodeValue());
break;
case "description":
tag.setDescription(XmlHelper.getTextContent(tagAttribute));
break;
case "path":
tag.setPath(tagAttribute.getFirstChild().getNodeValue());
break;
case "example":
tag.setExample(XmlHelper.getTextContent(tagAttribute));
break;
default:
break;
}
}
}
tag.setDeprecated(Strings.CS.contains(tag.getDescription(), "@deprecated"));
return tag;
}
/**
* Parse a {@code function} element.
*
* @param tagNode Node
*
* @return a ElFunction instance
*/
private static ElFunction parseFunction(Node tagNode) {
ElFunction tag = new ElFunction();
NodeList tagAttributes = tagNode.getChildNodes();
for (int k = 0; k < tagAttributes.getLength(); k++) {
Node tagAttribute = tagAttributes.item(k);
String nodeName = tagAttribute.getNodeName();
if (nodeName != null) {
switch (nodeName) {
case "name":
tag.setName(tagAttribute.getFirstChild().getNodeValue());
break;
case "description":
tag.setDescription(XmlHelper.getTextContent(tagAttribute));
break;
case "example":
tag.setExample(XmlHelper.getTextContent(tagAttribute));
break;
case "function-class":
tag.setFunctionClass(
StringUtils.trim(XmlHelper.getTextContent(tagAttribute)));
break;
case "function-signature":
String signature = XmlHelper.getTextContent(tagAttribute);
tag.setFunctionSignature(signature);
tag.setParameters(StringUtils.substringBetween(signature, "(", ")"));
break;
default:
break;
}
}
}
tag.setDeprecated(Strings.CS.contains(tag.getDescription(), "@deprecated"));
return tag;
}
/**
* Parse a {@code tag} element.
*
* @param tagNode Node
*
* @return a Tag instance
*/
private static Tag parseTag(Node tagNode) {
Tag tag = new Tag();
Set<TagAttribute> attributes = new TreeSet<>();
Set<TagVariable> variables = new TreeSet<>();
NodeList tagAttributes = tagNode.getChildNodes();
for (int k = 0; k < tagAttributes.getLength(); k++) {
Node tagAttribute = tagAttributes.item(k);
String nodeName = tagAttribute.getNodeName();
if (nodeName != null) {
switch (nodeName) {
case "name":
// tag class name
tag.setName(tagAttribute.getFirstChild().getNodeValue());
break;
case "description":
case "info":
tag.setDescription(XmlHelper.getTextContent(tagAttribute));
break;
case "tag-class":
case "tagclass":
tag.setTagClass(StringUtils.trim(
tagAttribute.getFirstChild().getNodeValue()));
break;
case "body-content":
case "bodycontent":
tag.setBodycontent(tagAttribute.getFirstChild().getNodeValue());
break;
case "example":
tag.setExample(XmlHelper.getTextContent(tagAttribute));
break;
case "tei-class":
case "teiclass":
// tei class name
tag.setTeiClass(StringUtils.trim(
tagAttribute.getFirstChild().getNodeValue()));
break;
case "attribute":
TagAttribute attribute = parseTagAttribute(tagAttribute);
attributes.add(attribute);
break;
case "variable":
TagVariable variable = parseTagVariable(tagAttribute);
variables.add(variable);
break;
default:
break;
}
}
tag.setAttributes(attributes.toArray(TagAttribute[]::new));
tag.setVariables(variables.toArray(TagVariable[]::new));
}
tag.setDeprecated(Strings.CS.contains(tag.getDescription(), "@deprecated"));
return tag;
}
/**
* Parse an {@code attribute} element.
*
* @param tagAttribute Node
*
* @return TagAttribute instance
*/
private static TagAttribute parseTagAttribute(Node tagAttribute) {
TagAttribute attribute = new TagAttribute();
NodeList attributeParams = tagAttribute.getChildNodes();
for (int z = 0; z < attributeParams.getLength(); z++) {
Node param = attributeParams.item(z);
if (param.getNodeType() != Node.TEXT_NODE && param.hasChildNodes()
&& param.getNodeName() != null) {
switch (param.getNodeName()) {
case "name":
attribute.setName(param.getFirstChild().getNodeValue());
break;
case "type":
attribute.setType(StringUtils.trim(param.getFirstChild().getNodeValue()));
break;
case "description":
attribute.setDescription(XmlHelper.getTextContent(param));
break;
case "required":
attribute.setRequired(Strings.CS.contains(StringUtils.lowerCase(
param.getFirstChild().getNodeValue()), "true"));
break;
case "rtexprvalue":
attribute.setRtexprvalue(Strings.CS.contains(StringUtils.lowerCase(
param.getFirstChild().getNodeValue()), "true"));
break;
default:
break;
}
}
}
attribute.setDeprecated(Strings.CS.contains(attribute.getDescription(), "@deprecated"));
return attribute;
}
/**
* Parse an {@code attribute} element.
*
* @param node Node
*
* @return TagAttribute instance
*/
private static TagVariable parseTagVariable(Node node) {
TagVariable variable = new TagVariable();
NodeList attributeParams = node.getChildNodes();
for (int z = 0; z < attributeParams.getLength(); z++) {
Node param = attributeParams.item(z);
if (param.getNodeType() != Node.TEXT_NODE && param.hasChildNodes()
&& param.getNodeName() != null) {
switch (param.getNodeName()) {
case "name-given":
variable.setNameGiven(param.getFirstChild().getNodeValue());
break;
case "name-from-attribute":
variable.setNameFromAttribute(param.getFirstChild().getNodeValue());
break;
case "variable-class":
variable.setType(param.getFirstChild().getNodeValue());
break;
case "scope":
variable.setScope(param.getFirstChild().getNodeValue());
break;
case "description":
case "info":
variable.setDescription(XmlHelper.getTextContent(param));
break;
default:
break;
}
}
}
variable.setDeprecated(Strings.CS.contains(variable.getDescription(), "@deprecated"));
return variable;
}
}