Skip to content

Commit

Permalink
HTML parser separated from actual element construction
Browse files Browse the repository at this point in the history
  • Loading branch information
hugithordarson committed Oct 18, 2024
1 parent 02892cc commit abf4513
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 170 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
package ng.appserver.templating;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;

import ng.appserver.NGAssociation;
import ng.appserver.NGAssociationFactory;

/**
* Represents a declaration of a dynamic tag
*
Expand All @@ -18,68 +13,11 @@

public record NGDeclaration( boolean isInline, String name, String type, Map<String, NGBindingValue> bindings ) {

public record NGBindingValue( boolean isQuoted, String value ) {}

public NGDeclaration {
Objects.requireNonNull( name );
Objects.requireNonNull( type );
Objects.requireNonNull( bindings );
}

public Map<String, NGAssociation> associations() {
final Map<String, NGAssociation> associations = new HashMap<>();

for( Entry<String, NGBindingValue> entry : bindings.entrySet() ) {
associations.put( entry.getKey(), toAssociation( entry.getValue() ) );
}

return associations;
}

private NGAssociation toAssociation( NGBindingValue bindingValue ) {

if( isInline() ) {
try {
return bindingValueForInlineBindingString( bindingValue.value() );
}
catch( NGHTMLFormatException e ) {
throw new RuntimeException( e );
}
}
else {
return NGAssociationFactory.associationWithValue( bindingValue.value(), bindingValue.isQuoted() );
}
}

public record NGBindingValue( boolean isQuoted, String value ) {}

public static NGAssociation bindingValueForInlineBindingString( String value ) throws NGHTMLFormatException {
Objects.requireNonNull( value );

if( value.startsWith( "\"" ) ) {
value = value.substring( 1 );

if( value.endsWith( "\"" ) ) {
value = value.substring( 0, value.length() - 1 );
}
else {
throw new NGHTMLFormatException( value + " starts with quote but does not end with one." );
}

if( value.startsWith( "$" ) ) {
value = value.substring( 1 );

if( value.endsWith( "VALID" ) ) {
value = value.replaceFirst( "\\s*//\\s*VALID", "" );
}

return NGAssociationFactory.associationWithValue( value, false );
}
else {
value = value.replaceAll( "\\\\\\$", "\\$" );
value = value.replaceAll( "\\\"", "\"" );
return NGAssociationFactory.associationWithValue( value, true );
}
}

return NGAssociationFactory.associationWithValue( value, false );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,6 @@
import java.util.List;
import java.util.Objects;

import ng.appserver.NGComponentReference;
import ng.appserver.NGElement;
import ng.appserver.elements.NGDynamicGroup;
import ng.appserver.elements.NGHTMLBareString;

/**
* Represents a dynamic tag in the HTML part of a template
*/
Expand Down Expand Up @@ -50,67 +45,8 @@ public NGDynamicHTMLTag parentTag() {
return _parentTag;
}

/**
* @return The tag's template
*/
public NGElement template() {

if( _children == null ) {
return null;
}

final List<NGElement> childElements = combineAndWrapBareStringElements( _children );

if( childElements.size() == 1 ) {
final NGElement onlyElement = childElements.get( 0 );

if( onlyElement instanceof NGComponentReference ) {
return new NGDynamicGroup( declarationName(), null, onlyElement );
}

return onlyElement;
}

return new NGDynamicGroup( declarationName(), null, childElements );
}

