/**
* 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.transit_data_federation.impl.tripplanner;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.PostConstruct;
import org.onebusaway.container.refresh.Refreshable;
import org.onebusaway.gtfs.model.AgencyAndId;
import org.onebusaway.transit_data_federation.impl.RefreshableResources;
import org.onebusaway.transit_data_federation.services.FederatedTransitDataBundle;
import org.onebusaway.transit_data_federation.services.transit_graph.StopEntry;
import org.onebusaway.transit_data_federation.services.transit_graph.TransitGraphDao;
import org.onebusaway.transit_data_federation.services.tripplanner.CompactedTransferPattern;
import org.onebusaway.transit_data_federation.services.tripplanner.HubNode;
import org.onebusaway.transit_data_federation.services.tripplanner.TransferNode;
import org.onebusaway.transit_data_federation.services.tripplanner.TransferParent;
import org.onebusaway.transit_data_federation.services.tripplanner.TransferPattern;
import org.onebusaway.transit_data_federation.services.tripplanner.TransferPatternData;
import org.onebusaway.transit_data_federation.services.tripplanner.TransferPatternService;
import org.onebusaway.utility.ObjectSerializationLibrary;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
class TransferPatternServiceImpl implements TransferPatternService {
private static Logger _log = LoggerFactory.getLogger(TransferPatternServiceImpl.class);
private Map<StopEntry, TransferPattern> _transferPatternsByStop = new HashMap<StopEntry, TransferPattern>();
private FederatedTransitDataBundle _bundle;
private TransitGraphDao _transitGraphDao;
private boolean _enabled = true;
@Autowired
public void setBundle(FederatedTransitDataBundle bundle) {
_bundle = bundle;
}
@Autowired
public void setTransitGraphDao(TransitGraphDao transitGraphDao) {
_transitGraphDao = transitGraphDao;
}
public void setEnabled(boolean enabled) {
_enabled = enabled;
}
@PostConstruct
@Refreshable(dependsOn = RefreshableResources.TRANSFER_PATTERNS)
public void setup() throws IOException, ClassNotFoundException {
_transferPatternsByStop.clear();
if( ! _enabled )
return;
File path = _bundle.getSerializedTransferPatternsPath();
if (!path.exists())
return;
_log.info("loading transfer patterns...");
Map<AgencyAndId, CompactedTransferPattern> patternsByStopId = ObjectSerializationLibrary.readObject(path);
_log.info("transfer patterns loaded");
for (Map.Entry<AgencyAndId, CompactedTransferPattern> entry : patternsByStopId.entrySet()) {
AgencyAndId stopId = entry.getKey();
StopEntry stop = _transitGraphDao.getStopEntryForId(stopId, true);
CompactedTransferPattern pattern = entry.getValue();
pattern.setAllStops(_transitGraphDao.getAllStops());
_transferPatternsByStop.put(stop, pattern);
}
}
@Override
public boolean isEnabled() {
return !_transferPatternsByStop.isEmpty();
}
@Override
public TransferParent getTransferPatternsForStops(
TransferPatternData transferPatternData, StopEntry stopFrom,
List<StopEntry> stopsTo) {
TransferParent root = getTransferPatternsForStopsInternal(
transferPatternData, stopFrom, stopsTo);
transferPatternData.clearMinRemainingWeights();
return root;
}
@Override
public Collection<TransferNode> getReverseTransferPatternsForStops(
TransferPatternData transferPatternData, Iterable<StopEntry> stopsFrom,
StopEntry stopTo) {
List<StopEntry> stopToAsList = Arrays.asList(stopTo);
TransferParent root = new TransferParent(transferPatternData);
TransferPatternData forwardData = new TransferPatternData();
for (StopEntry stopFrom : stopsFrom) {
TransferParent trees = getTransferPatternsForStopsInternal(forwardData,
stopFrom, stopToAsList);
Set<TransferNode> visited = new HashSet<TransferNode>();
for (TransferNode tree : trees.getTransfers())
reverseTree(tree, root, true, visited);
}
transferPatternData.clearMinRemainingWeights();
return root.getTransfers();
}
@Override
public Collection<TransferNode> expandNode(HubNode node) {
throw new IllegalStateException();
// return getTransferPatternForStops(node.getHubStop(), node.getStopsTo());
}
/****
* Private Methods
****/
private TransferParent getTransferPatternsForStopsInternal(
TransferPatternData transferPatternData, StopEntry stopFrom,
List<StopEntry> stopsTo) {
TransferParent root = new TransferParent(transferPatternData);
TransferPattern pattern = _transferPatternsByStop.get(stopFrom);
if (pattern == null)
return root;
pattern.getTransfersForStops(root, stopsTo);
Map<StopEntry, List<TransferParent>> hubParentsByStop = pattern.getTransfersForHubStops(root);
for (Map.Entry<StopEntry, List<TransferParent>> entry : hubParentsByStop.entrySet()) {
StopEntry hubStop = entry.getKey();
List<TransferParent> parents = entry.getValue();
TransferParent nodes = transferPatternData.getNodesForHubStop(hubStop);
if (nodes == null) {
nodes = getTransferPatternsForStopsInternal(transferPatternData,
hubStop, stopsTo);
transferPatternData.setNodesForHubStop(hubStop, nodes);
}
for (TransferParent parent : parents) {
for (TransferNode node : nodes.getTransfers()) {
parent.addTransferNode(node);
}
}
}
return root;
}
/**
* We want to reverse the transfer pattern tree. Given a starting node, we
* search down the tree until we reach an end-point and then construct tree
* back up. Since there can be cycles in the tree
*
* @param node
* @param root
* @param exitAllowed
* @param visited
* @return
*/
private List<TransferParent> reverseTree(TransferNode node,
TransferParent root, boolean exitAllowed, Set<TransferNode> visited) {
visited.add(node);
List<TransferParent> results = new ArrayList<TransferParent>();
if (node.isExitAllowed()) {
TransferNode extended = root.extendTree(node.getToStop(),
node.getFromStop(), exitAllowed);
results.add(extended);
}
for (TransferNode subNode : node.getTransfers()) {
/**
* There can be circular paths in the transfer tree. If we've already
* visited the sub-node, we don't visit it again.
*/
if (visited.contains(subNode))
continue;
List<TransferParent> parents = reverseTree(subNode, root, false, visited);
for (TransferParent parent : parents) {
TransferNode extended = parent.extendTree(node.getToStop(),
node.getFromStop(), exitAllowed);
results.add(extended);
}
}
return results;
}
}