Skip to content
Fanon Jupkwo edited this page Jun 13, 2022 · 15 revisions

User Guide

Welcome to the jsgenerator wiki!

Here you'll know more how it works under the hood.

This project is built on top of jsoup library, a Java HTML parser / jsoup GitHub Repository. It's all about using Nodes to generate JavaScript. We invite you to have a look at how the jsoup Node class looks like.

The core method here is the method named convert of ConvertService interface implemented by ConvertServiceImpl class:

    /**
     * Converts the Html string to Js string.
     *
     * @param content the Html string
     * @return a String Object containing Js
     *
     * @throws NoHTMLCodeException if content is null
     *
     */

    public String convert(String content) throws NoHTMLCodeException {

	if (content == null) {

	    throw new NoHTMLCodeException("There is no html content.");
	}

	if (content.isBlank()) {

	    throw new NoHTMLCodeException("The content has nothing to translate.");

	}

	Element htmlDoc = Jsoup.parse(content, "", Parser.xmlParser());

	/*
	 * trim() is added to delete leading and trailing space. Before adding that, the
	 * generated Js code contained these not important spaces. It was difficult to
	 * test this method.
	 */

	String result = parseElement(htmlDoc).trim();

	/*
	 * Without this line, the result of convert method with same parameter changed
	 * everytime if we used the same object to call this function. The result of
	 * convert with same parameter was constant if we used different objects. To
	 * avoid this issue, we were forced to create different objects but that's not
	 * how it should be done.
	 *
	 * Now, the program clears the list of used tags in order to always get an empty
	 * list when we call this method.
	 */
	usedTags.clear();

	return result;
    }

First, the string content parameter is turned to an object of type Element extending the Node class by the static parse method of Jsoup class from the jsoup library:

	Element htmlDoc = Jsoup.parse(content, "", Parser.xmlParser());

Secondly, we use our in-house parsing method named parseElement to start creating the JavaScript code:

	String result = parseElement(htmlDoc).trim();
    /**
     * Goes through the Jsoup Elements and converts them to JSElement objects.
     *
     * @param element Jsoup Element
     * @return the generated code in JS
     * @throws HTMLUnknownElementException if an invalid HTML tag is used
     *
     */

    private String parseElement(Element element) throws HTMLUnknownElementException {

	logger.log(Level.INFO, " **** METHOD -- parseElement(Element element) -- Analyzing element : tagName = "
		+ element.tagName() + " -> " + element + "\n" + "---------------" + "\n");

	/*
	 * If the element is not the root and is unknown then there is a problem.
	 * Without the first condition "!element.root().equals(element)", there will be
	 * an exception thrown if the element is the root
	 */

	if (!element.root().equals(element) && !element.tag().isKnownTag()) {

	    throw new HTMLUnknownElementException(
		    "\"" + element + " -> " + element.tagName() + "\"" + " is not a valid HTML Element.");
	}

	StringBuilder generatedCode = new StringBuilder();

	for (Element child : element.children()) {
	    generatedCode.append(parseElement(child)).append("\n"); // recursive

	    JSElement childJsElement = new JSElement(child, jsVariableDeclaration);

	    // parse this current element

	    generatedCode.append(parse(usedTags, childJsElement));

	    // append this current element's children code to parent code
	    String appends = appendChild(childJsElement);

	    if (!appends.equals("")) {
		generatedCode.append(appends);
	    }
	}
	return generatedCode.toString();
    }

Each Element object has its children so we are making a recursive call to generate JavaScript variables for all of them. Let's focus on the 2 most important lines of this method, we are calling 2 in-house methods that will go straight to the point:

	    // parse this current element

	    generatedCode.append(parse(usedTags, childJsElement));

	    // append this current element's children code to parent code
	    String appends = appendChild(childJsElement);
    

Here, we are creating the JavaScript variables and setting attributes:

    /**
     * For this element, it returns the code to append the element to the parent
     *
     * @param usedTags  List of used tags in the document
     * @param jsElement
     * @return code to append the element to the parent
     * @throws HTMLUnknownElementException if an invalid HTML tag is used
     */

    private String parse(List<String> usedTags, JSElement jsElement) throws HTMLUnknownElementException {

	if (!jsElement.getElement().tag().isKnownTag()) {

	    throw new HTMLUnknownElementException(
		    "\"" + jsElement.getElement().tagName() + "\"" + " is not a valid HTML Element.");
	}

	// search tag name among used tags
	usedTags.stream().filter(s -> s.equals(jsElement.getElement().tagName()))
		.forEach(s -> jsElement.getElement().tagName(jsElement.getElement().tagName() + "_"));

	// tag name
	String tag = jsElement.getElement().tagName();

	usedTags.add(tag);

	/*
	 * Tag name should not contain _ TODO: Check that
	 */

	StringBuilder generatedCode = new StringBuilder(jsElement.getJsVariableDeclaration().getKeyword() + " " + tag
		+ " = document.createElement(\"" + tag.replace("_", "") + "\");\n");

	// attributes: attributes of the element
	Attributes attributes = jsElement.getElement().attributes();

	// Given an element, it adds the attributes to the element

	for (Attribute attribute : attributes) {
	    generatedCode.append(tag).append(".setAttribute(\"").append(attribute.getKey()).append("\", \"")
		    .append(attribute.getValue()).append("\");\n");
	}

	return generatedCode.toString();
    }
    

Here, we are appending children of type Node whether each child is an Element or TextNode:

    /**
     * For this element, it returns the code to append the child of type Element or
     * TextNode
     *
     * @param jsElement
     * @return code to append the child of type Element or TextNode
     */

    private String appendChild(JSElement jsElement) {

	StringBuilder generatedCode = new StringBuilder();

	boolean hasChld = jsElement.getElement().childrenSize() > 0;

	// tag name
	String tag = jsElement.getElement().tagName();

	// If the tag is not self closing

	if (!jsElement.getElement().tag().isSelfClosing()) {

	    if (jsElement.getElement().childNodes().size() > 0) {

		for (Node childNode : jsElement.getElement().childNodes()) {

		    if (childNode instanceof Element) {

			Element childElement = (Element) childNode;

			generatedCode.append(jsElement.getElement().tagName()).append(".appendChild(")
				.append(childElement.tagName()).append(");\n");

		    }

		    // text nodes: text nodes of the element (content in between tags)

		    if (childNode instanceof TextNode) {

			TextNode textNode = (TextNode) childNode;

			/*
			 * We concat trailing space because trim() removes all leading and trailing
			 * spaces even the ones that are mandatory.
			 *
			 */

			if (!textNode.isBlank()) {
			    generatedCode.append(tag).append(".appendChild(document.createTextNode(\"")
				    .append(textNode.toString().replace("\n", "").trim().concat(" ")).append("\"));\n");
			}

		    }

		}

	    }

	}

	return generatedCode.toString();
    }
    

Clone this wiki locally