Search in sources :

Example 1 with Resolver

use of io.quarkus.qute.Resolver in project quarkus by quarkusio.

the class MessageBundles method setupNamespaceResolvers.

static void setupNamespaceResolvers(@Observes EngineBuilder builder, BundleContext context) {
    // Avoid injecting "Instance<Object> instance" which prevents unused beans removal
    ArcContainer container = Arc.container();
    // For every bundle register a new resolver
    for (Entry<String, Map<String, Class<?>>> entry : context.getBundleInterfaces().entrySet()) {
        final String bundle = entry.getKey();
        final Map<String, Resolver> interfaces = new HashMap<>();
        Resolver resolver = null;
        for (Entry<String, Class<?>> locEntry : entry.getValue().entrySet()) {
            if (locEntry.getKey().equals(DEFAULT_LOCALE)) {
                resolver = (Resolver) container.select(locEntry.getValue(), Default.Literal.INSTANCE).get();
                continue;
            }
            Instance<?> found = container.select(locEntry.getValue(), new Localized.Literal(locEntry.getKey()));
            if (found.isUnsatisfied()) {
                throw new IllegalStateException(Qute.fmt("Bean not found for localized interface [{e.value}] and locale [{e.key}]").data("e", locEntry).render());
            }
            if (found.isAmbiguous()) {
                throw new IllegalStateException(Qute.fmt("Multiple beans found for localized interface [{e.value}] and locale [{e.key}]").data("e", locEntry).render());
            }
            interfaces.put(locEntry.getKey(), (Resolver) found.get());
        }
        final Resolver defaultResolver = resolver;
        builder.addNamespaceResolver(new NamespaceResolver() {

            @Override
            public CompletionStage<Object> resolve(EvalContext context) {
                Object locale = context.getAttribute(ATTRIBUTE_LOCALE);
                if (locale == null) {
                    Object selectedVariant = context.getAttribute(TemplateInstance.SELECTED_VARIANT);
                    if (selectedVariant != null) {
                        locale = ((Variant) selectedVariant).getLocale();
                    }
                    if (locale == null) {
                        return defaultResolver.resolve(context);
                    }
                }
                // First try the exact match
                Resolver localeResolver = interfaces.get(locale instanceof Locale ? ((Locale) locale).toLanguageTag() : locale.toString());
                if (localeResolver == null && locale instanceof Locale) {
                    // Next try the language
                    localeResolver = interfaces.get(((Locale) locale).getLanguage());
                }
                return localeResolver != null ? localeResolver.resolve(context) : defaultResolver.resolve(context);
            }

            @Override
            public String getNamespace() {
                return bundle;
            }
        });
    }
}
Also used : Locale(java.util.Locale) ArcContainer(io.quarkus.arc.ArcContainer) Resolver(io.quarkus.qute.Resolver) NamespaceResolver(io.quarkus.qute.NamespaceResolver) HashMap(java.util.HashMap) EvalContext(io.quarkus.qute.EvalContext) Variant(io.quarkus.qute.Variant) NamespaceResolver(io.quarkus.qute.NamespaceResolver) HashMap(java.util.HashMap) Map(java.util.Map) CompletionStage(java.util.concurrent.CompletionStage)

Example 2 with Resolver

use of io.quarkus.qute.Resolver in project quarkus by quarkusio.

the class MessageBundleProcessor method generateImplementation.

