/************************************************************************************
MRailSim - a model railway simulation program - http://mrailsim.sourceforge.net/
Copyright (C) 2004,2007 Bernd Arnold
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU 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 net.sf.mrailsim.rails;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.sf.mrailsim.main.Game;
import net.sf.mrailsim.shared.Coordinates;
import net.sf.mrailsim.shared.Logger;
import net.sf.mrailsim.signals.MainSectionSignal;
import net.sf.mrailsim.signals.MainSignal;
import net.sf.mrailsim.signals.PreSignal;
import net.sf.mrailsim.signals.Signal;
public class TrackManager {
/**
* The list with all tracks.
*/
private ArrayList<Track> trackList;
private ArrayList<TrackSection> trackSectionList;
private ArrayList<Signal> signalList;
/**
* Class constructor; allocates a new empty track list.
*/
public TrackManager() {
trackList = new ArrayList<Track>();
trackSectionList = new ArrayList<TrackSection>();
signalList = new ArrayList<Signal>();
}
/**
* Returns the total number of registered tracks.
* @return the count of tracks
*/
public int getTrackCount() {
return trackList.size();
}
/**
* Returns the track object with the given id
* @param id the id of the track
* @return the track object or <code>null</code>, if not found
*/
public Track getTrack( long id ) {
for ( Track item: trackList ) {
if ( item.getId() == id ) {
return item;
}
}
return null;
}
public TrackSection getTrackSection( long id ) {
for ( TrackSection item: trackSectionList ) {
if ( item.getId() == id ) {
return item;
}
}
return null;
}
public Signal getSignal( long id ) {
for ( Signal item: signalList ) {
if ( item.getId() == id ) {
return item;
}
}
return null;
}
/**
* Adds a track to the list. This should be the only one function that adds
* tracks to the track list.
* @param track the new track
*/
void addTrack( Track track ) {
// TODO: Check if id was already assigned - if yes, exception!
trackList.add( track );
}
/**
* Parses the string describing a new track and calls the addTrack(long, String, int, String) method.
* @param s describing the new track, for example: <code>Id=1,Type=Straight,Length=50,Nodes=(1,3)</code>
* @see #addTrack(long, String, int, String)
*/
public void addTrack( String s ) {
Pattern pattern = Pattern.compile( "^Id=(.*),Type=(.*),Length=(.*),Nodes=\\((.*)\\)$" );
Matcher matcher = pattern.matcher( s );
if ( ! matcher.matches() ) {
// TODO: Handle right
System.err.println( "addTrack mismatch: " + s );
System.exit(1);
}
long id = Long.parseLong( matcher.group( 1 ) );
String type = matcher.group( 2 );
int length = Integer.parseInt( matcher.group( 3 ) );
String nodes = matcher.group( 4 );
addTrack( id, type, length, nodes );
}
public void addTrackSection( String s ) {
Pattern pattern = Pattern.compile( "^Id=(.*)$" );
Matcher matcher = pattern.matcher( s );
if ( ! matcher.matches() ) {
// TODO: Handle right
System.err.println( "addTrackSection mismatch: " + s );
System.exit(1);
}
long id = Long.parseLong( matcher.group( 1 ) );
addTrackSection( id );
}
private void addTrackSection( long id ) {
// TODO: Check if id was already added
trackSectionList.add( new TrackSection( id ) );
}
/**
* Adds a new track.
* @param id id of the track
* @param type type of the track
* @param length the length of the track
* @param nodeString the nodes of the track
*/
private void addTrack( long id, String type, int length, String nodeString ) {
Track track = null;
Node nodes[];
Junction j = new Junction();
Logger.log( Logger.Component.TRACK, Logger.Level.INFORMATION,
String.format( "Adding track #%d, %s, nodes: %s.",
id, type, nodeString )
);
String nodeStrings[] = nodeString.split( "," );
long nodeIds[] = new long[ nodeStrings.length ];
for ( int i = 0; i < nodeStrings.length; i++ ) {
nodeIds[ i ] = Integer.parseInt( nodeStrings[ i ] );
}
nodeStrings = null;
if ( type.toLowerCase().equals( "straight" ) ) {
// nodes should be like '1,2'
if ( nodeIds.length == 2 ) {
track = new TrackStraight( id, length, j );
nodes = Game.nodeManager.registerNodes( nodeIds, track );
Position p = new Position( new NodeConnector( nodes[ 0 ], nodes[ 1 ] ) ) ;
j.add( p );
}
}
if ( type.toLowerCase().equals( "x" ) ) {
// nodes should be like '1,2,3,4'
if ( nodeIds.length == 4 ) {
track = new TrackX( id, length, j );
nodes = Game.nodeManager.registerNodes( nodeIds, track );
j.add( new Position(
new NodeConnector( nodes[ 0 ], nodes[ 1 ] ),
new NodeConnector( nodes[ 2 ], nodes[ 3 ] ) ) );
}
}
if ( type.toLowerCase().equals( "switch_1" ) ) {
// nodes should be like '1,2,3'
if ( nodeIds.length == 3 ) {
track = new TrackSwitch1( id, length, j );
nodes = Game.nodeManager.registerNodes( nodeIds, track );
j.add( new Position( new NodeConnector( nodes[ 0 ], nodes[ 1 ] ) ) );
j.add( new Position( new NodeConnector( nodes[ 0 ], nodes[ 2 ] ) ) );
}
}
if ( type.toLowerCase().equals( "switch_2" ) ) {
// nodes should be like '1,2,3,4'
if ( nodeIds.length == 4 ) {
track = new TrackSwitch2( id, length, j );
nodes = Game.nodeManager.registerNodes( nodeIds, track );
j.add( new Position( new NodeConnector( nodes[ 0 ], nodes[ 1 ] ) ) );
j.add( new Position( new NodeConnector( nodes[ 0 ], nodes[ 2 ] ) ) );
j.add( new Position( new NodeConnector( nodes[ 0 ], nodes[ 3 ] ) ) );
}
}
if ( type.toLowerCase().equals( "switch_x" ) ) {
// nodes should be like '1,2,3,4'
if ( nodeIds.length == 4 ) {
track = new TrackSwitchX( id, length, j );
nodes = Game.nodeManager.registerNodes( nodeIds, track );
j.add( new Position(
new NodeConnector( nodes[ 0 ], nodes[ 1 ] ),
new NodeConnector( nodes[ 2 ], nodes[ 3 ] ) ) );
j.add( new Position(
new NodeConnector( nodes[ 0 ], nodes[ 2 ] ),
new NodeConnector( nodes[ 3 ], nodes[ 1 ] ) ) );
}
}
if ( track != null ) {
addTrack( track );
return;
}
// TODO: Do it right and remove all these silly lines
System.err.println( "addTrack unkown: " + id + ", type " + type + ", nodes " + nodeString );
System.exit( 1 );
}
/**
* Prints on the console which way a train would drive. Each track
* a train would enter is printed as well as the node ids of the
* incoming and outgoing nodes.
*
* @param track the track where to start
* @param node the node where the track was entered
* @param depth the number of following tracks that should be investigated
*/
public void printWayFrom( Track track, Node node, int depth ) {
Track currentTrack = track;
Node lastNode = node;
System.out.println( "The way goes just like this:" );
System.out.println( " Started at track: #" + currentTrack.getId() + ", node #" + lastNode.getId() );
while ( depth > 0 ) {
Node nextNode = null;
Track nextTrack = null;
Node followingNode = null;
try {
nextNode = currentTrack.getOutgoingNode( lastNode );
nextTrack = nextNode.getOtherTrack( currentTrack );
followingNode = nextTrack.getOutgoingNode( nextNode );
} catch (Exception e) {
e.printStackTrace();
System.exit( 2 );
}
String out = String.format( " Next track: %5d, entered at node #%5d, will leave at node #%5d.",
nextTrack.getId(), nextNode.getId(), followingNode.getId() );
System.out.println( out );
currentTrack = nextTrack;
lastNode = nextNode;
depth--;
}
}
/**
* Sets a new position of a track. The new position is transmitted
* to the specified track and handled there.
* @param s specifies which track and position, for example
* <code>Id=5,Newpos=1</code> or <code>Id=5,Nextpos</code>
* @see Track#setPosition(int)
*/
public void setPosition( String s ) {
Pattern pattern;
Matcher matcher;
pattern = Pattern.compile( "^Id=(\\d+),Newpos=(\\d+)$" );
matcher = pattern.matcher( s );
if ( matcher.matches() ) {
long id = Long.parseLong( matcher.group( 1 ) );
int pos = Integer.parseInt( matcher.group( 2 ) );
Track track = getTrack( id );
if ( track != null ) {
track.setPosition( pos );
} else {
// TODO: Handle better
System.err.println( "setPosition(String): Track #" + id + " does not exist." );
}
return;
}
pattern = Pattern.compile( "^Id=(\\d+),Nextpos$" );
matcher = pattern.matcher( s );
if ( matcher.matches() ) {
long id = Long.parseLong( matcher.group( 1 ) );
Track track = getTrack( id );
if ( track != null ) {
track.setNextPosition();
} else {
// TODO: Handle better
System.err.println( "setPosition(String): Track #" + id + " does not exist." );
}
return;
}
// TODO: Handle right
System.err.println( "setPosition mismatch: " + s );
System.exit(1);
}
/**
* Draw all the tracks and signals into the given graphic context.
* @param g The graphic context.
*/
public void draw( Graphics g ) {
for ( Track item : trackList ) {
item.draw( g );
}
for ( Signal item : signalList ) {
item.draw( g );
}
}
public void setTrackSection( String s ) {
Pattern pattern = Pattern.compile( "^Section=(\\d+),Track=(\\d+)$" );
Matcher matcher = pattern.matcher( s );
if ( ! matcher.matches() ) {
// TODO: Handle right
System.err.println( "setTrackSection mismatch: " + s );
System.exit(1);
}
long sectionId = Long.parseLong( matcher.group( 1 ) );
long trackId = Integer.parseInt( matcher.group( 2 ) );
TrackSection trackSection = getTrackSection( sectionId );
if ( trackSection != null ) {
trackSection.addTrack( trackId );
} else {
// TODO: Handle better
System.err.println( "setSection(String): Section #" + sectionId + " does not exist." );
}
}
public void addSignal( String s ) {
Pattern pattern;
Matcher matcher;
pattern = Pattern.compile( "^Id=(.*),Section=(.*)$" );
matcher = pattern.matcher( s );
if ( matcher.matches() ) {
long signalId = Long.parseLong( matcher.group( 1 ) );
long sectionId = Long.parseLong( matcher.group( 2 ) );
addSectionSignal( signalId, sectionId );
return;
}
pattern = Pattern.compile( "^Id=(.*),Lookahead=(.*),StartTrack=(.*),StartNode=(.*)$" );
matcher = pattern.matcher( s );
if ( matcher.matches() ) {
long signalId = Long.parseLong( matcher.group( 1 ) );
int lookAmount = Integer.parseInt( matcher.group( 2 ) );
long trackId = Long.parseLong( matcher.group( 3 ) );
long nodeId = Long.parseLong( matcher.group( 4 ) );
addLookaheadSignal( signalId, lookAmount, trackId, nodeId );
return;
}
pattern = Pattern.compile( "^Id=(.*),Main=(.*),BoundTrack=(.*),BoundNode=(.*)$" );
matcher = pattern.matcher( s );
if ( matcher.matches() ) {
long signalId = Long.parseLong( matcher.group( 1 ) );
long mainId = Long.parseLong( matcher.group( 2 ) );
long trackId = Long.parseLong( matcher.group( 3 ) );
long nodeId = Long.parseLong( matcher.group( 4 ) );
addPreSignal( signalId, mainId, trackId, nodeId );
return;
}
// TODO: Handle right
System.err.println( "addSignal mismatch: " + s );
System.exit( 1 );
}
private void addSectionSignal( long signalId, long sectionId ) {
TrackSection section = getTrackSection( sectionId );
if ( section == null ) {
// TODO: Handle right
System.err.println( "addSectionSignal: section #" + sectionId + " not found." );
System.exit(1);
}
// TODO: Check if id was already added
MainSectionSignal signal = new MainSectionSignal( signalId );
signal.setTrackSection( section );
signalList.add( signal );
}
private void addLookaheadSignal( long signalId, int aheadAmount, long trackId, long nodeId ) {
Track track = getTrack( trackId );
Node node = Game.nodeManager.getNode( nodeId );
if ( track == null ) {
// TODO: Handle right
System.err.println( "addLookAheadSignal: track #" + trackId + " not found." );
System.exit(1);
}
if ( node == null ) {
// TODO: Handle right
System.err.println( "addLookAheadSignal: node #" + nodeId + " not found." );
System.exit(1);
}
// TODO: Remove and handle right
// if ( aheadAmount != 1 ) {
// System.err.println( "addLookAheadSignal: signal #" + signalId + " amount not 1." );
// System.exit(1);
// }
// TODO: Check if id was already added
// MainLookaheadSignal signal = new MainLookaheadSignal( signalId );
// signal.setLookaheads( aheadAmount, track, node );
MainSignal signal = new MainSignal( signalId );
signal.setBound( track, node );
signal.makeLookaheadSignal( aheadAmount );
signal.update();
signalList.add( signal );
}
private void addPreSignal( long preSignalId, long mainSignalId, long trackId, long nodeId ) {
Signal signal = getSignal( mainSignalId );
if ( ! ( signal instanceof MainSignal ) ) {
// TODO: Handle right
System.err.println( "addPreSignal: signal #" + mainSignalId + " not a main signal." );
System.exit(1);
}
// TODO: Check if id was already added
PreSignal preSignal = new PreSignal( preSignalId );
preSignal.setMainSignal( (MainSignal) signal );
preSignal.setBound( getTrack( trackId ), Game.nodeManager.getNode( nodeId ) );
signalList.add( preSignal );
}
public void setSignalCoordinates( String s ) {
Pattern pattern = Pattern.compile( "^Id=(\\d+),X=(\\d+),Y=(\\d+)$" );
Matcher matcher = pattern.matcher( s );
if ( ! matcher.matches() ) {
// TODO: Handle right
System.err.println( "setSignalCoordinates mismatch: " + s );
System.exit(1);
}
long id = Long.parseLong( matcher.group( 1 ) );
int x = Integer.parseInt( matcher.group( 2 ) );
int y = Integer.parseInt( matcher.group( 3 ) );
Signal signal = getSignal( id );
if ( signal != null ) {
signal.setCoordinates( new Coordinates( x, y ) );
} else {
// TODO: Handle better
System.err.println( "setSignalCoordinates(String): Signal #" + id + " does not exist." );
}
}
public void mouseClickedOn( int x, int y ) {
for ( Signal s : signalList ) {
if ( s.matchCoordinates( x, y ) ) {
s.handleMouseClick();
}
}
}
}