/**
* Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org>
*
* 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 org.onebusaway.federations.impl;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.onebusaway.federations.FederatedService;
import org.onebusaway.geospatial.model.CoordinateBounds;
/**
* Provides a number of convenience methods for working with
* {@link FederatedService} instances.
*
* @author bdferris
* @see FederatedService
*/
public class FederatedServiceLibrary {
/**
* Given a list of {@link FederatedService} instances {@code
* federatedServiceInstances} that implement a target {@code
* federatedServiceInterface}, query each instance in turn to determine its
* set of agencies and geographic bounds, verifying that no two distinct
* federated service instances have overlapping agency ids or geographic
* bounds. We construct a map from each service instance to its set of agency
* ids along with their geographic regions.
*
* @param federatedServiceInstances
* @param federatedServiceInterface
* @return a map of service instances along with their agency ids and coverage
* areas
*/
public static Map<FederatedService, Map<String, List<CoordinateBounds>>> getFederatedServiceAgencyCoverage(
List<? extends FederatedService> federatedServiceInstances,
Class<? extends FederatedService> federatedServiceInterface) {
Map<FederatedService, Map<String, List<CoordinateBounds>>> byProvider = new HashMap<FederatedService, Map<String, List<CoordinateBounds>>>();
for (FederatedService service : federatedServiceInstances) {
if (!federatedServiceInterface.isAssignableFrom(service.getClass()))
throw new IllegalArgumentException("service provider " + service
+ " not instance of " + federatedServiceInterface.getName());
Map<String, List<CoordinateBounds>> agencyIdsWithCoverageArea = service.getAgencyIdsWithCoverageArea();
for (Map.Entry<String, List<CoordinateBounds>> entry : agencyIdsWithCoverageArea.entrySet()) {
String agencyId = entry.getKey();
List<CoordinateBounds> coverage = entry.getValue();
checkAgencyAndCoverageAgainstExisting(byProvider, agencyId, coverage,
federatedServiceInterface, true);
}
byProvider.put(service, agencyIdsWithCoverageArea);
}
return byProvider;
}
/**
* Given an existing set of {@link FederatedService} instances along with
* their set of agency ids and geographic bounds, examine an additional agency
* id and coverage area from another service instance and verify that there is
* no overlap.
*
* @param byProvider existing {@link FederatedService} instances along with
* their agency ids and coverage areas
* @param agencyId a new agency id to check
* @param coverage the coverage area for that agency
* @param serviceInterface the target service interface
* @param failHard throw an exception if overlap is found
* @return true if there is no overlap (good), other false (bad)
*/
public static boolean checkAgencyAndCoverageAgainstExisting(
Map<FederatedService, Map<String, List<CoordinateBounds>>> byProvider,
String agencyId, List<CoordinateBounds> coverage,
Class<?> serviceInterface, boolean failHard) {
for (Map<String, List<CoordinateBounds>> other : byProvider.values()) {
// Check to see if the specified agencyId has already been defined
// elsewhere
if (other.containsKey(agencyId)) {
if (failHard)
throw new IllegalArgumentException("agency \"" + agencyId
+ "\" is handled by multiple providers for service "
+ serviceInterface.getName());
else
return false;
}
// Check to see if the agencyId has coverage overlap with an agency from
// another provider
for (Map.Entry<String, List<CoordinateBounds>> otherEntry : other.entrySet()) {
String otherAgencyId = otherEntry.getKey();
List<CoordinateBounds> otherCoverage = otherEntry.getValue();
for (CoordinateBounds otherRectangle : otherCoverage) {
for (CoordinateBounds rectangle : coverage) {
if (rectangle.intersects(otherRectangle)) {
if (failHard)
throw new IllegalArgumentException("agency \"" + agencyId
+ "\" has overlap with agency \"" + otherAgencyId
+ "\" in separate service providers of type "
+ serviceInterface.getName());
else
return false;
}
}
}
}
}
return true;
}
}