How to apply same process for differents objects with different method name but same returned type

Viewed 67

Here is my problem, I have 2 classes in this example (but almost 10 IRL) auto generated from an xsd

public Toto {
    public Toto(String date, String valueFromToto) {
        this.date = date;
        this.valueFromToto = valueFromToto;
    }
    public String getDate() {
        return this.date;
    }

    public String getValueFromToto() {
        return this.valueFromToto;
    }
}

public Titi {
    public Titi(String date, String valueFromTiti) {
        this.date = date;
        this.valueFromTiti = valueFromTiti;
    }
    public String getDate() {
        return this.date;
    }

    public String getValueFromTiti() {
        return this.valueFromTiti ;
    }
}

And I'd like to have the same process for each classes But I don't know the best way to do this. I was looking about "reflection" but that seams to be a bad solution. How about functionnal interface ?

    public String test(List<Toto or Titi> liste, LocalDate date) {

        for (Toto or Titi o : liste) {

            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("ddMMyyyy");
            LocalDate localDateToTest = LocalDate.parse(o.getDate(), formatter);
            if (date.isAfter(localDateToTest))
                return o.getValueFromToto() or o.getValueFromTiti();
        }
        return "";
    }

If someone can point me in the right direction Thanks in advance

2 Answers

JAXB2 Bindings can be used to customize the code generation so that both methods have the same name. And JAXB2 Basics Inheritance plugins (https://github.com/highsource/jaxb2-basics/wiki/JAXB2-Inheritance-Plugin) can be used to make them both inherit an interface.

Basically you would provide a binding file (*.xjb) like this one (it's just an example, not tested):

<?xml version="1.0"?>
<jxb:bindings
    xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:inheritance="http://jaxb2-commons.dev.java.net/basic/inheritance"
    jxb:extensionBindingPrefixes="inheritance"
    jxb:version="2.1">
    <jxb:bindings schemaLocation="MyXSD.xsd">
        <jxb:bindings node="//xs:complexType[@name='Toto']//xs:element[@name='valueFromToto']">
            <jxb:property name="value"/>
        </jxb:bindings>
        <jxb:bindings node="//xs:complexType[@name='Toto']">
            <jxb:class name="Toto" />
            <inheritance:implements>org.example.WithValue</inheritance:implements>
        </jxb:bindings>
        <jxb:bindings node="//xs:complexType[@name='Titi']//xs:element[@name='valueFromTiti']">
            <jxb:property name="value"/>
        </jxb:bindings>
        <jxb:bindings node="//xs:complexType[@name='Titi']">
            <jxb:class name="Toto" />
            <inheritance:implements>org.example.WithValue</inheritance:implements>
        </jxb:bindings>
    </jxb:bindings>
</jxb:bindings>

org.example.WithValue would look like that:

package org.example;

public interface WithValue {
   String getValue();
}

If you are using Maven to generate the code from the XSD, the plugin configuration would look like that (where both the XSD and the binding file are in the /src/main/resources/xsd/ directory):

<plugin>
    <groupId>org.jvnet.jaxb2.maven2</groupId>
    <artifactId>maven-jaxb2-plugin</artifactId>
    <version>${maven-jaxb2-plugin.version}</version>
    <executions>
        <execution>
            <id>generate</id>
            <goals>
                <goal>generate</goal>
            </goals>
            <phase>generate-sources</phase>
            <configuration>
                <bindingDirectory>${project.basedir}/src/main/resources/xsd/</bindingDirectory>
                <schemaDirectory>${project.basedir}/src/main/resources/xsd/</schemaDirectory>
                <args>
                    <arg>-Xinheritance</arg>
                </args>
                <plugins>
                    <plugin>
                        <groupId>org.jvnet.jaxb2_commons</groupId>
                        <artifactId>jaxb2-basics</artifactId>
                        <version>${jaxb2-basics.version}</version>
                    </plugin>
                </plugins>
            </configuration>
        </execution>
    </executions>
</plugin>

You can have a generic method that uses a Predicate to filter and a Function to map the value while returning the Optional of mapped value.

public <T, R> Optional<R> firstMatchingValue(List<T> inputList, Predicate<T> filteringLogic,
                                             Function<T, R> valueMapper) {
    return inputList.stream()
            .filter(filteringLogic)
            .findFirst()
            .map(valueMapper);
}

At the caller, you can make use of this for example with Titi model in a way as follows:

LocalDate date = LocalDate.now();
List<Titi> typeList = List.of(new Titi("", "")); // fill in the list
Predicate<Titi> filter = o -> date.isAfter(LocalDate.parse(o.getValueFromTiti()));
String test = firstMatchingValue(typeList, filter, Titi::getValueFromTiti).orElse("");
Related