/* This program is free software: you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package org.opentripplanner.routing.core;
import java.io.Serializable;
import java.util.HashSet;
import org.onebusaway.gtfs.model.AgencyAndId;
import org.onebusaway.gtfs.model.Route;
import org.opentripplanner.common.model.T2;
import org.opentripplanner.gtfs.GtfsLibrary;
/**
* A RouteMatcher is a collection of routes based on IDs, short name and/or agency IDs.
*
* We currently support route full IDs (agency ID + route ID), agency ID + route name, or route name only. Support for other matching expression can
* be easily added later on.
*/
public class RouteMatcher implements Cloneable, Serializable {
private static final long serialVersionUID = 8066547338465440312L;
/* Set of full matching route ids (agency ID + route ID) */
private HashSet<AgencyAndId> agencyAndRouteIds = new HashSet<AgencyAndId>();
/* Set of full matching route code/names (agency ID + route code/name) */
private HashSet<T2<String, String>> agencyIdAndRouteNames = new HashSet<T2<String, String>>();
/* Set of matching route names (without specifying an agency ID) */
private HashSet<String> routeNames = new HashSet<String>();
private static RouteMatcher EMPTY_MATCHER = new RouteMatcher();
private RouteMatcher() {
}
/**
* Return an empty matcher (which match no routes).
*/
public static RouteMatcher emptyMatcher() {
return EMPTY_MATCHER;
}
/**
* Build a new RouteMatcher from a string representation.
*
* @param routeSpecList A comma-separated list of route spec, each of the format [agencyId]_[routeName]_[routeId] Please note that this format is
* not really intuitive as it does not follow the OBA-gtfslib AgencyAndId standard ('agencyID_routeId'). This was kept for
* backward-compatibility purposes. If the original routeName contains some "_" each *must* be replaced by a space.
* @return A RouteMatcher
* @throws IllegalArgumentException If the string representation is invalid.
*/
public static RouteMatcher parse(String routeSpecList) {
if (routeSpecList == null)
return emptyMatcher();
RouteMatcher retval = new RouteMatcher();
int n = 0;
for (String element : routeSpecList.split(",")) {
if (element.length() == 0)
continue;
n++;
String[] routeSpec = element.split("_", 3);
if (routeSpec.length != 2 && routeSpec.length != 3) {
throw new IllegalArgumentException("Wrong route spec format: " + element);
}
String agencyId = routeSpec[0];
if (agencyId.length() == 0)
agencyId = null;
String routeName = routeSpec[1];
if (routeName.length() == 0)
routeName = null;
String routeId = routeSpec.length > 2 ? routeSpec[2] : null;
if (routeId != null && routeId.length() == 0)
routeId = null;
if (agencyId != null && routeId != null && routeName == null) {
// Case 1: specified agency ID and route ID but no route name
retval.agencyAndRouteIds.add(new AgencyAndId(agencyId, routeId));
} else if (agencyId != null && routeName != null && routeId == null) {
// Case 2: specified agency ID and route name but no route ID
retval.agencyIdAndRouteNames.add(new T2<String, String>(agencyId, routeName));
} else if (agencyId == null && routeName != null && routeId == null) {
// Case 3: specified route name only
retval.routeNames.add(routeName);
} else {
throw new IllegalArgumentException("Wrong route spec format: " + element);
}
}
if (n == 0)
return emptyMatcher();
return retval;
}
public boolean matches(Route route) {
if (this == EMPTY_MATCHER)
return false;
if (agencyAndRouteIds.contains(route.getId()))
return true;
String routeName = GtfsLibrary.getRouteName(route).replace("_", " ");
if (agencyIdAndRouteNames.contains(new T2<String, String>(route.getId().getAgencyId(),
routeName)))
return true;
if (routeNames.contains(routeName))
return true;
return false;
}
public String asString() {
StringBuilder builder = new StringBuilder();
for (AgencyAndId agencyAndId : agencyAndRouteIds) {
builder.append(agencyAndId.getAgencyId() + "__" + agencyAndId.getId());
builder.append(",");
}
for (T2<String, String> agencyIdAndRouteName : agencyIdAndRouteNames) {
builder.append(agencyIdAndRouteName.getFirst() + "_" + agencyIdAndRouteName.getSecond());
builder.append(",");
}
for (String routeName : routeNames) {
builder.append("_" + routeName);
builder.append(",");
}
if (builder.length() > 0) {
builder.setLength(builder.length() - 1);
}
return builder.toString();
}
@Override
public String toString() {
return String.format(
"RouteMatcher<agencyAndRouteIds=%s agencyIdAndRouteNames=%s routeNames=%s>",
agencyAndRouteIds, agencyIdAndRouteNames, routeNames);
}
@Override
public boolean equals(Object another) {
if (another == null || !(another instanceof RouteMatcher))
return false;
if (another == this)
return true;
RouteMatcher anotherMatcher = (RouteMatcher) another;
return agencyAndRouteIds.equals(anotherMatcher.agencyAndRouteIds)
&& agencyIdAndRouteNames.equals(anotherMatcher.agencyIdAndRouteNames)
&& routeNames.equals(anotherMatcher.routeNames);
}
@Override
public int hashCode() {
return agencyAndRouteIds.hashCode() + agencyIdAndRouteNames.hashCode()
+ routeNames.hashCode();
}
public RouteMatcher clone() {
try {
return (RouteMatcher) super.clone();
} catch (CloneNotSupportedException e) {
/* this will never happen since our super is the cloneable object */
throw new RuntimeException(e);
}
}
}