uk.ac.open.kmi.iserve.discovery.disco.impl.GenericLogicDiscoverer.java Source code

Java tutorial

Introduction

Here is the source code for uk.ac.open.kmi.iserve.discovery.disco.impl.GenericLogicDiscoverer.java

Source

/*
 * Copyright (c) 2013. Knowledge Media Institute - The Open University
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package uk.ac.open.kmi.iserve.discovery.disco.impl;

import com.google.common.collect.*;
import com.google.inject.Inject;
import uk.ac.open.kmi.iserve.discovery.api.ConceptMatcher;
import uk.ac.open.kmi.iserve.discovery.api.MatchResult;
import uk.ac.open.kmi.iserve.discovery.api.OperationDiscoverer;
import uk.ac.open.kmi.iserve.discovery.api.ServiceDiscoverer;
import uk.ac.open.kmi.iserve.discovery.disco.LogicConceptMatchType;
import uk.ac.open.kmi.iserve.discovery.disco.MatchResultsMerger;
import uk.ac.open.kmi.iserve.sal.manager.ServiceManager;
import uk.ac.open.kmi.msm4j.vocabulary.MSM;
import uk.ac.open.kmi.msm4j.vocabulary.SAWSDL;

import java.net.URI;
import java.util.Map;
import java.util.Set;

/**
 * GenericLogicDiscoverer provides discovery for both Operations and Services
 *
 * @author <a href="mailto:carlos.pedrinaci@open.ac.uk">Carlos Pedrinaci</a> (KMi - The Open University)
 * @author <a href="mailto:luca.panziera@open.ac.uk">Luca Panziera</a> (KMi - The Open University)
 * @since 27/09/2013
 */
public class GenericLogicDiscoverer implements OperationDiscoverer, ServiceDiscoverer {

    private final ServiceManager serviceManager;
    private final ConceptMatcher conceptMatcher;

    @Inject
    public GenericLogicDiscoverer(ServiceManager serviceManager, ConceptMatcher conceptMatcher) {
        this.serviceManager = serviceManager;
        this.conceptMatcher = conceptMatcher;
    }

    /**
     * Generic implementation for finding all the Services or Operations that have ALL the given types as inputs or outputs.
     *
     * @param entityType   the MSM URI of the type of entity we are looking for. Only supports Service and Operation.
     * @param relationship the MSM URI of the relationship we are looking for. Only supports hasInput and hasOutput.
     * @param types        the input/output types (modelReferences that is) we are looking for
     * @return a Map mapping operation/services URIs to MatchResults.
     */
    private Map<URI, MatchResult> findAll(URI entityType, URI relationship, Set<URI> types) {

        // Ensure that we have been given correct parameters
        if (types == null || types.isEmpty()
                || (!entityType.toASCIIString().equals(MSM.Service.getURI())
                        && !entityType.toASCIIString().equals(MSM.Operation.getURI()))
                || (!relationship.toASCIIString().equals(MSM.hasInput.getURI())
                        && !entityType.toASCIIString().equals(MSM.hasOutput.getURI())
                        && !relationship.toASCIIString().equals(SAWSDL.modelReference.getURI()))) {

            return ImmutableMap.of();
        }

        // Expand the input types to get all that match enough to be consumed
        // The structure is: <OriginalType, MatchingType, MatchResult>
        Table<URI, URI, MatchResult> expandedTypes;
        if (relationship.toASCIIString().equals(SAWSDL.modelReference.getURI())) {
            expandedTypes = HashBasedTable.create();
            for (URI type : types) {
                expandedTypes.putAll(this.conceptMatcher.listMatchesAtMostOfType(ImmutableSet.of(type),
                        LogicConceptMatchType.Subsume));
                expandedTypes.putAll(
                        this.conceptMatcher.listMatchesOfType(ImmutableSet.of(type), LogicConceptMatchType.Exact));
            }
        } else {
            expandedTypes = this.conceptMatcher.listMatchesAtLeastOfType(types, LogicConceptMatchType.Plugin);
        }

        // Track all the results in a multimap to push the details up the stack
        ListMultimap<URI, MatchResult> result = ArrayListMultimap.create();

        // Do the intersection of those operations that can consume each of the inputs separately
        boolean firstTime = true;
        Map<URI, MatchResult> intermediateMatches;
        Map<URI, Map<URI, MatchResult>> rowMap = expandedTypes.rowMap();
        // For each original type
        for (URI inputType : rowMap.keySet()) {
            // obtain those entities that match any of the expanded matching types
            intermediateMatches = findSome(entityType, relationship, rowMap.get(inputType).keySet());
            if (firstTime) {
                // Add all entries
                firstTime = false;
                for (Map.Entry<URI, MatchResult> entry : intermediateMatches.entrySet()) {
                    result.put(entry.getKey(), entry.getValue());
                }
            } else {
                // Put all the values from the intersection
                Set<URI> intersection = Sets.intersection(result.keySet(), intermediateMatches.keySet());
                for (URI opUri : intersection) {
                    result.put(opUri, intermediateMatches.get(opUri));
                }

                // Drop all the values from the difference
                // Use an immutable copy since the views will be changed
                Set<URI> difference = Sets.difference(result.keySet(), intermediateMatches.keySet())
                        .immutableCopy();
                for (URI opUri : difference) {
                    result.removeAll(opUri);
                }
            }
        }

        // Merge the results into a single map using Union
        return Maps.transformValues(result.asMap(), MatchResultsMerger.INTERSECTION);

    }

