This is an automated email from the ASF dual-hosted git repository.

cstamas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git


The following commit(s) were added to refs/heads/master by this push:
     new afe56ab39 Feat: Pull in version filter builder (#1867)
afe56ab39 is described below

commit afe56ab396d6ba3183e3356845a787fb4eacfb94
Author: Tamas Cservenak <[email protected]>
AuthorDate: Mon May 11 23:08:37 2026 +0200

    Feat: Pull in version filter builder (#1867)
    
    Version builder filter should be shared across all Resolver users, hence it 
makes sense to have it in Resolver (instead of 2 copies in Maven 3.10 and 4).
---
 .../aether/collection/VersionFilterBuilder.java    |  47 ++++
 .../impl/collect/DefaultVersionFilterBuilder.java  | 176 +++++++++++++
 .../collect/DefaultVersionFilterBuilderTest.java   | 276 +++++++++++++++++++++
 3 files changed, 499 insertions(+)

diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/collection/VersionFilterBuilder.java
 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/VersionFilterBuilder.java
new file mode 100644
index 000000000..19d89d3b3
--- /dev/null
+++ 
b/maven-resolver-api/src/main/java/org/eclipse/aether/collection/VersionFilterBuilder.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.eclipse.aether.collection;
+
+import java.util.Optional;
+import java.util.function.Function;
+
+import org.eclipse.aether.version.VersionConstraint;
+
+/**
+ * Builds {@link VersionFilter} instances out of input expression string.
+ *
+ * @since 2.0.18
+ */
+public interface VersionFilterBuilder {
+    /**
+     * Config property for version filter suppression. Presence of this key 
will suppress filter created by this builder.
+     * This key is not meant for users, but to programmatically signal 
filtering suppression.
+     */
+    String VERSION_FILTER_SUPPRESSED = "aether.versionFilter.suppressed";
+
+    /**
+     * Builds a version filter based on the given filter expression.
+     *
+     * @param filterExpression a string containing filter expressions, may be 
{@code null}.
+     * @param versionConstraintParser version constraint parts to be used 
during parsing, must not be {@code null}.
+     * @return optional version filter, never {@code null}.
+     */
+    Optional<VersionFilter> buildVersionFilter(
+            String filterExpression, Function<String, VersionConstraint> 
versionConstraintParser);
+}
diff --git 
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterBuilder.java
 
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterBuilder.java
new file mode 100644
index 000000000..59577df0d
--- /dev/null
+++ 
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterBuilder.java
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.eclipse.aether.internal.impl.collect;
+
+import javax.inject.Named;
+import javax.inject.Singleton;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.collection.VersionFilter;
+import org.eclipse.aether.collection.VersionFilterBuilder;
+import org.eclipse.aether.util.ConfigUtils;
+import org.eclipse.aether.util.graph.version.ChainedVersionFilter;
+import 
org.eclipse.aether.util.graph.version.ContextPredicateDelegatingVersionFilter;
+import org.eclipse.aether.util.graph.version.ContextualSnapshotVersionFilter;
+import org.eclipse.aether.util.graph.version.GenericQualifiersVersionFilter;
+import org.eclipse.aether.util.graph.version.HighestVersionFilter;
+import org.eclipse.aether.util.graph.version.LowestVersionFilter;
+import org.eclipse.aether.util.graph.version.ReleaseVersionFilter;
+import org.eclipse.aether.util.graph.version.SnapshotVersionFilter;
+import org.eclipse.aether.util.graph.version.VersionPredicateVersionFilter;
+import org.eclipse.aether.version.VersionConstraint;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Builds {@link VersionFilter} instances out of input expression string.
+ *
+ * Expression is a semicolon separated list of filters to apply. By default, 
no version filter is applied (like in Maven 3).
+ * <br/>
+ * Supported filters:
+ * <ul>
+ *     <li>{@code "s"} - contextual snapshot filter (project version decides 
are snapshots allowed or not)</li>
+ *     <li>{@code "nosnapshot"} - unconditional snapshot filter (no snapshot 
versions selected from ranges)</li>
+ *     <li>{@code "norelease"} - unconditional release filter (no release 
versions selected from ranges)</li>
+ *     <li>{@code "nopreview"} - unconditional preview filter (no preview 
versions selected from ranges)</li>
+ *     <li>{@code "noprerelease"} - unconditional pre-release filter (no 
preview and rc/cr versions selected from ranges)</li>
+ *     <li>{@code "noqualifier"} - unconditional any-qualifier filter (no 
version with any qualifier selected from ranges)</li>
+ *     <li>{@code "h"} (shorthand of {@code h(1)}) or {@code "h(num)"} - 
highest N version (based on version ordering)</li>
+ *     <li>{@code "l"} (shorthand of {@code l(1)}) or {@code "l(num)"} - 
lowest N version (based on version ordering)</li>
+ *     <li>{@code "e(V)"} - exclusion filter (excludes versions matching V 
version constraint)</li>
+ *     <li>{@code "i(V)"} - inclusion filter (includes versions matching V 
version constraint)</li>
+ * </ul>
+ * Every filter expression may have "scope" applied, in form of {@code 
@G[:A]}. Presence of "scope" narrows the
+ * application of filter to given G or G:A.
+ * <p>
+ * In case of multiple "similar" rule scopes, user should enlist rules from 
"most specific" to "least specific".
+ * <p>
+ * Example filter expression: <code>"h(5);s;e(1)@org.foo:bar</code> will cause:
+ * <ul>
+ *     <li>ranges are filtered for "top 5" (instead of full range)</li>
+ *     <li>snapshots are banned if root project is not a snapshot</li>
+ *     <li>if range for <code>org.foo:bar</code> is being processed, version 1 
is omitted</li>
+ * </ul>
+ * Values in this property builds 
<code>org.eclipse.aether.collection.VersionFilter</code> instance.
+ *
+ * @since 2.0.18
+ */
+@Singleton
+@Named
+public class DefaultVersionFilterBuilder implements VersionFilterBuilder {
+    /**
+     * Builds a version filter based on the given filter expression.
+     *
+     * @param filterExpression a string containing filter expressions, may be 
{@code null}.
+     * @param versionConstraintParser version constraint parts to be used 
during parsing, must not be {@code null}.
+     * @return optional version filter, never {@code null}.
+     */
+    @Override
+    public Optional<VersionFilter> buildVersionFilter(
+            String filterExpression, Function<String, VersionConstraint> 
versionConstraintParser) {
+        requireNonNull(versionConstraintParser);
+        ArrayList<VersionFilter> filters = new ArrayList<>();
+        if (filterExpression != null) {
+            List<String> expressions = 
Arrays.stream(filterExpression.split(";"))
+                    .filter(s -> !s.trim().isEmpty())
+                    .collect(Collectors.toList());
+            for (String expression : expressions) {
+                Predicate<Artifact> scopePredicate;
+                VersionFilter filter;
+                if (expression.contains("@")) {
+                    String remainder = 
expression.substring(expression.indexOf('@') + 1);
+                    if (remainder.contains(":")) {
+                        String g = remainder.substring(0, 
remainder.indexOf(':'));
+                        String a = remainder.substring(remainder.indexOf(':') 
+ 1);
+                        scopePredicate =
+                                artifact -> g.equals(artifact.getGroupId()) && 
a.equals(artifact.getArtifactId());
+                    } else {
+                        scopePredicate = artifact -> 
remainder.equals(artifact.getGroupId());
+                    }
+                    expression = expression.substring(0, 
expression.indexOf('@'));
+                } else {
+                    scopePredicate = null;
+                }
+                if ("s".equals(expression)) {
+                    filter = new ContextualSnapshotVersionFilter();
+                } else if ("nosnapshot".equals(expression)) {
+                    filter = new SnapshotVersionFilter();
+                } else if ("norelease".equals(expression)) {
+                    filter = new ReleaseVersionFilter();
+                } else if ("nopreview".equals(expression)) {
+                    filter = 
GenericQualifiersVersionFilter.previewVersionFilter();
+                } else if ("noprerelease".equals(expression)) {
+                    filter = 
GenericQualifiersVersionFilter.preReleaseVersionFilter();
+                } else if ("noqualifier".equals(expression)) {
+                    filter = 
GenericQualifiersVersionFilter.anyQualifierVersionFilter();
+                } else if ("h".equals(expression)) {
+                    filter = new HighestVersionFilter();
+                } else if ("l".equals(expression)) {
+                    filter = new LowestVersionFilter();
+                } else if ((expression.startsWith("h(") || 
expression.startsWith("l(")) && expression.endsWith(")")) {
+                    int num = Integer.parseInt(expression.substring(2, 
expression.length() - 1));
+                    if (expression.startsWith("h(")) {
+                        filter = new HighestVersionFilter(num);
+                    } else {
+                        filter = new LowestVersionFilter(num);
+                    }
+                } else if ((expression.startsWith("e(") || 
(expression.startsWith("i("))) && expression.endsWith(")")) {
+                    VersionConstraint versionConstraint =
+                            
versionConstraintParser.apply(expression.substring(2, expression.length() - 1));
+                    if (expression.startsWith("e(")) {
+                        // exclude
+                        filter = new VersionPredicateVersionFilter(v -> 
!versionConstraint.containsVersion(v));
+                    } else {
+                        // include
+                        filter = new 
VersionPredicateVersionFilter(versionConstraint::containsVersion);
+                    }
+                } else {
+                    throw new IllegalArgumentException("Unsupported filter 
expression: " + expression);
+                }
+
+                filters.add(contextPredicate(scopePredicate, filter));
+            }
+        }
+        if (filters.isEmpty()) {
+            return Optional.empty();
+        } else if (filters.size() == 1) {
+            return Optional.of(filters.get(0));
+        } else {
+            return Optional.of(ChainedVersionFilter.newInstance(filters));
+        }
+    }
+
+    private VersionFilter contextPredicate(Predicate<Artifact> 
artifactPredicate, VersionFilter filter) {
+        Predicate<VersionFilter.VersionFilterContext> contextPredicate =
+                c -> !ConfigUtils.getBoolean(c.getSession(), false, 
VERSION_FILTER_SUPPRESSED);
+        if (artifactPredicate != null) {
+            contextPredicate = contextPredicate.and(
+                    c -> 
artifactPredicate.test(c.getDependency().getArtifact()));
+        }
+        return new ContextPredicateDelegatingVersionFilter(contextPredicate, 
filter);
+    }
+}
diff --git 
a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterBuilderTest.java
 
b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterBuilderTest.java
new file mode 100644
index 000000000..13cdef5e5
--- /dev/null
+++ 
b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/collect/DefaultVersionFilterBuilderTest.java
@@ -0,0 +1,276 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.eclipse.aether.internal.impl.collect;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.eclipse.aether.RepositoryException;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.DefaultArtifact;
+import org.eclipse.aether.collection.VersionFilter;
+import org.eclipse.aether.collection.VersionFilterBuilder;
+import org.eclipse.aether.graph.Dependency;
+import org.eclipse.aether.resolution.VersionRangeRequest;
+import org.eclipse.aether.resolution.VersionRangeResult;
+import 
org.eclipse.aether.util.graph.version.ContextPredicateDelegatingVersionFilter;
+import org.eclipse.aether.util.version.GenericVersionScheme;
+import org.eclipse.aether.version.InvalidVersionSpecificationException;
+import org.eclipse.aether.version.Version;
+import org.eclipse.aether.version.VersionConstraint;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class DefaultVersionFilterBuilderTest {
+    final GenericVersionScheme versionScheme = new GenericVersionScheme();
+    RepositorySystemSession session;
+    DefaultVersionFilterBuilder factory;
+    Map<String, Object> sessionConfigProperties;
+
+    @BeforeEach
+    public void prepare() {
+        session = mock(RepositorySystemSession.class);
+        sessionConfigProperties = new HashMap<>();
+        
when(session.getConfigProperties()).thenReturn(sessionConfigProperties);
+        factory = new DefaultVersionFilterBuilder();
+    }
+
+    private Version version(String spec) {
+        try {
+            return versionScheme.parseVersion(spec);
+        } catch (InvalidVersionSpecificationException e) {
+            throw new RuntimeException(e); // never happens
+        }
+    }
+
+    private VersionConstraint versionConstraint(String spec) {
+        try {
+            return versionScheme.parseVersionConstraint(spec);
+        } catch (InvalidVersionSpecificationException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    public void versionFilterUnsupportedExpression() {
+        // null and empty string are OK
+        assertThrows(IllegalArgumentException.class, () -> 
factory.buildVersionFilter("[*", this::versionConstraint));
+        assertThrows(
+                IllegalArgumentException.class, () -> 
factory.buildVersionFilter("foobar", this::versionConstraint));
+    }
+
+    /**
+     * Simple assertions for {@code h} filter,
+     */
+    @Test
+    public void versionFilterHighest() {
+        VersionFilter vf;
+        vf = factory.buildVersionFilter("h", 
this::versionConstraint).orElse(null);
+        assertNotNull(vf);
+        assertInstanceOf(ContextPredicateDelegatingVersionFilter.class, vf);
+
+        vf = factory.buildVersionFilter("h(5)", 
this::versionConstraint).orElse(null);
+        assertNotNull(vf);
+        assertInstanceOf(ContextPredicateDelegatingVersionFilter.class, vf);
+
+        vf = factory.buildVersionFilter("h(1)@group", 
this::versionConstraint).orElse(null);
+        assertNotNull(vf);
+        assertInstanceOf(ContextPredicateDelegatingVersionFilter.class, vf); 
// this is wrapped instance
+    }
+
+    /**
+     * Creating {@code h(1@group)} and incoming artifact G does not match => 
not applied.
+     */
+    @Test
+    public void versionFilterSuppressed() throws RepositoryException {
+        VersionFilter vf;
+
+        vf = factory.buildVersionFilter("h(2)@group", 
this::versionConstraint).orElse(null);
+        assertNotNull(vf);
+
+        List<Version> versions = Arrays.asList(version("1.0"), version("1.1"), 
version("1.2"));
+
+        DefaultVersionFilterContext context = new 
DefaultVersionFilterContext(session);
+        VersionRangeResult result =
+                new VersionRangeResult(new 
VersionRangeRequest()).setVersions(new ArrayList<>(versions));
+        context.set(new Dependency(new DefaultArtifact("group:a:[1,)"), ""), 
result);
+
+        vf.filterVersions(context);
+
+        // hit
+        assertEquals(2, context.get().size());
+        assertEquals(version("1.1"), context.get().get(0));
+        assertEquals(version("1.2"), context.get().get(1));
+
+        
sessionConfigProperties.put(VersionFilterBuilder.VERSION_FILTER_SUPPRESSED, 
Boolean.TRUE);
+
+        context = new DefaultVersionFilterContext(session);
+        context.set(new Dependency(new DefaultArtifact("group:a:[1,)"), ""), 
result);
+
+        // suppressed
+        assertEquals(versions, context.get());
+    }
+
+    /**
+     * Creating {@code h(1@group)} and incoming artifact G does not match => 
not applied.
+     */
+    @Test
+    public void versionFilterHLFuncMiss() throws RepositoryException {
+        VersionFilter vf;
+
+        vf = factory.buildVersionFilter("h(2)@group", 
this::versionConstraint).orElse(null);
+        assertNotNull(vf);
+
+        List<Version> versions = Arrays.asList(version("1.0"), version("1.1"), 
version("1.2"));
+
+        DefaultVersionFilterContext context = new 
DefaultVersionFilterContext(session);
+        VersionRangeResult result =
+                new VersionRangeResult(new 
VersionRangeRequest()).setVersions(new ArrayList<>(versions));
+        context.set(new Dependency(new DefaultArtifact("g:a:[1,)"), ""), 
result);
+
+        vf.filterVersions(context);
+
+        vf.filterVersions(context);
+    }
+
+    /**
+     * Creating {@code h(1@group)} and incoming artifact G does match => 
applied.
+     */
+    @Test
+    public void versionFilterHLFuncHit() throws RepositoryException {
+        VersionFilter vf;
+
+        vf = factory.buildVersionFilter("h(2)@group", 
this::versionConstraint).orElse(null);
+        assertNotNull(vf);
+
+        List<Version> versions = Arrays.asList(version("1.0"), version("1.1"), 
version("1.2"));
+
+        DefaultVersionFilterContext context = new 
DefaultVersionFilterContext(session);
+        VersionRangeResult result =
+                new VersionRangeResult(new 
VersionRangeRequest()).setVersions(new ArrayList<>(versions));
+        context.set(new Dependency(new DefaultArtifact("group:a:[1,)"), ""), 
result);
+
+        vf.filterVersions(context);
+
+        assertEquals(2, context.get().size());
+        assertEquals(version("1.1"), context.get().get(0));
+        assertEquals(version("1.2"), context.get().get(1));
+    }
+
+    /**
+     * Creating {@code l(1@group)} and incoming artifact G does not match => 
not applied.
+     */
+    @Test
+    public void versionFilterLowestFuncMiss() throws RepositoryException {
+        VersionFilter vf;
+
+        vf = factory.buildVersionFilter("l(2)@group", 
this::versionConstraint).orElse(null);
+        assertNotNull(vf);
+
+        List<Version> versions = Arrays.asList(version("1.0"), version("1.1"), 
version("1.2"));
+
+        DefaultVersionFilterContext context = new 
DefaultVersionFilterContext(session);
+        VersionRangeResult result =
+                new VersionRangeResult(new 
VersionRangeRequest()).setVersions(new ArrayList<>(versions));
+        context.set(new Dependency(new DefaultArtifact("g:a:[1,)"), ""), 
result);
+
+        vf.filterVersions(context);
+
+        assertEquals(versions, context.get());
+    }
+
+    /**
+     * Creating {@code l(1@group)} and incoming artifact G does match => 
applied.
+     */
+    @Test
+    public void versionFilterLowestFuncHit() throws RepositoryException {
+        VersionFilter vf;
+
+        vf = factory.buildVersionFilter("l(2)@group", 
this::versionConstraint).orElse(null);
+        assertNotNull(vf);
+
+        List<Version> versions = Arrays.asList(version("1.0"), version("1.1"), 
version("1.2"));
+
+        DefaultVersionFilterContext context = new 
DefaultVersionFilterContext(session);
+        VersionRangeResult result =
+                new VersionRangeResult(new 
VersionRangeRequest()).setVersions(new ArrayList<>(versions));
+        context.set(new Dependency(new DefaultArtifact("group:a:[1,)"), ""), 
result);
+
+        vf.filterVersions(context);
+
+        assertEquals(2, context.get().size());
+        assertEquals(version("1.0"), context.get().get(0));
+        assertEquals(version("1.1"), context.get().get(1));
+    }
+
+    @Test
+    public void versionFilterExcludeFuncHit() throws RepositoryException {
+        VersionFilter vf;
+
+        vf = factory.buildVersionFilter("e([1.1,2.0))@group:a", 
this::versionConstraint)
+                .orElse(null);
+        assertNotNull(vf);
+
+        List<Version> versions = Arrays.asList(version("1.0"), version("1.1"), 
version("1.2"), version("2.0"));
+
+        DefaultVersionFilterContext context = new 
DefaultVersionFilterContext(session);
+        VersionRangeResult result =
+                new VersionRangeResult(new 
VersionRangeRequest()).setVersions(new ArrayList<>(versions));
+        context.set(new Dependency(new DefaultArtifact("group:a:[1,)"), ""), 
result);
+
+        vf.filterVersions(context);
+
+        assertEquals(2, context.get().size());
+        assertEquals(version("1.0"), context.get().get(0));
+        assertEquals(version("2.0"), context.get().get(1));
+    }
+
+    @Test
+    public void versionFilterIncludeFuncHit() throws RepositoryException {
+        VersionFilter vf;
+
+        vf = factory.buildVersionFilter("i([1.1,),[2.0,))@group:a", 
this::versionConstraint)
+                .orElse(null);
+        assertNotNull(vf);
+
+        List<Version> versions = Arrays.asList(version("1.0"), version("1.1"), 
version("1.2"), version("2.0"));
+
+        DefaultVersionFilterContext context = new 
DefaultVersionFilterContext(session);
+        VersionRangeResult result =
+                new VersionRangeResult(new 
VersionRangeRequest()).setVersions(new ArrayList<>(versions));
+        context.set(new Dependency(new DefaultArtifact("group:a:[1,)"), ""), 
result);
+
+        vf.filterVersions(context);
+
+        assertEquals(3, context.get().size());
+        assertEquals(version("1.1"), context.get().get(0));
+        assertEquals(version("1.2"), context.get().get(1));
+        assertEquals(version("2.0"), context.get().get(2));
+    }
+}

Reply via email to