diff options
author | Håkon Hallingstad <hakon@yahooinc.com> | 2022-01-10 19:23:35 +0100 |
---|---|---|
committer | Håkon Hallingstad <hakon@yahooinc.com> | 2022-01-11 13:20:03 +0100 |
commit | 27386c0e1e6f592ac8e7ceaee33eb40ac4aa9985 (patch) | |
tree | 8cfc8ef215121a144b6a438c6fd4409bde8eeb20 /node-admin/src/main | |
parent | 32f7e0d2b655a823a9e5a6d03310b54db1286ef0 (diff) |
Add if section
Diffstat (limited to 'node-admin/src/main')
14 files changed, 236 insertions, 153 deletions
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/Form.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/Form.java index 00e2e87fc5a..c95885d7753 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/Form.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/Form.java @@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.node.admin.task.util.template; import com.yahoo.vespa.hosted.node.admin.task.util.text.CursorRange; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -13,25 +14,25 @@ import java.util.Optional; * @see Template * @author hakonhall */ -public class Form extends Section { - private final Form parent; +public class Form { + private Form parent = null; + private final CursorRange range; private final List<Section> sections; - private final Map<String, String> variables; + private final Map<String, String> values = new HashMap<>(); private final Map<String, SubformSection> subforms; - Form(Form parent, CursorRange range, List<Section> sections, Map<String, String> variables, - Map<String, SubformSection> subforms) { - super(range); - this.parent = parent; + Form(CursorRange range, List<Section> sections, Map<String, SubformSection> subforms) { + this.range = new CursorRange(range); this.sections = List.copyOf(sections); - this.variables = variables; // Mutable and referenced by the variable sections this.subforms = Map.copyOf(subforms); } + void setParent(Form parent) { this.parent = parent; } + /** Set the value of a variable expression, e.g. %{=color}. */ public Form set(String name, String value) { - variables.put(name, value); + values.put(name, value); return this; } @@ -52,24 +53,30 @@ public class Form extends Section { public Form add(String name) { var section = subforms.get(name); if (section == null) { - throw new NoSuchNameTemplateException(this, name); + throw new NoSuchNameTemplateException(range, name); } return section.add(); } public String render() { - var buffer = new StringBuilder((int) (range().length() * 1.2 + 128)); + var buffer = new StringBuilder((int) (range.length() * 1.2 + 128)); appendTo(buffer); return buffer.toString(); } - @Override public void appendTo(StringBuilder buffer) { sections.forEach(section -> section.appendTo(buffer)); } + /** Returns a deep copy of this. No changes to this affects the returned form, and vice versa. */ + Form copy() { + var builder = new FormBuilder(range.start()); + sections.forEach(section -> section.appendCopyTo(builder.topLevelSectionList())); + return builder.build(); + } + Optional<String> getVariableValue(String name) { - String value = variables.get(name); + String value = values.get(name); if (value != null) return Optional.of(value); if (parent != null) { return parent.getVariableValue(name); diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/FormBuilder.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/FormBuilder.java index d0274d7777a..833e5916fce 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/FormBuilder.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/FormBuilder.java @@ -2,67 +2,80 @@ package com.yahoo.vespa.hosted.node.admin.task.util.template; import com.yahoo.vespa.hosted.node.admin.task.util.text.Cursor; -import com.yahoo.vespa.hosted.node.admin.task.util.text.CursorRange; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.function.Consumer; /** * @author hakonhall */ class FormBuilder { - private final List<Section> sections = new ArrayList<>(); - private final Map<String, String> variables = new HashMap<>(); + /** The top-level section list in this form. */ + private final SectionList sectionList; + private final List<Section> allSections = new ArrayList<>(); + private final Map<String, VariableSection> sampleVariables = new HashMap<>(); + private final Map<String, IfSection> sampleIfSections = new HashMap<>(); private final Map<String, SubformSection> subforms = new HashMap<>(); - private final Form parent; - private final CursorRange range; - static Form build(Form parent, CursorRange range, List<Consumer<FormBuilder>> sections) { - var builder = new FormBuilder(parent, range); - sections.forEach(section -> section.accept(builder)); - return builder.build(); + FormBuilder(Cursor start) { + this.sectionList = new SectionList(start, this); } - private FormBuilder(Form parent, CursorRange range) { - this.parent = parent; - this.range = new CursorRange(range); - } + SectionList topLevelSectionList() { return sectionList; } - FormBuilder addLiteralSection(CursorRange range) { - sections.add(new LiteralSection(range)); - return this; + void addLiteralSection(LiteralSection section) { + allSections.add(section); } - FormBuilder addVariableSection(CursorRange range, String name, Cursor nameOffset) { - var section = new VariableSection(range, name, nameOffset); - sections.add(section); - return this; - } + void addVariableSection(VariableSection section) { + // It's OK if the same name is used in an if-directive (as long as the value is boolean, + // determined when set on a form). + + SubformSection existing = subforms.get(section.name()); + if (existing != null) + throw new NameAlreadyExistsTemplateException(section.name(), existing.nameOffset(), + section.nameOffset()); - FormBuilder addSubformSection(CursorRange range, String name, Template body) { - checkNameIsAvailable(name, range); - var section = new SubformSection(range, name, body); - sections.add(section); - subforms.put(section.name(), section); - return this; + sampleVariables.put(section.name(), section); + allSections.add(section); } - private Form build() { - var form = new Form(parent, range, sections, variables, subforms); - sections.forEach(section -> section.setForm(form)); - return form; + void addIfSection(IfSection section) { + // It's OK if the same name is used in a variable section (as long as the value is boolean, + // determined when set on a form). + + SubformSection subform = subforms.get(section.name()); + if (subform != null) + throw new NameAlreadyExistsTemplateException(section.name(), subform.nameOffset(), + section.nameOffset()); + + sampleIfSections.put(section.name(), section); + allSections.add(section); } - private void checkNameIsAvailable(String name, CursorRange range) { - if (nameIsDefined(name)) { - throw new NameAlreadyExistsTemplateException(name, range); - } + void addSubformSection(SubformSection section) { + VariableSection variableSection = sampleVariables.get(section.name()); + if (variableSection != null) + throw new NameAlreadyExistsTemplateException(section.name(), variableSection.nameOffset(), + section.nameOffset()); + + IfSection ifSection = sampleIfSections.get(section.name()); + if (ifSection != null) + throw new NameAlreadyExistsTemplateException(section.name(), ifSection.nameOffset(), + section.nameOffset()); + + SubformSection previous = subforms.put(section.name(), section); + if (previous != null) + throw new NameAlreadyExistsTemplateException(section.name(), previous.nameOffset(), + section.nameOffset()); + allSections.add(section); } - private boolean nameIsDefined(String name) { - return variables.containsKey(name) || subforms.containsKey(name); + Form build() { + var form = new Form(sectionList.range(), sectionList.sections(), subforms); + allSections.forEach(section -> section.setForm(form)); + return form; } } diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/IfSection.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/IfSection.java new file mode 100644 index 00000000000..33e62750f69 --- /dev/null +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/IfSection.java @@ -0,0 +1,57 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.node.admin.task.util.template; + +import com.yahoo.vespa.hosted.node.admin.task.util.text.Cursor; +import com.yahoo.vespa.hosted.node.admin.task.util.text.CursorRange; + +import java.util.Optional; + +/** + * @author hakonhall + */ +public class IfSection extends Section { + private final boolean negated; + private final String name; + private final Cursor nameOffset; + private final SectionList ifSections; + + public IfSection(CursorRange range, boolean negated, String name, Cursor nameOffset, + SectionList ifSections) { + super(range); + this.negated = negated; + this.name = name; + this.nameOffset = nameOffset; + this.ifSections = ifSections; + } + + String name() { return name; } + Cursor nameOffset() { return nameOffset; } + + @Override + void appendTo(StringBuilder buffer) { + Optional<String> stringValue = form().getVariableValue(name); + if (stringValue.isEmpty()) + throw new TemplateNameNotSetException(name, nameOffset); + + final boolean value; + if (stringValue.get().equals("true")) { + value = true; + } else if (stringValue.get().equals("false")) { + value = false; + } else { + throw new NotBooleanValueTemplateException(name); + } + + boolean condition = negated ? !value : value; + if (condition) { + ifSections.sections().forEach(section -> section.appendTo(buffer)); + } + } + + @Override + void appendCopyTo(SectionList sectionList) { + SectionList ifSectionCopy = new SectionList(ifSections.range().start(), sectionList.formBuilder()); + ifSections.sections().forEach(section -> section.appendCopyTo(ifSectionCopy)); + sectionList.appendIfSection(negated, name, nameOffset, range().end(), ifSectionCopy); + } +} diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/LiteralSection.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/LiteralSection.java index 852056fa283..c03653253af 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/LiteralSection.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/LiteralSection.java @@ -18,4 +18,9 @@ class LiteralSection extends Section { void appendTo(StringBuilder buffer) { range().appendTo(buffer); } + + @Override + void appendCopyTo(SectionList sectionList) { + sectionList.appendLiteralSection(range().end()); + } } diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/NoSuchNameTemplateException.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/NoSuchNameTemplateException.java index f6d630846a2..706d347d39d 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/NoSuchNameTemplateException.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/NoSuchNameTemplateException.java @@ -1,11 +1,13 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.node.admin.task.util.template; +import com.yahoo.vespa.hosted.node.admin.task.util.text.CursorRange; + /** * @author hakonhall */ public class NoSuchNameTemplateException extends TemplateException { - public NoSuchNameTemplateException(Section section, String name) { - super("No such element '" + name + "' in the " + describeSection(section.range())); + public NoSuchNameTemplateException(CursorRange range, String name) { + super("No such element '" + name + "' in the " + describeSection(range)); } } diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/NotBooleanValueTemplateException.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/NotBooleanValueTemplateException.java new file mode 100644 index 00000000000..6c6d157bb47 --- /dev/null +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/NotBooleanValueTemplateException.java @@ -0,0 +1,11 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.node.admin.task.util.template; + +/** + * @author hakonhall + */ +public class NotBooleanValueTemplateException extends TemplateException { + public NotBooleanValueTemplateException(String name) { + super(name + " was set to a non-boolean value: must be true or false"); + } +} diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/Section.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/Section.java index f12dfb8d8b7..234915770f8 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/Section.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/Section.java @@ -25,4 +25,6 @@ abstract class Section { protected CursorRange range() { return range; } abstract void appendTo(StringBuilder buffer); + + abstract void appendCopyTo(SectionList sectionList); } diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/SectionList.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/SectionList.java index b5017246f31..679888678bf 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/SectionList.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/SectionList.java @@ -6,7 +6,6 @@ import com.yahoo.vespa.hosted.node.admin.task.util.text.CursorRange; import java.util.ArrayList; import java.util.List; -import java.util.function.Consumer; /** * A mutable list of sections at the same level that can be used to build a form, e.g. the if-body. @@ -14,42 +13,51 @@ import java.util.function.Consumer; * @author hakonhall */ class SectionList { - private TemplateBuilder templateBuilder; - private final Cursor start; private final Cursor end; + private final FormBuilder formBuilder; - private final List<Consumer<FormBuilder>> sections = new ArrayList<>(); + private final List<Section> sections = new ArrayList<>(); - SectionList(Cursor start) { + SectionList(Cursor start, FormBuilder formBuilder) { this.start = new Cursor(start); this.end = new Cursor(start); + this.formBuilder = formBuilder; } - /** Must be invoked once before any other method. */ - void setTemplateBuilder(TemplateBuilder templateBuilder) { - this.templateBuilder = templateBuilder; - } + CursorRange range() { return new CursorRange(start, end); } + FormBuilder formBuilder() { return formBuilder; } + List<Section> sections() { return List.copyOf(sections); } void appendLiteralSection(Cursor end) { CursorRange range = verifyAndUpdateEnd(end); - sections.add((FormBuilder builder) -> builder.addLiteralSection(range)); + var section = new LiteralSection(range); + formBuilder.addLiteralSection(section); + sections.add(section); } - void appendVariableSection(String name, Cursor nameOffset, Cursor end) { + VariableSection appendVariableSection(String name, Cursor nameOffset, Cursor end) { CursorRange range = verifyAndUpdateEnd(end); - templateBuilder.addVariable(name, nameOffset); - sections.add(formBuilder -> formBuilder.addVariableSection(range, name, nameOffset)); + var section = new VariableSection(range, name, nameOffset); + formBuilder.addVariableSection(section); + sections.add(section); + return section; } - void appendSubformSection(String name, Cursor nameCursor, Cursor end, Template body) { + void appendIfSection(boolean negated, String name, Cursor nameOffset, Cursor end, + SectionList ifSections) { CursorRange range = verifyAndUpdateEnd(end); - templateBuilder.addSubform(name, nameCursor); - sections.add(formBuilder -> formBuilder.addSubformSection(range, name, body)); + var section = new IfSection(range, negated, name, nameOffset, ifSections); + formBuilder.addIfSection(section); + sections.add(section); } - CursorRange range() { return new CursorRange(start, end); } - List<Consumer<FormBuilder>> sections() { return List.copyOf(sections); } + void appendSubformSection(String name, Cursor nameOffset, Cursor end, Form body) { + CursorRange range = verifyAndUpdateEnd(end); + var section = new SubformSection(range, name, nameOffset, body); + formBuilder.addSubformSection(section); + sections.add(section); + } private CursorRange verifyAndUpdateEnd(Cursor newEnd) { var range = new CursorRange(this.end, newEnd); diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/SubformSection.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/SubformSection.java index 360a37964b2..fa65589d7d5 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/SubformSection.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/SubformSection.java @@ -1,6 +1,7 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.node.admin.task.util.template; +import com.yahoo.vespa.hosted.node.admin.task.util.text.Cursor; import com.yahoo.vespa.hosted.node.admin.task.util.text.CursorRange; import java.util.ArrayList; @@ -13,26 +14,44 @@ import java.util.List; * @author hakonhall */ class SubformSection extends Section { - private final Template body; private final String name; + private final Cursor nameOffset; + private final Form body; private final List<Form> elements = new ArrayList<>(); - SubformSection(CursorRange range, String name, Template body) { + SubformSection(CursorRange range, String name, Cursor nameOffset, Form body) { super(range); this.name = name; + this.nameOffset = new Cursor(nameOffset); this.body = body; } String name() { return name; } + Cursor nameOffset() { return new Cursor(nameOffset); } + + @Override + void setForm(Form form) { + super.setForm(form); + body.setParent(form); + } Form add() { - var form = body.instantiate(form()); - elements.add(form); - return form; + Form element = body.copy(); + element.setParent(form()); + elements.add(element); + return element; } @Override void appendTo(StringBuilder buffer) { elements.forEach(form -> form.appendTo(buffer)); } + + @Override + void appendCopyTo(SectionList sectionList) { + // avoid copying elements for now + // Optimization: Reuse body in copy, since it is only used for copying. + + sectionList.appendSubformSection(name, nameOffset, range().end(), body); + } } diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/Template.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/Template.java index 47ff28b2d6b..a3ffcf46bdf 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/Template.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/Template.java @@ -1,13 +1,6 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.node.admin.task.util.template; -import com.yahoo.vespa.hosted.node.admin.task.util.text.Cursor; -import com.yahoo.vespa.hosted.node.admin.task.util.text.CursorRange; - -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - /** * The Java representation of a template text. * @@ -18,6 +11,7 @@ import java.util.function.Consumer; * section: literal | variable | subform * literal: plain text not containing %{ * variable: %{=identifier} + * if: %{if [!]identifier}template[%{else}template]%{end} * subform: %{form identifier}template%{end} * identifier: a valid Java identifier * </pre> @@ -38,18 +32,7 @@ import java.util.function.Consumer; * @author hakonhall */ public class Template { - private final CursorRange range; - private final List<Consumer<FormBuilder>> sections; - private final Map<String, Cursor> variables; - private final Map<String, Cursor> subforms; - - public Template(CursorRange range, List<Consumer<FormBuilder>> sections, - Map<String, Cursor> variables, Map<String, Cursor> subforms) { - this.range = new CursorRange(range); - this.sections = List.copyOf(sections); - this.variables = Map.copyOf(variables); - this.subforms = Map.copyOf(subforms); - } + private final Form form; public static Template from(String text) { return from(text, new TemplateDescriptor()); } @@ -57,7 +40,9 @@ public class Template { return TemplateParser.parse(text, descriptor).template(); } - public Form instantiate() { return instantiate(null); } + Template(Form form) { + this.form = form; + } - Form instantiate(Form parent) { return FormBuilder.build(parent, range, sections); } + public Form instantiate() { return form.copy(); } } diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/TemplateBuilder.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/TemplateBuilder.java deleted file mode 100644 index d63ed7c9e70..00000000000 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/TemplateBuilder.java +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.node.admin.task.util.template; - -import com.yahoo.vespa.hosted.node.admin.task.util.text.Cursor; - -import java.util.HashMap; -import java.util.Map; - -/** - * @author hakonhall - */ -class TemplateBuilder { - private final SectionList sections; - - /** Example location (value) of a variable name (key). */ - private final Map<String, Cursor> variables = new HashMap<>(); - - /** Location of the name of each subform. */ - private final Map<String, Cursor> subforms = new HashMap<>(); - - TemplateBuilder(Cursor start) { - sections = new SectionList(start); - sections.setTemplateBuilder(this); - } - - SectionList sectionList() { return sections; } - - void addVariable(String name, Cursor nameOffset) { - Cursor existing = subforms.get(name); - if (existing != null) - throw new NameAlreadyExistsTemplateException(name, existing, nameOffset); - variables.put(name, new Cursor(nameOffset)); - } - - void addSubform(String name, Cursor nameCursor) { - Cursor existing = variables.get(name); - if (existing != null) - throw new NameAlreadyExistsTemplateException(name, existing, nameCursor); - - existing = subforms.put(name, nameCursor); - if (existing != null) - throw new NameAlreadyExistsTemplateException(name, existing, nameCursor); - } - - Template build() { - return new Template(sections.range(), sections.sections(), variables, subforms); - } -} diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/TemplateNameNotSetException.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/TemplateNameNotSetException.java index 0c715df2efd..d65c2a7c4d6 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/TemplateNameNotSetException.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/TemplateNameNotSetException.java @@ -8,6 +8,6 @@ import com.yahoo.vespa.hosted.node.admin.task.util.text.Cursor; */ public class TemplateNameNotSetException extends TemplateException { public TemplateNameNotSetException(String name, Cursor nameOffset) { - super(name + " at " + nameOffset.calculateLocation().lineAndColumnText() + " has not been set"); + super("Variable at " + nameOffset.calculateLocation().lineAndColumnText() + " has not been set: " + name); } } diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/TemplateParser.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/TemplateParser.java index b97745df3a6..ac38a23d4ad 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/TemplateParser.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/TemplateParser.java @@ -15,7 +15,7 @@ class TemplateParser { private final TemplateDescriptor descriptor; private final Cursor start; private final Cursor current; - private final TemplateBuilder templateBuilder; + private final FormBuilder formBuilder; static TemplateParser parse(String text, TemplateDescriptor descriptor) { return parse(new TemplateDescriptor(descriptor), new Cursor(text), EnumSet.of(Sentinel.EOT)); @@ -23,20 +23,20 @@ class TemplateParser { private static TemplateParser parse(TemplateDescriptor descriptor, Cursor start, EnumSet<Sentinel> sentinel) { var parser = new TemplateParser(descriptor, start); - parser.parse(parser.templateBuilder.sectionList(), sentinel); + parser.parse(parser.formBuilder.topLevelSectionList(), sentinel); return parser; } private enum Sentinel { END, EOT } - TemplateParser(TemplateDescriptor descriptor, Cursor start) { + private TemplateParser(TemplateDescriptor descriptor, Cursor start) { this.descriptor = descriptor; this.start = new Cursor(start); this.current = new Cursor(start); - this.templateBuilder = new TemplateBuilder(start); + this.formBuilder = new FormBuilder(start); } - Template template() { return templateBuilder.build(); } + Template template() { return new Template(formBuilder.build()); } private Sentinel parse(SectionList sectionList, EnumSet<Sentinel> sentinels) { do { @@ -60,7 +60,6 @@ class TemplateParser { } private Optional<Sentinel> parseSection(SectionList sectionList, EnumSet<Sentinel> sentinels) { - var startOfDirective = new Cursor(current); current.skip(descriptor.startDelimiter()); if (current.skip(Token.VARIABLE_DIRECTIVE_CHAR)) { @@ -78,6 +77,9 @@ class TemplateParser { case "form": parseSubformSection(sectionList); break; + case "if": + parseIfSection(sectionList); + break; default: throw new BadTemplateException(startOfType, "Unknown section '" + type + "'"); } @@ -106,7 +108,21 @@ class TemplateParser { TemplateParser bodyParser = parse(descriptor, current, EnumSet.of(Sentinel.END)); current.set(bodyParser.current); - sectionList.appendSubformSection(name, startOfName, current, bodyParser.template()); + sectionList.appendSubformSection(name, startOfName, current, bodyParser.formBuilder.build()); + } + + private void parseIfSection(SectionList sectionList) { + skipRequiredWhitespaces(); + boolean negated = current.skip(Token.NEGATE_CHAR); + current.skipWhitespaces(); + var startOfName = new Cursor(current); + String name = parseId(); + parseEndDelimiter(true); + + SectionList ifSectionList = new SectionList(current, formBuilder); + parse(ifSectionList, EnumSet.of(Sentinel.END)); + + sectionList.appendIfSection(negated, name, startOfName, current, ifSectionList); } private void skipRequiredWhitespaces() { diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/VariableSection.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/VariableSection.java index b384a2deb5a..bf211a01190 100644 --- a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/VariableSection.java +++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/template/VariableSection.java @@ -21,6 +21,7 @@ class VariableSection extends Section { } String name() { return name; } + Cursor nameOffset() { return new Cursor(nameOffset); } @Override void appendTo(StringBuilder buffer) { @@ -28,4 +29,9 @@ class VariableSection extends Section { .orElseThrow(() -> new TemplateNameNotSetException(name, nameOffset)); buffer.append(value); } + + @Override + void appendCopyTo(SectionList sectionList) { + sectionList.appendVariableSection(name, nameOffset, range().end()); + } } |