    /**
     * Generic implementation for finding all the Services or Operations that have SOME of the given types as inputs or outputs.
     *
     * @param entityType   the MSM URI of the type of entity we are looking for. Only supports Service and Operation.
     * @param relationship the MSM URI of the relationship we are looking for. Only supports hasInput and hasOutput.
     * @param types        the input/output types (modelReferences that is) we are looking for
     * @return a Map mapping operation/services URIs to MatchResults.
     */
    private Map<URI, MatchResult> findSome(URI entityType, URI relationship, Set<URI> types) {

        // Ensure that we have been given correct parameters
        if (types == null || types.isEmpty()
                || (!entityType.toASCIIString().equals(MSM.Service.getURI())
                        && !entityType.toASCIIString().equals(MSM.Operation.getURI()))
                || (!relationship.toASCIIString().equals(MSM.hasInput.getURI())
                        && !entityType.toASCIIString().equals(MSM.hasOutput.getURI())
                        && !relationship.toASCIIString().equals(SAWSDL.modelReference.getURI()))) {

            return ImmutableMap.of();
        }

        // Expand the input types to get all that match enough to be consumed
        // TODO: The leastOfType should be configurable
        Table<URI, URI, MatchResult> expandedTypes;
        if (relationship.toASCIIString().equals(SAWSDL.modelReference.getURI())) {
            expandedTypes = HashBasedTable.create();
            //TODO: fix this properly
            for (URI type : types) {
                expandedTypes.putAll(this.conceptMatcher.listMatchesAtMostOfType(ImmutableSet.of(type),
                        LogicConceptMatchType.Subsume));
                expandedTypes.putAll(
                        this.conceptMatcher.listMatchesOfType(ImmutableSet.of(type), LogicConceptMatchType.Exact));
            }

        } else {
            expandedTypes = this.conceptMatcher.listMatchesAtLeastOfType(types, LogicConceptMatchType.Plugin);
        }

        // Track all the results in a multimap to push the details up the stack
        Multimap<URI, MatchResult> result = ArrayListMultimap.create();

        // Find all the entities with modelReferences to the expanded types
        // The column view is the one with all the possible matches since a class will always match itself
        Map<URI, Map<URI, MatchResult>> columnMap = expandedTypes.columnMap();
        for (URI type : columnMap.keySet()) {
            Set<URI> entities = ImmutableSet.of();
            if (relationship.toASCIIString().equals(SAWSDL.modelReference.getURI())) {
                entities = listEntitesWithModelReference(entityType, type);
            } else if (relationship.toASCIIString().equals(MSM.hasInput.getURI())
                    || relationship.toASCIIString().equals(MSM.hasOutput.getURI())) {
                entities = listEntitiesWithType(entityType, relationship, type);
            }

            for (URI entity : entities) {
                result.putAll(entity, columnMap.get(type).values());
            }
        }

        // Merge the results into a single map using Union
        return Maps.transformValues(result.asMap(), MatchResultsMerger.UNION);

    }

    /**
     * Generic implementation for finding all the Services or Operations that have one specific model reference.
     *
     * @param entityType     the MSM URI of the type of entity we are looking for. Only supports Service and Operation.
     * @param modelReference the model reference URI we are looking for.
     * @returns a Set of URIs of matching services/operations. Note that this method makes no use of reasoning and therefore
     * the match will always be exact in this case.
     */

    private Set<URI> listEntitesWithModelReference(URI entityType, URI modelReference) {
        Set<URI> entities = ImmutableSet.of();
        // Deal with services
        if (entityType.toASCIIString().equals(MSM.Service.getURI())) {
            entities = this.serviceManager.listServicesWithModelReference(modelReference);
        } else if (entityType.toASCIIString().equals(MSM.Operation.getURI())) {
            entities = this.serviceManager.listOperationsWithModelReference(modelReference);
        }
        return entities;
    }

