Package eu.planets_project.ifr.core.servreg.api

Source Code of eu.planets_project.ifr.core.servreg.api.Query

package eu.planets_project.ifr.core.servreg.api;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;

import eu.planets_project.ifr.core.techreg.formats.FormatRegistry;
import eu.planets_project.ifr.core.techreg.formats.FormatRegistryFactory;
import eu.planets_project.services.datatypes.Queryable;
import eu.planets_project.services.datatypes.ServiceDescription;

/**
* Query by example functionality for service description instances.
* @see eu.planets_project.ifr.core.servreg.api.CoreRegistryTests
* @author Fabian Steeg (fabian.steeg@uni-koeln.de)
*/
final class Query {

    private List<ServiceDescription> descriptions = null;
    private static Logger log = Logger.getLogger(Query.class.getName());

    /**
     * @param descriptions The descriptions to query
     */
    Query(final List<ServiceDescription> descriptions) {
        this.descriptions = descriptions;
    }

    /**
     * @param sample The sample description
     * @param mode The matching strategy to use
     * @return Returns the service descriptions corresponding to the given sample instance
     */
    List<ServiceDescription> byExample(final ServiceDescription sample, final MatchingMode mode) {
        List<ServiceDescription> result = new ArrayList<ServiceDescription>();
        for (ServiceDescription description : descriptions) {
            if (matches(description, sample, mode)) {
                result.add(description);
            }
        }
        return result;
    }

    /**
     * Compares a candidate and a sample instance reflectively, using a marker annotation. This means the
     * ServiceDescription class can be extended by new values and these values can be used for the query by example
     * functionality without changing this class.
     * @param candidate The candidate service description
     * @param sample The query-by-example sample instance
     * @param mode The match mode to use
     * @return True, if the given candidate is described by the given sample, else false
     */
    private static boolean matches(final ServiceDescription candidate, final ServiceDescription sample,
            final MatchingMode mode) {
        if (candidate == null) {
            throw new IllegalArgumentException("Candidate service description is null!");
        }
        if (!Access.authorized(candidate)) {
            return false;
        }
        /* If no sample is given any description matches: */
        if (sample == null) {
            return true;
        }
        Class<? extends ServiceDescription> clazz = sample.getClass();
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            /*
             * We only compare values of methods marked with the Queryable annotation:
             */
            if (method.getAnnotation(Queryable.class) != null) {
                try {
                    Object sampleValue = method.invoke(sample);
                    Object candidateValue = method.invoke(candidate);
                    String message = String.format(
                            "Comparing values for method '%s' of query example '%s' to candidate instance '%s'.",
                            method.getName(), sampleValue, candidateValue);
                    log.fine(message);
                    /*
                     * If the sample value is null, we don't want to consider it:
                     */
                    if (sampleValue != null) {
                        if (candidateValue == null) {
                            /*
                             * The sample value is not null, but the candidate is, we have no match:
                             */
                            return false;
                        } else {
                            /*
                             * Now, none of the values are null... we now first check for Collections:
                             */
                            if (sampleValue instanceof Collection<?> && candidateValue instanceof Collection<?>) {
                                /*
                                 * They need not be equals, but all sample values should be in the candidate:
                                 */
                                Collection<?> cands = (Collection<?>) candidateValue;
                                Collection<?> samps = (Collection<?>) sampleValue;
                                if (!containsAll(cands, samps, mode)) {
                                    return false;
                                }
                            } else {
                                /*
                                 * Finally, we can do the normal comparison here: if two non-null, non-Collection values
                                 * are not equal the candidate does not match:
                                 */
                                if (!(mode.matches(candidateValue.toString(), sampleValue.toString()))) {
                                    return false;
                                }
                            }
                        }
                    }
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
        return true;
    }

    /**
     * @param cands The candidate collection
     * @param samps The sample collection
     * @param mode The matching mode to use
     * @return True, if all the sample elements match a candidate element
     */
    private static boolean containsAll(final Collection<?> cands, final Collection<?> samps, final MatchingMode mode) {
        int matched = 0;
        for (Object sample : samps) {
            for (Object candidate : cands) {
                /*
                 * If we are dealing with URIs, we want to map the different kinds (extension URIs, Pronom URIs) onto
                 * each other:
                 */
                if (candidate instanceof URI && sample instanceof URI) {
                    if (mappable((URI) candidate, (URI) sample, mode)) {
                        matched++;
                        break;
                    }
                }
                if (mode.matches(candidate.toString(), sample.toString())) {
                    matched++;
                    break;
                }
            }
        }
        return matched == samps.size();
    }

    /**
     * @param candidate The candidate registry entry
     * @param sample The query sample pattern
     * @param mode The query mode
     * @return True, if the sample can be mapped onto the candidate URI
     */
    private static boolean mappable(final URI candidate, final URI sample, final MatchingMode mode) {
        /* No mapping required: */
        if (mode.matches(candidate.toString(), sample.toString())) {
            return true;
        }
        FormatRegistry registry = FormatRegistryFactory.getFormatRegistry();
        List<URI> candidateAliases = registry.getFormatUriAliases(candidate);
        List<URI> sampleAliases = registry.getFormatUriAliases(sample);
        /* If one of the aliases of the candidate and the sample match we're OK: */
        for (URI sampleAlias : sampleAliases) {
            for (URI candidateAlias : candidateAliases) {
                if (mode.matches(candidateAlias.toString(), sampleAlias.toString())) {
                    return true;
                }
            }
        }
        return false;
    }
}
TOP

Related Classes of eu.planets_project.ifr.core.servreg.api.Query

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.