code: //package it.unibz.inf.ontop.protege.utils;
  //
  //import java.lang.reflect.Constructor;
  //import java.lang.reflect.Executable;
  //import java.lang.reflect.InvocationHandler;
  //import java.lang.reflect.InvocationTargetException;
  //import java.lang.reflect.Method;
  //import java.lang.reflect.Proxy;
  //import java.util.Arrays;
  //import java.util.Collection;
  //import java.util.Map;
  //import java.util.Map.Entry;
  //import java.util.function.BiFunction;
  //import java.util.stream.Collectors;
  //
  //import javax.annotation.Nullable;
  //
  //import com.google.common.base.Function;
  //import com.google.common.base.Functions;
  //import com.google.common.base.Joiner;
  //import com.google.common.base.Optional;
  //import com.google.common.base.Preconditions;
  //import com.google.common.base.Predicate;
  //import com.google.common.base.Throwables;
  //import com.google.common.collect.LinkedListMultimap;
  //import com.google.common.collect.ListMultimap;
  //import com.google.common.collect.Maps;
  //import com.google.common.collect.Multimap;
  //
  //import org.semanticweb.owlapi.model.IRI;
  //import org.semanticweb.owlapi.model.OWLAnnotationValue;
  //import org.semanticweb.owlapi.model.OWLDataFactory;
  //import org.semanticweb.owlapi.model.OWLIndividual;
  //import org.semanticweb.owlapi.model.OWLOntology;
  //import org.semanticweb.owlapi.model.OWLOntologyID;
  //import org.semanticweb.owlapi.search.EntitySearcher;
  //import org.semanticweb.owlapi.util.OWLObjectTransformer;
  //import org.slf4j.Logger;
  //import org.slf4j.LoggerFactory;
  //
  ///**
  // * Adapter class for calling OWL API methods involving Guava arguments and/or results.
  // * <p>
  // * Ontop and Protégé use different versions of Guava (resp. v30+ and v18), which cause clashes in
  // * the Ontop Protégé plugin where those versions coexist, one version provided by Protégé bundle
  // * and the other (newer) version by the Ontop Protégé plugin bundle, through the use of different
  // * OSGI {@link ClassLoader}s. As the Ontop Protégé plugin bundle is configured to use Ontop's
  // * Guava version (v30+), directly calling from the Ontop Protégé plugin any OWL API method that
  // * has a Guava class as argument or result will produce a runtime {@link LinkageError}, because
  // * the Guava classes seen by the compiler of the Ontop Protégé plugin are different from the ones
  // * coming from the Protégé bundle and seen by the JVM at runtime.
  // * </p>
  // * <p>
  // * The Guava version clash problem is overcome by calling OWL API methods involving Guava classes
  // * via reflection using this adapter class, which takes care of performing the necessary
  // * conversions between method arguments/results of mismatching Guava classes (same class name,
  // * different ClassLoaders). This adapter class defines methods for wrapping only the OWL API
  // * methods needed by the Ontop Protégé plugin. There are other unused OWL API methods that need
  // * adaptations, and they can be handled here by easily extending this adapter class. The following
  // * table reports a comprehensive list of OWL API methods requiring adaptation, along with the
  // * corresponding wrapper method definitions to be added to this adapter class (for the methods
  // * listed below, no further code change is needed besides adding the corresponding method
  // * declaration in the interface).
  // * </p>
  // * <table>
  // * <tr>
  // * <th>OWL API method requiring adaptation</th>
  // * <th>Corresponding adapter method to be defined</th>
  // * </tr>
  // * <tr>
  // * <td>{@link IRI#getRemainder()}</td>
  // * <td>{@code Optional<String> getRemainder(IRI self)}</td>
  // * </tr>
  // * <tr>
  // * <td>{@link OWLAnnotationValue#asIRI()}</td>
  // * <td>{@code Optional<IRI> asIRI(OWLAnnotationValue self)}</td>
  // * </tr>
  // * <tr>
  // * <td>{@link OWLAnnotationValue#asLiteral()}</td>
  // * <td>{@code Optional<OWLLiteral> asLiteral(OWLAnnotationValue self)}</td>
  // * </tr>
  // * <tr>
  // * <td>{@link OWLAnnotationValue#asAnonymousIndividual()}</td>
  // * <td>{@code Optional<OWLAnonymousIndividual> asAnonymousIndividual(OWLAnnotationValue self)}</td>
  // * </tr>
  // * <tr>
  // * <td>{@link OWLOntologyID#getOntologyIRI()}</td>
  // * <td>{@code Optional<IRI> getOntologyIRI(final OWLOntologyID self)}</td>
  // * </tr>
  // * <tr>
  // * <td>{@link OWLOntologyID#getVersionIRI()}</td>
  // * <td>{@code Optional<IRI> getVersionIRI(OWLOntologyID self)}</td>
  // * </tr>
  // * <tr>
  // * <td>{@link OWLOntologyID#getDefaultDocumentIRI()}</td>
  // * <td>{@code Optional<IRI> getDefaultDocumentIRI(OWLOntologyID self)}</td>
  // * </tr>
  // * <tr>
  // * <td>{@link EntitySearcher#getDataPropertyValues(OWLIndividual, OWLOntology)}</td>
  // * <td>{@code Multimap<OWLDataPropertyExpression, OWLLiteral> getDataPropertyValues(OWLIndividual i,
  // * OWLOntology ontology)}</td>
  // * </tr>
  // * <tr>
  // * <td>{@link EntitySearcher#getDataPropertyValues(OWLIndividual, Iterable)}</td>
  // * <td>{@code Multimap<OWLDataPropertyExpression, OWLLiteral> getDataPropertyValues(OWLIndividual i,
  // * Iterable<OWLOntology> ontologies)}</td>
  // * </tr>
  // * <tr>
  // * <td>{@link EntitySearcher#getObjectPropertyValues(OWLIndividual, OWLOntology)}</td>
  // * <td>{@code Multimap<OWLObjectPropertyExpression, OWLIndividual> getObjectPropertyValues(OWLIndividual
  // * i, OWLOntology ontology)}</td>
  // * </tr>
  // * <tr>
  // * <td>{@link EntitySearcher#getObjectPropertyValues(OWLIndividual, Iterable)}</td>
  // * <td>{@code Multimap<OWLObjectPropertyExpression, OWLIndividual> getObjectPropertyValues(OWLIndividual
  // * i, Iterable<OWLOntology> ontologies)}</td>
  // * </tr>
  // * <tr>
  // * <td>{@link EntitySearcher#getNegativeDataPropertyValues(OWLIndividual, OWLOntology)}</td>
  // * <td>{@code Multimap<OWLDataPropertyExpression, OWLLiteral> getNegativeDataPropertyValues(OWLIndividual
  // * i, OWLOntology ontology)}</td>
  // * </tr>
  // * <tr>
  // * <td>{@link EntitySearcher#getNegativeDataPropertyValues(OWLIndividual, Iterable)}</td>
  // * <td>{@code Multimap<OWLDataPropertyExpression, OWLLiteral> getNegativeDataPropertyValues(OWLIndividual
  // * i, Iterable<OWLOntology> ontologies)}</td>
  // * </tr>
  // * <tr>
  // * <td>{@link EntitySearcher#getNegativeObjectPropertyValues(OWLIndividual, OWLOntology)}</td>
  // * <td>{@code Multimap<OWLObjectPropertyExpression, OWLIndividual>
  // * getNegativeObjectPropertyValues(OWLIndividual i, OWLOntology ontology)}</td>
  // * </tr>
  // * <tr>
  // * <td>{@link EntitySearcher#getNegativeObjectPropertyValues(OWLIndividual, Iterable)}</td>
  // * <td>{@code Multimap<OWLObjectPropertyExpression, OWLIndividual>
  // * getNegativeObjectPropertyValues(OWLIndividual i, Iterable<OWLOntology> ontologies)}</td>
  // * </tr>
  // * <tr>
  // * <td>{@link OWLObjectTransformer#OWLObjectTransformer(Predicate, Function, OWLDataFactory, Class)}</td>
  // * <td>{@code <T> OWLObjectTransformer<T> newOWLObjectTransformer(Predicate<Object> predicate,
  // * Function<T, T> transformer, OWLDataFactory df, Class<T> witness)}</td>
  // * </tr>
  // * </table>
  // * <p>
  // * <b>Implementation note</b>: each adapter class method is mapped to a target executable member
  // * (method or constructor), which is located as follows:
  // * <ol>
  // * <li>first, a target instance method is searched by treating the first argument of the adapter
  // * method as the {@code self} object on which to call the target instance method; other arguments
  // * and result type must match in number and class names (as ClassLoaders may be different);
  // * <li>on failure, a target static method is searched in a selected list of target classes
  // * hard-coded in this adapter class; also in this case, arguments and result type must match in
  // * number and class names;
  // * <li>on failure, a target constructor is searched in the class corresponding to the result type
  // * of the adapter method; arguments must match in number and type;
  // * <li>on failure, the initialization of the adapter class fails.
  // * </ol>
  // * </p>
  // */
  //public interface OWLAPIAdapter {
  //
  //    /**
  //     * Adapter for {@link OWLOntologyID#getOntologyIRI()}.
  //     */
  //    Optional<IRI> getOntologyIRI(final OWLOntologyID self);
  //
  //    // EXTENSION POINT: add here other adapter methods for invoking target instance/static methods
  //    // or constructors whose arguments or result classes may use different ClassLoaders than the
  //    // one of the adapter. For each new method, add an entry to Map 'targets' below and (if
  //    // needed) implement necessary argument/result conversion logic in method 'newConverter'
  //
  //    /**
  //     * Default implementation of adapter interface, based on Java {@link Proxy} mechanism.
  //     */
  //    final OWLAPIAdapter INSTANCE = (OWLAPIAdapter) Proxy.newProxyInstance( //
  //            OWLAPIAdapter.class.getClassLoader(), //
  //            new Class<?>[] { OWLAPIAdapter.class }, //
  //            new InvocationHandler() {
  //
  //                /**
  //                 * Logger object, mainly useful for debugging.
  //                 */
  //                private final Logger logger = LoggerFactory.getLogger(OWLAPIAdapter.class);
  //
  //                /**
  //                 * Method handlers, created eagerly.
  //                 */
  //                private final Map<Method, BiFunction<Object, Object[], Object>> handlers = Arrays
  //                        .asList(OWLAPIAdapter.class.getDeclaredMethods()).stream()
  //                        .collect(Collectors.toMap(m -> m, m -> {
  //
  //                            // Try mapping to an instance method (defined by arg[0] class)
  //                            Executable target = null;
  //                            final Class<?>[] args = m.getParameterTypes();
  //                            if (args.length > 0) {
  //                                target = resolve(args[0], m.getName(),
  //                                        Arrays.copyOfRange(args, 1, args.length));
  //                            }
  //
  //                            // EXTENSION POINT: add here the classes where to look for target
  //                            // static methods
  //                            final Class<?>[] classes = new Class<?>[] { EntitySearcher.class };
  //
  //                            // Try mapping to a static method (defined by one of targetClasses)
  //                            for (int i = 0; target == null && i < classes.length; ++i) {
  //                                target = resolve(classes[i], m.getName(), args);
  //                            }
  //
  //                            // Try mapping to a constructor (defined by result class)
  //                            if (target == null) {
  //                                target = resolve(m.getReturnType(), null, args);
  //                            }
  //
  //                            // Check mapping was successful
  //                            Preconditions.checkArgument(target != null, "Could not map %s", m);
  //
  //                            // Create handler mapping source method to target executable
  //                            return newHandler(m, target);
  //                        }));
  //
  //                /**
  //                 * {@inheritDoc} Implements the call to an adapter interface method by delegating
  //                 * to the corresponding target method, implementing the necessary argument /
  //                 * result conversions.
  //                 */
  //                @Override
  //                public Object invoke(final Object proxy, final Method method, final Object[] args)
  //                        throws Throwable {
  //
  //                    // Delegate to handler associated to invoked method
  //                    return this.handlers.get(method).apply(null, args);
  //                }
  //
  //                /**
  //                 * Resolves the public executable member (method or constructor) exposed by the
  //                 * specified class and matching the specified name (null if a constructor) and
  //                 * argument classes. Note that argument classes are matched by name, ignoring the
  //                 * ClassLoader defining them.
  //                 *
  //                 * @param clazz
  //                 *            the class exposing the executable member
  //                 * @param name
  //                 *            the name of the executable member, in case it is a method
  //                 * @param argClasses
  //                 *            the classes of the arguments accepted by the executable member
  //                 * @return the resolved executable member, or null if no match was found
  //                 */
  //                private Executable resolve(final Class<?> clazz, @Nullable final String name,
  //                        final Class<?>... argClasses) {
  //
  //                    // Check all public constructors or methods
  //                    outer: for (final Executable e : name == null ? clazz.getConstructors()
  //                            : clazz.getMethods()) {
  //
  //                        // Skip candidate member if argument count does not match
  //                        if (e.getParameterCount() != argClasses.length) {
  //                            continue;
  //                        }
  //
  //                        // Skip candidate member if name does not match
  //                        if (name != null && !name.equals(e.getName())) {
  //                            continue;
  //                        }
  //
  //                        // Skip candidate member if one argument class does not match (name wise)
  //                        for (int i = 0; i < argClasses.length; ++i) {
  //                            if (!e.getParameterTypes()[i].getName()
  //                                    .equals(argClasses[i].getName())) {
  //                                continue outer;
  //                            }
  //                        }
  //
  //                        // Otherwise, return resolved executable member
  //                        return e;
  //                    }
  //
  //                    // No matching member found
  //                    return null;
  //                }
  //
  //                /**
  //                 * Calls the executable member (a method or constructor) on the {@code self}
  //                 * object specified (null if not needed) using the arguments supplied.
  //                 *
  //                 * @param executable
  //                 *            the executable to call
  //                 * @param self
  //                 *            the object on which to call the executable, in case it is an
  //                 *            instance method
  //                 * @param args
  //                 *            the arguments to supply to the executable
  //                 * @return the result of the call
  //                 */
  //                private Object call(final Executable executable, @Nullable final Object self,
  //                        final Object... args) {
  //
  //                    try {
  //                        // Log executable call
  //                        if (this.logger.isTraceEnabled()) {
  //                            final StringBuilder sb = new StringBuilder();
  //                            sb.append("Calling ").append(executable);
  //                            if (self != null) {
  //                                sb.append(" on ").append(self.getClass().getName()).append('@')
  //                                        .append(System.identityHashCode(self));
  //                            }
  //                            sb.append(" with ").append(args.length).append(" arguments (");
  //                            Joiner.on(", ").appendTo(sb, args);
  //                            sb.append(")");
  //                            this.logger.trace(sb.toString());
  //                        }
  //
  //                        // Handle constructors and methods calls separately
  //                        if (executable instanceof Constructor<?>) {
  //                            return ((Constructor<?>) executable).newInstance(args);
  //                        } else {
  //                            return ((Method) executable).invoke(self, args);
  //                        }
  //
  //                    } catch (final InvocationTargetException ex) {
  //                        // Unwrap the exception and propagate as runtime exception
  //                        Throwables.throwIfUnchecked(ex.getCause());
  //                        throw new RuntimeException(ex.getCause());
  //
  //                    } catch (IllegalAccessException | InstantiationException ex) {
  //                        // Should not happen
  //                        throw new Error(ex);
  //                    }
  //                }
  //
  //                /**
  //                 * Defines a 'handler' function that implements the supplied {@code source} method
  //                 * in terms of calling the supplied {@code target} executable member (method or
  //                 * constructor), performing the necessary argument and result conversions among
  //                 * classes having the same names but different ClassLoaders.
  //                 *
  //                 * @param source
  //                 *            the source method to implement in the handler
  //                 * @param target
  //                 *            the target executable (method or constructor) to call as part of the
  //                 *            source implementation
  //                 * @return the handler function
  //                 */
  //                private BiFunction<Object, Object[], Object> newHandler(final Method source,
  //                        final Executable target) {
  //
  //                    // Log handler creation
  //                    this.logger.debug("Creating handler implementing {} by calling {}", source,
  //                            target);
  //
  //                    // Determine # args to supply to target and their offset in source args
  //                    final int argsCount = target.getParameterCount();
  //                    final int argsOffset = source.getParameterCount() > argsCount ? 1 : 0;
  //
  //                    // Define a converter function to transform source args into target args
  //                    final Function<Object[], Object[]> argsConverter;
  //                    if (argsCount == 0) {
  //                        final Object[] emptyArgs = new Object[] {};
  //                        argsConverter = args -> emptyArgs;
  //                    } else {
  //                        @SuppressWarnings("unchecked")
  //                        final Function<Object, Object>[] converters = new Function[argsCount];
  //                        for (int i = 0; i < argsCount; ++i) {
  //                            converters[i] = newConverter(
  //                                    source.getParameterTypes()[i + argsOffset],
  //                                    target.getParameterTypes()[i]);
  //                        }
  //                        argsConverter = args -> {
  //                            final Object[] convertedArgs = new Object[argsCount];
  //                            for (int i = 0; i < argsCount; ++i) {
  //                                convertedArgs[i] = converters[i].apply(args[i + argsOffset]);
  //                            }
  //                            return convertedArgs;
  //                        };
  //                    }
  //
  //                    // Define a converter function to transform target result into source result
  //                    final Function<Object, Object> resultConverter = target instanceof Method
  //                            ? newConverter(((Method) target).getReturnType(),
  //                                    source.getReturnType())
  //                            : Functions.identity();
  //
  //                    // Define a 'self object, source args -> source result' combining invocation
  //                    // of converters and target executable member
  //                    return (self, args) -> resultConverter.apply( //
  //                            call(target, argsOffset == 0 ? self : args[0],
  //                                    argsConverter.apply(args)));
  //                }
  //
  //                /**
  //                 * Defines a 'converter' function from instances of {@code sourceClass} to
  //                 * instances of {@code targetClass}, these classes having the same name and
  //                 * compatible method definitions but using different ClassLoaders.
  //                 *
  //                 * @param sourceClass
  //                 *            the source class to convert from
  //                 * @param targetClass
  //                 *            the target class to convert to
  //                 * @return the converter function
  //                 */
  //                @SuppressWarnings("unchecked")
  //                private Function<Object, Object> newConverter(final Class<?> sourceClass,
  //                        final Class<?> targetClass) {
  //
  //                    // Retrieve class name, which MUST be the same for source and target classes
  //                    // (only ClassLoader may change)
  //                    final String name = sourceClass.getName();
  //                    Preconditions.checkArgument(name.equals(targetClass.getName()),
  //                            "mismatching class names: %s != %s", name, targetClass.getName());
  //
  //                    // Handle various conversion cases:
  //                    if (targetClass.isAssignableFrom(sourceClass)) {
  //                        // (1) no conversion needed
  //                        return Functions.identity();
  //
  //                    } else if (name.equals(Optional.class.getName())) {
  //                        // (2) guava Optional conversion
  //                        final Executable orNull = resolve(sourceClass, "orNull");
  //                        final Executable fromNullable = resolve(targetClass, "fromNullable",
  //                                Object.class);
  //                        return o -> call(fromNullable, null, call(orNull, o));
  //
  //                    } else if (name.equals(Multimap.class.getName())
  //                            || name.equals(ListMultimap.class.getName())
  //                            || name.equals(LinkedListMultimap.class.getName())) {
  //                        // (3) guava Multimap conversion
  //                        final Class<?> targetImplClass;
  //                        try {
  //                            targetImplClass = Class.forName(LinkedListMultimap.class.getName(),
  //                                    true, targetClass.getClassLoader());
  //                        } catch (final ClassNotFoundException ex) {
  //                            throw new Error(ex);
  //                        }
  //                        final Executable entries = resolve(sourceClass, "entries");
  //                        final Executable create = resolve(targetImplClass, "create");
  //                        final Executable put = resolve(targetImplClass, "put", Object.class,
  //                                Object.class);
  //                        return o -> {
  //                            final Object result = call(create, null);
  //                            for (final Entry<?, ?> e : (Collection<Entry<?, ?>>) call(entries,
  //                                    o)) {
  //                                call(put, result, e.getKey(), e.getValue());
  //                            }
  //                            return result;
  //                        };
  //
  //                    } else if (sourceClass.isInterface() && targetClass.isInterface()) {
  //                        // (4) interface conversion, handled using handlers + Java proxy mechanism
  //                        final Map<Method, BiFunction<Object, Object[], Object>> handlers;
  //                        handlers = Maps.newHashMap();
  //                        for (final Method method : targetClass.getMethods()) {
  //                            handlers.put(method, newHandler(method, resolve(sourceClass,
  //                                    method.getName(), method.getParameterTypes())));
  //                        }
  //                        return o -> Proxy.newProxyInstance(targetClass.getClassLoader(),
  //                                new Class<?>[] { targetClass },
  //                                (proxy, method, args) -> handlers.get(method).apply(o, args));
  //                    }
  //
  //                    // EXTENSION POINT: add here other conversion cases as required
  //
  //                    // Fail reporting unsupported conversion
  //                    throw new UnsupportedOperationException(
  //                            "No conversion implemented for class " + name);
  //                }
  //
  //            });
  //
  //}
  