1+ nested components in component attr values working; Each tests.

This commit is contained in:
JesseBrault0709 2024-05-31 16:50:30 +02:00
parent 93cf3905ae
commit 131fbaf155
9 changed files with 186 additions and 31 deletions

View File

@ -110,7 +110,8 @@ channels {
this.pushMode(GROOVY_CODE);
}
private boolean inAttrComponent() {
@Override
protected boolean inAttrComponent() {
return this.peekMode(1) == COMPONENT_ATTR_VALUE;
}
@ -269,7 +270,7 @@ mode IN_TAG;
ComponentClose
: GT
{
if (this.inAttrComponent() && this.attrComponentFinished()) {
if (this.inAttrComponent() && this.isAttrComponentFinished()) {
this.popMode();
this.popMode();
} else {
@ -283,7 +284,7 @@ ComponentSelfClose
{
if (this.inAttrComponent()) {
this.exitAttrComponent();
if (this.attrComponentFinished()) {
if (this.isAttrComponentFinished()) {
this.popMode();
this.popMode();
}
@ -338,7 +339,7 @@ ClosureAttrValueStart
ComponentAttrValueStart
: LEFT_CURLY InTagNlws? { this.isNext('<') }?
{
this.enterAttrComponent();
this.pushAttrComponent();
this.pushMode(COMPONENT_ATTR_VALUE);
}
;
@ -359,7 +360,10 @@ TagError
mode COMPONENT_ATTR_VALUE;
AttrComponentOpen
: LT { !isAnyOf(this.getNextChar(), '/', '>') }? -> type(ComponentOpen), pushMode(TAG_START)
: LT { !isAnyOf(this.getNextChar(), '/', '>') }?
{
this.enterAttrComponent();
} -> type(ComponentOpen), pushMode(TAG_START)
;
AttrClosingComponentOpen
@ -370,7 +374,10 @@ AttrClosingComponentOpen
;
AttrFragmentOpen
: LT GT -> type(FragmentOpen)
: LT GT
{
this.enterAttrComponent();
} -> type(FragmentOpen)
;
AttrFragmentClose

View File

@ -14,7 +14,8 @@ import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;
@ -59,7 +60,7 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
private boolean canPreamble = true;
private boolean inPreamble;
private boolean inConstructor;
private Deque<AtomicBoolean> attrComponentFinishedStack = new LinkedList<>();
private Deque<AtomicInteger> attrComponentCountStack = new LinkedList<>();
public AbstractWebViewComponentsLexer(CharStream input) {
@ -119,12 +120,12 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
this.inConstructor = inConstructor;
}
public Deque<AtomicBoolean> getAttrComponentFinishedStack() {
return this.attrComponentFinishedStack;
public Deque<AtomicInteger> getAttrComponentCountStack() {
return this.attrComponentCountStack;
}
public void setAttrComponentFinishedStack(Deque<AtomicBoolean> attrComponentFinishedStack) {
this.attrComponentFinishedStack = attrComponentFinishedStack;
public void setAttrComponentCountStack(Deque<AtomicInteger> attrComponentCountStack) {
this.attrComponentCountStack = attrComponentCountStack;
}
@Override
@ -134,7 +135,7 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
this.canPreamble = true;
this.inPreamble = false;
this.inConstructor = false;
this.attrComponentFinishedStack = new LinkedList<>();
this.attrComponentCountStack = new LinkedList<>();
super.reset();
}
@ -217,28 +218,42 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
this.inConstructor = false;
}
protected void enterAttrComponent() {
this.attrComponentFinishedStack.push(new AtomicBoolean());
}
protected abstract boolean inAttrComponent();
protected void exitAttrComponent() {
final AtomicBoolean attrComponentFinished = this.attrComponentFinishedStack.peek();
if (attrComponentFinished == null) {
throw new WebViewComponentBugError(new IllegalStateException());
}
attrComponentFinished.set(true);
}
protected boolean attrComponentFinished() {
final AtomicBoolean attrComponentFinished = this.attrComponentFinishedStack.peek();
if (attrComponentFinished == null) {
throw new WebViewComponentBugError(new IllegalStateException());
}
return attrComponentFinished.get();
protected void pushAttrComponent() {
this.attrComponentCountStack.push(new AtomicInteger());
}
protected void popAttrComponent() {
this.attrComponentFinishedStack.pop();
try {
this.attrComponentCountStack.pop();
} catch (NoSuchElementException e) {
throw new WebViewComponentBugError("attrComponentCountStack was empty.", e);
}
}
protected boolean isAttrComponentFinished() {
final AtomicInteger attrComponentFinished = this.attrComponentCountStack.peek();
if (attrComponentFinished == null) {
throw new WebViewComponentBugError(new IllegalStateException());
}
return attrComponentFinished.get() == 0;
}
protected void enterAttrComponent() {
final AtomicInteger attrComponentCount = this.attrComponentCountStack.peek();
if (attrComponentCount == null) {
throw new WebViewComponentBugError(new IllegalStateException("attrComponentCountStack is empty."));
}
attrComponentCount.incrementAndGet();
}
protected void exitAttrComponent() {
final AtomicInteger attrComponentCount = this.attrComponentCountStack.peek();
if (attrComponentCount == null) {
throw new WebViewComponentBugError(new IllegalStateException("attrComponentCountStack is empty."));
}
attrComponentCount.decrementAndGet();
}
protected String getNextCharsAsString(int numberOfChars) {

View File

@ -39,6 +39,7 @@ public class DefaultNodeFactory implements NodeFactory {
GStringValueNode.class,
JStringValueNode.class,
ClosureValueNode.class,
EmptyClosureValueNode.class,
ComponentValueNode.class,
PlainScriptletNode.class,
EqualsScriptletNode.class,

View File

@ -0,0 +1 @@
<Each items={menuItems} transform={<li><a href={it.path}>$it.name</a></li>} />

View File

@ -0,0 +1,36 @@
0: ComponentOpen[1,1](<)
1: TypedIdentifier[1,2](Each)
2: Nlws[1,6]( )
3: AttributeIdentifier[1,7](items)
4: Equals[1,12](=)
5: ClosureAttrValueStart[1,13]({)
6: GroovyCode[1,14](menuItems)
7: ClosureAttrValueEnd[1,23](})
8: Nlws[1,24]( )
9: AttributeIdentifier[1,25](transform)
10: Equals[1,34](=)
11: ComponentAttrValueStart[1,35]({)
12: ComponentOpen[1,36](<)
13: StringIdentifier[1,37](li)
14: ComponentClose[1,39](>)
15: ComponentOpen[1,40](<)
16: StringIdentifier[1,41](a)
17: Nlws[1,42]( )
18: AttributeIdentifier[1,43](href)
19: Equals[1,47](=)
20: ClosureAttrValueStart[1,48]({)
21: GroovyCode[1,49](it.path)
22: ClosureAttrValueEnd[1,56](})
23: ComponentClose[1,57](>)
24: DollarReferenceStart[1,58]($)
25: GroovyCode[1,59](it.name)
26: ClosingComponentOpen[1,66](</)
27: StringIdentifier[1,68](a)
28: ComponentClose[1,69](>)
29: ClosingComponentOpen[1,70](</)
30: StringIdentifier[1,72](li)
31: ComponentClose[1,74](>)
32: ComponentAttrValueEnd[1,75](})
33: Nlws[1,76]( )
34: ComponentSelfClose[1,77](/>)
35: RawText[1,79](\n)

View File

@ -0,0 +1 @@
<Each items={menuItems} transform={<li><a href={it.path}>$it.name</a></li>} />

View File

@ -0,0 +1,72 @@
compilationUnit[1,1..2,1]
body[1,1..1,79]
component[1,1..1,79]
selfClosingComponent[1,1..1,79]
ComponentOpen[1,1](<)
componentArgs[1,2..1,75]
componentType[1,2..1,6]
TypedIdentifier[1,2](Each)
attr[1,7..1,23]
keyValueAttr[1,7..1,23]
AttributeIdentifier[1,7](items)
Equals[1,12](=)
value[1,13..1,23]
closureAttrValue[1,13..1,23]
ClosureAttrValueStart[1,13]({)
GroovyCode[1,14](menuItems)
ClosureAttrValueEnd[1,23](})
attr[1,25..1,75]
keyValueAttr[1,25..1,75]
AttributeIdentifier[1,25](transform)
Equals[1,34](=)
value[1,35..1,75]
componentAttrValue[1,35..1,75]
ComponentAttrValueStart[1,35]({)
component[1,36..1,74]
componentWithChildren[1,36..1,74]
openComponent[1,36..1,39]
ComponentOpen[1,36](<)
componentArgs[1,37..1,39]
componentType[1,37..1,39]
StringIdentifier[1,37](li)
ComponentClose[1,39](>)
body[1,40..1,69]
component[1,40..1,69]
componentWithChildren[1,40..1,69]
openComponent[1,40..1,57]
ComponentOpen[1,40](<)
componentArgs[1,41..1,56]
componentType[1,41..1,41]
StringIdentifier[1,41](a)
attr[1,43..1,56]
keyValueAttr[1,43..1,56]
AttributeIdentifier[1,43](href)
Equals[1,47](=)
value[1,48..1,56]
closureAttrValue[1,48..1,56]
ClosureAttrValueStart[1,48]({)
GroovyCode[1,49](it.path)
ClosureAttrValueEnd[1,56](})
ComponentClose[1,57](>)
body[1,58..1,66]
bodyText[1,58..1,66]
bodyTextGroovyElement[1,58..1,66]
dollarReference[1,58..1,66]
DollarReferenceStart[1,58]($)
GroovyCode[1,59](it.name)
closingComponent[1,66..1,69]
ClosingComponentOpen[1,66](</)
componentType[1,68..1,68]
StringIdentifier[1,68](a)
ComponentClose[1,69](>)
closingComponent[1,70..1,74]
ClosingComponentOpen[1,70](</)
componentType[1,72..1,74]
StringIdentifier[1,72](li)
ComponentClose[1,74](>)
ComponentAttrValueEnd[1,75](})
ComponentSelfClose[1,77](/>)
bodyText[1,79..1,79]
text[1,79..1,79]
RawText[1,79](\n)
EOF[2,1](<EOF>)

View File

@ -1,6 +1,7 @@
package groowt.view.component.web
import groowt.view.component.context.DefaultComponentScope
import groowt.view.component.web.lib.Each
import groowt.view.component.web.lib.Echo
import groowt.view.component.web.lib.IntrinsicHtml
import org.codehaus.groovy.runtime.InvokerHelper
@ -12,6 +13,7 @@ class DefaultWebViewComponentScope extends DefaultComponentScope implements WebV
static DefaultWebViewComponentScope getDefaultRootScope() {
new DefaultWebViewComponentScope().tap {
addWithAttr(Echo)
addWithAttr(Each)
}
}

View File

@ -0,0 +1,20 @@
package groowt.view.component.web.lib
import org.junit.jupiter.api.Test
class EachTests extends AbstractWebViewComponentTests {
@Test
void simple() {
this.doTest('<Echo items={[0, 1, 2]}><Each items={items} /></Echo>', '012')
}
@Test
void withTransform() {
this.doTest(
'<Echo items={[0, 1, 2]}><Each items={items} transform={<p>$it</p>} /></Echo>',
'<p>0</p><p>1</p><p>2</p>'
)
}
}