Navigator

package com.tegl.commercial.trade.marketagent.navigation;

import com.tegl.commercial.trade.marketagent.InputException;

import com.tegl.commercial.trade.marketagent.MASettings;

import com.tegl.commercial.trade.marketagent.entities.SS;

import com.tegl.commercial.trade.marketagent.io.EveGameDataReader;

import com.tegl.commercial.trade.marketagent.util.MAC;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import java.util.*;

/**

* The Navigation engine wraps the internal model of the Eve universe. It handles all requests

* for information related to finding paths, distances, or relationships of entities in space.

*/

public class Navigator {

private static Log log = LogFactory.getLog(Navigator.class);

private static Map pathCache = new HashMap();

private static Map overMaxDistancePathCache = new HashMap();

private static final OriginSSVisitor originVisitor = new OriginSSVisitor();

private static final TerminusSSVisitor terminusVisitor = new TerminusSSVisitor();

private static boolean pathDirty = true;// Indicates that some sss may have their pathParents set

public static final int NO_DISTANCE_LIMIT = -1;

public static final int ZERO_JUMPS = 0;

// This is placed into a path object that has no valid route. No need to recreate it.

private static final LinkedList EMPTY_SSLIST = new LinkedList();

private static final String dlm = "=";

// ======================================================================

// Client access

// ======================================================================

public static int getDistance(SS origin, SS terminus) {

return findPath(origin, terminus, NO_DISTANCE_LIMIT, 0.0f, 1.0f).getJumps();

}

/**

* This method first checks to see if origin and dest are the same.

* If so, the distance is 0, and the Path contains an empty LinkedList,

* and we save some work.<br>

* So don't do your own 0-jump checking. This will do it for you.

*

* @param originId The starting system id

* @param terminusId The destination system id

* @param maxDistance The maximum allowable distance, inclusive. If 0, no limit is enforced

* @param minSecurityLimit The lowest allowable system sec rating for the route

* @param maxSecurityLimit The highest allowable system sec rating for the route

* @return The Path object, or null if no path exists within the search parameters.<br>

* The Path object contains:

* <ul>

* <li>A List of ss's which are the path.</li>

* <li>The # of jumps in the path</li>

* <li>The lowest security rating in the path</li>

* <li>The highest security rating in the path</li>

* </ul>

* @throws InputException if either the origin or terminus system was invalid

*/

public static Path findPath(int originId,

int terminusId,

int maxDistance,

float minSecurityLimit,

float maxSecurityLimit) {

SS origin = SS.ss(originId);

if (origin == null) {

throw new InputException("" + originId, "Origin system was not found");

}

SS terminus = SS.ss(terminusId);

if (terminus == null) {

throw new InputException("" + terminusId, "Terminus system was not found");

}

return findPath(origin, terminus, maxDistance, minSecurityLimit, maxSecurityLimit);

}

/**

* This method first checks to see if origin and dest are the same.

* If so, the distance is 0, and the Path contains an empty LinkedList,

* and we save some work.<br>

* So don't do your own 0-jump checking. This will do it for you.

*

* @param originName The starting system name

* @param terminusName The destination system name

* @param maxDistance The maximum allowable distance, inclusive. If 0, no limit is enforced

* @param minSecurityLimit The lowest allowable system sec rating for the route

* @param maxSecurityLimit The highest allowable system sec rating for the route

* @return The Path object, or null if no path exists within the search parameters.<br>

* The Path object contains:

* <ul>

* <li>A List of ss's which are the path.</li>

* <li>The # of jumps in the path</li>

* <li>The lowest security rating in the path</li>

* <li>The highest security rating in the path</li>

* </ul>

* @throws InputException if either the origin or terminus system was invalid

*/

public static Path findPath(String originName,

String terminusName,

int maxDistance,

float minSecurityLimit,

float maxSecurityLimit) {

SS origin = SS.ss(originName);

if (origin == null) {

throw new InputException(originName, "Origin system was not found");

}

SS terminus = SS.ss(terminusName);

if (terminus == null) {

throw new InputException(terminusName, "Terminus system was not found");

}

return findPath(origin, terminus, maxDistance, minSecurityLimit, maxSecurityLimit);

}

/**

* This method first checks to see if origin and dest are the same.

* If so, the distance is 0, and the Path contains an empty LinkedList,

* and we save some work.<br>

* So don't do your own 0-jump checking. This will do it for you.

*

* @param origin The starting system

* @param terminus The destination system

* @param maxDistance The maximum allowable distance, inclusive. If 0, no limit is enforced

* @param minSecurityLimit The lowest allowable system sec rating for the route

* @param maxSecurityLimit The highest allowable system sec rating for the route

* @return The Path object, or null if no path exists within the search parameters.<br>

* The Path object contains:

* <ul>

* <li>A List of ss's which are the path.</li>

* <li>The # of jumps in the path</li>

* <li>The lowest security rating in the path</li>

* <li>The highest security rating in the path</li>

* </ul>

*/

public static Path findPath(SS origin,

SS terminus,

int maxDistance,

float minSecurityLimit,

float maxSecurityLimit) {

log.trace("findPath()");

if (log.isDebugEnabled()) pl("===========Search: "+ origin.getName() + " -> " + terminus.getName() + "=============================");

// ==================================================

// First, check the cache holding paths that failed

// due to exceeding distance limits...

// ==================================================

String key = keyForOverMaxJumps(origin, terminus, maxDistance, minSecurityLimit, maxSecurityLimit);

if (overMaxDistancePathCache.get(key) != null) {

if (log.isDebugEnabled()) pl(key);

if (log.isDebugEnabled()) pl("FailCache hit: [Over distance limit] " + showKey(key));

return null;

}

// ==================================================

// Next, check the success cache...

// ==================================================

Path cached;

key = keyFor(origin, terminus, minSecurityLimit, maxSecurityLimit);

if ( (cached = (Path) pathCache.get(key) ) != null) {

pl(key);

p("Cache hit: ");

// There's one in the cache for the current secRating limits.

// But is it inside the distance limits?

// If so, return it. If not, return null. There IS no route.

if (maxDistance == NO_DISTANCE_LIMIT || cached.getJumps() <= maxDistance) {

if (log.isDebugEnabled()) pl("[VALID] " + cached);

if (log.isDebugEnabled()) pl(" " + showKey(key));

return cached;

} else {

if (log.isDebugEnabled()) pl("[Over jump limit] " + cached);

return null;

}

}

// ==================================================

// Quit early if origin == terminus

// ==================================================

if (origin == terminus) {

Path path = new Path(EMPTY_SSLIST, ZERO_JUMPS, origin.getSecurity(), terminus.getSecurity());

if (log.isDebugEnabled()) pl("Zero-jump route: " + origin.getName());

return path;

}

// ==================================================

// Quit early if the terminus has an out-of-bounds secRating:

// ==================================================

if (!isWithinSecRatingLimits(terminus, minSecurityLimit, maxSecurityLimit)) {

if (log.isDebugEnabled()) pl(terminus.getName() + " is out of sec-limits: " + terminus.getSecurity());

return null;

}

// ==================================================

// Otherwise, do the hard work

// ==================================================

SS[] originConnections = new SS[]{origin};

SS[] terminusConnections = new SS[]{terminus};

originVisitor.setSearchParams(origin, terminus, minSecurityLimit, maxSecurityLimit);

terminusVisitor.setSearchParams(origin, terminus, minSecurityLimit, maxSecurityLimit);

// Set Dirty, cuz we could have visitors setting pathParent fields now

pathDirty = true;

// Monitor the number of jumps we're making. If it goes over the max, might as well quit.

int pathDistance = 0;

// As long as we keep getting back 2 sets of connections to traverse and haven't broken

// the loop, traverse and keep going.

while (originConnections.length > 0 && terminusConnections.length > 0) {

try {

pl("-------------------------------------------------");

// Traverse one set of connections from origin and increment/check pathDistance

originConnections = traverseBfOneLevel(originConnections, originVisitor);

if (isOverDistanceLimit(++pathDistance, maxDistance, origin, terminus, "[O]", minSecurityLimit, maxSecurityLimit)) {

return null;

}

pl("-------------------------------------------------");

// Traverse one set of connections from dest and increment/check pathDistance

terminusConnections = traverseBfOneLevel(terminusConnections, terminusVisitor);

if (isOverDistanceLimit(++pathDistance, maxDistance, origin, terminus, "[T]", minSecurityLimit, maxSecurityLimit)) {

return null;

}

} catch (AbortTraversalException e) {

if (log.isDebugEnabled()) pl("Done: " + e.getMessage());

// ==========================================================================

// If we get an exception, it's because we're DONE traversing, but we could

// still have a pathDistance greater than maxJumps because the exception would

// get thrown before pathDistance would get incremented.

// ==========================================================================

if (isOverDistanceLimit(++pathDistance, maxDistance, origin, terminus, "[E]", minSecurityLimit, maxSecurityLimit)) {

return null;

}

// ==================================================

// SUCCESS!!

// Retrace the path and store it in the cache

// ==================================================

Path path = retracePath(e.getSS());

pathCache.put(keyFor(origin, terminus, minSecurityLimit, maxSecurityLimit), path);

clearPath(AbstractSSVisitor.getPathedSSs(), pathDirty);// Clean up all the references to pathParents

return path;

}

}

pl("No valid paths");

clearPath(AbstractSSVisitor.getPathedSSs(), pathDirty);// Clean up all the references to pathParents

return null;

}

/**

* Use this method when the origin system is your current system.

* <p>

* This method first checks to see if origin and dest are the same.

* If so, the distance is 0, and the Path contains an empty LinkedList,

* and we save some work.<br>

* So don't do your own 0-jump checking. This will do it for you.

*

* @param terminusId The destination system id

* @param minSecurityLimit The lowest allowable system sec rating for the route

* @param maxSecurityLimit The highest allowable system sec rating for the route

* @return The Path object, or null if no path exists within the search parameters.<br>

* The Path object contains:

* <ul>

* <li>A List of ss's which are the path.</li>

* <li>The # of jumps in the path</li>

* <li>The lowest security rating in the path</li>

* <li>The highest security rating in the path</li>

* </ul>

*/

public static Path findPathTo(int terminusId, float minSecurityLimit, float maxSecurityLimit) {

return findPath(SS.ss(MASettings.getCurrentSystemSsid()), SS.ss(terminusId), NO_DISTANCE_LIMIT, minSecurityLimit, maxSecurityLimit);

}

/**

* Performs a breadth-first node-traversal search from the "center" system outward until maxDistance is

* reached.

* @param center You want to find systems near this one

* @param maxDistance The max distance (inclusive) of the search.

* @return An array of SS objects which are within the maxDistance of the center system.

*/

public static SS[] findSystemsNear(SS center, int maxDistance) {

TrueVisitor trueVisitor = new TrueVisitor(center);

int distance = 1;

SS[] connections = new SS[]{center};

while (distance <= maxDistance) {

try {

connections = traverseBfOneLevel(connections, trueVisitor);

} catch (AbortTraversalException e) {

// won't happen for the trueVisitor

}

if (connections.length == 0) {

break;

}

else {

distance++;

}

}

ArrayList pathedSSs = trueVisitor.getPathedSSs();

SS[] systemsNear = (SS[]) pathedSSs.toArray(new SS[0]);

clearPath(pathedSSs, true);

return systemsNear;

}

static boolean isWithinSecRatingLimits(SS ss, float minSecurityLimit, float maxSecurityLimit) {

return ss.getSecurity() >= minSecurityLimit && ss.getSecurity() <= maxSecurityLimit;

}

// ======================================================================

// Private methods

// ======================================================================

private static boolean isOverDistanceLimit(int pathDistance,

int maxDistance,

SS origin,

SS terminus,

String prefix,

float lowSecLimit,

float highSecLimit) {

// If limit enforced, and over limit

boolean overLimit = maxDistance != NO_DISTANCE_LIMIT && pathDistance > maxDistance;

if (overLimit) {

overMaxDistancePathCache.put(keyForOverMaxJumps(origin, terminus, maxDistance, lowSecLimit, highSecLimit), "");

if (log.isDebugEnabled()) pl(prefix + "Over distance limit: " + pathDistance);

clearPath(AbstractSSVisitor.getPathedSSs(), pathDirty);// Clean up all the references to pathParents

}

return overLimit;

}

private static SS[] traverseBfOneLevel(SS[] systems, SSVisitor visitor) throws AbortTraversalException {

// Keep a list of the next level of connections

ArrayList connectionList = new ArrayList();

for (int i = 0; i < systems.length; i++) {

SS parentSS = systems[i];

SS[] connections = parentSS.getConnections();

// Visit the ss's connections. If the return value is false,

// then this path is no good; don't add the connections

for (int j = 0; j < connections.length; j++) {

SS connection = connections[j];

// If we approve of this ss, add it to the next connection

// list to be traversed. Otherwise, leave it out.

if (visitor.visit(connection, parentSS)) {

// Save off a list of all connections of these sss

connectionList.add(connection);

}

}

}

return (SS[]) connectionList.toArray(new SS[0]);

}

/**

* Nullify all references to pathParents

*/

private static void clearPath(List SSsToClear, boolean pathDirty) {

p("Clearing paths: ");

// Check to make sure we need to do this. Only if dirty

if (pathDirty) {

for (int i = 0; i < SSsToClear.size(); /*don't increment*/) {

SS ss = (SS) SSsToClear.remove(i);

if (log.isDebugEnabled()) logClear(ss, "" + SSsToClear.size()); // Don't even process parameters unless we're in debug mode.

ss.setOriginPathParent(null);

ss.setTerminusPathParent(null);

}

pl("");

pathDirty = false;

}

}

/**

* @param ss the center ss of the path - the one where the two expanding spheres meet. This

* ss is thrown with the AbortTraversalException

* @return A list of ss' which are the path. null if ss == null.

*/

private static Path retracePath(SS ss) {

// Sanity check

if (ss == null) {

return null;

}

// Keep track of the number of jumps

int distance = 0; // Don't add one for the origin system

// Keep track of lowest and highest security ratings

float lowSec = Float.MAX_VALUE; // Start high and reduce

float highSec = Float.MIN_VALUE; // Start low and increase

LinkedList ssList = new LinkedList();

// Start from the middle system:

SS termP = ss;

SS orgnP = ss;

ssList.add(ss);

lowSec = Math.min(lowSec, ss.getSecurity());

highSec = Math.max(highSec, ss.getSecurity());

// Traverse the terminus path (the back leg) first, adding systems on top of each other

while ((termP = termP.getTerminusPathParent()) != null) {

ssList.add(termP);

distance++; // increment distance

lowSec = Math.min(lowSec, termP.getSecurity());

highSec = Math.max(highSec, termP.getSecurity());

}

// Traverse the origin path (the front leg) first, adding systems to the front

while ((orgnP = orgnP.getOriginPathParent()) != null) {

ssList.addFirst(orgnP);

distance++; // increment distance

lowSec = Math.min(lowSec, orgnP.getSecurity());

highSec = Math.max(highSec, orgnP.getSecurity());

}

Path path = new Path(ssList, distance, lowSec, highSec);

return path;

}

/**

* Creates a key for the caching of this path.<br>

* The path can be different based on security limits. But the path will not be different

* for distance limits - it just may be filtered out. Therefore the key is made like this

* (with a delim between each element):<br>

* <pre>

* [originId][terminusId][lowSecLimit][highSecLimit]

* </pre>

* @param origin

* @param terminus

* @param lowSecLimit

* @param highSecLimit

* @return

*/

private static String keyFor(SS origin, SS terminus, float lowSecLimit, float highSecLimit) {

StringBuffer buf = new StringBuffer();

buf.append(origin.getSsid()).append(dlm);

buf.append(terminus.getSsid()).append(dlm);

buf.append(lowSecLimit).append(dlm);

buf.append(highSecLimit);

return buf.toString();

}

/**

* Creates a key for caching the info that the attempt to find this path with these max jump

* settings and secRating settings fails.

* @param origin

* @param terminus

* @param lowSecLimit

* @param highSecLimit

* @return a key for a failed-by-maxdistance path cache

*/

private static String keyForOverMaxJumps(SS origin,

SS terminus,

int maxDistance,

float lowSecLimit,

float highSecLimit) {

// start with the basic key. Add a delim, and the maxDistance

return keyFor(origin, terminus, lowSecLimit, highSecLimit) + dlm + maxDistance;

}

private static String showKey(String key) {

String s = null;

if (log.isDebugEnabled()) {

String[] tokens = key.split(dlm);

s = SS.nameFor(Integer.parseInt(tokens[0])) + " -> " + SS.nameFor(Integer.parseInt(tokens[1])) + " " + tokens[2] + "-" + tokens[3];

if (tokens.length == 5) {

s = s + " " + tokens[4];

}

}

return s;

}

// ======================================================================

// Visitor Classes

// ======================================================================

static abstract class AbstractSSVisitor implements SSVisitor {

protected static ArrayList pathedSSs = new ArrayList();

protected SS originSS;

protected SS terminusSS;

protected float minSecurityLimit = 0.0f;

protected float maxSecurityLimit = 1.0f;

public static ArrayList getPathedSSs() {

return pathedSSs;

}

public void setSearchParams(SS originSS, SS terminusSS, float minSecurityLimit, float maxSecurityLimit) {

this.originSS = originSS;

this.terminusSS = terminusSS;

this.minSecurityLimit = minSecurityLimit;

this.maxSecurityLimit = maxSecurityLimit;

}

}

static class OriginSSVisitor extends AbstractSSVisitor implements SSVisitor {

public boolean visit(SS ss, SS parentSS) throws AbortTraversalException {

if (log.isDebugEnabled()) p("[O->] (" + parentSS.getName() + ")->" + ss.getName() + ":");

// We bumped back into the originating ss or one we've visited already

if (ss == originSS || ss.getOriginPathParent() != null) {

pl(" -| loop");

return false;

}

if (!isWithinSecRatingLimits(ss, minSecurityLimit, maxSecurityLimit)) {

pl(" -| sec");

return false;

}

ss.setOriginPathParent(parentSS);

pathedSSs.add(ss); // Keep track so we can reset it later

// If this ss was visited by the other path...

if (ss.getTerminusPathParent() != null) {

pl(" !One!");

throw new AbortTraversalException("Found a terminus ss: " + ss, ss);

}

// If we have found the terminus ss

if (ss == terminusSS) {

pl(" !!It!!");

throw new AbortTraversalException("Found THE terminus ss: " + ss, ss);

}

pl(" ->");

return true;

}

}

static class TerminusSSVisitor extends AbstractSSVisitor implements SSVisitor {

public boolean visit(SS ss, SS parentSS) throws AbortTraversalException {

if (log.isDebugEnabled()) p("[<-T] (" + parentSS.getName() + ")->" + ss.getName() + ":");

// We bumped back into the originating ss or one we've visited already

if (ss == terminusSS || ss.getTerminusPathParent() != null) {

pl(" -| loop");

return false;

}

if (!isWithinSecRatingLimits(ss, minSecurityLimit, maxSecurityLimit)) {

pl(" -| sec");

return false;

}

ss.setTerminusPathParent(parentSS);

pathedSSs.add(ss); // Keep track so we can reset it later

// If this ss was visited by the other path...

if (ss.getOriginPathParent() != null) {

pl(" !One!");

throw new AbortTraversalException("Found an origin ss: " + ss, ss);

}

// If we have found the originination ss

if (ss == originSS) {

pl(" !!It!!");

throw new AbortTraversalException("Found THE origin ss: " + ss, ss);

}

pl(" ->");

return true;

}

}

// ======================================================================

// Command-line access

// ======================================================================

private static final String HSL = "+";

private static final String LSL = "-";

/**

* <pre>

* StartSysName|StartSysId DestSysName|DestSysId [-minSecRating] [+maxSecRating]

* </pre>

* <p>- or -

* <pre>

* StartSysName|StartSysId maxDistance

* </pre>

*

* @param args

* @throws Exception

*/

public static void main(String[] args) throws Exception {

if (args.length < 2) {

usage("Not enough arguments.");

return;

}

EveGameDataReader.init();

MASettings.init();

// If there are 2 args, and the second is 1 or 2 chars, treat it as a distance

if (args.length == 2 && args[1].length() < 3) {

SS center = SS.ssFor(args[0]);

if (center == null) {

usage("You must specify a \"center\" system.");

return;

}

int maxDistance = 0;

try {

maxDistance = Integer.parseInt(args[1]);

} catch (NumberFormatException e) {

usage("The max distance value you specified [" + args[1] + "] is not a number.");

return;

}

SS[] systemsNear = findSystemsNear(center, maxDistance);

for (int i = 0; i < systemsNear.length; i++) {

SS ss = systemsNear[i];

int distance = findPath(center, ss, NO_DISTANCE_LIMIT, 0.0f, 1.0f).getJumps();

System.out.println("(" + distance + ") " + ss);

}

return;

}

showPaths(args);

}

private static void showPaths(String[] args) {

try {

SS origin = SS.ssFor(args[0]);

SS terminus = SS.ssFor(args[1]);

if (origin == null || terminus == null) {

usage("You must specify an origin and a terminus system.");

return;

}

float minSecLimit = 0.0f;

float maxSecLimit = 1.0f;

Float[] minMaxSecs = getMinMaxArgs(args);

if (minMaxSecs != null) {

if (minMaxSecs[0] != null) {

minSecLimit = minMaxSecs[0].floatValue();

}

if (minMaxSecs[1] != null) {

maxSecLimit = minMaxSecs[1].floatValue();

}

}

System.out.println("Search: " + origin.getName() + " -> " + terminus.getName() + MAC.I + " " + LSL + (minSecLimit) + " " + HSL + (maxSecLimit));

Path path = findPath(origin, terminus, NO_DISTANCE_LIMIT, minSecLimit, maxSecLimit);

fullPrintPath(path);

} catch (Exception e) {

System.err.println("There was an error" + MAC.CR);

System.err.println(e.getMessage() + MAC.CR + "See below for details." + MAC.CR);

usage(null);

e.printStackTrace();

}

}

private static Float[] getMinMaxArgs(String[] args) {

Float[] minMax = new Float[2];

for (int i = 2; i < 4; i++) {

String arg;

try {

arg = args[i];

} catch (Exception e) {

continue;

}

if (arg.startsWith(LSL)) {

minMax[0] = new Float(arg.substring(1));

}

else if (arg.startsWith(HSL)) {

minMax[1] = new Float(arg.substring(1));

}

}

return minMax;

}

private static void usage(String errMsg) {

StringBuffer usage = new StringBuffer();

errMsg = errMsg == null ? "" : errMsg;

usage.append(errMsg).append(MAC.CR).append(MAC.CR);

usage.append(MAC.CR).append("USAGE: ").append(MAC.CR);

usage.append("Get path:").append(MAC.CR);

usage.append("nav startSystem destSystem [" + LSL + "minSecRating] [" + HSL + "maxSecRating]").append(MAC.CR);

usage.append(MAC.CR);

usage.append("OR" + MAC.CR);

usage.append("Get list of systems within n jumps:").append(MAC.CR);

usage.append("nav startSystem maxDistance").append(MAC.CR);

usage.append(MAC.CR);

usage.append("Where:").append(MAC.CR);

usage.append("startSystem").append(" = Exact name OR ID of start system").append(MAC.CR);

usage.append("destSystem").append(" = Exact name OR ID of destination system").append(MAC.CR);

usage.append(LSL + "minSecRating").append(" = Only Systems with this secrating or higher will be included in the route.").append(" (OPTIONAL) (Default: 0.0)").append(MAC.CR);

usage.append(HSL + "maxSecRating").append(" = Only Systems with this secrating or lower will be included in the route.").append(" (OPTIONAL) (Default: 1.0)").append(MAC.CR);

usage.append("maxDistance").append(" = max distance from startSystem").append(MAC.CR);

usage.append(MAC.CR);

usage.append("Example:*").append(MAC.CR);

usage.append("---------------------------").append(MAC.CR);

usage.append("nav Bar Asghatil " + LSL + "0.8").append(MAC.CR);

usage.append("---------------------------").append(MAC.CR);

usage.append("The resulting route will go thru Jarizza (0.8), because Sucha is 0.7").append(MAC.CR);

usage.append("*Note that you must include the '" + LSL + "' and '" + HSL + "' signs").append(MAC.CR);

usage.append(MAC.CR);

usage.append("Example:").append(MAC.CR);

usage.append("---------------------------").append(MAC.CR);

usage.append("nav Sasta 2 ").append(MAC.CR);

usage.append("---------------------------").append(MAC.CR);

usage.append("You will get a list of all systems within 2 jumps of Sasta").append(MAC.CR);

usage.append(MAC.CR);

System.out.println(usage.toString());

}

private static void fullPrintPath(Path path) {

if (path != null) {

List ssList = path.getSsList();

System.out.println("===================================================================");

System.out.println("Jumps = " + path.getJumps());

System.out.println("LowSec = " + path.getLowSec());

System.out.println("HighSec = " + path.getHighSec());

for (int i = 0; i < ssList.size(); i++) {

SS ss = (SS) ssList.get(i);

System.out.println(i + " " + ss);

}

} else {

System.out.println("No path");

}

}

// ======================================================================

// Debug methods and junk

// ======================================================================

private static void logClear(SS ss, String s) {

// Don't even do this if we're not in debug mode.

if (log.isDebugEnabled()) {

SS oParent = ss.getOriginPathParent();

SS tParent = ss.getTerminusPathParent();

if (oParent == null && tParent == null) {

log.error("No parents set!");

}

if (oParent != null) {

String oParentName = (oParent == null) ? "oParentNull" : oParent.getName();

String oName = (ss == null) ? "oNameNull" : ss.getName();

p("([O]" + oParentName + " -> " + oName + ") " + s + " ");

}

if (tParent != null) {

String tParentName = (tParent == null) ? "tParentNull" : tParent.getName();

String tName = (ss == null) ? "tNameNull" : ss.getName();

p("([T]" + tParentName + " -> " + tName + ") " + s + " ");

}

}

}

private static StringBuffer mem = new StringBuffer();

private static void p(String s) {

if (log.isDebugEnabled()) {

mem.append(s);

}

}

private static void pl(String s) {

if (log.isDebugEnabled()) {

log.debug(mem + s);

mem.delete(0, mem.length()); // clear it

}

}

private static String listConnections(SS[] terminusConnections) {

StringBuffer buf = new StringBuffer("connections of: ");

for (int i = 0; i < terminusConnections.length; i++) {

SS ss = terminusConnections[i];

buf.append(ss.getName());

if (i != terminusConnections.length - 1) {

buf.append(", ");

}

}

return buf.toString();

}

}