/**
* Iterates through the list, combining adjacent strings and wrapping them in NGHTMLBareStrings
* Other elements get added directly to the element list.
*/
private static List<NGElement> combineAndWrapBareStringElements( List<Object> children ) {
final List<NGElement> childElements = new ArrayList<>( children.size() );

final StringBuilder sb = new StringBuilder( 128 );

for( final Object currentChild : children ) {

if( currentChild instanceof String ) {
// If we encounter a string, we append it to the StringBuilder
sb.append( (String)currentChild );
}
else {
// If we encounter any other element and we still have unwrapped strings in our builder,
// we take the string data we've collected, wrap it up and add it to the element list.
if( sb.length() > 0 ) {
final NGHTMLBareString bareString = new NGHTMLBareString( sb.toString() );
childElements.add( bareString );
sb.setLength( 0 );
}

// ... and then add the element itself
childElements.add( (NGElement)currentChild );
}
}

// If the last element happened to be a string, the StringBuilder will still have data so we wrap it here
if( sb.length() > 0 ) {
final NGHTMLBareString bareString = new NGHTMLBareString( sb.toString() );
childElements.add( bareString );
sb.setLength( 0 );
}

return childElements;
public List<Object> children() {
return _children;
}

public void addChildElement( final Object stringOrElement ) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
package ng.appserver.templating;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.StringTokenizer;

import ng.appserver.NGApplication;
import ng.appserver.NGElement;
import ng.appserver.elements.NGGenericContainer;
import ng.appserver.elements.NGHTMLBareString;
import ng.appserver.elements.NGHTMLCommentString;
import ng.appserver.templating.NGDeclaration.NGBindingValue;

/**
Expand Down Expand Up @@ -54,19 +48,19 @@ public NGTemplateParser( final String htmlString, final String declarationString
_declarationString = declarationString;
}

public NGElement parse() throws NGDeclarationFormatException, NGHTMLFormatException {
public PNode parse() throws NGDeclarationFormatException, NGHTMLFormatException {

// Somewhat ugly hack to prevent the template parser from returning a null template for an empty HTML String (which is not what we want)
if( _htmlString.isEmpty() ) {
return new NGHTMLBareString( "" );
return new PHTMLNode( "" );
}

_declarations = NGDeclarationParser.declarationsWithString( _declarationString );

return parseHTML();
}

private NGElement parseHTML() throws NGHTMLFormatException, NGDeclarationFormatException {
private PNode parseHTML() throws NGHTMLFormatException, NGDeclarationFormatException {

new NGHTMLParser( this, _htmlString ).parseHTML();

Expand All @@ -76,7 +70,7 @@ private NGElement parseHTML() throws NGHTMLFormatException, NGDeclarationFormatE
throw new NGHTMLFormatException( "There is an unbalanced dynamic tag named '%s'.".formatted( currentDynamicTagName ) );
}

return _currentDynamicTag.template();
return new PGroupNode( _currentDynamicTag );
}

public void didParseOpeningWebObjectTag( String parsedString ) throws NGHTMLFormatException {
Expand Down Expand Up @@ -110,13 +104,13 @@ public void didParseClosingWebObjectTag( final String parsedString ) throws NGDe
throw new NGHTMLFormatException( message );
}

final NGElement element = dynamicElement( _currentDynamicTag, _declarations );
final PNode node = node( _currentDynamicTag, _declarations );
_currentDynamicTag = parentDynamicTag;
_currentDynamicTag.addChildElement( element );
_currentDynamicTag.addChildElement( node );
}

public void didParseComment( final String parsedString ) {
NGHTMLCommentString commentString = new NGHTMLCommentString( parsedString );
PCommentNode commentString = new PCommentNode( parsedString );
_currentDynamicTag.addChildElement( commentString );
}

Expand Down Expand Up @@ -159,14 +153,14 @@ static String extractDeclarationName( final String tagPart ) throws NGHTMLFormat
throw new NGHTMLFormatException( "Can't initialize dynamic tag '%s', no 'name' attribute found".formatted( tagPart ) );
}

private static NGElement dynamicElement( NGDynamicHTMLTag tag, final Map<String, NGDeclaration> declarations ) throws NGDeclarationFormatException {
public static PNode node( NGDynamicHTMLTag tag, final Map<String, NGDeclaration> declarations ) throws NGDeclarationFormatException {
final NGDeclaration declaration = declarations.get( tag.declarationName() );

if( declaration == null ) {
throw new NGDeclarationFormatException( "No declaration for dynamic element (or component) named '%s'".formatted( tag.declarationName() ) );
}

return NGApplication.dynamicElementWithName( declaration.type(), declaration.associations(), tag.template(), Collections.emptyList() );
return new PBasicNode( tag, declaration );
}

private static NGDeclaration parseInlineTag( final String tag, final int colonIndex, final int nextInlineBindingNumber ) throws NGHTMLFormatException {
Expand Down Expand Up @@ -252,9 +246,11 @@ else if( currentBuffer == valueBuffer ) {
// Acts only on tags, where we have "dynamified" inside the tag parser
// this takes the value found after the "wo:" part in the element and generates a WOGenericContainer with that value
// as the elementName binding
elementType = elementType.replaceAll( NGHTMLParser.WO_REPLACEMENT_MARKER, "" );
bindings.put( "elementName", new NGBindingValue( true, elementType ) );
elementType = NGGenericContainer.class.getSimpleName();

// FIXME: Fix up in "New parser world"
// elementType = elementType.replaceAll( NGHTMLParser.WO_REPLACEMENT_MARKER, "" );
// bindings.put( "elementName", new NGBindingValue( true, elementType ) );
// elementType = NGGenericContainer.class.getSimpleName();
}

final String declarationName = "_%s_%s".formatted( elementType, nextInlineBindingNumber );
Expand Down
Loading

0 comments on commit abf4513

Please sign in to comment.