From c530d602429ec9f9c9586b4a0f940736ec6c493f Mon Sep 17 00:00:00 2001 From: Roy Teeuwen Date: Mon, 5 Dec 2022 10:34:00 +0100 Subject: [PATCH] SLING-11706: Add basename provider to allow global basename setting instead of only per component --- pom.xml | 4 +- .../extension/i18n/I18nBasenameProvider.java | 36 +++++ .../extension/I18nRuntimeExtension.java | 17 +- .../extension/I18nRuntimeExtensionTest.java | 150 ++++++++++++++++++ 4 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/apache/sling/scripting/sightly/engine/extension/i18n/I18nBasenameProvider.java create mode 100644 src/test/java/org/apache/sling/scripting/sightly/impl/engine/extension/I18nRuntimeExtensionTest.java diff --git a/pom.xml b/pom.xml index e784fe56..c0514c0a 100644 --- a/pom.xml +++ b/pom.xml @@ -234,7 +234,7 @@ org.apache.sling org.apache.sling.commons.osgi - 2.1.0 + 2.4.0 provided @@ -349,7 +349,7 @@ org.apache.sling org.apache.sling.testing.sling-mock.junit4 - 2.5.0 + 3.2.2 test diff --git a/src/main/java/org/apache/sling/scripting/sightly/engine/extension/i18n/I18nBasenameProvider.java b/src/main/java/org/apache/sling/scripting/sightly/engine/extension/i18n/I18nBasenameProvider.java new file mode 100644 index 00000000..8f652ad6 --- /dev/null +++ b/src/main/java/org/apache/sling/scripting/sightly/engine/extension/i18n/I18nBasenameProvider.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * 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.apache.sling.scripting.sightly.engine.extension.i18n; + +import org.apache.sling.scripting.sightly.render.RenderContext; +import org.jetbrains.annotations.Nullable; +import org.osgi.annotation.versioning.ConsumerType; + +@ConsumerType +public interface I18nBasenameProvider { + + /** + * Provides a way to define a basename that is not passed as parameter on the i18n runtime extension + * + * @param renderContext the renderContext passed initially to the HTL Script Engine + * @return the basename if one is found, {@code null} otherwise + */ + @Nullable String getBasename(RenderContext renderContext); + +} diff --git a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/I18nRuntimeExtension.java b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/I18nRuntimeExtension.java index 39a82367..4e654846 100644 --- a/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/I18nRuntimeExtension.java +++ b/src/main/java/org/apache/sling/scripting/sightly/impl/engine/extension/I18nRuntimeExtension.java @@ -31,10 +31,13 @@ import org.apache.sling.api.scripting.SlingScriptHelper; import org.apache.sling.i18n.ResourceBundleProvider; import org.apache.sling.scripting.sightly.extension.RuntimeExtension; +import org.apache.sling.scripting.sightly.engine.extension.i18n.I18nBasenameProvider; import org.apache.sling.scripting.sightly.impl.utils.BindingsUtils; import org.apache.sling.scripting.sightly.render.RenderContext; import org.apache.sling.scripting.sightly.render.RuntimeObjectModel; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,6 +51,9 @@ public class I18nRuntimeExtension implements RuntimeExtension { private static final Logger LOG = LoggerFactory.getLogger(I18nRuntimeExtension.class); + @Reference(cardinality = ReferenceCardinality.OPTIONAL) + private I18nBasenameProvider i18nBasenameProvider; + @Override public Object call(final RenderContext renderContext, Object... arguments) { ExtensionUtils.checkArgumentCount(RuntimeExtension.I18N, arguments, 2); @@ -56,11 +62,20 @@ public Object call(final RenderContext renderContext, Object... arguments) { Map options = (Map) arguments[1]; String locale = runtimeObjectModel.toString(options.get("locale")); String hint = runtimeObjectModel.toString(options.get("hint")); - String basename = runtimeObjectModel.toString(options.get("basename")); + String basename = getBasename(renderContext, options); final Bindings bindings = renderContext.getBindings(); return get(bindings, text, locale, basename, hint); } + private String getBasename(RenderContext renderContext, Map options) { + RuntimeObjectModel runtimeObjectModel = renderContext.getObjectModel(); + String basename = runtimeObjectModel.toString(options.get("basename")); + if (StringUtils.isEmpty(basename) && i18nBasenameProvider != null) { + return i18nBasenameProvider.getBasename(renderContext); + } + return basename; + } + private String get(final Bindings bindings, String text, String locale, String basename, String hint) { final SlingScriptHelper slingScriptHelper = BindingsUtils.getHelper(bindings); diff --git a/src/test/java/org/apache/sling/scripting/sightly/impl/engine/extension/I18nRuntimeExtensionTest.java b/src/test/java/org/apache/sling/scripting/sightly/impl/engine/extension/I18nRuntimeExtensionTest.java new file mode 100644 index 00000000..d175a99f --- /dev/null +++ b/src/test/java/org/apache/sling/scripting/sightly/impl/engine/extension/I18nRuntimeExtensionTest.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * 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.apache.sling.scripting.sightly.impl.engine.extension; + +import com.google.common.collect.ImmutableMap; +import org.apache.sling.api.scripting.SlingBindings; +import org.apache.sling.i18n.ResourceBundleProvider; +import org.apache.sling.scripting.sightly.SightlyException; +import org.apache.sling.scripting.sightly.engine.extension.i18n.I18nBasenameProvider; +import org.apache.sling.scripting.sightly.render.AbstractRuntimeObjectModel; +import org.apache.sling.scripting.sightly.render.RenderContext; +import org.apache.sling.scripting.sightly.render.RuntimeObjectModel; +import org.apache.sling.testing.mock.sling.MockResourceBundle; +import org.apache.sling.testing.mock.sling.junit.SlingContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import javax.script.Bindings; +import javax.script.SimpleBindings; +import java.util.Collections; + +import static org.junit.Assert.assertEquals; + +public class I18nRuntimeExtensionTest { + + @Rule + public final SlingContext slingContext = new SlingContext(); + + + public static final String BASENAME = "test-basename"; + public static final String KEY = "test-key"; + public static final String DEFAULT_VALUE = "test-value"; + public static final String BASENAME_VALUE = "test-basename-value"; + + private RenderContext renderContext; + private I18nRuntimeExtension subject; + + @Before + public void before() { + setupResourceBundles(); + renderContext = setupRenderContext(); + } + + @NotNull + private RenderContext setupRenderContext() { + return new RenderContext() { + @Override + public RuntimeObjectModel getObjectModel() { + return new AbstractRuntimeObjectModel() { + }; + } + + @Override + public Bindings getBindings() { + Bindings bindings = new SimpleBindings(); + bindings.put(SlingBindings.SLING, slingContext.slingScriptHelper()); + bindings.put(SlingBindings.REQUEST, slingContext.request()); + return bindings; + } + + @Override + public Object call(String s, Object... objects) { + return null; + } + }; + } + + private void setupResourceBundles() { + ResourceBundleProvider resourceBundleProvider = slingContext.getService(ResourceBundleProvider.class); + MockResourceBundle defaultResourceBundle = (MockResourceBundle) resourceBundleProvider.getResourceBundle(slingContext.request().getLocale()); + MockResourceBundle basenameResourceBundle = (MockResourceBundle) resourceBundleProvider.getResourceBundle(BASENAME, slingContext.request().getLocale()); + defaultResourceBundle.put(KEY, DEFAULT_VALUE); + basenameResourceBundle.put(KEY, BASENAME_VALUE); + } + + + @Test + public void testNonExistingKey() { + subject = slingContext.registerInjectActivateService(new I18nRuntimeExtension()); + String key = "non-existing"; + assertEquals("Key should not change when it does not exist", key, subject.call(renderContext, key, Collections.emptyMap())); + } + + @Test(expected = SightlyException.class) + public void testMissingArguments() { + subject = slingContext.registerInjectActivateService(new I18nRuntimeExtension()); + subject.call(renderContext, "fails"); + } + + @Test + public void testDefaultValue() { + subject = slingContext.registerInjectActivateService(new I18nRuntimeExtension()); + assertEquals(DEFAULT_VALUE, subject.call(renderContext, KEY, Collections.emptyMap())); + } + + @Test + public void testBasenameValue() { + subject = slingContext.registerInjectActivateService(new I18nRuntimeExtension()); + assertEquals(BASENAME_VALUE, subject.call(renderContext, KEY, ImmutableMap.of("basename", BASENAME))); + } + + @Test + public void testBasenameThroughProviderValue() { + slingContext.registerService(I18nBasenameProvider.class, new MockI18nBasenameProvider(BASENAME)); + subject = slingContext.registerInjectActivateService(new I18nRuntimeExtension()); + assertEquals(BASENAME_VALUE, subject.call(renderContext, KEY, Collections.emptyMap())); + } + + @Test + public void testEmptyBasenameThroughProviderValue() { + slingContext.registerService(I18nBasenameProvider.class, new MockI18nBasenameProvider(null)); + subject = slingContext.registerInjectActivateService(new I18nRuntimeExtension()); + assertEquals(DEFAULT_VALUE, subject.call(renderContext, KEY, Collections.emptyMap())); + } + + static class MockI18nBasenameProvider implements I18nBasenameProvider { + + private final String basename; + + public MockI18nBasenameProvider(String basename) { + this.basename = basename; + } + + @Override + public @Nullable String getBasename(RenderContext renderContext) { + return basename; + } + } + + +}