    /**
     * Generic implementation for finding all the Services or Operations that have one specific type as input or output.
     *
     * @param entityType   the MSM URI of the type of entity we are looking for. Only supports Service and Operation.
     * @param relationship the MSM URI of the relationship we are looking for. Only supports hasInput and hasOutput.
     * @param type         the input/output type (modelReference that is) we are looking for.
     * @returns a Set of URIs of matching services/operations. Note that this method makes no use of reasoning and therefore
     * the match will always be exact in this case.
     */
    private Set<URI> listEntitiesWithType(URI entityType, URI relationship, URI type) {
        Set<URI> entities = ImmutableSet.of();
        // Deal with services
        if (entityType.toASCIIString().equals(MSM.Service.getURI())) {
            // Get the adequate type
            if (relationship.toASCIIString().equals(MSM.hasInput.getURI())) {
                entities = this.serviceManager.listServicesWithInputType(type);

            } else if (relationship.toASCIIString().equals(MSM.hasOutput.getURI())) {
                entities = this.serviceManager.listServicesWithOutputType(type);
            }
            // Deal with operations
        } else if (entityType.toASCIIString().equals(MSM.Operation.getURI())) {

            // Get the adequate type
            if (relationship.toASCIIString().equals(MSM.hasInput.getURI())) {
                entities = this.serviceManager.listOperationsWithInputType(type);

            } else if (relationship.toASCIIString().equals(MSM.hasOutput.getURI())) {
                entities = this.serviceManager.listOperationsWithOutputType(type);
            }
        }
        return entities;
    }

    /**
     * Discover registered operations that consume all the types of input provided. That is, all those that have as input
     * the types provided. All the input types should be matched to different inputs.
     *
     * @param inputTypes the types of input to be consumed
     * @return a Set containing all the matching operations. If there are no solutions, the Set should be empty, not null.
     */
    @Override
    public Map<URI, MatchResult> findOperationsConsumingAll(Set<URI> inputTypes) {
        return findAll(URI.create(MSM.Operation.getURI()), URI.create(MSM.hasInput.getURI()), inputTypes);
    }

    /**
     * Discover registered operations that consume some (i.e., at least one) of the types of input provided. That is, all
     * those that have as input the types provided.
     *
     * @param inputTypes the types of input to be consumed
     * @return a Set containing all the matching operations. If there are no solutions, the Set should be empty, not null.
     */
    @Override
    public Map<URI, MatchResult> findOperationsConsumingSome(Set<URI> inputTypes) {
        return findSome(URI.create(MSM.Operation.getURI()), URI.create(MSM.hasInput.getURI()), inputTypes);
    }

    /**
     * Discover registered operations that produce all of the types of output provided. That is, all those that can be
     * matched to the output types provided. Note, each output should be matched to a different type.
     *
     * @param outputTypes the types of output to be produced
     * @return a Set containing all the matching operations. If there are no solutions, the Set should be empty, not null.
     */
    @Override
    public Map<URI, MatchResult> findOperationsProducingAll(Set<URI> outputTypes) {
        return findAll(URI.create(MSM.Operation.getURI()), URI.create(MSM.hasOutput.getURI()), outputTypes);
    }

    /**
     * Discover registered operations that produce some (i.e., at least one) of the types of output provided. That is, all
     * those for which at least one output can be matched to the output types provided.
     *
     * @param outputTypes the types of output to be produced
     * @return a Set containing all the matching operations. If there are no solutions, the Set should be empty, not null.
     */
    @Override
    public Map<URI, MatchResult> findOperationsProducingSome(Set<URI> outputTypes) {
        return findSome(URI.create(MSM.Operation.getURI()), URI.create(MSM.hasOutput.getURI()), outputTypes);
    }

    /**
     * Discover the operations that are classified by all the types given. That is, all those that have some level of
     * matching with respect to all operations provided in the model references.
     *
     * @param modelReferences the classifications to match against
     * @return a Set containing all the matching operations. If there are no solutions, the Set should be empty, not null.
     */
    @Override
    public Map<URI, MatchResult> findOperationsClassifiedByAll(Set<URI> modelReferences) {
        return findAll(URI.create(MSM.Operation.getURI()), URI.create(SAWSDL.modelReference.getURI()),
                modelReferences);
    }

