Java tutorial
/* * Copyright (C) 2011-2016 Rinde van Lon, iMinds-DistriNet, KU Leuven * * 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 com.github.rinde.rinsim.core.model.road; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Verify.verifyNotNull; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Maps.newLinkedHashMap; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Set; import javax.annotation.Nullable; import javax.measure.quantity.Length; import javax.measure.quantity.Velocity; import javax.measure.unit.Unit; import com.github.rinde.rinsim.core.model.time.TimeLapse; import com.github.rinde.rinsim.event.EventAPI; import com.github.rinde.rinsim.geom.Point; import com.google.common.base.Predicate; import com.google.common.collect.Sets; /** * A common space neutral implementation of {@link RoadModel}. It implements a * data structure for managing objects and locations and checks many * preconditions as defined in {@link RoadModel}. * @author Rinde van Lon * @param <T> The type of the location representation that is used for storing * object locations. This location representation should only be used * internally in the model. */ public abstract class AbstractRoadModel<T> extends GenericRoadModel { /** * A mapping of {@link RoadUser} to location. */ protected volatile Map<RoadUser, T> objLocs; /** * A mapping of {@link MovingRoadUser}s to {@link DestinationPath}s. */ protected Map<MovingRoadUser, DestinationPath> objDestinations; /** * A reference to {@link RoadUnits}. */ protected final RoadUnits unitConversion; /** * Create a new instance. * @param distanceUnit The distance unit used to interpret all supplied * distances. * @param speedUnit The speed unit used to interpret all supplied speeds. */ protected AbstractRoadModel(Unit<Length> distanceUnit, Unit<Velocity> speedUnit) { super(); unitConversion = new RoadUnits(distanceUnit, speedUnit); objLocs = Collections.synchronizedMap(new LinkedHashMap<RoadUser, T>()); objDestinations = newLinkedHashMap(); } /** * A function for converting the location representation to a {@link Point}. * @param locObj The location to be converted. * @return A {@link Point} indicating the position as represented by the * specified location. */ protected abstract Point locObj2point(T locObj); /** * A function for converting a {@link Point} to the location representation of * this model. * @param point The {@link Point} to be converted. * @return The location. */ protected abstract T point2LocObj(Point point); @Override public MoveProgress followPath(MovingRoadUser object, Queue<Point> path, TimeLapse time) { checkArgument(objLocs.containsKey(object), "Object: %s must have a location.", object); checkArgument(!path.isEmpty(), "Path can not be empty, found empty path for %s.", object); checkArgument(time.hasTimeLeft(), "Can not follow path when no time is left. For road user %s.", object); final Point dest = newArrayList(path).get(path.size() - 1); objDestinations.put(object, new DestinationPath(dest, path)); final MoveProgress mp = doFollowPath(object, path, time); eventDispatcher.dispatchEvent(new MoveEvent(self, object, mp)); return mp; } @Override public MoveProgress moveTo(MovingRoadUser object, Point destination, TimeLapse time) { Queue<Point> path; if (objDestinations.containsKey(object) && objDestinations.get(object).destination.equals(destination)) { // is valid move? -> assume it is path = objDestinations.get(object).path; } else { path = new LinkedList<>(getShortestPathTo(object, destination)); objDestinations.put(object, new DestinationPath(destination, path)); } final MoveProgress mp = doFollowPath(object, path, time); eventDispatcher.dispatchEvent(new MoveEvent(self, object, mp)); return mp; } @Override public MoveProgress moveTo(MovingRoadUser object, RoadUser destination, TimeLapse time) { return moveTo(object, getPosition(destination), time); } /** * Should be overridden by subclasses to define actual * {@link RoadModel#followPath(MovingRoadUser, Queue, TimeLapse)} behavior. * @param object The object that is moved. * @param path The path that is followed. * @param time The time that is available for travel. * @return A {@link MoveProgress} instance containing the actual travel * details. */ protected abstract MoveProgress doFollowPath(MovingRoadUser object, Queue<Point> path, TimeLapse time); @Override @Nullable public Point getDestination(MovingRoadUser object) { if (objDestinations.containsKey(object)) { return objDestinations.get(object).destination; } return null; } @Override public void addObjectAt(RoadUser newObj, Point pos) { checkArgument(!objLocs.containsKey(newObj), "Object is already added: %s.", newObj); objLocs.put(newObj, point2LocObj(pos)); eventDispatcher.dispatchEvent(new RoadModelEvent(RoadEventType.ADD_ROAD_USER, this, newObj)); } @Override public void addObjectAtSamePosition(RoadUser newObj, RoadUser existingObj) { checkArgument(!objLocs.containsKey(newObj), "Object %s is already added.", newObj); checkArgument(objLocs.containsKey(existingObj), "Object %s does not exist.", existingObj); objLocs.put(newObj, objLocs.get(existingObj)); eventDispatcher.dispatchEvent(new RoadModelEvent(RoadEventType.ADD_ROAD_USER, this, newObj)); } @Override public void removeObject(RoadUser roadUser) { checkArgument(objLocs.containsKey(roadUser), "RoadUser: %s does not exist.", roadUser); objLocs.remove(roadUser); objDestinations.remove(roadUser); eventDispatcher.dispatchEvent(new RoadModelEvent(RoadEventType.REMOVE_ROAD_USER, this, roadUser)); } @Override public void clear() { objLocs.clear(); objDestinations.clear(); } @Override public boolean containsObject(RoadUser obj) { return objLocs.containsKey(obj); } @Override public boolean containsObjectAt(RoadUser obj, Point p) { if (containsObject(obj)) { return objLocs.get(obj).equals(p); } return false; } @Override public boolean equalPosition(RoadUser obj1, RoadUser obj2) { return containsObject(obj1) && containsObject(obj2) && getPosition(obj1).equals(getPosition(obj2)); } @Override public Map<RoadUser, Point> getObjectsAndPositions() { final Map<RoadUser, T> copiedMap; synchronized (objLocs) { copiedMap = new LinkedHashMap<>(); copiedMap.putAll(objLocs); // it is save to release the lock now } final Map<RoadUser, Point> theMap = new LinkedHashMap<>(); for (final java.util.Map.Entry<RoadUser, T> entry : copiedMap.entrySet()) { theMap.put(entry.getKey(), locObj2point(entry.getValue())); } return theMap; } @Override public Point getPosition(RoadUser roadUser) { checkArgument(containsObject(roadUser), "RoadUser does not exist: %s.", roadUser); return locObj2point(objLocs.get(roadUser)); } @Override public Collection<Point> getObjectPositions() { return getObjectsAndPositions().values(); } @Override public Set<RoadUser> getObjects() { synchronized (objLocs) { final Set<RoadUser> copy = new LinkedHashSet<>(); copy.addAll(objLocs.keySet()); return copy; } } @Override public Set<RoadUser> getObjects(Predicate<RoadUser> predicate) { return Sets.filter(getObjects(), predicate); } @SuppressWarnings("unchecked") @Override public <Y extends RoadUser> Set<Y> getObjectsAt(RoadUser roadUser, Class<Y> type) { final Set<Y> result = new HashSet<>(); for (final RoadUser ru : getObjects(new SameLocationPredicate(roadUser, type, self))) { result.add((Y) ru); } return result; } @Override @SuppressWarnings("unchecked") public <Y extends RoadUser> Set<Y> getObjectsOfType(final Class<Y> type) { return (Set<Y>) getObjects(new Predicate<RoadUser>() { @Override public boolean apply(@Nullable RoadUser input) { return type.isInstance(input); } }); } @Override public List<Point> getShortestPathTo(RoadUser fromObj, RoadUser toObj) { checkArgument(objLocs.containsKey(toObj), "To object (%s) should be in RoadModel.", toObj); return getShortestPathTo(fromObj, getPosition(toObj)); } @Override public List<Point> getShortestPathTo(RoadUser fromObj, Point to) { checkArgument(objLocs.containsKey(fromObj), "From object (%s) should be in RoadModel.", fromObj); return getShortestPathTo(getPosition(fromObj), to); } @Override public boolean doRegister(RoadUser roadUser) { LOGGER.info("register {}", roadUser); roadUser.initRoadUser(self); return true; } @Override public boolean unregister(RoadUser roadUser) { final boolean contains = containsObject(roadUser); LOGGER.info("unregister {} succes: {}", roadUser, contains); if (contains) { removeObject(roadUser); return true; } return false; } @Override public final EventAPI getEventAPI() { return eventDispatcher.getPublicEventAPI(); } @Override public Unit<Length> getDistanceUnit() { return unitConversion.getExDistUnit(); } @Override public Unit<Velocity> getSpeedUnit() { return unitConversion.getExSpeedUnit(); } @SuppressWarnings("null") private static class SameLocationPredicate implements Predicate<RoadUser> { private final RoadUser reference; private final RoadModel model; private final Class<?> type; SameLocationPredicate(final RoadUser pReference, final Class<?> pType, final RoadModel pModel) { reference = pReference; type = pType; model = pModel; } @Override public boolean apply(@Nullable RoadUser input) { return type.isInstance(input) && model.equalPosition(verifyNotNull(input), reference); } } /** * Simple class for storing destinations and paths leading to them. * @author Rinde van Lon */ protected class DestinationPath { /** * The destination of the path. */ public final Point destination; /** * The path leading to the destination. */ public final Queue<Point> path; DestinationPath(Point dest, Queue<Point> p) { destination = dest; path = p; } } }