/** * Visits a {@link PrintNode}, wrapping it with an HtmlPrintNode node if it occurs in {@link * HtmlState#TAG} or {@link HtmlState#PCDATA}. This allows the code generator to handle those * print statements separately and know the state in which they occurred. If the {@link PrintNode} * occurs in {@link HtmlState#ATTR_VALUE}, then the print node becomes a part of the current * attribute's value. */ @Override protected void visitPrintNode(PrintNode node) { checkForValidSoyNodeLocation(node); HtmlPrintNode htmlPrintNode; switch (getState()) { case ATTR_VALUE: // A PrintNode in an attribute value, add it to the current attribute values, which will get // added to the attribute node once the attribute value ends. currentAttributeValues.add(node); node.getParent().removeChild(node); break; case TAG: // A PrintNode in the tag context - this is a print of something that has kind="attributes", // keep track of the context using an HtmlPrintNode so that the code generator knows what. // to do with it. htmlPrintNode = new HtmlPrintNode( idGen.genId(), node, HtmlPrintNode.Context.HTML_TAG, node.getSourceLocation()); node.getParent().replaceChild(node, htmlPrintNode); break; case PCDATA: // A PrintNode in the pcdata context - this is a print of something that is the child of // an HTML element. This could be html, text, css, etc., just need to keep track of the // the context and the code generator will do the right thing with each type. htmlPrintNode = new HtmlPrintNode( idGen.genId(), node, HtmlPrintNode.Context.HTML_PCDATA, node.getSourceLocation()); node.getParent().replaceChild(node, htmlPrintNode); break; default: break; } }
/** * Handles a character within {@link HtmlState#TAG_NAME}, where the name of a tag must be present. * * @param node The node that the current character belongs to. * @param c The current character being examined. */ private void handleHtmlTagName(RawTextNode node, char c) { if (CharMatcher.WHITESPACE.matches(c) || c == '>') { currentTag = consumeText(false); // No tag name, saw something like <> or < >. if (currentTag.length() <= 0) { SourceLocation sl = deriveSourceLocation(node); errorReporter.report(sl, MISSING_TAG_NAME); } // Currently, closing tags and open tags are handled through the states. If this is not a // closing tag, then an open tag needs to be started. if (!currentTag.startsWith("/")) { SourceLocation sl = deriveSourceLocation(node); transformMapping.put(node, new HtmlOpenTagStartNode(idGen.genId(), currentTag, sl)); } if (c == '>') { // Handle close tags and tags that only have a tag name (e.g. <div>). handleHtmlTag(node, c); } else { // Get ready to capture attributes. currentAttributeValues = new ArrayList<>(); setState(HtmlState.TAG); } } else { currentText.append(c); } }
/** * Creates a new {@link HtmlAttributeNode} and maps it to node, taking all the attribute values * (text, conditionals, print statements) and adding them to the new attribute node. * * @param node The node that the mapped node comes from. */ private void createAttribute(RawTextNode node) { SourceLocation sl = deriveSourceLocation(node); HtmlAttributeNode htmlAttributeNode = new HtmlAttributeNode(idGen.genId(), currentAttributeName, sl); htmlAttributeNode.addChildren(currentAttributeValues); transformMapping.put(node, htmlAttributeNode); currentAttributeValues = new ArrayList<>(); }
/** * Creates a new {@link HtmlTextNode} and maps it to node. * * @param node The node that the mapped node comes from. */ private void createTextNode(RawTextNode node) { // Consume text, removing unnecessary whitespace String currentString = consumeText(true); if (currentString.length() > 0) { SourceLocation sl = deriveSourceLocation(node); transformMapping.put(node, new HtmlTextNode(idGen.genId(), currentString, sl)); } }
/** * Creates a {@link RawTextNode} for the current part of an attribute value and adds it to the * pending attribute value array. * * @param node The node that the mapped node comes from. */ private void createAttributeValueNode(RawTextNode node) { String currentString = consumeText(false); // Check to see if the currentText is empty. This may occur when we have something like // disabled="" or disabled="{$foo}" after the print tag is finished. if (currentString.length() > 0) { SourceLocation sl = deriveSourceLocation(node); currentAttributeValues.add(new RawTextNode(idGen.genId(), currentString, sl)); } }
/** * Handles a character within {@link HtmlState#TAG}, where either an attribute declaration or the * end of a tag may appear. * * @param node The node that the current character belongs to. * @param c The current character being examined. */ private void handleHtmlTag(RawTextNode node, char c) { if (c == '>') { // Found the end of the tag - create the appropriate open tag or close tag node, depending // on which we are ending. SourceLocation sl = deriveSourceLocation(node); if (currentTag.startsWith("/")) { transformMapping.put( node, new HtmlCloseTagNode(idGen.genId(), currentTag.substring(1), sl)); } else { transformMapping.put(node, new HtmlOpenTagEndNode(idGen.genId(), currentTag, sl)); } setState(HtmlState.PCDATA); } else if (CharMatcher.WHITESPACE.matches(c)) { // Skip whitespace characters. } else { setState(HtmlState.ATTRIBUTE_NAME); currentText.append(c); } }