    /**
     * Discover the operations that are classified by some (i.e., at least one) of the types given. That is, all those
     * that have some level of matching with respect to at least one entity provided in the model references.
     *
     * @param modelReferences the classifications to match against
     * @return a Set containing all the matching operations. If there are no solutions, the Set should be empty, not null.
     */
    @Override
    public Map<URI, MatchResult> findOperationsClassifiedBySome(Set<URI> modelReferences) {
        return findSome(URI.create(MSM.Operation.getURI()), URI.create(SAWSDL.modelReference.getURI()),
                modelReferences);
    }

    /**
     * Discover registered operations that can be invoked based on the types of input provided. That is, all those that
     * do not have any mandatory input that cannot be fulfilled with the provided ones.
     *
     * @param inputTypes the types of inputs provided
     * @return a Set containing all the invocable operations. If there are no solutions, the Set should be empty, not null.
     */
    @Override
    public Map<URI, MatchResult> findOperationsInvocableWith(Set<URI> inputTypes) {
        return ImmutableMap.of(); // TODO: implement
    }

    /**
     * Discover registered services that consume all the types of input provided. That is, all those that have as input
     * the types provided. All the input types should be matched to different inputs.
     *
     * @param inputTypes the types of input to be consumed
     * @return a Set containing all the matching services. If there are no solutions, the Set should be empty, not null.
     */
    @Override
    public Map<URI, MatchResult> findServicesConsumingAll(Set<URI> inputTypes) {
        return findAll(URI.create(MSM.Service.getURI()), URI.create(MSM.hasInput.getURI()), inputTypes);
    }

    /**
     * Discover registered services that consume some (i.e., at least one) of the types of input provided. That is, all
     * those that have as input the types provided.
     *
     * @param inputTypes the types of input to be consumed
     * @return a Set containing all the matching services. If there are no solutions, the Set should be empty, not null.
     */
    @Override
    public Map<URI, MatchResult> findServicesConsumingSome(Set<URI> inputTypes) {
        return findSome(URI.create(MSM.Service.getURI()), URI.create(MSM.hasInput.getURI()), inputTypes);
    }

    /**
     * Discover registered services that produce all of the types of output provided. That is, all those that can be
     * matched to the output types provided. Note, each output should be matched to a different type.
     *
     * @param outputTypes the types of output to be produced
     * @return a Set containing all the matching services. If there are no solutions, the Set should be empty, not null.
     */
    @Override
    public Map<URI, MatchResult> findServicesProducingAll(Set<URI> outputTypes) {
        return findAll(URI.create(MSM.Service.getURI()), URI.create(MSM.hasOutput.getURI()), outputTypes);
    }

    /**
     * Discover registered services that produce some (i.e., at least one) of the types of output provided. That is, all
     * those for which at least one output can be matched to the output types provided.
     *
     * @param outputTypes the types of output to be produced
     * @return a Set containing all the matching services. If there are no solutions, the Set should be empty, not null.
     */
    @Override
    public Map<URI, MatchResult> findServicesProducingSome(Set<URI> outputTypes) {
        return findSome(URI.create(MSM.Service.getURI()), URI.create(MSM.hasOutput.getURI()), outputTypes);
    }

    /**
     * Discover the services that are classified by all the types given. That is, all those that have some level of
     * matching with respect to all services provided in the model references.
     *
     * @param modelReferences the classifications to match against
     * @return a Set containing all the matching services. If there are no solutions, the Set should be empty, not null.
     */
    @Override
    public Map<URI, MatchResult> findServicesClassifiedByAll(Set<URI> modelReferences) {
        return findAll(URI.create(MSM.Service.getURI()), URI.create(SAWSDL.modelReference.getURI()),
                modelReferences);
    }

    /**
     * Discover the services that are classified by some (i.e., at least one) of the types given. That is, all those
     * that have some level of matching with respect to at least one entity provided in the model references.
     *
     * @param modelReferences the classifications to match against
     * @return a Set containing all the matching services. If there are no solutions, the Set should be empty, not null.
     */
    @Override
    public Map<URI, MatchResult> findServicesClassifiedBySome(Set<URI> modelReferences) {
        return findSome(URI.create(MSM.Service.getURI()), URI.create(SAWSDL.modelReference.getURI()),
                modelReferences);
    }

    /**
     * Discover registered services that can be invoked based on the types of input provided. That is, all those that
     * do not have any mandatory input that cannot be fulfilled with the provided ones.
     *
     * @param inputTypes the types of inputs provided
     * @return a Set containing all the invocable services. If there are no solutions, the Set should be empty, not null.
     */
    @Override
    public Map<URI, MatchResult> findServicesInvocableWith(Set<URI> inputTypes) {
        return ImmutableMap.of(); // TODO: implement
    }
}