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;
}
});
}
}
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('/', '.');
}
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());
}
Aggregations