This is an automated email from the ASF dual-hosted git repository. vy pushed a commit to branch LOG4J2-3393 in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git
commit b3a979a996b1c7893976d941fe94279ef3d8770f Author: Volkan Yazici <[email protected]> AuthorDate: Mon Feb 7 10:53:48 2022 +0100 LOG4J2-3393 Refactor JTL TemplateResolvers into smaller components. --- .../template/json/resolver/TemplateResolvers.java | 224 +++++++++++++++------ 1 file changed, 160 insertions(+), 64 deletions(-) diff --git a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TemplateResolvers.java b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TemplateResolvers.java index 20ad802..450b874 100644 --- a/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TemplateResolvers.java +++ b/log4j-layout-template-json/src/main/java/org/apache/logging/log4j/layout/template/json/resolver/TemplateResolvers.java @@ -24,6 +24,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; +import java.util.stream.IntStream; /** * Main class for compiling {@link TemplateResolver}s from a template. @@ -192,6 +193,28 @@ public final class TemplateResolvers { return ofResolver(context, map); } + // Collect field resolver contexts. + List<FieldResolverContext<V>> fieldResolverContexts = + populateFieldResolverMethods(context, map); + + // Short-circuit if the object is empty. + final int fieldCount = fieldResolverContexts.size(); + if (fieldCount == 0) { + @SuppressWarnings("unchecked") + final TemplateResolver<V> emptyObjectResolver = + (TemplateResolver<V>) EMPTY_OBJECT_RESOLVER; + return emptyObjectResolver; + } + + // Create the resolver. + return new MapResolver<>(fieldResolverContexts); + + } + + private static <V, C extends TemplateResolverContext<V, C>> List<FieldResolverContext<V>> populateFieldResolverMethods( + final C context, + final Map<String, Object> map) { + // Create resolver for each object field. final List<String> fieldNames = new ArrayList<>(); final List<TemplateResolver<V>> fieldResolvers = new ArrayList<>(); @@ -204,15 +227,6 @@ public final class TemplateResolvers { } }); - // Short-circuit if the object is empty. - final int fieldCount = fieldNames.size(); - if (fieldCount == 0) { - @SuppressWarnings("unchecked") - final TemplateResolver<V> emptyObjectResolver = - (TemplateResolver<V>) EMPTY_OBJECT_RESOLVER; - return emptyObjectResolver; - } - // Prepare field names to avoid escape and truncation costs at runtime. final List<String> fieldPrefixes = fieldNames .stream() @@ -225,70 +239,152 @@ public final class TemplateResolvers { }) .collect(Collectors.toList()); - return new TemplateResolver<V>() { - - @Override - public boolean isResolvable() { - // We have already excluded unresolvable ones while collecting - // the resolvers. Hence it is safe to return true here. - return true; - } - - /** - * The parent resolver checking if each child is resolvable given - * the passed {@code value}. - * - * This is an optimization to skip the rendering of a parent if all - * its children are not resolvable given the passed {@code value}. - */ - @Override - public boolean isResolvable(final V value) { - for (int fieldIndex = 0; fieldIndex < fieldCount; fieldIndex++) { - final TemplateResolver<V> fieldResolver = fieldResolvers.get(fieldIndex); - final boolean resolvable = fieldResolver.isResolvable(value); - if (resolvable) { - return true; - } - } - return false; - } - - /** - * The parent resolver combining all child resolver executions. - */ - @Override - public void resolve(final V value, final JsonWriter jsonWriter) { - final StringBuilder jsonWriterStringBuilder = jsonWriter.getStringBuilder(); - jsonWriter.writeObjectStart(); - for (int resolvedFieldCount = 0, fieldIndex = 0; fieldIndex < fieldCount; fieldIndex++) { + // Collect field resolver contexts. + final int fieldCount = fieldNames.size(); + return IntStream + .range(0, fieldCount) + .mapToObj(fieldIndex -> { final TemplateResolver<V> fieldResolver = fieldResolvers.get(fieldIndex); - final boolean resolvable = fieldResolver.isResolvable(value); - if (!resolvable) { - continue; - } - final boolean succeedingEntry = resolvedFieldCount > 0; + final FieldResolverMethod<V> fieldResolverMethod; final boolean flattening = fieldResolver.isFlattening(); if (flattening) { - final int initLength = jsonWriterStringBuilder.length(); - fieldResolver.resolve(value, jsonWriter, succeedingEntry); - final boolean resolved = jsonWriterStringBuilder.length() > initLength; - if (resolved) { - resolvedFieldCount++; - } + fieldResolverMethod = new FlatteningFieldResolverMethod<>(fieldResolver); } else { - if (succeedingEntry) { - jsonWriter.writeSeparator(); - } final String fieldPrefix = fieldPrefixes.get(fieldIndex); - jsonWriter.writeRawString(fieldPrefix); - fieldResolver.resolve(value, jsonWriter, succeedingEntry); - resolvedFieldCount++; + fieldResolverMethod = new PrefixedFieldResolverMethod<>(fieldPrefix, fieldResolver); } + return new FieldResolverContext<>(fieldResolver, fieldResolverMethod); + }) + .collect(Collectors.toList()); + + } + + private static final class FieldResolverContext<V> { + + private final TemplateResolver<V> resolver; + + private final FieldResolverMethod<V> resolverMethod; + + private FieldResolverContext(final TemplateResolver<V> resolver, final FieldResolverMethod<V> resolverMethod) { + this.resolver = resolver; + this.resolverMethod = resolverMethod; + } + + } + + @FunctionalInterface + private interface FieldResolverMethod<V> { + + boolean resolve(V value, JsonWriter jsonWriter, boolean succeedingEntry); + + } + + private static final class FlatteningFieldResolverMethod<V> implements FieldResolverMethod<V> { + + private final TemplateResolver<V> fieldResolver; + + private FlatteningFieldResolverMethod(final TemplateResolver<V> fieldResolver) { + this.fieldResolver = fieldResolver; + } + + @Override + public boolean resolve(final V value, final JsonWriter jsonWriter, final boolean succeedingEntry) { + final boolean resolvable = fieldResolver.isResolvable(value); + if (!resolvable) { + return false; + } + final StringBuilder jsonWriterStringBuilder = jsonWriter.getStringBuilder(); + final int initLength = jsonWriterStringBuilder.length(); + fieldResolver.resolve(value, jsonWriter, succeedingEntry); + return jsonWriterStringBuilder.length() > initLength; + } + + } + + private static final class PrefixedFieldResolverMethod<V> implements FieldResolverMethod<V> { + + private final String fieldPrefix; + + private final TemplateResolver<V> fieldResolver; + + private PrefixedFieldResolverMethod(final String fieldPrefix, final TemplateResolver<V> fieldResolver) { + this.fieldPrefix = fieldPrefix; + this.fieldResolver = fieldResolver; + } + + @Override + public boolean resolve(final V value, final JsonWriter jsonWriter, final boolean succeedingEntry) { + final boolean resolvable = fieldResolver.isResolvable(value); + if (!resolvable) { + return false; + } + if (succeedingEntry) { + jsonWriter.writeSeparator(); + } + jsonWriter.writeRawString(fieldPrefix); + fieldResolver.resolve(value, jsonWriter, succeedingEntry); + return true; + } + + } + + private static final class MapResolver<V> implements TemplateResolver<V> { + + private final List<FieldResolverContext<V>> fieldResolverContexts; + + private MapResolver(final List<FieldResolverContext<V>> fieldResolverContexts) { + this.fieldResolverContexts = fieldResolverContexts; + } + + @Override + public boolean isResolvable() { + // We have already excluded unresolvable ones while collecting + // the resolvers; it is safe to return true here. + return true; + } + + /** + * The parent resolver checking if each child is resolvable given + * the passed {@code value}. + * + * This is an optimization to skip the rendering of a parent if all + * its children are not resolvable for the given {@code value}. + */ + @Override + public boolean isResolvable(final V value) { + int fieldCount = fieldResolverContexts.size(); + // noinspection ForLoopReplaceableByForEach (avoid iterator instantiation) + for (int fieldIndex = 0; fieldIndex < fieldCount; fieldIndex++) { + final TemplateResolver<V> fieldResolver = fieldResolverContexts.get(fieldIndex).resolver; + final boolean resolvable = fieldResolver.isResolvable(value); + if (resolvable) { + return true; } - jsonWriter.writeObjectEnd(); } + return false; + } - }; + /** + * The parent resolver combining all child resolver executions. + */ + @Override + public void resolve(final V value, final JsonWriter jsonWriter) { + jsonWriter.writeObjectStart(); + int fieldCount = fieldResolverContexts.size(); + for (int resolvedFieldCount = 0, fieldIndex = 0; fieldIndex < fieldCount; fieldIndex++) { + FieldResolverContext<V> fieldResolverContext = fieldResolverContexts.get(fieldIndex); + final boolean resolvable = fieldResolverContext.resolver.isResolvable(value); + if (!resolvable) { + continue; + } + final boolean succeedingEntry = resolvedFieldCount > 0; + final boolean resolved = fieldResolverContext.resolverMethod.resolve(value, jsonWriter, succeedingEntry); + if (resolved) { + resolvedFieldCount++; + } + } + jsonWriter.writeObjectEnd(); + } }