private String generateImplementation(ClassInfo defaultBundleInterface, String defaultBundleImpl, ClassInfoWrapper bundleInterfaceWrapper, ClassOutput classOutput, BuildProducer<MessageBundleMethodBuildItem> messageTemplateMethods, Map<String, String> messageTemplates, String locale) {
    ClassInfo bundleInterface = bundleInterfaceWrapper.getClassInfo();
    LOGGER.debugf("Generate bundle implementation for %s", bundleInterface);
    AnnotationInstance bundleAnnotation = defaultBundleInterface != null ? defaultBundleInterface.classAnnotation(Names.BUNDLE) : bundleInterface.classAnnotation(Names.BUNDLE);
    AnnotationValue nameValue = bundleAnnotation.value();
    String bundleName = nameValue != null ? nameValue.asString() : MessageBundle.DEFAULT_NAME;
    AnnotationValue defaultKeyValue = bundleAnnotation.value(BUNDLE_DEFAULT_KEY);
    String baseName;
    if (bundleInterface.enclosingClass() != null) {
        baseName = DotNames.simpleName(bundleInterface.enclosingClass()) + ValueResolverGenerator.NESTED_SEPARATOR + DotNames.simpleName(bundleInterface);
    } else {
        baseName = DotNames.simpleName(bundleInterface);
    }
    if (locale != null) {
        baseName = baseName + "_" + locale;
    }
    String targetPackage = DotNames.internalPackageNameWithTrailingSlash(bundleInterface.name());
    String generatedName = targetPackage + baseName + SUFFIX;
    // MyMessages_Bundle implements MyMessages, Resolver
    Builder builder = ClassCreator.builder().classOutput(classOutput).className(generatedName).interfaces(bundleInterface.name().toString(), Resolver.class.getName());
    if (defaultBundleImpl != null) {
        builder.superClass(defaultBundleImpl);
    }
    ClassCreator bundleCreator = builder.build();
    // key -> method
    Map<String, MethodInfo> keyMap = new LinkedHashMap<>();
    List<MethodInfo> methods = new ArrayList<>(bundleInterfaceWrapper.methods());
    // Sort methods
    methods.sort(Comparator.comparing(MethodInfo::name).thenComparing(Comparator.comparing(MethodInfo::toString)));
    for (MethodInfo method : methods) {
        if (!method.returnType().name().equals(DotNames.STRING)) {
            throw new MessageBundleException(String.format("A message bundle interface method must return java.lang.String on %s: %s", bundleInterface, method));
        }
        LOGGER.debugf("Found message bundle method %s on %s", method, bundleInterface);
        MethodCreator bundleMethod = bundleCreator.getMethodCreator(MethodDescriptor.of(method));
        AnnotationInstance messageAnnotation;
        if (defaultBundleInterface != null) {
            MethodInfo defaultBundleMethod = bundleInterfaceWrapper.method(method.name(), method.parameters().toArray(new Type[] {}));
            if (defaultBundleMethod == null) {
                throw new MessageBundleException(String.format("Default bundle method not found on %s: %s", bundleInterface, method));
            }
            messageAnnotation = defaultBundleMethod.annotation(Names.MESSAGE);
        } else {
            messageAnnotation = method.annotation(Names.MESSAGE);
        }
        if (messageAnnotation == null) {
            throw new MessageBundleException("A message bundle interface method must be annotated with @Message: " + bundleInterface.name() + "#" + method.name());
        }
        String key = getKey(method, messageAnnotation, defaultKeyValue);
        if (key.equals(MESSAGE)) {
            throw new MessageBundleException(String.format("A message bundle interface method must not use the key 'message' which is reserved for dynamic lookup; defined for %s#%s()", bundleInterface, method.name()));
        }
        if (keyMap.containsKey(key)) {
            throw new MessageBundleException(String.format("Duplicate key [%s] found on %s", key, bundleInterface));
        }
        keyMap.put(key, method);
        String messageTemplate = messageTemplates.get(method.name());
        if (messageTemplate == null) {
            messageTemplate = getMessageAnnotationValue(messageAnnotation);
        }
        if (messageTemplate == null && defaultBundleInterface != null) {
            // method is annotated with @Message without value() -> fallback to default locale
            messageTemplate = getMessageAnnotationValue((defaultBundleInterface.method(method.name(), method.parameters().toArray(new Type[] {}))).annotation(Names.MESSAGE));
        }
        if (messageTemplate == null) {
            throw new MessageBundleException(String.format("Message template for key [%s] is missing for default locale", key));
        }
        String templateId = null;
        if (messageTemplate.contains("}")) {
            if (defaultBundleInterface != null) {
                if (locale == null) {
                    AnnotationInstance localizedAnnotation = bundleInterface.classAnnotation(Names.LOCALIZED);
                    locale = localizedAnnotation.value().asString();
                }
                templateId = bundleName + "_" + locale + "_" + key;
            } else {
                templateId = bundleName + "_" + key;
            }
        }
        MessageBundleMethodBuildItem messageBundleMethod = new MessageBundleMethodBuildItem(bundleName, key, templateId, method, messageTemplate, defaultBundleInterface == null);
        messageTemplateMethods.produce(messageBundleMethod);
        if (!messageBundleMethod.isValidatable()) {
            // No expression/tag - no need to use qute
            bundleMethod.returnValue(bundleMethod.load(messageTemplate));
        } else {
            // Obtain the template, e.g. msg_hello_name
            ResultHandle template = bundleMethod.invokeStaticMethod(io.quarkus.qute.deployment.Descriptors.BUNDLES_GET_TEMPLATE, bundleMethod.load(templateId));
            // Create a template instance
            ResultHandle templateInstance = bundleMethod.invokeInterfaceMethod(io.quarkus.qute.deployment.Descriptors.TEMPLATE_INSTANCE, template);
            List<Type> paramTypes = method.parameters();
            if (!paramTypes.isEmpty()) {
                // Set data
                int i = 0;
                Iterator<Type> it = paramTypes.iterator();
                while (it.hasNext()) {
                    String name = getParameterName(method, i);
                    bundleMethod.invokeInterfaceMethod(io.quarkus.qute.deployment.Descriptors.TEMPLATE_INSTANCE_DATA, templateInstance, bundleMethod.load(name), bundleMethod.getMethodParam(i));
                    i++;
                    it.next();
                }
            }
            // Render the template
            // At this point it's already validated that the method returns String
            bundleMethod.returnValue(bundleMethod.invokeInterfaceMethod(io.quarkus.qute.deployment.Descriptors.TEMPLATE_INSTANCE_RENDER, templateInstance));
        }
    }
    implementResolve(defaultBundleImpl, bundleCreator, keyMap);
    bundleCreator.close();
    return generatedName.replace('/', '.');
}
Also used : Resolver(io.quarkus.qute.Resolver) Builder(io.quarkus.gizmo.ClassCreator.Builder) ArrayList(java.util.ArrayList) ClassCreator(io.quarkus.gizmo.ClassCreator) LinkedHashMap(java.util.LinkedHashMap) Type(org.jboss.jandex.Type) MethodCreator(io.quarkus.gizmo.MethodCreator) AnnotationValue(org.jboss.jandex.AnnotationValue) MethodInfo(org.jboss.jandex.MethodInfo) ResultHandle(io.quarkus.gizmo.ResultHandle) AssignableResultHandle(io.quarkus.gizmo.AssignableResultHandle) AnnotationInstance(org.jboss.jandex.AnnotationInstance) ClassInfo(org.jboss.jandex.ClassInfo)

