1+ nested components in component attr values working; Each tests.
This commit is contained in:
parent
93cf3905ae
commit
131fbaf155
@ -110,7 +110,8 @@ channels {
|
|||||||
this.pushMode(GROOVY_CODE);
|
this.pushMode(GROOVY_CODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean inAttrComponent() {
|
@Override
|
||||||
|
protected boolean inAttrComponent() {
|
||||||
return this.peekMode(1) == COMPONENT_ATTR_VALUE;
|
return this.peekMode(1) == COMPONENT_ATTR_VALUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +270,7 @@ mode IN_TAG;
|
|||||||
ComponentClose
|
ComponentClose
|
||||||
: GT
|
: GT
|
||||||
{
|
{
|
||||||
if (this.inAttrComponent() && this.attrComponentFinished()) {
|
if (this.inAttrComponent() && this.isAttrComponentFinished()) {
|
||||||
this.popMode();
|
this.popMode();
|
||||||
this.popMode();
|
this.popMode();
|
||||||
} else {
|
} else {
|
||||||
@ -283,7 +284,7 @@ ComponentSelfClose
|
|||||||
{
|
{
|
||||||
if (this.inAttrComponent()) {
|
if (this.inAttrComponent()) {
|
||||||
this.exitAttrComponent();
|
this.exitAttrComponent();
|
||||||
if (this.attrComponentFinished()) {
|
if (this.isAttrComponentFinished()) {
|
||||||
this.popMode();
|
this.popMode();
|
||||||
this.popMode();
|
this.popMode();
|
||||||
}
|
}
|
||||||
@ -338,7 +339,7 @@ ClosureAttrValueStart
|
|||||||
ComponentAttrValueStart
|
ComponentAttrValueStart
|
||||||
: LEFT_CURLY InTagNlws? { this.isNext('<') }?
|
: LEFT_CURLY InTagNlws? { this.isNext('<') }?
|
||||||
{
|
{
|
||||||
this.enterAttrComponent();
|
this.pushAttrComponent();
|
||||||
this.pushMode(COMPONENT_ATTR_VALUE);
|
this.pushMode(COMPONENT_ATTR_VALUE);
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
@ -359,7 +360,10 @@ TagError
|
|||||||
mode COMPONENT_ATTR_VALUE;
|
mode COMPONENT_ATTR_VALUE;
|
||||||
|
|
||||||
AttrComponentOpen
|
AttrComponentOpen
|
||||||
: LT { !isAnyOf(this.getNextChar(), '/', '>') }? -> type(ComponentOpen), pushMode(TAG_START)
|
: LT { !isAnyOf(this.getNextChar(), '/', '>') }?
|
||||||
|
{
|
||||||
|
this.enterAttrComponent();
|
||||||
|
} -> type(ComponentOpen), pushMode(TAG_START)
|
||||||
;
|
;
|
||||||
|
|
||||||
AttrClosingComponentOpen
|
AttrClosingComponentOpen
|
||||||
@ -370,7 +374,10 @@ AttrClosingComponentOpen
|
|||||||
;
|
;
|
||||||
|
|
||||||
AttrFragmentOpen
|
AttrFragmentOpen
|
||||||
: LT GT -> type(FragmentOpen)
|
: LT GT
|
||||||
|
{
|
||||||
|
this.enterAttrComponent();
|
||||||
|
} -> type(FragmentOpen)
|
||||||
;
|
;
|
||||||
|
|
||||||
AttrFragmentClose
|
AttrFragmentClose
|
||||||
|
@ -14,7 +14,8 @@ import org.slf4j.LoggerFactory;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.LinkedList;
|
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.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -59,7 +60,7 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
|
|||||||
private boolean canPreamble = true;
|
private boolean canPreamble = true;
|
||||||
private boolean inPreamble;
|
private boolean inPreamble;
|
||||||
private boolean inConstructor;
|
private boolean inConstructor;
|
||||||
private Deque<AtomicBoolean> attrComponentFinishedStack = new LinkedList<>();
|
private Deque<AtomicInteger> attrComponentCountStack = new LinkedList<>();
|
||||||
|
|
||||||
|
|
||||||
public AbstractWebViewComponentsLexer(CharStream input) {
|
public AbstractWebViewComponentsLexer(CharStream input) {
|
||||||
@ -119,12 +120,12 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
|
|||||||
this.inConstructor = inConstructor;
|
this.inConstructor = inConstructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Deque<AtomicBoolean> getAttrComponentFinishedStack() {
|
public Deque<AtomicInteger> getAttrComponentCountStack() {
|
||||||
return this.attrComponentFinishedStack;
|
return this.attrComponentCountStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAttrComponentFinishedStack(Deque<AtomicBoolean> attrComponentFinishedStack) {
|
public void setAttrComponentCountStack(Deque<AtomicInteger> attrComponentCountStack) {
|
||||||
this.attrComponentFinishedStack = attrComponentFinishedStack;
|
this.attrComponentCountStack = attrComponentCountStack;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -134,7 +135,7 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
|
|||||||
this.canPreamble = true;
|
this.canPreamble = true;
|
||||||
this.inPreamble = false;
|
this.inPreamble = false;
|
||||||
this.inConstructor = false;
|
this.inConstructor = false;
|
||||||
this.attrComponentFinishedStack = new LinkedList<>();
|
this.attrComponentCountStack = new LinkedList<>();
|
||||||
super.reset();
|
super.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,28 +218,42 @@ public abstract class AbstractWebViewComponentsLexer extends Lexer {
|
|||||||
this.inConstructor = false;
|
this.inConstructor = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void enterAttrComponent() {
|
protected abstract boolean inAttrComponent();
|
||||||
this.attrComponentFinishedStack.push(new AtomicBoolean());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void exitAttrComponent() {
|
protected void pushAttrComponent() {
|
||||||
final AtomicBoolean attrComponentFinished = this.attrComponentFinishedStack.peek();
|
this.attrComponentCountStack.push(new AtomicInteger());
|
||||||
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 popAttrComponent() {
|
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) {
|
protected String getNextCharsAsString(int numberOfChars) {
|
||||||
|
@ -39,6 +39,7 @@ public class DefaultNodeFactory implements NodeFactory {
|
|||||||
GStringValueNode.class,
|
GStringValueNode.class,
|
||||||
JStringValueNode.class,
|
JStringValueNode.class,
|
||||||
ClosureValueNode.class,
|
ClosureValueNode.class,
|
||||||
|
EmptyClosureValueNode.class,
|
||||||
ComponentValueNode.class,
|
ComponentValueNode.class,
|
||||||
PlainScriptletNode.class,
|
PlainScriptletNode.class,
|
||||||
EqualsScriptletNode.class,
|
EqualsScriptletNode.class,
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
<Each items={menuItems} transform={<li><a href={it.path}>$it.name</a></li>} />
|
@ -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)
|
@ -0,0 +1 @@
|
|||||||
|
<Each items={menuItems} transform={<li><a href={it.path}>$it.name</a></li>} />
|
@ -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>)
|
@ -1,6 +1,7 @@
|
|||||||
package groowt.view.component.web
|
package groowt.view.component.web
|
||||||
|
|
||||||
import groowt.view.component.context.DefaultComponentScope
|
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.Echo
|
||||||
import groowt.view.component.web.lib.IntrinsicHtml
|
import groowt.view.component.web.lib.IntrinsicHtml
|
||||||
import org.codehaus.groovy.runtime.InvokerHelper
|
import org.codehaus.groovy.runtime.InvokerHelper
|
||||||
@ -12,6 +13,7 @@ class DefaultWebViewComponentScope extends DefaultComponentScope implements WebV
|
|||||||
static DefaultWebViewComponentScope getDefaultRootScope() {
|
static DefaultWebViewComponentScope getDefaultRootScope() {
|
||||||
new DefaultWebViewComponentScope().tap {
|
new DefaultWebViewComponentScope().tap {
|
||||||
addWithAttr(Echo)
|
addWithAttr(Echo)
|
||||||
|
addWithAttr(Each)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user