Skip to content

Commit

Permalink
Allow using @Translatable on objects
Browse files Browse the repository at this point in the history
Up until now, the `@Translatable` annotation
only worked on Strings and Enums.
To be able to also specify more complex objects as
translatable, the annotation is extended by an
optional `toStringMethod` which is used to retrieve
a string value to translate for those objects.
  • Loading branch information
AntonOellerer committed Oct 17, 2022
1 parent 48b78c0 commit 3e16d3e
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 1 deletion.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
}

group 'com.docutools'
version = '1.5.6-beta.1'
version = '1.5.7-alpha.1'

sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,10 @@

@Retention(RetentionPolicy.RUNTIME)
public @interface Translatable {
/**
* Specify the method which should be applied to retrieve a (translatable) string from the annotated object.
*
* @return The name of the method to use to retrieve the string which should be translated from objects
*/
String toStringMethod() default "toString";
}
19 changes: 19 additions & 0 deletions src/main/java/com/docutools/jocument/impl/ReflectionResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,8 @@ private Optional<PlaceholderData> resolveSimplePlaceholder(Object property, Stri
} else {
return Optional.of(new ScalarPlaceholderData<>(enumProperty));
}
} else if (isFieldAnnotatedWith(bean.getClass(), placeholderName, Translatable.class)) {
return getObjectTranslation(placeholderName, locale, options);
} else if (property instanceof Enum || property instanceof String || ReflectionUtils.isWrapperType(property.getClass())) {
return Optional.of(new ScalarPlaceholderData<>(property));
} else if (property instanceof Temporal temporal) {
Expand All @@ -404,6 +406,23 @@ private Optional<PlaceholderData> resolveSimplePlaceholder(Object property, Stri
}
}

private Optional<PlaceholderData> getObjectTranslation(String placeholderName, Locale locale, GenerationOptions options) {
var translatable = ReflectionUtils.findFieldAnnotation(bean.getClass(), placeholderName, Translatable.class);
if (translatable.isPresent()) {
try {
Optional<Object> beanProperty = getBeanProperty(placeholderName);
if (beanProperty.isPresent()) {
String toStringMethod = translatable.get().toStringMethod();
String propertyString = (String) beanProperty.get().getClass().getMethod(toStringMethod).invoke(beanProperty.get());
return Optional.of(new ScalarPlaceholderData<>(options.translate(propertyString, locale).orElse(propertyString)));
}
} catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) {
logger.error("Could not invoke custom 'toString' method", e);
}
}
return Optional.empty();
}

private Optional<Object> getBeanProperty(String placeholderName) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
var ignoreCasePlaceholder = placeholderName.toLowerCase();
if (SELF_REFERENCE.equals(placeholderName)) {
Expand Down
21 changes: 21 additions & 0 deletions src/test/java/com/docutools/jocument/ReflectionResolvingTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;

import com.docutools.jocument.impl.CustomPlaceholderRegistryImpl;
import com.docutools.jocument.impl.ReflectionResolver;
import com.docutools.jocument.sample.model.Person;
import com.docutools.jocument.sample.model.SampleModelData;
import com.docutools.jocument.sample.model.Ship;
import com.docutools.jocument.sample.model.Uniform;
Expand Down Expand Up @@ -253,4 +255,23 @@ void shouldResolveWrongMatchMethods(Object clazz)
// Assert
assertThat(position.isEmpty(), equalTo(true));
}

@Test
void shouldTranslateShipName() {
Person picardPerson = SampleModelData.PICARD_PERSON;
picardPerson.setFavouriteShip(SampleModelData.ENTERPRISE);
GenerationOptions generationOptions = new GenerationOptionsBuilder()
.withTranslation((s, locale) -> {
if (s.equals("USS Enterprise")) {
return Optional.of("VSS Unternehmung");
} else {
return Optional.empty();
}
}).build();
var resolver = new ReflectionResolver(picardPerson, new CustomPlaceholderRegistryImpl(), generationOptions);

var shipName = resolver.resolve("favouriteShip");

assertThat(shipName.get().toString(), equalTo("VSS Unternehmung"));
}
}
11 changes: 11 additions & 0 deletions src/test/java/com/docutools/jocument/sample/model/Person.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.docutools.jocument.sample.model;

import com.docutools.jocument.annotations.Format;
import com.docutools.jocument.annotations.Translatable;
import java.time.Instant;
import java.time.LocalDate;
import java.time.Period;
Expand All @@ -13,6 +14,8 @@ public class Person {
@Format(value = "dd.MM.yyyy")
private final LocalDate birthDate;
private final Instant entryDate;
@Translatable(toStringMethod = "shipName")
private Ship favouriteShip;

public Person(String firstName, String lastName, LocalDate birthDate) {
this.firstName = firstName;
Expand Down Expand Up @@ -44,4 +47,12 @@ public int getAge() {
public Instant getEntryDate() {
return entryDate;
}

public void setFavouriteShip(Ship favouriteShip) {
this.favouriteShip = favouriteShip;
}

public Ship getFavouriteShip() {
return favouriteShip;
}
}
3 changes: 3 additions & 0 deletions src/test/java/com/docutools/jocument/sample/model/Ship.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,7 @@ public Optional<String> getNumberOfServices(String placeholder) {
return Optional.of(String.valueOf(services.size()));
}

public String shipName() {
return this.name;
}
}

0 comments on commit 3e16d3e

Please sign in to comment.