Skip to content

Commit

Permalink
Implement lit template parser for limited template formats which exte…
Browse files Browse the repository at this point in the history
…nds LitTemplate element (#8979)

Implement lit template parser for limited template formats which extends LitTemplate element

Fixes #8966
# Conflicts:
#	flow-server/src/main/java/com/vaadin/flow/component/littemplate/LitTemplateDataAnalyzer.java
#	flow-server/src/main/java/com/vaadin/flow/component/polymertemplate/BundleParser.java
#	flow-server/src/test/resources/META-INF/VAADIN/config/stats.json
  • Loading branch information
Denis authored and caalador committed Jan 26, 2021
1 parent ccf6c79 commit 04d8831
Show file tree
Hide file tree
Showing 12 changed files with 900 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
import com.vaadin.flow.component.HasComponents;
import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.dependency.JsModule;
import com.vaadin.flow.component.littemplate.LitTemplateParser.LitTemplateParserFactory;
import com.vaadin.flow.component.polymertemplate.Id;
import com.vaadin.flow.component.polymertemplate.PolymerTemplate;
import com.vaadin.flow.di.Instantiator;
import com.vaadin.flow.dom.Element;
import com.vaadin.flow.internal.UsageStatistics;
import com.vaadin.flow.server.VaadinService;
Expand Down Expand Up @@ -61,9 +63,34 @@ public abstract class LitTemplate extends Component {

/**
* Creates the component mapped to a LitElement.
* <p>
* The call is delegated to
* {@link #LitTemplate(LitTemplateParser, VaadinService)} via
* {@code VaadinService.getCurrent()} as a service and parser created via
* {@link LitTemplateParserFactory} retrieved from {@link Instantiator}.
*
* @see #LitTemplate(LitTemplateParser, VaadinService)
* @see VaadinService
* @see LitTemplateParserFactory
* @see Instantiator
* @see Instantiator#getOrCreate(Class)
*/
protected LitTemplate() {
LitTemplateInitializer templateInitializer = new LitTemplateInitializer(this, VaadinService.getCurrent());
this(getParser(VaadinService.getCurrent()), VaadinService.getCurrent());
}

/**
* Creates the component component mapped to a LitElement using the provided
* {@code parser} and {@code service}.
*
* @param parser
* a template parser
* @param service
* the related service instance
*/
protected LitTemplate(LitTemplateParser parser, VaadinService service) {
LitTemplateInitializer templateInitializer = new LitTemplateInitializer(
this, VaadinService.getCurrent());
templateInitializer.initChildElements();
}

Expand All @@ -80,4 +107,10 @@ public Stream<Component> getChildren() {
return super.getChildren();
}

static LitTemplateParser getParser(VaadinService service) {
LitTemplateParserFactory factory = service.getInstantiator()
.getOrCreate(LitTemplateParserFactory.class);
return factory.createParser();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,51 @@

import java.io.Serializable;
import java.util.Collections;
import java.util.Optional;

import org.jsoup.nodes.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.vaadin.flow.component.Tag;
import com.vaadin.flow.component.littemplate.LitTemplateParser.TemplateData;
import com.vaadin.flow.component.polymertemplate.IdCollector;
import com.vaadin.flow.component.polymertemplate.TemplateDataAnalyzer.ParserData;
import com.vaadin.flow.internal.AnnotationReader;
import com.vaadin.flow.server.VaadinService;

/**
* Template data analyzer which produces immutable data required for template
* initializer using provided template class and a parser.
*
* @author Vaadin Ltd
* @since
*
*/
class LitTemplateDataAnalyzer implements Serializable {

private final Class<? extends LitTemplate> templateClass;
private final LitTemplateParser parser;
private final VaadinService service;
private final String tag;

/**
* Create an instance of the analyzer using the {@code templateClass} and the
* template {@code parser}.
*
* @param templateClass a template type
* @param templateClass
* a template type
* @param parser
* a template parser
* @param service
* the related service instance
*/
LitTemplateDataAnalyzer(Class<? extends LitTemplate> templateClass) {
LitTemplateDataAnalyzer(Class<? extends LitTemplate> templateClass,
LitTemplateParser parser, VaadinService service) {
this.templateClass = templateClass;
this.parser = parser;
this.service = service;
tag = getTag(templateClass);
}

/**
Expand All @@ -48,10 +70,35 @@ class LitTemplateDataAnalyzer implements Serializable {
* @return the template data
*/
ParserData parseTemplate() {
IdCollector idExtractor = new IdCollector(templateClass, null, null);
TemplateData templateData = parser.getTemplateContent(templateClass,
tag, service);
if (templateData == null) {
getLogger().info("Couldn't parse template for {} class. "
+ "Only specific Lit template format is supported. Please check that your template definition"
+ " directly contains 'render' method which returns html`_template_content_`.",
templateClass);
}

Element templateRoot = templateData == null ? null
: templateData.getTemplateElement();
String modulePath = templateData == null ? null
: templateData.getModulePath();
IdCollector idExtractor = new IdCollector(templateClass, modulePath,
templateRoot);
idExtractor.collectInjectedIds(Collections.emptySet());
return new ParserData(idExtractor.getIdByField(), idExtractor.getTagById(), Collections.emptySet(),
Collections.emptyList());
}

private String getTag(Class<? extends LitTemplate> clazz) {
Optional<String> tagNameAnnotation = AnnotationReader
.getAnnotationFor(clazz, Tag.class).map(Tag::value);
assert tagNameAnnotation.isPresent();
return tagNameAnnotation.get();
}

private static Logger getLogger() {
return LoggerFactory.getLogger(LitTemplateDataAnalyzer.class.getName());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@
*/
package com.vaadin.flow.component.littemplate;

import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

import com.vaadin.flow.component.littemplate.LitTemplateParser.LitTemplateParserFactory;
import com.vaadin.flow.component.polymertemplate.IdMapper;
import com.vaadin.flow.component.polymertemplate.TemplateDataAnalyzer.ParserData;
import com.vaadin.flow.di.Instantiator;
import com.vaadin.flow.dom.Element;
import com.vaadin.flow.internal.ReflectionCache;
import com.vaadin.flow.server.VaadinService;
Expand All @@ -27,26 +30,50 @@
* Template initialization related logic.
*
* @author Vaadin Ltd
* @since
*
*/
public class LitTemplateInitializer {
private static final ReflectionCache<LitTemplate, ParserData> CACHE = new ReflectionCache<>(
templateClass -> new LitTemplateDataAnalyzer(templateClass)
.parseTemplate());
private static final ConcurrentHashMap<LitTemplateParser, ReflectionCache<LitTemplate, ParserData>> CACHE = new ConcurrentHashMap<>();

private final LitTemplate template;

private final ParserData parserData;

/**
* Creates a new initializer instance.
* <p>
* The call is delegated to the
* {@link #LitTemplateInitializer(LitTemplate, LitTemplateParser, VaadinService)}
* with parser created via {@link LitTemplateParserFactory} retrieved from
* {@link Instantiator}.
*
* @param template
* a template to initialize
* a template to initialize
* @param service
* the related service
* the related service
*
* @see VaadinService
* @see LitTemplateParserFactory
* @see Instantiator
* @see Instantiator#getOrCreate(Class)
*/
public LitTemplateInitializer(LitTemplate template, VaadinService service) {
this(template, LitTemplate.getParser(service), service);
}

/**
* Creates a new initializer instance.
*
* @param template
* a template to initialize
* @param parser
* lit template parser
* @param service
* the related service
*/
LitTemplateInitializer(LitTemplate template, LitTemplateParser parser,
VaadinService service) {
this.template = template;

boolean productionMode = service.getDeploymentConfiguration()
Expand All @@ -56,10 +83,15 @@ public LitTemplateInitializer(LitTemplate template, VaadinService service) {

ParserData data = null;
if (productionMode) {
data = CACHE.get(templateClass);
ReflectionCache<LitTemplate, ParserData> cache = CACHE
.computeIfAbsent(parser, analyzer -> new ReflectionCache<>(
clazz -> new LitTemplateDataAnalyzer(clazz,
analyzer, service).parseTemplate()));
data = cache.get(templateClass);
}
if (data == null) {
data = new LitTemplateDataAnalyzer(templateClass).parseTemplate();
data = new LitTemplateDataAnalyzer(templateClass, parser, service)
.parseTemplate();
}
parserData = data;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Copyright 2000-2020 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.flow.component.littemplate;

import org.jsoup.nodes.Element;

import com.vaadin.flow.di.Instantiator;
import com.vaadin.flow.server.VaadinService;

/**
* Lit template content parser.
* <p>
* It returns a JSOUP element representing the content of template for the given
* template class.
*
* @see LitTemplateParserImpl
*
* @author Vaadin Ltd
* @since
*
*/
@FunctionalInterface
public interface LitTemplateParser {

/**
* Lit template parser factory.
* <p>
* To be able to create a parser which can be provided as SPI use
* {@link Instantiator} to create the factory and then get a parser from it:
*
* <pre>
* <code>
* Instantiator instantiator = ...;
* LitTemplateParserFactory factory = instantiator.getOrCreate(LitTemplateParserFactory.class);
* LitTemplateParser parser = factory.createParser();
* </code>
* </pre>
* <p>
*
* @author Vaadin Ltd
* @see LitTemplateParser
* @since
*
*/
class LitTemplateParserFactory {

/**
* Creates a Lit template parser instance.
*
* @return a lit template parser instance
*/
public LitTemplateParser createParser() {
return LitTemplateParserImpl.getInstance();
}
}

/**
* Wrapper for the parsing result.
* <p>
* The data contains path uri where the template is declared and its content
* as an {@link Element} instance.
*
* @author Vaadin Ltd
* @since
*
*/
class TemplateData {

private final String modulePath;
private final Element templateElement;

public TemplateData(String uri, Element element) {
modulePath = uri;
templateElement = element;
}

/**
* Gets the uri where the template is declared.
*
* @return template uri
*/
public String getModulePath() {
return modulePath;
}

/**
* Gets the content of the template.
*
* @return the content of the template
*/
public Element getTemplateElement() {
return templateElement;
}
}

/**
* Gets the template data which contains a JSOUP {@link Element}
* representing the template content and the template uri.
*
* @param clazz
* the template class
* @param tag
* the template tag name
* @param service
* the related Vaadin service
*
* @return the template data, may be {@code null}
*/
TemplateData getTemplateContent(Class<? extends LitTemplate> clazz,
String tag, VaadinService service);
}

0 comments on commit 04d8831

Please sign in to comment.