Example 3 with Resolver

use of io.quarkus.qute.Resolver in project quarkus by quarkusio.

the class SimpleGeneratorTest method testGenerator.

@Test
public void testGenerator() throws Exception {
    Class<?> clazz = SimpleGeneratorTest.class.getClassLoader().loadClass("io.quarkus.qute.generator.MyService_ValueResolver");
    ValueResolver resolver = (ValueResolver) clazz.getDeclaredConstructor().newInstance();
    assertEquals("Foo", resolver.resolve(new TestEvalContext(new MyService(), "getName", null)).toCompletableFuture().get(1, TimeUnit.SECONDS).toString());
    assertEquals("[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]", resolver.resolve(new TestEvalContext(new MyService(), "getList", e -> "foo".equals(e.getParts().get(0).getName()) ? CompletableFuture.completedFuture("foo") : CompletableFuture.completedFuture(Integer.valueOf(10)), "1", "foo")).toCompletableFuture().get(1, TimeUnit.SECONDS).toString());
    assertEquals("oof", resolver.resolve(new TestEvalContext(new MyService(), "getTestName", null)).toCompletableFuture().get(1, TimeUnit.SECONDS).toString());
    assertEquals("Emma", resolver.resolve(new TestEvalContext(new MyService(), "getAnotherTestName", v -> CompletableFuture.completedFuture(v.getParts().get(0).getName()), "Emma")).toCompletableFuture().get(1, TimeUnit.SECONDS).toString());
    assertEquals("NOT_FOUND", resolver.resolve(new TestEvalContext(new MyService(), "surname", null)).toCompletableFuture().get(1, TimeUnit.SECONDS).toString());
}
Also used : Assertions.fail(org.junit.jupiter.api.Assertions.fail) DotName(org.jboss.jandex.DotName) Type(org.jboss.jandex.Type) CompletableFuture(java.util.concurrent.CompletableFuture) ClassInfo(org.jboss.jandex.ClassInfo) Resolver(io.quarkus.qute.Resolver) HashSet(java.util.HashSet) Indexer(org.jboss.jandex.Indexer) MethodInfo(org.jboss.jandex.MethodInfo) BeforeAll(org.junit.jupiter.api.BeforeAll) Assertions.assertEquals(org.junit.jupiter.api.Assertions.assertEquals) Index(org.jboss.jandex.Index) Kind(org.jboss.jandex.Type.Kind) PrimitiveType(org.jboss.jandex.PrimitiveType) Set(java.util.Set) IOException(java.io.IOException) InvocationTargetException(java.lang.reflect.InvocationTargetException) TimeUnit(java.util.concurrent.TimeUnit) Test(org.junit.jupiter.api.Test) EngineBuilder(io.quarkus.qute.EngineBuilder) ValueResolver(io.quarkus.qute.ValueResolver) TestEvalContext(io.quarkus.qute.TestEvalContext) List(java.util.List) CompletionStage(java.util.concurrent.CompletionStage) Engine(io.quarkus.qute.Engine) NamespaceResolver(io.quarkus.qute.NamespaceResolver) InputStream(java.io.InputStream) TestEvalContext(io.quarkus.qute.TestEvalContext) ValueResolver(io.quarkus.qute.ValueResolver) Test(org.junit.jupiter.api.Test)

Aggregations

Resolver (io.quarkus.qute.Resolver)3 NamespaceResolver (io.quarkus.qute.NamespaceResolver)2 CompletionStage (java.util.concurrent.CompletionStage)2 ClassInfo (org.jboss.jandex.ClassInfo)2 MethodInfo (org.jboss.jandex.MethodInfo)2 Type (org.jboss.jandex.Type)2 ArcContainer (io.quarkus.arc.ArcContainer)1 AssignableResultHandle (io.quarkus.gizmo.AssignableResultHandle)1 ClassCreator (io.quarkus.gizmo.ClassCreator)1 Builder (io.quarkus.gizmo.ClassCreator.Builder)1 MethodCreator (io.quarkus.gizmo.MethodCreator)1 ResultHandle (io.quarkus.gizmo.ResultHandle)1 Engine (io.quarkus.qute.Engine)1 EngineBuilder (io.quarkus.qute.EngineBuilder)1 EvalContext (io.quarkus.qute.EvalContext)1 TestEvalContext (io.quarkus.qute.TestEvalContext)1 ValueResolver (io.quarkus.qute.ValueResolver)1 Variant (io.quarkus.qute.Variant)1 IOException (java.io.IOException)1 InputStream (java.io.InputStream)1