Compare commits

...

57 Commits

Author SHA1 Message Date
LO Kam Tao Leo 4bdbf01bbb Various changes 3 years ago
LO Kam Tao Leo b4288758ad Updating the unit test in preparation for the technical renew on Network Rail's data feed 3 years ago
LO Kam Tao Leo 656c80ea92 Skeleton for UUIDUtil 3 years ago
LO Kam Tao Leo dc6ac2e355 Add and update dependencies 3 years ago
LO Kam Tao Leo fb111633ee New test case and fixed incorrect value for speed 3 years ago
LO Kam Tao Leo e8dfee5c9d Amending function parseInt 3 years ago
LO Kam Tao Leo 6fed025104 Modify the data a bit to have a better test 3 years ago
LO Kam Tao Leo ec8c12693b Updating more data 3 years ago
LO Kam Tao Leo 6fe1ff8c08 Parsing train schedule. Need some work to fill in the missing data. 3 years ago
LO Kam Tao Leo d41aa83d8c Loading the train association data 3 years ago
LO Kam Tao Leo 5f956af5f1 Partial implementation of TrainAssociationDao 3 years ago
LO Kam Tao Leo 6ddac54209 Processing TIPLOCs 3 years ago
LO Kam Tao Leo bd4d911bdd Commit before actually start processing the TIPLOCs 3 years ago
LO Kam Tao Leo 600cba85ee Insert the tiploc records 3 years ago
LO Kam Tao Leo 95682d7204 Refactoring the code 3 years ago
LO Kam Tao Leo 2d31f1369e New models and utils 3 years ago
LO Kam Tao Leo 34a5e89677 Update document, and added getters and setters 3 years ago
LO Kam Tao Leo 7128328a60 Remove part of the document which seems to cause the build to be failed. 3 years ago
LO Kam Tao Leo 90613802e3 New models 3 years ago
LO Kam Tao Leo 04dc9442e9 Updated pom 3 years ago
LO Kam Tao Leo 52b0f1e31b Parsing TIPLOC data 3 years ago
LO Kam Tao Leo 7aa91850bf Also checking the hashcode 3 years ago
LO Kam Tao Leo d7cbe9cad5 Parsing Schedule Associations 3 years ago
LO Kam Tao Leo fefe42fe56 Changes in package 3 years ago
LO Kam Tao Leo eaf70b7dd1 New util class and refactor test packages 3 years ago
LO Kam Tao Leo 5cb8c977e4 Refactoring the code 3 years ago
LO Kam Tao Leo 06422ffbf0 New enums 3 years ago
LO Kam Tao Leo f326f49891 Skeleton for importing SCHEDULE feed 3 years ago
LO Kam Tao Leo 977306c640 Retriving SMART data 3 years ago
LO Kam Tao Leo 5de7b39c8b Add a helper class 3 years ago
LO Kam Tao Leo 7f6dc009ba Importing SMART data 3 years ago
LO Kam Tao Leo 4e69fd8a1d Additional test case 3 years ago
LO Kam Tao Leo 3d930cd506 Fixing incorrect handling of berth offset 3 years ago
LO Kam Tao Leo 2b3171271e Parsing data 3 years ago
LO Kam Tao Leo 952208dff1 Additional test case 3 years ago
LO Kam Tao Leo a63dd871bd Code refactoring 3 years ago
LO Kam Tao Leo be38739c23 Basic operation on SMART 3 years ago
LO Kam Tao Leo 32fdd0e4ae Refactor code and adding SMART data 3 years ago
LO Kam Tao Leo 3400f84628 Basic search for CORPUS 3 years ago
LO Kam Tao Leo 9a46a5455a Foundation for a more flexible search. 3 years ago
LO Kam Tao Leo b8095f476c Refactoring for the incorrect package 3 years ago
LO Kam Tao Leo 1148a15f1a Extra function for handling CORPUS 3 years ago
LO Kam Tao Leo 71be192408 Job to insert the records. 3 years ago
LO Kam Tao Leo 260a5abbda Triggering the ReferenceDataJob if enabled in config 3 years ago
LO Kam Tao Leo a634fcc886 Insertion of CORPUS 3 years ago
LO Kam Tao Leo 0cdb689f09 Skeleton for cron job 3 years ago
LO Kam Tao Leo 4818efef9d Merge branch 'feature-db-basic' into feature-cron-basic 3 years ago
LO Kam Tao Leo 8111c693ce Basic Dao structure and MetadataDao 3 years ago
LO Kam Tao Leo dbbfb5609c Added dependency 3 years ago
LO Kam Tao Leo a5aa9f4aa1 Basic database function 3 years ago
LO Kam Tao Leo e1532f5179 Adding dependency and added url construct 3 years ago
LO Kam Tao Leo b0b5a2e173 Skeleton code to initialize the DatabaseManager 3 years ago
LO Kam Tao Leo c44f6fe63b Skeleton code for database 3 years ago
LO Kam Tao Leo b2e6f020b0 Updated unit test 3 years ago
LO Kam Tao Leo f866ca44f9 Basic unit test 3 years ago
LO Kam Tao Leo 0d1bed4265 Loading configuration 3 years ago
LO Kam Tao Leo 9dc80d90f2 Skeleton 3 years ago
  1. 9
      .gitignore
  2. 79
      .idea/workspace.xml
  3. 95
      pom.xml
  4. 138
      src/main/java/org/leolo/nrdatad/App.java
  5. 159
      src/main/java/org/leolo/nrdatad/ConfigurationManager.java
  6. 32
      src/main/java/org/leolo/nrdatad/Constants.java
  7. 108
      src/main/java/org/leolo/nrdatad/cron/ReferenceDataJob.java
  8. 273
      src/main/java/org/leolo/nrdatad/cron/ScheduleImportJob.java
  9. 69
      src/main/java/org/leolo/nrdatad/db/BaseDao.java
  10. 146
      src/main/java/org/leolo/nrdatad/db/CorpusDao.java
  11. 50
      src/main/java/org/leolo/nrdatad/db/DataSourceDao.java
  12. 27
      src/main/java/org/leolo/nrdatad/db/DatabaseManager.java
  13. 42
      src/main/java/org/leolo/nrdatad/db/MetadataDao.java
  14. 65
      src/main/java/org/leolo/nrdatad/db/ParameterStore.java
  15. 17
      src/main/java/org/leolo/nrdatad/db/SearchMode.java
  16. 229
      src/main/java/org/leolo/nrdatad/db/SmartDao.java
  17. 41
      src/main/java/org/leolo/nrdatad/db/TiplocDao.java
  18. 63
      src/main/java/org/leolo/nrdatad/db/TrainAssociationDao.java
  19. 24
      src/main/java/org/leolo/nrdatad/db/TrainScheduleDao.java
  20. 387
      src/main/java/org/leolo/nrdatad/db/mariadb/CorpusDaoImpl.java
  21. 36
      src/main/java/org/leolo/nrdatad/db/mariadb/DataSourceDaoImpl.java
  22. 98
      src/main/java/org/leolo/nrdatad/db/mariadb/DatabaseManager.java
  23. 116
      src/main/java/org/leolo/nrdatad/db/mariadb/MetadataDaoImpl.java
  24. 324
      src/main/java/org/leolo/nrdatad/db/mariadb/SmartDaoImpl.java
  25. 131
      src/main/java/org/leolo/nrdatad/db/mariadb/TiplocDaoImpl.java
  26. 248
      src/main/java/org/leolo/nrdatad/db/mariadb/TrainAssociationDaoImpl.java
  27. 40
      src/main/java/org/leolo/nrdatad/db/mariadb/TrainScheduleDaoImpl.java
  28. 37
      src/main/java/org/leolo/nrdatad/exception/NoSuchRecordException.java
  29. 27
      src/main/java/org/leolo/nrdatad/model/AssociationCategory.java
  30. 125
      src/main/java/org/leolo/nrdatad/model/Corpus.java
  31. 98
      src/main/java/org/leolo/nrdatad/model/DataSource.java
  32. 192
      src/main/java/org/leolo/nrdatad/model/ScheduleAssociation.java
  33. 30
      src/main/java/org/leolo/nrdatad/model/ShortTermPlanningIndicator.java
  34. 190
      src/main/java/org/leolo/nrdatad/model/Smart.java
  35. 33
      src/main/java/org/leolo/nrdatad/model/SmartEvent.java
  36. 65
      src/main/java/org/leolo/nrdatad/model/SmartStepType.java
  37. 75
      src/main/java/org/leolo/nrdatad/model/Tiploc.java
  38. 155
      src/main/java/org/leolo/nrdatad/model/TrainSchedule.java
  39. 207
      src/main/java/org/leolo/nrdatad/model/TrainScheduleLocation.java
  40. 27
      src/main/java/org/leolo/nrdatad/model/TrainScheduleLocationRecordIdentity.java
  41. 252
      src/main/java/org/leolo/nrdatad/model/TrainScheduleSector.java
  42. 39
      src/main/java/org/leolo/nrdatad/util/DateUtil.java
  43. 76
      src/main/java/org/leolo/nrdatad/util/HttpUtil.java
  44. 32
      src/main/java/org/leolo/nrdatad/util/JSONUtil.java
  45. 25
      src/main/java/org/leolo/nrdatad/util/Range.java
  46. 32
      src/main/java/org/leolo/nrdatad/util/TUIDDateFormat.java
  47. 58
      src/main/java/org/leolo/nrdatad/util/TimeUtil.java
  48. 26
      src/main/java/org/leolo/nrdatad/util/UUIDUtil.java
  49. 18
      src/main/resources/log4j2.xml
  50. 5
      src/test/java/org/leolo/nrdatad/AppTest.java
  51. 92
      src/test/java/org/leolo/nrdatad/ConfigurationTest.java
  52. 270
      src/test/java/org/leolo/nrdatad/ReferenceDataParserTest.java
  53. 31
      src/test/java/org/leolo/nrdatad/ScheduleEnumTest.java
  54. 29
      src/test/java/org/leolo/nrdatad/SmartEnumTest.java
  55. 59
      src/test/java/org/leolo/nrdatad/db/test/DatabaseManager.java
  56. 40
      src/test/java/org/leolo/nrdatad/db/test/MetadataDao.java
  57. 105
      src/test/java/org/leolo/nrdatad/model/ScheduleAssociationTest.java
  58. 30
      src/test/java/org/leolo/nrdatad/model/TiplocTest.java
  59. 210
      src/test/java/org/leolo/nrdatad/model/TrainScheduleTest.java
  60. 41
      src/test/java/org/leolo/nrdatad/util/DateUtilTest.java
  61. 48
      src/test/java/org/leolo/nrdatad/util/JSONUtilTest.java
  62. 20
      src/test/java/org/leolo/nrdatad/util/RangeTest.java
  63. 34
      src/test/java/org/leolo/nrdatad/util/TUIDDateFormatTest.java
  64. 31
      src/test/java/org/leolo/nrdatad/util/TestUtil.java
  65. 75
      src/test/java/org/leolo/nrdatad/util/TimeUtilTest.java
  66. 178
      src/test/resources/org/leolo/nrdatad/test/V56331_N.json
  67. 113
      src/test/resources/org/leolo/nrdatad/test/V56332_N.json

9
.gitignore vendored

@ -35,4 +35,11 @@ build/
.vscode/
### Mac OS ###
.DS_Store
.DS_Store
### Configuration file
/nrdatad.conf
ri_log*
test-*.tmp
.idea/workspace.xml
.idea/

79
.idea/workspace.xml

@ -1,79 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AutoImportSettings">
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="b9554e7b-3332-419c-960c-8bfbee0d5311" name="Changes" comment="">
<change afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/aws.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/pom.xml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/main/java/org/leolo/nrdatad/App.java" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/test/java/org/leolo/AppTest.java" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProjectId" id="2GCtKiOdhwqUrJrlolmjYzZXLwa" />
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
<component name="ProjectViewState">
<option name="flattenPackages" value="true" />
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"RunOnceActivity.OpenProjectViewOnStart": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"WebServerToolWindowFactoryState": "false"
}
}]]></component>
<component name="RunManager">
<configuration name="Main" type="Application" factoryName="Application">
<option name="MAIN_CLASS_NAME" value="org.leolo.nrdatad.App" />
<module name="nr-data-damon" />
<extension name="software.aws.toolkits.jetbrains.core.execution.JavaAwsConnectionExtension">
<option name="credential" />
<option name="region" />
<option name="useCurrentConnection" value="false" />
</extension>
<method v="2">
<option name="Make" enabled="true" />
</method>
</configuration>
</component>
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="b9554e7b-3332-419c-960c-8bfbee0d5311" name="Changes" comment="" />
<created>1665904334835</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1665904334835</updated>
<workItem from="1665904336661" duration="319000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="Vcs.Log.Tabs.Properties">
<option name="TAB_STATES">
<map>
<entry key="MAIN">
<value>
<State />
</value>
</entry>
</map>
</option>
</component>
</project>

95
pom.xml

@ -14,17 +14,80 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<ver-log4j2>2.19.0</ver-log4j2>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${ver-log4j2}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${ver-log4j2}</version>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.5.0</version>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>3.0.6</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20220924</version>
</dependency>
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>23.1.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>${ver-log4j2}</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20220924</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.uuid</groupId>
<artifactId>java-uuid-generator</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>jug</groupId>
<artifactId>jug</artifactId>
<version>2.0.0</version>
</dependency>
</dependencies>
<build>
@ -71,5 +134,31 @@
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<show>private</show>
</configuration>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

138
src/main/java/org/leolo/nrdatad/App.java

@ -1,11 +1,147 @@
package org.leolo.nrdatad;
import org.apache.commons.cli.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.leolo.nrdatad.cron.ScheduleImportJob;
import org.leolo.nrdatad.db.CorpusDao;
import org.leolo.nrdatad.db.DatabaseManager;
import org.leolo.nrdatad.cron.ReferenceDataJob;
import org.leolo.nrdatad.model.Corpus;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.Collection;
/**
* Hello world!
*
*/
public class App {
Logger log = LogManager.getLogger();
private ConfigurationManager config = ConfigurationManager.getInstance();
private Scheduler scheduler;
public static void main( String[] args ) {
System.out.println( "Hello World!" );
Option confFile = Option.builder()
.required(false)
.hasArg(true)
.argName("fileName")
.option("c")
.longOpt("config-file")
.build();
Options opts = new Options();
opts.addOption(confFile);
String confPath = "nrdatad.conf";
try {
CommandLine cli = new DefaultParser().parse(opts, args);
if(cli.hasOption(confFile)){
confPath = cli.getOptionValue(confFile);
}
} catch (ParseException e) {
throw new RuntimeException(e);
}
new App().run(confPath);
}
private void run(String configPath){
log.always().log("System Started.");
try {
config.loadConfiguration(configPath);
} catch (IOException e) {
log.atError().withThrowable(e).log("Unable to load confog file");
return;
}
// log.atDebug()
if(config.getProperty("general.debug","false").equals("true")){
for(String key:config.stringPropertyNames()){
if(key.endsWith("pwd")){
log.atDebug().log("Config: {} : ****({} chars)", key, config.getProperty(key).length());
}else{
log.atDebug().log("Config: {} : {}", key, config.getProperty(key));
}
}
}
String databaseManagerClass = config.getProperty("db.manager","org.leolo.nrdatad.db.DatabaseManager");
log.atInfo().log("Creating database manager {}", databaseManagerClass);
try {
config.setDatabaseManager((DatabaseManager) Class.forName(databaseManagerClass).getConstructor().newInstance());
} catch (
InstantiationException|IllegalAccessException|InvocationTargetException|
NoSuchMethodException|ClassNotFoundException e
) {
log.atFatal().log("Unable to create instance of {}", databaseManagerClass);
System.exit(1);
return;
} catch (ClassCastException e){
log.atFatal().log("{} is not a DatabaseManager", databaseManagerClass);
System.exit(1);
return;
}
config.getDatabaseManager().initPool();
if(config.getDatabaseManager().checkConnection())
log.atInfo().log("Database connected!");
else{
log.atFatal().log("Unable to connect to database.");
System.exit(1);
return;
}
loadCronJob();
while(true);
}
private void testRefData() {
log.atInfo().log("Testing reference data");
ConfigurationManager conf = ConfigurationManager.getInstance();
CorpusDao.Query query = new CorpusDao.QueryBuilder()
.addCrsCode("ECR").addCrsCode("VIC")
.addDescription("croydon").build();
try {
Collection<Corpus> list = conf.getDatabaseManager().getCORPUSDao().executeQuery(query);
log.atDebug().log("{} entries found", list.size());
for(Corpus corpus:list){
log.atDebug().log("Found entry : {}", corpus);
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
private void loadCronJob() {
log.atDebug().log("Start loading cron jobs");
try {
scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
if(ConfigurationManager.getInstance().getProperty(Constants.Configuration.ALWAYS_RUN_REF_DATA,"").equals("true")){
log.atInfo().log("Loading Reference data now");
scheduler.scheduleJob(
JobBuilder.newJob(ReferenceDataJob.class).withIdentity("J-OF-"+Constants.CronJob.REFERENCE_DATA).build(),
TriggerBuilder.newTrigger().withIdentity("T-OF-"+Constants.CronJob.REFERENCE_DATA).startAt(DateBuilder.evenSecondDateAfterNow()).build()
);
}
scheduler.scheduleJob(
JobBuilder.newJob(ReferenceDataJob.class).withIdentity("J-CW-"+Constants.CronJob.REFERENCE_DATA).build(),
TriggerBuilder.newTrigger().withIdentity("T-CW-"+Constants.CronJob.REFERENCE_DATA)
.withSchedule(CronScheduleBuilder.cronSchedule("* 10 1 ? * 1")).build()
);
if(ConfigurationManager.getInstance().getProperty(Constants.Configuration.ALWAYS_RUN_LTP,"").equals("true")){
log.atInfo().log("Loading Schedule data now");
scheduler.scheduleJob(
JobBuilder.newJob(ScheduleImportJob.class).withIdentity("J-OF-"+Constants.CronJob.SCHEDULE_IMPORT).build(),
TriggerBuilder.newTrigger().withIdentity("T-OF-"+Constants.CronJob.SCHEDULE_IMPORT).startAt(DateBuilder.evenSecondDateAfterNow()).build()
);
}
} catch (SchedulerException e) {
log.atFatal().withThrowable(e).log("Unable to create cron jobs");
}
}
}

159
src/main/java/org/leolo/nrdatad/ConfigurationManager.java

@ -0,0 +1,159 @@
package org.leolo.nrdatad;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.leolo.nrdatad.db.DatabaseManager;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.*;
import java.util.function.BiConsumer;
public final class ConfigurationManager {
private static ConfigurationManager instance;
Logger logger = LogManager.getLogger();
Properties prop = new Properties();
private boolean hasCachedEthAddress = false;
private String cachedEthAddress = null;
private DatabaseManager databaseManager;
public DatabaseManager getDatabaseManager() {
return databaseManager;
}
public void setDatabaseManager(DatabaseManager databaseManager) {
this.databaseManager = databaseManager;
}
public synchronized static ConfigurationManager getInstance(){
if(instance==null){
instance = new ConfigurationManager();
}
return instance;
}
private ConfigurationManager(){
}
public void loadConfiguration(String configPath) throws IOException{
logger.atInfo().log("Loading configuration file {}.", configPath);
try(FileReader reader = new FileReader(configPath)) {
prop.load(reader);
}
logger.atInfo().log("Loaded {} setting(s)", prop.size());
}
public Object setProperty(String key, String value) {
return prop.setProperty(key, value);
}
public String getProperty(String key) {
return prop.getProperty(key);
}
public String getProperty(String key, String defaultValue) {
return prop.getProperty(key, defaultValue);
}
public Enumeration<?> propertyNames() {
return prop.propertyNames();
}
public Set<String> stringPropertyNames() {
return prop.stringPropertyNames();
}
public void list(PrintStream out) {
prop.list(out);
}
public void list(PrintWriter out) {
prop.list(out);
}
public int size() {
return prop.size();
}
public boolean isEmpty() {
return prop.isEmpty();
}
public Enumeration<Object> keys() {
return prop.keys();
}
public Enumeration<Object> elements() {
return prop.elements();
}
public boolean contains(Object value) {
return prop.contains(value);
}
public boolean containsValue(Object value) {
return prop.containsValue(value);
}
public boolean containsKey(Object key) {
return prop.containsKey(key);
}
public Set<Object> keySet() {
return prop.keySet();
}
public Collection<Object> values() {
return prop.values();
}
public Set<Map.Entry<Object, Object>> entrySet() {
return prop.entrySet();
}
protected void clear(){
prop.clear();
}
public Object getOrDefault(Object key, Object defaultValue) {
return prop.getOrDefault(key, defaultValue);
}
public void forEach(BiConsumer<? super Object, ? super Object> action) {
prop.forEach(action);
}
//Special handling for getting the ethernet address
public String getEthAddress(){
if(hasCachedEthAddress){
return cachedEthAddress;
}
String addr = prop.getProperty("uuid.ethAddr");
if (addr == null){
//There are no ethernet address available. Return null
hasCachedEthAddress = true;
return null;
}
StringBuilder sb = new StringBuilder();
for(char ch:addr.toCharArray()){
if(ch>='0'&&ch<='9'){
sb.append(ch);
}else if ((ch >= 'a' && ch <= 'f') ||(ch>='A'&&ch<='F')) {
sb.append(Character.toLowerCase(ch));
}
}
if(sb.length()==12){
cachedEthAddress = sb.toString();
hasCachedEthAddress = true;
return cachedEthAddress;
}else{
hasCachedEthAddress=true;
return null;
}
}
}

32
src/main/java/org/leolo/nrdatad/Constants.java

@ -0,0 +1,32 @@
package org.leolo.nrdatad;
public final class Constants {
public static class Metadata{
public static final String LAST_LTP = "01-last-ltp-time";
}
public static class CronJob{
public static final String REFERENCE_DATA = "refd";
public static final String SCHEDULE_IMPORT = "scji";
public static final int SCHEDULE_BATCH_SIZE = 10000;
}
public static class Configuration {
public static final String ALWAYS_RUN_LTP = "cron.ltp.always";
public static final String ALWAYS_RUN_REF_DATA = "cron.ref.always";
public static final String NETWORK_RAIL_USER = "network.user";
public static final String NETWORK_RAIL_PASSWORD = "network.pwd";
public static final String TEMP_DIR = "file.temp_dir";
}
public static class NetworkRailURI{
public static final String CORPUS_URL = "https://publicdatafeeds.networkrail.co.uk/ntrod/SupportingFileAuthenticate?type=CORPUS";
public static final String SMART_URL = "https://publicdatafeeds.networkrail.co.uk/ntrod/SupportingFileAuthenticate?type=SMART";
public static final String SCHEDULE_URL = "https://publicdatafeeds.networkrail.co.uk/ntrod/CifFileAuthenticate?type=CIF_ALL_FULL_DAILY&day=toc-full";
}
public static class DataSourceNames {
public static final String SOURCE_NETWORK_RAIL = "Network Rail";
}
}

108
src/main/java/org/leolo/nrdatad/cron/ReferenceDataJob.java

@ -0,0 +1,108 @@
package org.leolo.nrdatad.cron;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONObject;
import org.leolo.nrdatad.ConfigurationManager;
import org.leolo.nrdatad.Constants;
import org.leolo.nrdatad.db.CorpusDao;
import org.leolo.nrdatad.model.Corpus;
import org.leolo.nrdatad.model.DataSource;
import org.leolo.nrdatad.model.Smart;
import org.leolo.nrdatad.util.HttpUtil;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
public class ReferenceDataJob implements Job {
Logger log = LogManager.getLogger();
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
log.atInfo().log("Load reference data triggered at {}", context.getFireTime());
//There are 2 different kind of reference data. Create 2 thread and deal with them
new Thread(()->{processCORPUS();}).start();
new Thread(()->{processSMART();}).start();
}
private void processSMART() {
ConfigurationManager conf = ConfigurationManager.getInstance();
log.atInfo().log("Loading SMART");
String json = null;
try {
json = HttpUtil.sendHttpRequestForGZipFile(
Constants.NetworkRailURI.SMART_URL,
conf.getProperty(Constants.Configuration.NETWORK_RAIL_USER),
conf.getProperty(Constants.Configuration.NETWORK_RAIL_PASSWORD)
);
} catch (IOException|URISyntaxException e) {
log.atError().withThrowable(e).log("Error when getting CORPUS data");
return;
}
JSONObject rootObj = new JSONObject(json);
JSONArray rootArray = rootObj.getJSONArray("BERTHDATA");
log.atInfo().log("{} entries found", rootArray.length());
Collection<Smart> smarts = new ArrayList<>();
DataSource ds = conf.getDatabaseManager().getDataSourceDao().generateAndInsertUUID(Constants.DataSourceNames.SOURCE_NETWORK_RAIL,"SMART", false);
for (int i = 0; i < rootArray.length(); i++) {
JSONObject obj = rootArray.getJSONObject(i);
Smart smart = Smart.parseJSON(obj);
if(!smart.checkEnums()){
log.atInfo().log("Improper SMART record: {}", obj);
continue;
}
smart.setDataSource(ds);
smarts.add(smart);
}
try {
conf.getDatabaseManager().getSmartDao().truncateTable();
conf.getDatabaseManager().getSmartDao().addAll(smarts);
} catch (SQLException e) {
log.atError().withThrowable(e).log("Unable to insert records");
}
log.atInfo().log("Done inserting SMART");
}
private void processCORPUS(){
ConfigurationManager conf = ConfigurationManager.getInstance();
log.atInfo().log("Loading CORPUS");
String json = null;
try {
json = HttpUtil.sendHttpRequestForGZipFile(
Constants.NetworkRailURI.CORPUS_URL,
conf.getProperty(Constants.Configuration.NETWORK_RAIL_USER),
conf.getProperty(Constants.Configuration.NETWORK_RAIL_PASSWORD)
);
} catch (IOException|URISyntaxException e) {
log.atError().withThrowable(e).log("Error when getting CORPUS data");
return;
}
JSONObject rootObj = new JSONObject(json);
JSONArray rootArray = rootObj.getJSONArray("TIPLOCDATA");
log.atInfo().log("{} entries found", rootArray.length());
ArrayList<Corpus> corpusList = new ArrayList<>();
DataSource ds = conf.getDatabaseManager().getDataSourceDao().generateAndInsertUUID(Constants.DataSourceNames.SOURCE_NETWORK_RAIL,"CORPUS", false);
for(int i=0;i<rootArray.length();i++){
JSONObject obj = rootArray.getJSONObject(i);
Corpus corpus = Corpus.parseJSON(obj);
corpus.setDataSource(ds);
corpusList.add(corpus);
}
log.atInfo().log("Going to replace the data");
try {
CorpusDao cDao = conf.getDatabaseManager().getCORPUSDao();
cDao.replaceAll(corpusList);
} catch (SQLException e) {
log.atError().withThrowable(e).log("Unable to insert records");
}
log.atInfo().log("Done inserting CORPUS");
}
}

273
src/main/java/org/leolo/nrdatad/cron/ScheduleImportJob.java

@ -0,0 +1,273 @@
package org.leolo.nrdatad.cron;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONObject;
import org.leolo.nrdatad.ConfigurationManager;
import org.leolo.nrdatad.Constants;
import org.leolo.nrdatad.model.ScheduleAssociation;
import org.leolo.nrdatad.model.Tiploc;
import org.leolo.nrdatad.model.TrainSchedule;
import org.leolo.nrdatad.util.HttpUtil;
import org.leolo.nrdatad.util.TUIDDateFormat;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import java.io.*;
import java.net.URISyntaxException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
public class ScheduleImportJob implements Job {
Logger log = LogManager.getLogger();
private File baseTempDir;
private ConfigurationManager conf = ConfigurationManager.getInstance();
public static final String TYPE_HEADER = "JsonTimetableV1";
public static final String TYPE_ASSOCIATION = "JsonAssociationV1";
public static final String TYPE_TIPLOC = "TiplocV1";
public static final String TYPE_SCHEDULE = "JsonScheduleV1";
public static final String TYPE_EOF = "EOF";
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
log.atInfo().log("Loading schedule from SCHEDULE stream");
final String RUN_ID = Long.toHexString(System.currentTimeMillis()&0xffffff);
log.atInfo().log("RUN_ID is {}", RUN_ID);
baseTempDir = new File(conf.getProperty(Constants.Configuration.TEMP_DIR)+"/nrdatad/SCHEDULE-"+RUN_ID);
baseTempDir.mkdirs();
ExecutorService threadPool = Executors.newFixedThreadPool(5);
int countH = 0, countA=0, countT=0, countS=0;
int batchH = 0, batchA=0, batchT=0, batchS=0;
try(BufferedReader br = new BufferedReader(new InputStreamReader(HttpUtil.getHttpGZipStream(
Constants.NetworkRailURI.SCHEDULE_URL,
conf.getProperty(Constants.Configuration.NETWORK_RAIL_USER),
conf.getProperty(Constants.Configuration.NETWORK_RAIL_PASSWORD)
)))
){
PrintWriter pa = new PrintWriter(new GZIPOutputStream(new FileOutputStream(new File(baseTempDir, "A_0"))));
PrintWriter pt = new PrintWriter(new GZIPOutputStream(new FileOutputStream(new File(baseTempDir, "T_0"))));
PrintWriter ps = new PrintWriter(new GZIPOutputStream(new FileOutputStream(new File(baseTempDir, "S_0"))));
int count = 0;
while(true){
String line = br.readLine();
if(line==null){
break;
}
JSONObject obj = new JSONObject(line);
String objectType = obj.keys().next();
count++;
if(TYPE_HEADER.equals(objectType)){
//TODO: Deal with the header.
countH++;
} else if (TYPE_ASSOCIATION.equals(objectType)){
pa.println(obj.getJSONObject(TYPE_ASSOCIATION));
if(++countA%Constants.CronJob.SCHEDULE_BATCH_SIZE==0){
log.atInfo().log("New batch for association.");
pa.flush();
pa.close();
final int OLD_BATCH = batchA;
threadPool.execute(()->{processAssociation("A_"+OLD_BATCH);});
pa = new PrintWriter(new GZIPOutputStream(new FileOutputStream(new File(baseTempDir, "A_"+(++batchA)))));
}
} else if (TYPE_TIPLOC.equals(objectType)) {
pt.println(obj.getJSONObject(TYPE_TIPLOC));
if(++countT%Constants.CronJob.SCHEDULE_BATCH_SIZE==0){
log.atInfo().log("New batch for tiploc.");
pt.flush();
pt.close();
final int OLD_BATCH = batchT;
threadPool.execute(()->{processTiploc("T_"+OLD_BATCH);});
pt = new PrintWriter(new GZIPOutputStream(new FileOutputStream(new File(baseTempDir, "T_"+(++batchT)))));
}
} else if (TYPE_SCHEDULE.equals(objectType)) {
ps.println(obj.getJSONObject(TYPE_SCHEDULE));
if(++countS%Constants.CronJob.SCHEDULE_BATCH_SIZE==0){
log.atInfo().log("New batch for schedule.");
ps.flush();
ps.close();
ps = new PrintWriter(new GZIPOutputStream(new FileOutputStream(new File(baseTempDir, "S_"+(++batchS)))));
}
} else if (TYPE_EOF.equals(objectType)) {
log.atInfo().log("EOF found!");
} else {
log.atInfo().log("Unmatched type {}", objectType);
}
}
pa.flush();
pa.close();
pt.flush();
pt.close();
ps.flush();
ps.close();
final int OLD_BATCH_A = batchA;
threadPool.execute(()->{processAssociation("A_"+OLD_BATCH_A);});
final int OLD_BATCH_T = batchT;
threadPool.execute(()->{processTiploc("T_"+OLD_BATCH_T);});
log.atInfo().log("There are {} records on total. Including {} headers, {} association, {} TIPLOC and {} schedules", count, countH, countA, countT, countS);
}catch (IOException | URISyntaxException e){
log.atError().withThrowable(e).log("Unable to read SCHEDULE feed");
return;
}
threadPool.shutdown();
try {
threadPool.awaitTermination(12, TimeUnit.HOURS);
} catch (InterruptedException e) {
log.atWarn().log("SCHEDULE import job interrupted");
}
}
private void processAssociation(String file){
log.atDebug().log("Processing {}",file);
try(
BufferedReader br = new BufferedReader(
new InputStreamReader(
new GZIPInputStream(
new FileInputStream(
new File(baseTempDir, file)
)
)
)
);
PrintWriter pw = new PrintWriter(new File(baseTempDir, file+"_v"))
){
int count = 0;
ArrayList<ScheduleAssociation> scheduleAssociations = new ArrayList<>();
TUIDDateFormat tuidDateFormat = new TUIDDateFormat();
while(true){
String line = br.readLine();
if(line == null){
break;
}
ScheduleAssociation sa = ScheduleAssociation.parseJSON(line);
scheduleAssociations.add(sa);
pw.println(sa.getMainUid()+","+sa.getAssoUid()+","+sa.getAssociationLocation()+","+
tuidDateFormat.format(sa.getStartDate())+","+
tuidDateFormat.format(sa.getEndDate())+","+
sa.getStpIndicator());
if(scheduleAssociations.size()>1000){
try{
ConfigurationManager.getInstance().getDatabaseManager().getTrainAssociationDao().replaceAll(scheduleAssociations);
} catch (SQLException e){
log.atWarn().withThrowable(e).log("Unable to update records. {}",file);
}
log.atDebug().log("Processed a batch of associations");
scheduleAssociations.clear();
}
}
try{
ConfigurationManager.getInstance().getDatabaseManager().getTrainAssociationDao().replaceAll(scheduleAssociations);
} catch (SQLException e){
log.atWarn().withThrowable(e).log("Unable to update records. {}", file);
}
log.atDebug().log("Processed last batch of associations");
} catch (IOException e){
log.atError().withThrowable(e).log("Error when reading file.");
}
try{
new File(baseTempDir, file).delete();
} catch (Exception e){
log.atWarn().withThrowable(e).log("Unable to remove temp file {}", file);
}
}
private void processTiploc(String file){
log.atDebug().log("Processing {}",file);
try(
BufferedReader br = new BufferedReader(
new InputStreamReader(
new GZIPInputStream(
new FileInputStream(
new File(baseTempDir, file)
)
)
)
)
){
int count = 0;
ArrayList<Tiploc> tiplocs = new ArrayList<>();
while(true){
String line = br.readLine();
if(line == null){
break;
}
tiplocs.add(Tiploc.parseJSON(line));
if(tiplocs.size()>1000){
try{
ConfigurationManager.getInstance().getDatabaseManager().getTiplocDao().replaceAll(tiplocs);
} catch (SQLException e){
log.atWarn().withThrowable(e).log("Unable to update records.");
}
log.atDebug().log("Processed a batch of TIPLOCs");
tiplocs.clear();
}
}
try{
ConfigurationManager.getInstance().getDatabaseManager().getTiplocDao().replaceAll(tiplocs);
} catch (SQLException e){
log.atWarn().withThrowable(e).log("Unable to update records.");
}
log.atDebug().log("Processed last batch of TIPLOCs");
} catch (IOException e){
log.atError().withThrowable(e).log("Error when reading file.");
}
try{
new File(baseTempDir, file).delete();
} catch (Exception e){
log.atWarn().withThrowable(e).log("Unable to remove temp file {}", file);
}
}
private void processSchedule(String file){
log.atDebug().log("Processing {}",file);
try(
BufferedReader br = new BufferedReader(
new InputStreamReader(
new GZIPInputStream(
new FileInputStream(
new File(baseTempDir, file)
)
)
)
)
){
int count = 0;
ArrayList<TrainSchedule> schedules = new ArrayList<>();
while(true){
String line = br.readLine();
if(line == null){
break;
}
TrainSchedule trainSchedule = TrainSchedule.parseJSON(line);
if(Character.isDigit(trainSchedule.getTrainStatus().charAt(0))){
trainSchedule.setSchedeuleType("WTT");
}else{
trainSchedule.setSchedeuleType("STP");
}
schedules.add(trainSchedule);
if(schedules.size()>1000){
//Replace
log.atDebug().log("Processed a batch of SCHEDULE");
schedules.clear();
}
}
//Replace
log.atDebug().log("Processed last batch of SCHEDULE");
} catch (IOException e){
log.atError().withThrowable(e).log("Error when reading file.");
}
try{
new File(baseTempDir, file).delete();
} catch (Exception e){
log.atWarn().withThrowable(e).log("Unable to remove temp file {}", file);
}
}
}

69
src/main/java/org/leolo/nrdatad/db/BaseDao.java

@ -0,0 +1,69 @@
package org.leolo.nrdatad.db;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.sql.*;
import java.util.Calendar;
public abstract class BaseDao {
public enum ReplaceResult {
INSERTED,
UPDATED,
NO_ACTION;
}
private DatabaseManager manager;
public BaseDao(DatabaseManager manager){
this.manager = manager;
}
protected Connection getConnection() throws SQLException{
return manager.getConnection();
}
protected void setString(PreparedStatement stmt, int pos, @Nullable String data) throws SQLException{
if(data==null||data.strip().length()==0){
stmt.setNull(pos, Types.CHAR);
}else{
stmt.setString(pos, data);
}
}
protected void setInt(PreparedStatement stmt, int pos, int val) throws SQLException{
stmt.setInt(pos, val);
}
protected void setDate(PreparedStatement stmt, int pos, @Nullable java.util.Date data) throws SQLException{
if(data==null){
stmt.setNull(pos, Types.DATE);
} else {
stmt.setTimestamp(pos, new Timestamp(data.getTime()));
}
}
protected void setDate(PreparedStatement stmt, int pos, @Nullable java.util.Date data, @NotNull Calendar calendar) throws SQLException{
if(data==null){
stmt.setNull(pos, Types.DATE);
} else {
stmt.setTimestamp(pos, new Timestamp(data.getTime()), calendar);
}
}
protected String getQueryParams(int count){
if(count <= 0){
throw new IllegalArgumentException("count must be greater than 0");
}
StringBuilder sb = new StringBuilder();
for(int i=0;i<count;i++){
if(i>0){
sb.append(",");
}
sb.append("?");
}
return sb.toString();
}
}

146
src/main/java/org/leolo/nrdatad/db/CorpusDao.java

@ -0,0 +1,146 @@
package org.leolo.nrdatad.db;
import org.leolo.nrdatad.model.Corpus;
import java.sql.SQLException;
import java.util.*;
public abstract class CorpusDao extends BaseDao{
public CorpusDao(DatabaseManager manager) {
super(manager);
}
public static class QueryBuilder{
private Set<String> stanox = new HashSet<>();
private Set<String> uicCode = new HashSet<>();
private Set<String> crsCode = new HashSet<>();
private Set<String> tiplocCode = new HashSet<>();
private Set<String> nlcCode = new HashSet<>();
private Set<String> description = new HashSet<>();
SearchMode searchMode = SearchMode.MATCH_ALL_GROUP;
public QueryBuilder setSearchMode(SearchMode searchMode) {
this.searchMode = searchMode;
return this;
}
public QueryBuilder addStanox(String stanox){
this.stanox.add(stanox);
return this;
}
public QueryBuilder addUicCode(String uicCode){
this.uicCode.add(uicCode);
return this;
}
public QueryBuilder addCrsCode(String crsCode){
this.crsCode.add(crsCode);
return this;
}
public QueryBuilder addTiplocCode(String tiplocCode){
this.tiplocCode.add(tiplocCode);
return this;
}
public QueryBuilder addNlcCode(String nlcCode){
this.nlcCode.add(nlcCode);
return this;
}
public QueryBuilder addDescription(String description){
this.description.add(description);
return this;
}
public Query build(){
return new Query(stanox, uicCode, crsCode, tiplocCode, nlcCode, description, searchMode);
}
}
public static class Query{
private Set<String> stanox = new HashSet<>();
private Set<String> uicCode = new HashSet<>();
private Set<String> crsCode = new HashSet<>();
private Set<String> tiplocCode = new HashSet<>();
private Set<String> nlcCode = new HashSet<>();
private Set<String> description = new HashSet<>();
SearchMode searchMode = SearchMode.MATCH_ALL_GROUP;
private Query(Set<String> stanox, Set<String> uicCode, Set<String> crsCode, Set<String> tiplocCode, Set<String> nlcCode, Set<String> description, SearchMode searchMode) {
this.stanox = Collections.unmodifiableSet(stanox);
this.uicCode = Collections.unmodifiableSet(uicCode);
this.crsCode = Collections.unmodifiableSet(crsCode);
this.tiplocCode = Collections.unmodifiableSet(tiplocCode);
this.nlcCode = Collections.unmodifiableSet(nlcCode);
this.description = Collections.unmodifiableSet(description);
this.searchMode = searchMode;
}
public Set<String> getStanox() {
return stanox;
}
public Set<String> getUicCode() {
return uicCode;
}
public Set<String> getCrsCode() {
return crsCode;
}
public Set<String> getTiplocCode() {
return tiplocCode;
}
public Set<String> getNlcCode() {
return nlcCode;
}
public Set<String> getDescription() {
return description;
}
public SearchMode getSearchMode() {
return searchMode;
}
}
public abstract Collection<Corpus> executeQuery(Query query) throws SQLException;
public abstract void add(Corpus corpus) throws SQLException;
public void addAll(Collection<Corpus> datas) throws SQLException {
for(Corpus corpus: datas){
add(corpus);
}
}
public abstract void truncateTable() throws SQLException;
public abstract boolean hasNlcCode(String nlcCode) throws SQLException;
public abstract void update(Corpus corpus) throws SQLException;
public void replace(Corpus corpus) throws SQLException{
if(corpus.getNlcCode()==null || !hasNlcCode(corpus.getNlcCode())){
add(corpus);
}else{
update(corpus);
}
}
public void replaceAll(Collection<Corpus> datas) throws SQLException{
for(Corpus corpus: datas){
replace(corpus);
}
}
public abstract Collection<Corpus> searchCORPUSByStanoxCode(String stanoxCode) throws SQLException;
public abstract Collection<Corpus> searchCORPUSByUicCode(String uicCode) throws SQLException;
public abstract Collection<Corpus> searchCORPUSByCrsCode(String crsCode) throws SQLException;
public abstract Collection<Corpus> searchCORPUSByTiplocCode(String tiplocCode) throws SQLException;
public abstract Corpus searchCORPUSByNlcCode(String nlcCode) throws SQLException;
public abstract List<Corpus> searchCORPUSByName(String name) throws SQLException;
public abstract Corpus delete(String nlcCode) throws SQLException;
}

50
src/main/java/org/leolo/nrdatad/db/DataSourceDao.java

@ -0,0 +1,50 @@
package org.leolo.nrdatad.db;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.leolo.nrdatad.model.DataSource;
import java.sql.SQLException;
import java.util.Date;
public abstract class DataSourceDao extends BaseDao{
private Logger log = LogManager.getLogger();
public DataSourceDao(DatabaseManager manager) {
super(manager);
}
public abstract void insert(DataSource ds) throws SQLException;
public DataSource generateAndInsertUUID(String provider, String streamName, boolean isMessage, Date startDate, String messageId){
DataSource ds = new DataSource(provider, streamName, isMessage, startDate, messageId);
tryInsert(ds);
return ds;
}
public DataSource generateAndInsertUUID(String provider, String streamName, boolean isMessage){
DataSource ds = new DataSource(provider, streamName, isMessage);
tryInsert(ds);
return ds;
}
public DataSource generateAndInsertUUID(String provider, String streamName, boolean isMessage, String messageId){
DataSource ds = new DataSource(provider, streamName, isMessage, messageId);
tryInsert(ds);
return ds;
}
private void tryInsert(DataSource ds) {
new Thread(()->{
log.atInfo().log("Trying to insert record {}", ds.getUuid());
try{
insert(ds);
} catch (SQLException e){
log.atWarn().withThrowable(e).log("Unable to insert data source record {}", ds.getUuid());
}
}).start();
}
}

27
src/main/java/org/leolo/nrdatad/db/DatabaseManager.java

@ -0,0 +1,27 @@
package org.leolo.nrdatad.db;
import java.sql.Connection;
import java.sql.SQLException;
public interface DatabaseManager {
public void initPool();
public boolean checkConnection();
public Connection getConnection() throws SQLException;
public MetadataDao getMetadataDao();
public CorpusDao getCORPUSDao();
public SmartDao getSmartDao();
public TiplocDao getTiplocDao();
public TrainAssociationDao getTrainAssociationDao();
public TrainScheduleDao getTrainScheduleDao();
public DataSourceDao getDataSourceDao();
}

42
src/main/java/org/leolo/nrdatad/db/MetadataDao.java

@ -0,0 +1,42 @@
package org.leolo.nrdatad.db;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.leolo.nrdatad.ConfigurationManager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Set;
import java.util.function.BiConsumer;
public abstract class MetadataDao extends BaseDao {
Logger log = LogManager.getLogger();
public MetadataDao(DatabaseManager manager){
super(manager);
}
public abstract String getMetadata(String key, String defaultValue) throws SQLException;
public String getMetadata(String key) throws SQLException{
return getMetadata(key, null);
}
public abstract void updateMetadata(String key, @NotNull String value) throws SQLException;
public abstract Set<String> getKeys() throws SQLException;
public abstract void forEach(BiConsumer<String, String> action) throws SQLException;
public boolean containsKey(String key) throws SQLException{
return getKeys().contains(key);
}
public int entryCount() throws SQLException{
return getKeys().size();
}
}

65
src/main/java/org/leolo/nrdatad/db/ParameterStore.java

@ -0,0 +1,65 @@
package org.leolo.nrdatad.db;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class ParameterStore extends ArrayList<String> {
@Override
public int indexOf(Object o) {
return super.indexOf(o)+1;
}
public void addAllObject(@NotNull Collection<?> c){
for(Object obj:c){
add(c.toString());
}
}
public void addObject(Object obj){
add(obj==null?null:obj.toString());
}
@Override
public int lastIndexOf(Object o) {
return super.lastIndexOf(o)+1;
}
@Override
public String get(int index) {
return super.get(index-1);
}
@Override
public String set(int index, String element) {
return super.set(index-1, element);
}
@Override
public void add(int index, String element) {
super.add(index-1, element);
}
@Override
public String remove(int index) {
return super.remove(index-1);
}
@Override
public boolean addAll(int index, Collection<? extends String> c) {
return super.addAll(index-1, c);
}
@Override
protected void removeRange(int fromIndex, int toIndex) {
super.removeRange(fromIndex-1, toIndex-1);
}
@Override
public List<String> subList(int fromIndex, int toIndex) {
return super.subList(fromIndex-1, toIndex-1);
}
}

17
src/main/java/org/leolo/nrdatad/db/SearchMode.java

@ -0,0 +1,17 @@
package org.leolo.nrdatad.db;
public enum SearchMode {
/**
* Indicate all condition must be matched
*/
MATCH_ALL,
/**
* Indicate at least one condition in every group must be matched
*/
MATCH_ALL_GROUP,
/**
* Indicate at least one condition must be matched.
*/
MATCH_ANY;
}

229
src/main/java/org/leolo/nrdatad/db/SmartDao.java

@ -0,0 +1,229 @@
package org.leolo.nrdatad.db;
import org.leolo.nrdatad.model.Corpus;
import org.leolo.nrdatad.model.Smart;
import org.leolo.nrdatad.model.SmartEvent;
import org.leolo.nrdatad.model.SmartStepType;
import org.leolo.nrdatad.util.Range;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public abstract class SmartDao extends BaseDao{
public SmartDao(DatabaseManager manager) {
super(manager);
}
public abstract void add(Smart smart) throws SQLException;
public abstract void truncateTable() throws SQLException;
public void addAll(Collection<Smart> smarts) throws SQLException{
for(Smart smart: smarts){
add(smart);
}
}
public abstract Collection<Smart> searchSmartByBerth(String fromBerth, String toBerth) throws SQLException;
public abstract Collection<Smart> searchFromByBerth(String fromBerth) throws SQLException;
public abstract Collection<Smart> searchToBerth(String toBerth) throws SQLException;
public abstract Collection<Smart> searchSmartByBerthLine(String fromBerth, String toBerth, String fromLine, String toLine) throws SQLException;
public abstract Collection<Smart> searchSmartByFromBerthLine(String fromBerth,String fromLine) throws SQLException;
public abstract Collection<Smart> searchSmartByToBerthLine(String toBerth, String toLine) throws SQLException;
public abstract Collection<Smart> searchSmartByStanox(String stanox) throws SQLException;
public abstract Collection<Smart> executeQuery(Query query) throws SQLException;
public static class QueryBuilder{
private Set<String> td = new HashSet<>();
private Set<String> fromBerth = new HashSet<>();
private Set<String> toBerth = new HashSet<>();
private Set<String> fromLine = new HashSet<>();
private Set<String> toLine = new HashSet<>();
private Set<Range<Integer>> berthOffset = new HashSet<>();
private Set<String> platform = new HashSet<>();
private Set<SmartEvent> event = new HashSet<>();
private Set<String> route = new HashSet<>();
private Set<String> stanox = new HashSet<>();
private Set<String> stationName = new HashSet<>();
private Set<SmartStepType> stepType = new HashSet<>();
private Set<String> comment = new HashSet<>();
private SearchMode searchMode = SearchMode.MATCH_ALL_GROUP;
public QueryBuilder addTd(String td){
this.td.add(td);
return this;
}
public QueryBuilder addFromBerth(String fromBerth){
this.fromBerth.add(fromBerth);
return this;
}
public QueryBuilder addToBerth(String toBerth){
this.toBerth.add(toBerth);
return this;
}
public QueryBuilder addFromLine(String fromLine){
this.fromLine.add(fromLine);
return this;
}
public QueryBuilder addToLine(String toLine){
this.toLine.add(toLine);
return this;
}
public QueryBuilder addBerthOffset(int bound1, int bound2){
this.berthOffset.add(new Range<>(bound1, bound2));
return this;
}
public QueryBuilder addPlatform(String platform){
this.platform.add(platform);
return this;
}
public QueryBuilder addEvent(SmartEvent event){
this.event.add(event);
return this;
}
public QueryBuilder addRoute(String route){
this.route.add(route);
return this;
}
public QueryBuilder addStanox(String stanox){
this.stanox.add(stanox);
return this;
}
public QueryBuilder addStationName(String stationName){
this.stationName.add(stationName);
return this;
}
public QueryBuilder addStepType(SmartStepType stepType){
this.stepType.add(stepType);
return this;
}
public QueryBuilder addComment(String comment){
this.comment.add(comment);
return this;
}
public QueryBuilder setSearchMode(SearchMode searchMode){
this.searchMode=searchMode;
return this;
}
public Query build(){
return new Query(td, fromBerth, toBerth, fromLine, toLine, berthOffset, platform, event, route, stanox, stationName, stepType, comment, searchMode);
}
}
public static class Query{
private Set<String> td;
private Set<String> fromBerth;
private Set<String> toBerth;
private Set<String> fromLine;
private Set<String> toLine;
private Set<Range<Integer>> berthOffset;
private Set<String> platform;
private Set<SmartEvent> event;
private Set<String> route;
private Set<String> stanox;
private Set<String> stationName;
private Set<SmartStepType> stepType;
private Set<String> comment;
private SearchMode searchMode = SearchMode.MATCH_ALL_GROUP;
public Query(Set<String> td, Set<String> fromBerth, Set<String> toBerth, Set<String> fromLine, Set<String> toLine, Set<Range<Integer>> berthOffset, Set<String> platform, Set<SmartEvent> event, Set<String> route, Set<String> stanox, Set<String> stationName, Set<SmartStepType> stepType, Set<String> comment, SearchMode searchMode) {
this.td = Collections.unmodifiableSet(td);
this.fromBerth = Collections.unmodifiableSet(fromBerth);
this.toBerth = Collections.unmodifiableSet(toBerth);
this.fromLine = Collections.unmodifiableSet(fromLine);
this.toLine = Collections.unmodifiableSet(toLine);
this.berthOffset = Collections.unmodifiableSet(berthOffset);
this.platform = Collections.unmodifiableSet(platform);
this.event = Collections.unmodifiableSet(event);
this.route = Collections.unmodifiableSet(route);
this.stanox = Collections.unmodifiableSet(stanox);
this.stationName = Collections.unmodifiableSet(stationName);
this.stepType = Collections.unmodifiableSet(stepType);
this.comment = Collections.unmodifiableSet(comment);
this.searchMode = searchMode;
}
public Set<String> getTd() {
return td;
}
public Set<String> getFromBerth() {
return fromBerth;
}
public Set<String> getToBerth() {
return toBerth;
}
public Set<String> getFromLine() {
return fromLine;
}
public Set<String> getToLine() {
return toLine;
}
public Set<Range<Integer>> getBerthOffset() {
return berthOffset;
}
public Set<String> getPlatform() {
return platform;
}
public Set<SmartEvent> getEvent() {
return event;
}
public Set<String> getRoute() {
return route;
}
public Set<String> getStanox() {
return stanox;
}
public Set<String> getStationName() {
return stationName;
}
public Set<SmartStepType> getStepType() {
return stepType;
}
public Set<String> getComment() {
return comment;
}
public SearchMode getSearchMode() {
return searchMode;
}
}
}

41
src/main/java/org/leolo/nrdatad/db/TiplocDao.java

@ -0,0 +1,41 @@
package org.leolo.nrdatad.db;
import org.leolo.nrdatad.model.Tiploc;
import java.sql.SQLException;
import java.util.Collection;
public abstract class TiplocDao extends BaseDao{
public TiplocDao(DatabaseManager manager) {
super(manager);
}
public abstract void insert(Tiploc tiploc) throws SQLException;
public void insertAll(Collection<Tiploc> tiplocs) throws SQLException{
for(Tiploc tiploc: tiplocs){
insert(tiploc);
}
}
public abstract boolean hasTiploc(String tiplocCode) throws SQLException;
public void replace(Tiploc tiploc) throws SQLException{
if(hasTiploc(tiploc.getTiplocCode())){
update(tiploc);
} else {
insert(tiploc);
}
}
public abstract void update(Tiploc tiploc) throws SQLException;
public void replaceAll(Collection<Tiploc> tiplocs) throws SQLException{
for(Tiploc tiploc:tiplocs){
replace(tiploc);
}
}
public abstract void truncateTable() throws SQLException;
}

63
src/main/java/org/leolo/nrdatad/db/TrainAssociationDao.java

@ -0,0 +1,63 @@
package org.leolo.nrdatad.db;
import org.leolo.nrdatad.model.ScheduleAssociation;
import java.sql.SQLException;
import java.util.Collection;
public abstract class TrainAssociationDao extends BaseDao{
public TrainAssociationDao(DatabaseManager manager) {
super(manager);
}
public abstract void insert(ScheduleAssociation scheduleAssociation) throws SQLException;
public abstract boolean hasTrainAssociation(String auid) throws SQLException;
public final boolean hasTrainAssociation(ScheduleAssociation scheduleAssociation) throws SQLException {
return hasTrainAssociation(scheduleAssociation.getAuid());
}
public abstract void update(ScheduleAssociation scheduleAssociation) throws SQLException;
public abstract void updateLastCheck(String auid) throws SQLException;
public final void updateLastCheck(ScheduleAssociation scheduleAssociation) throws SQLException{
updateLastCheck(scheduleAssociation.getAuid());
}
public abstract ReplaceResult replace(ScheduleAssociation scheduleAssociation) throws SQLException;
public abstract int getHashCode(String auid) throws SQLException;
public final int getHashCode(ScheduleAssociation scheduleAssociation) throws SQLException{
return getHashCode(scheduleAssociation.getAuid());
}
public void insertAll(Collection<ScheduleAssociation> scheduleAssociations) throws SQLException{
for(ScheduleAssociation scheduleAssociation:scheduleAssociations){
insert(scheduleAssociation);
}
}
public void updateAll(Collection<ScheduleAssociation> scheduleAssociations) throws SQLException{
for (ScheduleAssociation scheduleAssociation:scheduleAssociations){
update(scheduleAssociation);
}
}
public void updateAllLastCheck(Collection<ScheduleAssociation> scheduleAssociations) throws SQLException{
for (ScheduleAssociation scheduleAssociation:scheduleAssociations){
updateLastCheck(scheduleAssociation);
}
}
public void replaceAll(Collection<ScheduleAssociation> scheduleAssociations) throws SQLException{
for (ScheduleAssociation scheduleAssociation:scheduleAssociations){
replace(scheduleAssociation);
}
}
}

24
src/main/java/org/leolo/nrdatad/db/TrainScheduleDao.java

@ -0,0 +1,24 @@
package org.leolo.nrdatad.db;
import org.leolo.nrdatad.model.TrainSchedule;
import java.sql.SQLException;
import java.util.Collection;
public abstract class TrainScheduleDao extends BaseDao {
public TrainScheduleDao(DatabaseManager databaseManager){
super(databaseManager);
}
public abstract void insert(TrainSchedule schedule) throws SQLException;
public void insertAll(Collection<TrainSchedule> schedules) throws SQLException{
for(TrainSchedule ts: schedules){
insert(ts);
}
}
public abstract boolean hasSUID(String suid) throws SQLException;
}

387
src/main/java/org/leolo/nrdatad/db/mariadb/CorpusDaoImpl.java

@ -0,0 +1,387 @@
package org.leolo.nrdatad.db.mariadb;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.leolo.nrdatad.db.DatabaseManager;
import org.leolo.nrdatad.db.ParameterStore;
import org.leolo.nrdatad.db.SearchMode;
import org.leolo.nrdatad.model.Corpus;
import java.sql.*;
import java.util.*;
public class CorpusDaoImpl extends org.leolo.nrdatad.db.CorpusDao {
Logger log = LogManager.getLogger();
public CorpusDaoImpl(DatabaseManager manager) {
super(manager);
}
@Override
public Collection<Corpus> executeQuery(org.leolo.nrdatad.db.CorpusDao.Query query) throws SQLException {
Vector<Corpus> corpuses = new Vector<>();
ParameterStore params = new ParameterStore();
StringBuilder sql = new StringBuilder();
if(query.getSearchMode()== SearchMode.MATCH_ALL_GROUP || query.getSearchMode()==SearchMode.MATCH_ANY){
sql.append("SELECT * FROM corpus WHERE ");
String keyWord;
if(query.getSearchMode() == SearchMode.MATCH_ALL_GROUP){
sql.append("1=1 ");
keyWord = "AND ";
}else{
sql.append("1=0 ");
keyWord = "OR ";
}
if(query.getStanox().size()>0){
sql.append(keyWord).append("stanox IN (")
.append(getQueryParams(query.getStanox().size())).append(") ");
params.addAll(query.getStanox());
}
if(query.getUicCode().size()>0){
sql.append(keyWord).append("uic_code IN (")
.append(getQueryParams(query.getUicCode().size())).append(") ");
params.addAll(query.getUicCode());
}
if(query.getCrsCode().size()>0){
sql.append(keyWord).append("crs_code IN (")
.append(getQueryParams(query.getCrsCode().size())).append(") ");
params.addAll(query.getCrsCode());
}
if(query.getTiplocCode().size()>0){
sql.append(keyWord).append("tiploc_code IN (")
.append(getQueryParams(query.getTiplocCode().size())).append(") ");
params.addAll(query.getTiplocCode());
}
if(query.getNlcCode().size()>0){
sql.append(keyWord).append("nlc_code IN(")
.append(getQueryParams(query.getNlcCode().size())).append(") ");
params.addAll(query.getNlcCode());
}
if(query.getDescription().size()>0){
sql.append(keyWord).append("(");
boolean hasDesc = false;
for(String str:query.getDescription()){
if(hasDesc){
sql.append("OR ");
}
sql.append("(`desc` LIKE ? OR short_desc LIKE ? ) ");
hasDesc = true;
params.add("%"+str+"%");
params.add("%"+str+"%");
}
sql.append(") ");
}
log.atDebug().log("SQL={}",sql);
}else{
log.always().log("Search mode {} is yet to be implemented.", query.getSearchMode());
}
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql.toString())
){
for(int i=1;i<=params.size();i++){
log.atDebug().log("Param {} = {}", i, params.get(i));
setString(pstmt, i, params.get(i));
}
try(ResultSet rs = pstmt.executeQuery()){
while(rs.next()){
corpuses.add(parseCORPUS(rs));
}
}
}
return corpuses;
}
@Override
public void add(@NotNull Corpus corpus) throws SQLException {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(
"INSERT INTO corpus (stanox, uic_code, crs_code, tiploc_code, nlc_code, `desc`, short_desc, data_source) " +
"VALUES (?,?,?,?,?,?,?,?)")
){
setString(pstmt, 1, corpus.getStanoxCode());
setString(pstmt, 2, corpus.getUicCode());
setString(pstmt, 3, corpus.getCrsCode());
setString(pstmt, 4, corpus.getTiplocCode());
setString(pstmt, 5, corpus.getNlcCode());
setString(pstmt, 6, corpus.getLongDescription());
setString(pstmt, 7, corpus.getShortDescription());
setString(pstmt, 8, corpus.getDataSource().getUuid().toString());
pstmt.executeUpdate();
conn.commit();
}
}
@Override
public void addAll(@NotNull Collection<Corpus> datas) throws SQLException {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(
"INSERT INTO corpus (stanox, uic_code, crs_code, tiploc_code, nlc_code, `desc`, short_desc, data_source) " +
"VALUES (?,?,?,?,?,?,?,?)")
){
for(Corpus corpus:datas) {
setString(pstmt, 1, corpus.getStanoxCode());
setString(pstmt, 2, corpus.getUicCode());
setString(pstmt, 3, corpus.getCrsCode());
setString(pstmt, 4, corpus.getTiplocCode());
setString(pstmt, 5, corpus.getNlcCode());
setString(pstmt, 6, corpus.getLongDescription());
setString(pstmt, 7, corpus.getShortDescription());
setString(pstmt, 8, corpus.getDataSource().getUuid().toString());
pstmt.addBatch();
}
pstmt.executeBatch();
conn.commit();
}
}
@Override
public void replaceAll(@NotNull Collection<Corpus> datas) throws SQLException {
int totalCount = 0, insertCount = 0, updateCount = 0;
HashSet<String> pendingInsert = new HashSet<>();
try(
Connection conn = getConnection();
PreparedStatement psIns = conn.prepareStatement(
"INSERT INTO corpus (stanox, uic_code, crs_code, tiploc_code, `desc`, short_desc, data_source, nlc_code) " +
"VALUES (?,?,?,?,?,?,?,?)");
PreparedStatement psUpd = conn.prepareStatement(
"UPDATE corpus " +
"SET stanox=?, uic_code=?, crs_code=?, tiploc_code=?, `desc`=?, short_desc=?, data_source = ? " +
"WHERE nlc_code = ?"
)
){
for(Corpus corpus:datas){
totalCount++;
Corpus existing = searchCORPUSByNlcCode(corpus.getNlcCode());
PreparedStatement stmt = null;
if(corpus.equals(existing)){
log.atDebug().log("CORPUS {} not modified", corpus.getNlcCode());
continue;
}else if(existing==null){
if(pendingInsert.contains(corpus.getNlcCode())){
log.atDebug().log("CORPUS {} already arranged for insert", corpus.getNlcCode());
continue;
}
pendingInsert.add(corpus.getNlcCode());
stmt = psIns;
insertCount++;
log.atDebug().log("CORPUS {} to be inserted", corpus.getNlcCode());
}else{
stmt = psUpd;
updateCount++;
log.atDebug().log("CORPUS {} to be updated", corpus.getNlcCode());
}
setString(stmt, 1, corpus.getStanoxCode());
setString(stmt, 2, corpus.getUicCode());
setString(stmt, 3, corpus.getCrsCode());
setString(stmt, 4, corpus.getTiplocCode());
setString(stmt, 5, corpus.getLongDescription());
setString(stmt, 6, corpus.getShortDescription());
setString(stmt, 7, corpus.getDataSource().getUuid().toString());
setString(stmt, 8, corpus.getNlcCode());
stmt.addBatch();
}
if(insertCount>0){
psIns.executeBatch();
log.atInfo().log("Inserted {} CORPUS records", insertCount);
}
if(updateCount>0){
psUpd.executeBatch();
log.atInfo().log("Updated {} CORPUS records", updateCount);
}
if(insertCount==0 && updateCount==0){
log.atInfo().log("CORPUS data already up to date. ");
}else{
conn.commit();
log.atInfo().log("Done committing CORPUS data");
}
}
}
@Override
public void truncateTable() throws SQLException {
try(
Connection conn = getConnection();
Statement stmt = conn.createStatement()
){
stmt.executeQuery("TRUNCATE TABLE corpus");
}
}
@Override
public boolean hasNlcCode(String nlcCode) throws SQLException {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT 1 FROM corpus WHERE nlc_code = ?")
){
pstmt.setString(1, nlcCode);
try(ResultSet rs = pstmt.executeQuery()){
return rs.next();
}
}
}
@Override
public void update(Corpus corpus) throws SQLException {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(
"UPDATE corpus " +
"SET stanox=?, uic_code=?, crs_code=?, tiploc_code=?, `desc`=?, short_desc=?, data_source = ? " +
"WHERE nlc_code = ?"
)
){
setString(pstmt, 1, corpus.getStanoxCode());
setString(pstmt, 2, corpus.getUicCode());
setString(pstmt, 3, corpus.getCrsCode());
setString(pstmt, 4, corpus.getTiplocCode());
setString(pstmt, 5, corpus.getLongDescription());
setString(pstmt, 6, corpus.getShortDescription());
setString(pstmt, 7, corpus.getDataSource().getUuid().toString());
setString(pstmt, 8, corpus.getNlcCode());
pstmt.executeUpdate();
}
}
@Override
public Collection<Corpus> searchCORPUSByStanoxCode(String stanoxCode) throws SQLException {
Collection<Corpus> corpuses = new Vector<>();
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM corpus WHERE stanox = ?")
){
pstmt.setString(1, stanoxCode);
try(ResultSet rs = pstmt.executeQuery()){
while(rs.next()){
corpuses.add(parseCORPUS(rs));
}
}
}
return corpuses;
}
@Override
public Collection<Corpus> searchCORPUSByUicCode(String uicCode) throws SQLException {
Collection<Corpus> corpuses = new Vector<>();
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM corpus WHERE uic_code = ?")
){
pstmt.setString(1, uicCode);
try(ResultSet rs = pstmt.executeQuery()){
while(rs.next()){
corpuses.add(parseCORPUS(rs));
}
}
}
return corpuses;
}
@Override
public Collection<Corpus> searchCORPUSByCrsCode(String crsCode) throws SQLException {
Collection<Corpus> corpuses = new Vector<>();
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM corpus WHERE crs_code = ?")
){
pstmt.setString(1, crsCode);
try(ResultSet rs = pstmt.executeQuery()){
while(rs.next()){
corpuses.add(parseCORPUS(rs));
}
}
}
return corpuses;
}
@Override
public Collection<Corpus> searchCORPUSByTiplocCode(String tiplocCode) throws SQLException {
Collection<Corpus> corpuses = new Vector<>();
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM corpus WHERE tiploc_code = ?")
){
pstmt.setString(1, tiplocCode);
try(ResultSet rs = pstmt.executeQuery()){
while(rs.next()){
corpuses.add(parseCORPUS(rs));
}
}
}
return corpuses;
}
@Override
public Corpus searchCORPUSByNlcCode(String nlcCode) throws SQLException {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM corpus WHERE nlc_code = ?")
){
pstmt.setString(1, nlcCode);
try(ResultSet rs = pstmt.executeQuery()){
if(rs.next()){
return parseCORPUS(rs);
}
}
}
return null;
}
@Override
public List<Corpus> searchCORPUSByName(String name) throws SQLException {
List<Corpus> corpuses = new Vector<>();
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(
"SELECT * FROM corpus WHERE " +
"match(`desc`) against (?) OR " +
"match(short_desc) against (?) " +
"ORDER BY match(`desc`) against (?)+match(short_desc) against (?)"
)
){
pstmt.setString(1, name);
pstmt.setString(2, name);
pstmt.setString(3, name);
pstmt.setString(4, name);
try(ResultSet rs = pstmt.executeQuery()){
while(rs.next()){
corpuses.add(parseCORPUS(rs));
}
}
}
return corpuses;
}
private Corpus parseCORPUS(ResultSet rs) throws SQLException{
Corpus corpus = new Corpus();
corpus.setStanoxCode(rs.getString("stanox"));
corpus.setUicCode(rs.getString("uic_code"));
corpus.setCrsCode(rs.getString("crs_code"));
corpus.setTiplocCode(rs.getString("tiploc_code"));
corpus.setNlcCode(rs.getString("nlc_code"));
corpus.setLongDescription(rs.getString("desc"));
corpus.setShortDescription(rs.getString("short_desc"));
return corpus;
}
@Override
public Corpus delete(String nlcCode) throws SQLException {
Corpus deletedEntry = searchCORPUSByNlcCode(nlcCode);
if(deletedEntry!=null){
try(
Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement("DELETE FROM corpus WHERE nlc_code = ?")
){
stmt.setString(1, nlcCode);
stmt.executeUpdate();
}
}
return deletedEntry;
}
}

36
src/main/java/org/leolo/nrdatad/db/mariadb/DataSourceDaoImpl.java

@ -0,0 +1,36 @@
package org.leolo.nrdatad.db.mariadb;
import org.leolo.nrdatad.db.DataSourceDao;
import org.leolo.nrdatad.db.DatabaseManager;
import org.leolo.nrdatad.model.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class DataSourceDaoImpl extends DataSourceDao {
public DataSourceDaoImpl(DatabaseManager manager){
super(manager);
}
@Override
public void insert(DataSource ds) throws SQLException {
try(
Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(
"INSERT INTO data_source (uuid, provider, data_stream, is_messsage, start_date, message_id) " +
"VALUES (?,?,?,?,?,?)"
)
){
setString(stmt,1, ds.getUuid().toString());
setString(stmt,2, ds.getProvider());
setString(stmt,3, ds.getStreamName());
setString(stmt,4, ds.isMessage()?"Y":"N");
setDate (stmt,5, ds.getStartDate());
setString(stmt,6, ds.getMessageId());
stmt.executeUpdate();
conn.commit();
}
}
}

98
src/main/java/org/leolo/nrdatad/db/mariadb/DatabaseManager.java

@ -0,0 +1,98 @@
package org.leolo.nrdatad.db.mariadb;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.leolo.nrdatad.ConfigurationManager;
import org.leolo.nrdatad.db.*;
import org.mariadb.jdbc.MariaDbPoolDataSource;
import java.sql.*;
public class DatabaseManager implements org.leolo.nrdatad.db.DatabaseManager{
Logger logger = LogManager.getLogger();
private MariaDbPoolDataSource ds;
@Override
public void initPool() {
logger.atDebug().log("Start initialize the database pool");
ConfigurationManager conf = ConfigurationManager.getInstance();
if(
!conf.containsKey("db.host")||
!conf.containsKey("db.user")||
!conf.containsKey("db.pwd")||
!conf.containsKey("db.name")
) {
logger.atFatal().log("Missing required property");
System.exit(1);
return;
}
String url = "jdbc:mariadb://"+conf.getProperty("db.host")+
":"+conf.getProperty("db.port", "3306")+
"/"+conf.getProperty("db.name");
logger.atDebug().log("URL={}",url);
try {
ds = new MariaDbPoolDataSource(url);
// ds.setMinPoolSize(1);
// ds.setMaxPoolSize(Integer.parseInt(conf.getOrDefault("db.poolsize", "20").toString()));
ds.setUser(conf.getProperty("db.user").toString());
ds.setPassword(conf.getProperty("db.pwd").toString());
} catch (SQLException e) {
logger.atFatal().withThrowable(e).log("Cannot connect to DB");
System.exit(-2);
}
}
@Override
public boolean checkConnection() {
try(
Connection conn = ds.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT 1")
){
return rs.next();
}catch(SQLException e){
return false;
}
}
@Override
public Connection getConnection() throws SQLException{
Connection conn = ds.getConnection();
conn.setAutoCommit(false);
return conn;
}
@Override
public MetadataDao getMetadataDao() {
return new MetadataDaoImpl(this);
}
@Override
public CorpusDao getCORPUSDao() {
return new CorpusDaoImpl(this);
}
@Override
public SmartDao getSmartDao() {
return new SmartDaoImpl(this);
}
@Override
public TiplocDao getTiplocDao() {
return new TiplocDaoImpl(this);
}
@Override
public TrainAssociationDao getTrainAssociationDao() {
return new TrainAssociationDaoImpl(this);
}
@Override
public TrainScheduleDao getTrainScheduleDao() {
return null;
}
@Override
public DataSourceDao getDataSourceDao() {
return new DataSourceDaoImpl(this);
}
}

116
src/main/java/org/leolo/nrdatad/db/mariadb/MetadataDaoImpl.java

@ -0,0 +1,116 @@
package org.leolo.nrdatad.db.mariadb;
import org.jetbrains.annotations.NotNull;
import java.sql.*;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeMap;
import java.util.function.BiConsumer;
public class MetadataDaoImpl extends org.leolo.nrdatad.db.MetadataDao {
private static Object SYNC_TOKEN = new Object();
public MetadataDaoImpl(DatabaseManager manager){
super(manager);
}
@Override
public String getMetadata(String key, String defaultValue) throws SQLException {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT value FROM metadata WHERE `key` = ?")
){
pstmt.setString(1, key);
try(ResultSet rs = pstmt.executeQuery()){
if(rs.next()){
return rs.getString(1);
}
}
}
return defaultValue;
}
@Override
public void updateMetadata(@NotNull String key, @NotNull String value) throws SQLException {
synchronized (SYNC_TOKEN) {
if (containsKey(key)) {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("UPDATE metadata SET value = ? WHERE `key` = ?")
){
pstmt.setString(1, value);
pstmt.setString(2, key);
pstmt.executeUpdate();
}
} else {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("INSERT INTO metadata (`key`, value) VALUES (?,?)")
){
pstmt.setString(1, key);
pstmt.setString(2, value);
pstmt.executeUpdate();
}
}
}
}
@Override
public Set<String> getKeys() throws SQLException {
HashSet<String> set = new HashSet<>();
try(
Connection conn = getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT `key` FROM metadata")
){
while(rs.next()){
set.add(rs.getString(1));
}
}
return set;
}
@Override
public void forEach(BiConsumer<String, String> action) throws SQLException {
TreeMap<String, String> map = new TreeMap<>();
try(
Connection conn = getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT `key`, value FROM metadata")
){
while(rs.next()){
map.put(rs.getString(1), rs.getString(2));
}
}
map.forEach(action);
}
@Override
public boolean containsKey(String key) throws SQLException {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT 1 FROM metadata WHERE `key` = ?")
){
pstmt.setString(1, key);
try(ResultSet rs = pstmt.executeQuery()){
return rs.next();
}
}
}
@Override
public int entryCount() throws SQLException {
try(
Connection conn = getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT COUNT(1) FROM metadata")
){
rs.next();
return rs.getInt(1);
}
}
}

324
src/main/java/org/leolo/nrdatad/db/mariadb/SmartDaoImpl.java

@ -0,0 +1,324 @@
package org.leolo.nrdatad.db.mariadb;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.leolo.nrdatad.db.DatabaseManager;
import org.leolo.nrdatad.db.ParameterStore;
import org.leolo.nrdatad.db.SearchMode;
import org.leolo.nrdatad.model.Smart;
import org.leolo.nrdatad.model.SmartEvent;
import org.leolo.nrdatad.model.SmartStepType;
import org.leolo.nrdatad.util.Range;
import java.sql.*;
import java.util.Collection;
import java.util.Vector;
public class SmartDaoImpl extends org.leolo.nrdatad.db.SmartDao {
Logger log = LogManager.getLogger();
public SmartDaoImpl(DatabaseManager manager) {
super(manager);
}
@Override
public void add(Smart smart) throws SQLException {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(
"INSERT INTO smart (" +
"td, from_berth, to_berth, from_line, to_line, breth_offset, platform, event, route, " +
"stanox, station_name, step_type, comment, data_source" +
") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)")
){
setString(pstmt, 1, smart.getTd());
setString(pstmt, 2, smart.getFromBerth());
setString(pstmt, 3, smart.getToBerth());
setString(pstmt, 4, smart.getFromLine());
setString(pstmt, 5, smart.getToLine());
setInt (pstmt, 6, smart.getBerthOffset());
setString(pstmt, 7, smart.getPlatform());
setString(pstmt, 8, smart.getEvent().getCode());
setString(pstmt, 9, smart.getRoute());
setString(pstmt, 10, smart.getStanox());
setString(pstmt, 11, smart.getStationName());
setString(pstmt, 12, smart.getStepType().getCode());
setString(pstmt, 13, smart.getComment());
setString(pstmt, 14, smart.getDataSource().getUuid().toString());
pstmt.executeUpdate();
conn.commit();
}
}
@Override
public void addAll(Collection<Smart> smarts) throws SQLException {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(
"INSERT INTO smart (" +
"td, from_berth, to_berth, from_line, to_line, breth_offset, platform, event, route, " +
"stanox, station_name, step_type, comment, data_source" +
") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?, ?)")
){
for(Smart smart:smarts){
setString(pstmt, 1, smart.getTd());
setString(pstmt, 2, smart.getFromBerth());
setString(pstmt, 3, smart.getToBerth());
setString(pstmt, 4, smart.getFromLine());
setString(pstmt, 5, smart.getToLine());
setInt (pstmt, 6, smart.getBerthOffset());
setString(pstmt, 7, smart.getPlatform());
setString(pstmt, 8, smart.getEvent().getCode());
setString(pstmt, 9, smart.getRoute());
setString(pstmt, 10, smart.getStanox());
setString(pstmt, 11, smart.getStationName());
setString(pstmt, 12, smart.getStepType().getCode());
setString(pstmt, 13, smart.getComment());
setString(pstmt, 14, smart.getDataSource().getUuid().toString());
pstmt.addBatch();
}
pstmt.executeBatch();
conn.commit();
}
}
@Override
public void truncateTable() throws SQLException {
try(
Connection conn = getConnection();
Statement stmt = conn.createStatement()
){
stmt.executeUpdate("TRUNCATE TABLE smart");
}
}
private Smart parseResultSet(ResultSet rs) throws SQLException{
Smart smart = new Smart();
smart.setSmartId(rs.getInt("smart_id"));
smart.setTd(rs.getString("td"));
smart.setFromBerth(rs.getString("from_berth"));
smart.setToBerth(rs.getString("to_berth"));
smart.setFromLine(rs.getString("from_line"));
smart.setToLine(rs.getString("to_line"));
smart.setBerthOffset(rs.getInt("berth_offset"));
smart.setPlatform(rs.getString("platform"));
smart.setEvent(SmartEvent.parseCode(rs.getString("event")));
smart.setRoute(rs.getString("route"));
smart.setStanox(rs.getString("stanox"));
smart.setStationName(rs.getString("station_name"));
smart.setStepType(SmartStepType.parseCode(rs.getString("step_type")));
smart.setComment(rs.getString("comment"));
return smart;
}
@Override
public Collection<Smart> searchSmartByBerth(String fromBerth, String toBerth) throws SQLException {
Vector<Smart> vector = new Vector<>();
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM smart WHERE from_berth = ? AND to_berth = ?");
){
pstmt.setString(1, fromBerth);
pstmt.setString(2, toBerth);
try(ResultSet rs = pstmt.executeQuery()){
vector.add(parseResultSet(rs));
}
}
return vector;
}
@Override
public Collection<Smart> searchFromByBerth(String fromBerth) throws SQLException {
Vector<Smart> vector = new Vector<>();
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM smart WHERE from_berth =?");
){
pstmt.setString(1, fromBerth);
try(ResultSet rs = pstmt.executeQuery()){
vector.add(parseResultSet(rs));
}
}
return vector;
}
@Override
public Collection<Smart> searchToBerth(String toBerth) throws SQLException {Vector<Smart> vector = new Vector<>();
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT * FROM smart WHERE to_berth = ?");
){
pstmt.setString(1, toBerth);
try(ResultSet rs = pstmt.executeQuery()){
vector.add(parseResultSet(rs));
}
}
return vector;
}
@Override
public Collection<Smart> searchSmartByBerthLine(String fromBerth, String toBerth, String fromLine, String toLine) throws SQLException {
Vector<Smart> vector = new Vector<>();
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(
"SELECT * FROM smart WHERE from_berth = ? AND to_berth = ? AND from_line = ? AND to_line = ?");
){
pstmt.setString(1, fromBerth);
pstmt.setString(2, toBerth);
pstmt.setString(3, fromLine);
pstmt.setString(4, toLine);
try(ResultSet rs = pstmt.executeQuery()){
vector.add(parseResultSet(rs));
}
}
return vector;
}
@Override
public Collection<Smart> searchSmartByFromBerthLine(String fromBerth, String fromLine) throws SQLException {
Vector<Smart> vector = new Vector<>();
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(
"SELECT * FROM smart WHERE from_berth = ? AND to_berth = ? AND from_line = ? AND to_line = ?");
){
pstmt.setString(1, fromBerth);
pstmt.setString(2, fromLine);
try(ResultSet rs = pstmt.executeQuery()){
vector.add(parseResultSet(rs));
}
}
return vector;
}
@Override
public Collection<Smart> searchSmartByToBerthLine(String toBerth, String toLine) throws SQLException {
Vector<Smart> vector = new Vector<>();
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(
"SELECT * FROM smart WHERE from_berth = ? AND to_berth = ? AND from_line = ? AND to_line = ?");
){
pstmt.setString(1, toBerth);
pstmt.setString(2, toLine);
try(ResultSet rs = pstmt.executeQuery()){
vector.add(parseResultSet(rs));
}
}
return vector;
}
@Override
public Collection<Smart> searchSmartByStanox(String stanox) throws SQLException {Vector<Smart> vector = new Vector<>();
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(
"SELECT * FROM smart WHERE stanox = ?");
){
pstmt.setString(1, stanox);
try(ResultSet rs = pstmt.executeQuery()){
vector.add(parseResultSet(rs));
}
}
return vector;
}
@Override
public Collection<Smart> executeQuery(Query query) throws SQLException {
Vector<Smart> smarts = new Vector<>();
ParameterStore ps = new ParameterStore();
StringBuilder sql = new StringBuilder();
if(query.getSearchMode()== SearchMode.MATCH_ALL_GROUP || query.getSearchMode()==SearchMode.MATCH_ANY){
sql.append("SELECT * FROM smart WHERE ");
String keyWord;
if(query.getSearchMode() == SearchMode.MATCH_ALL_GROUP){
sql.append("1=1 ");
keyWord = "AND ";
}else{
sql.append("1=0 ");
keyWord = "OR ";
}
if(query.getTd().size()>0){
sql.append(keyWord).append("td IN (").append(getQueryParams(query.getTd().size())).append(") ");
ps.addAll(query.getTd());
}
if(query.getFromBerth().size()>0){
sql.append(keyWord).append("from_berth IN(").append(getQueryParams(query.getFromBerth().size())).append(") ");
ps.addAll(query.getFromBerth());
}
if(query.getToBerth().size()>0){
sql.append(keyWord).append("to_berth IN(").append(getQueryParams(query.getToBerth().size())).append(") ");
ps.addAll(query.getToBerth());
}
if(query.getFromLine().size()>0){
sql.append(keyWord).append("from_line IN(").append(getQueryParams(query.getFromLine().size())).append(") ");
ps.addAll(query.getFromLine());
}
if (query.getToLine().size()>0){
sql.append(keyWord).append("to_line (").append(getQueryParams(query.getToLine().size())).append(") ");
ps.addAll(query.getToLine());
}
if (query.getBerthOffset().size()>0){
sql.append(keyWord).append("(1=0 ");
for(Range<Integer> range: query.getBerthOffset()){
sql.append("OR (berth_offset BETWEEN ? AND ?) ");
ps.addObject(range.getLowerBound());
ps.addObject(range.getUpperBound());
}
sql.append(") ");
}
if(query.getPlatform().size()>0){
sql.append(keyWord).append("platform IN(").append(getQueryParams(query.getPlatform().size())).append(") ");
ps.addAll(query.getPlatform());
}
if(query.getEvent().size()>0){
sql.append(keyWord).append("event IN(").append(getQueryParams(query.getEvent().size())).append(") ");
ps.addAllObject(query.getEvent());
}
if(query.getRoute().size()>0){
sql.append(keyWord).append("route IN(").append(getQueryParams(query.getRoute().size())).append(") ");
ps.addAll(query.getRoute());
}
if(query.getStanox().size()>0){
sql.append(keyWord).append("stanox IN(").append(getQueryParams(query.getStanox().size())).append(") ");
ps.addAll(query.getStanox());
}
if(query.getStationName().size()>0){
sql.append(keyWord).append("(1=0 ");
for(String stationName: query.getStationName()){
sql.append("OR station_name LIKE ? ");
ps.add("%"+stationName+"%");
}
sql.append(") ");
}
if(query.getStepType().size()>0){
sql.append(keyWord).append("step_type IN(").append(getQueryParams(query.getStepType().size())).append(") ");
ps.addAllObject(query.getStepType());
}
if(query.getComment().size()>0){
sql.append(keyWord).append("(1=0 ");
for(String comment: query.getComment()){
sql.append("OR comment LIKE ? ");
ps.add("%"+comment+"%");
}
sql.append(") ");
}
}
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql.toString())
){
for(int i=1;i<=ps.size();i++){
log.atDebug().log("Param {} = {}", i, ps.get(i));
setString(pstmt, i, ps.get(i));
}
try(ResultSet rs = pstmt.executeQuery()){
while(rs.next()){
smarts.add(parseResultSet(rs));
}
}
}
return smarts;
}
}

131
src/main/java/org/leolo/nrdatad/db/mariadb/TiplocDaoImpl.java

@ -0,0 +1,131 @@
package org.leolo.nrdatad.db.mariadb;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.leolo.nrdatad.db.DatabaseManager;
import org.leolo.nrdatad.db.TiplocDao;
import org.leolo.nrdatad.model.Tiploc;
import java.sql.*;
import java.util.Collection;
public class TiplocDaoImpl extends TiplocDao {
private Logger log = LogManager.getLogger();
public TiplocDaoImpl(DatabaseManager manager) {
super(manager);
}
@Override
public void insert(Tiploc tiploc) throws SQLException {
try (
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("INSERT INTO tiploc (tiploc_code, nalco, stanox, crs_code, description, tps_description) VALUES (?,?,?,?,?,?)")
) {
setString(pstmt, 1, tiploc.getTiplocCode());
setString(pstmt, 2, tiploc.getNalco());
setString(pstmt, 3, tiploc.getStanox());
setString(pstmt, 4, tiploc.getCrsCode());
setString(pstmt, 5, tiploc.getDescription());
setString(pstmt, 6, tiploc.getTpsDescription());
pstmt.executeUpdate();
conn.commit();
}
}
@Override
public void insertAll(Collection<Tiploc> tiplocs) throws SQLException {
try (
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("INSERT INTO tiploc (tiploc_code, nalco, stanox, crs_code, description, tps_description) VALUES (?,?,?,?,?,?)")
) {
for(Tiploc tiploc:tiplocs) {
setString(pstmt, 1, tiploc.getTiplocCode());
setString(pstmt, 2, tiploc.getNalco());
setString(pstmt, 3, tiploc.getStanox());
setString(pstmt, 4, tiploc.getCrsCode());
setString(pstmt, 5, tiploc.getDescription());
setString(pstmt, 6, tiploc.getTpsDescription());
pstmt.addBatch();
}
pstmt.executeBatch();
conn.commit();
}
}
@Override
public void replaceAll(Collection<Tiploc> tiplocs) throws SQLException {
try (
Connection conn = getConnection();
PreparedStatement psIns = conn.prepareStatement("INSERT INTO tiploc (tiploc_code, nalco, stanox, crs_code, description, tps_description) VALUES (?,?,?,?,?,?)");
PreparedStatement psUpd = conn.prepareStatement("UPDATE tiploc SET nalco=?, stanox=?, crs_code=?, description=?, tps_description=? WHERE tiploc_code=?")
) {
for(Tiploc tiploc:tiplocs) {
if(hasTiploc(tiploc.getTiplocCode())){
setString(psUpd, 1, tiploc.getNalco());
setString(psUpd, 2, tiploc.getStanox());
setString(psUpd, 3, tiploc.getCrsCode());
setString(psUpd, 4, tiploc.getDescription());
setString(psUpd, 5, tiploc.getTpsDescription());
setString(psUpd, 6, tiploc.getTiplocCode());
psUpd.addBatch();
} else {
setString(psIns, 1, tiploc.getTiplocCode());
setString(psIns, 2, tiploc.getNalco());
setString(psIns, 3, tiploc.getStanox());
setString(psIns, 4, tiploc.getCrsCode());
setString(psIns, 5, tiploc.getDescription());
setString(psIns, 6, tiploc.getTpsDescription());
psIns.addBatch();
}
}
psIns.executeBatch();
psUpd.executeBatch();
conn.commit();
}
}
@Override
public boolean hasTiploc(String tiplocCode) throws SQLException {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT 1 FROM tiploc WHERE tiploc_code = ?")
){
pstmt.setString(1, tiplocCode);
try(ResultSet rs = pstmt.executeQuery()){
return rs.next();
}
}
}
@Override
public void update(Tiploc tiploc) throws SQLException {
try (
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("UPDATE tiploc SET nalco=?, stanox=?, crs_code=?, description=?, tps_description=? WHERE tiploc_code=?")
) {
setString(pstmt, 1, tiploc.getNalco());
setString(pstmt, 2, tiploc.getStanox());
setString(pstmt, 3, tiploc.getCrsCode());
setString(pstmt, 4, tiploc.getDescription());
setString(pstmt, 5, tiploc.getTpsDescription());
setString(pstmt, 6, tiploc.getTiplocCode());
pstmt.executeUpdate();
conn.commit();
}
}
@Override
public void truncateTable() throws SQLException{
try(
Connection conn = getConnection();
Statement stmt = conn.createStatement()
){
log.atInfo().log("Truncating table tiploc");
stmt.executeQuery("TRUNCATE TABLE tiploc");
}
}
}

248
src/main/java/org/leolo/nrdatad/db/mariadb/TrainAssociationDaoImpl.java

@ -0,0 +1,248 @@
package org.leolo.nrdatad.db.mariadb;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.leolo.nrdatad.db.DatabaseManager;
import org.leolo.nrdatad.db.TrainAssociationDao;
import org.leolo.nrdatad.exception.NoSuchRecordException;
import org.leolo.nrdatad.model.ScheduleAssociation;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
public class TrainAssociationDaoImpl extends TrainAssociationDao {
private Logger log = LogManager.getLogger();
public TrainAssociationDaoImpl(DatabaseManager manager) {
super(manager);
}
@Override
public void insert(ScheduleAssociation scheduleAssociation) throws SQLException {
try (
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("INSERT INTO train_association (" +
"auid, main_uid, asso_uid, start_date, end_date, " + //1-5
"asso_days, asso_type, date_ind, asso_loc, base_suffix, " + //6-10
"asso_suffix, stp_ind, hash_code, last_chk) " + //11-14
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,NOW())")
){
fillInsertPreparedStatement(pstmt, scheduleAssociation);
pstmt.executeUpdate();
conn.commit();
}
}
@Override
public boolean hasTrainAssociation(String auid) throws SQLException {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT 1 FROM train_association WHERE auid = ?")
){
setString(pstmt, 1, auid);
try(ResultSet rs = pstmt.executeQuery()){
return rs.next();
}
}
}
@Override
public void update(ScheduleAssociation scheduleAssociation) throws SQLException {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("UPDATE train_association SET " +
"main_uid=?, asso_uid=?, start_date=?, end_date=?, asso_days=?," +
"asso_type=?,date_ind=?,asso_loc=?,base_suffix=?, asso_suffix=?," +
"stp_ind=?, hash_code=?,last_chk=NOW() WHERE auid=?")
){
fillUpdatePreparedStatement(pstmt, scheduleAssociation);
pstmt.executeUpdate();
conn.commit();
}
}
@Override
public void updateLastCheck(String auid) throws SQLException {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("UPDATE train_association SET last_chk = NOW() WHERE auid = ?")
){
setString(pstmt,1, auid);
pstmt.executeUpdate();
conn.commit();
}
}
@Override
public ReplaceResult replace(ScheduleAssociation scheduleAssociation) throws SQLException {
Object checkResult = _getHashCode(scheduleAssociation.getAuid());
if(checkResult==null){
insert(scheduleAssociation);
return ReplaceResult.INSERTED;
} else if (((int)checkResult) == scheduleAssociation.hashCode()){
updateLastCheck(scheduleAssociation);
return ReplaceResult.NO_ACTION;
} else {
update(scheduleAssociation);
return ReplaceResult.UPDATED;
}
}
@Override
public int getHashCode(String auid) throws SQLException {
Object result = _getHashCode(auid);
if(result instanceof Integer){
return (Integer) result;
}
throw new NoSuchRecordException();
}
private Object _getHashCode(String auid) throws SQLException {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("SELECT hash_code FROM train_association WHERE auid = ?")
){
setString(pstmt, 1, auid);
try (ResultSet rs = pstmt.executeQuery()){
if(rs.next()){
return rs.getInt(1);
}
}
}
return null;
}
@Override
public void insertAll(Collection<ScheduleAssociation> scheduleAssociations) throws SQLException {
try (
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("INSERT INTO train_association (" +
"auid, main_uid, asso_uid, start_date, end_date, " + //1-5
"asso_days, asso_type, date_ind, asso_loc, base_suffix, " + //6-10
"asso_suffix, stp_ind, hash_code, last_chk) " + //11-14
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,NOW())")
){
for(ScheduleAssociation scheduleAssociation:scheduleAssociations) {
fillInsertPreparedStatement(pstmt, scheduleAssociation);
pstmt.addBatch();
}
pstmt.executeBatch();
conn.commit();
}
}
@Override
public void updateAll(Collection<ScheduleAssociation> scheduleAssociations) throws SQLException {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("UPDATE train_association SET " +
"main_uid=?, asso_uid=?, start_date=?, end_date=?, asso_days=?," +
"asso_type=?,date_ind=?,asso_loc=?,base_suffix=?, asso_suffix=?," +
"stp_ind=?, hash_code=?,last_chk=NOW() WHERE auid=?")
){
for(ScheduleAssociation scheduleAssociation:scheduleAssociations) {
fillUpdatePreparedStatement(pstmt, scheduleAssociation);
pstmt.addBatch();
}
pstmt.executeBatch();
conn.commit();
}
}
private void fillUpdatePreparedStatement(PreparedStatement pstmt, ScheduleAssociation scheduleAssociation) throws SQLException {
setString(pstmt, 1, scheduleAssociation.getMainUid());
setString(pstmt, 2, scheduleAssociation.getAssoUid());
setDate(pstmt, 3, scheduleAssociation.getStartDate());
setDate(pstmt, 4, scheduleAssociation.getEndDate());
setString(pstmt, 5, scheduleAssociation.getAssoDays());
setString(pstmt, 6, scheduleAssociation.getAssociationCategory()==null?"":scheduleAssociation.getAssociationCategory().getCode());
setInt(pstmt, 7, scheduleAssociation.getAssociationDate());
setString(pstmt, 8, scheduleAssociation.getAssociationLocation());
setString(pstmt, 9, scheduleAssociation.getBaseLocationSuffix());
setString(pstmt, 10, scheduleAssociation.getAssocLocationSuffix());
setString(pstmt, 11, scheduleAssociation.getStpIndicator()==null?"":scheduleAssociation.getStpIndicator().getCode());
setInt(pstmt, 12, scheduleAssociation.hashCode());
setString(pstmt, 13, scheduleAssociation.getAuid());
}
@Override
public void updateAllLastCheck(Collection<ScheduleAssociation> scheduleAssociations) throws SQLException {
try(
Connection conn = getConnection();
PreparedStatement pstmt = conn.prepareStatement("UPDATE train_association SET last_chk = NOW() WHERE auid = ?")
){
for(ScheduleAssociation scheduleAssociation:scheduleAssociations) {
setString(pstmt, 1, scheduleAssociation.getAuid());
pstmt.addBatch();
}
pstmt.executeBatch();
conn.commit();
}
}
@Override
public void replaceAll(Collection<ScheduleAssociation> scheduleAssociations) throws SQLException {
int insCount = 0;
int updCount = 0;
int nopCount = 0;
try (
Connection conn = getConnection();
PreparedStatement psIns = conn.prepareStatement("INSERT IGNORE INTO train_association (" +
"auid, main_uid, asso_uid, start_date, end_date, " + //1-5
"asso_days, asso_type, date_ind, asso_loc, base_suffix, " + //6-10
"asso_suffix, stp_ind, hash_code, last_chk) " + //11-14
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,NOW())");
PreparedStatement psUpd = conn.prepareStatement("UPDATE train_association SET " +
"main_uid=?, asso_uid=?, start_date=?, end_date=?, asso_days=?," +
"asso_type=?,date_ind=?,asso_loc=?,base_suffix=?, asso_suffix=?," +
"stp_ind=?, hash_code=?,last_chk=NOW() WHERE auid=?");
PreparedStatement psNop = conn.prepareStatement("UPDATE train_association SET last_chk = NOW() WHERE auid = ?")
){
for(ScheduleAssociation scheduleAssociation:scheduleAssociations){
Object checkResult = _getHashCode(scheduleAssociation.getAuid());
if(checkResult==null){
//Insert
fillInsertPreparedStatement(psIns, scheduleAssociation);
psIns.addBatch();
insCount++;
} else if (((int)checkResult) == scheduleAssociation.hashCode()){
//NOP
setString(psNop, 1, scheduleAssociation.getAuid());
psNop.addBatch();
nopCount++;
} else {
//Update
fillUpdatePreparedStatement(psUpd, scheduleAssociation);
psUpd.addBatch();
updCount++;
}
}
psIns.executeBatch();
psNop.executeBatch();
psUpd.executeBatch();
conn.commit();
log.atDebug().log("Batch info: {} Inserted. {} Updated. {} NOP.", insCount, updCount, nopCount);
}
}
private void fillInsertPreparedStatement(PreparedStatement psIns, ScheduleAssociation scheduleAssociation) throws SQLException {
setString(psIns, 1, scheduleAssociation.getAuid());
setString(psIns, 2, scheduleAssociation.getMainUid());
setString(psIns, 3, scheduleAssociation.getAssoUid());
setDate(psIns, 4, scheduleAssociation.getStartDate());
setDate(psIns, 5, scheduleAssociation.getEndDate());
setString(psIns, 6, scheduleAssociation.getAssoDays());
setString(psIns, 7, scheduleAssociation.getAssociationCategory()==null?"":scheduleAssociation.getAssociationCategory().getCode());
setInt(psIns, 8, scheduleAssociation.getAssociationDate());
setString(psIns, 9, scheduleAssociation.getAssociationLocation());
setString(psIns, 10, scheduleAssociation.getBaseLocationSuffix());
setString(psIns, 11, scheduleAssociation.getAssocLocationSuffix());
setString(psIns, 12, scheduleAssociation.getStpIndicator()==null?"":scheduleAssociation.getStpIndicator().getCode());
setInt(psIns, 13, scheduleAssociation.hashCode());
}
}

40
src/main/java/org/leolo/nrdatad/db/mariadb/TrainScheduleDaoImpl.java

@ -0,0 +1,40 @@
package org.leolo.nrdatad.db.mariadb;
import org.leolo.nrdatad.db.DatabaseManager;
import org.leolo.nrdatad.db.TrainScheduleDao;
import org.leolo.nrdatad.model.TrainSchedule;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TrainScheduleDaoImpl extends TrainScheduleDao {
public TrainScheduleDaoImpl(DatabaseManager databaseManager) {
super(databaseManager);
}
@Override
public void insert(TrainSchedule schedule) throws SQLException {
//Insert into train_schedule, train_schedule_location
try(Connection conn = getConnection()){
//Insert the train_schedule first
try (
PreparedStatement pstmt = conn.prepareStatement(
"INSERT INTO train_schedule (suid, train_uid, start_date, end_date, bank_holiday, signal_id, rsid, train_status, train_category, power_type, planned_speed, operating_character, class_avail, sleeper, reservation, catering, atoc_code) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"
)
){
setString(pstmt, 1, schedule.getSUID());
setInt (pstmt, 2, 1); //This is the first one
setString(pstmt, 3, schedule.getTrainUid());
char status = schedule.getTrainStatus().charAt(0);
}
}
}
@Override
public boolean hasSUID(String suid) throws SQLException {
return false;
}
}

37
src/main/java/org/leolo/nrdatad/exception/NoSuchRecordException.java

@ -0,0 +1,37 @@
package org.leolo.nrdatad.exception;
import java.sql.SQLException;
public class NoSuchRecordException extends SQLException {
public NoSuchRecordException(String reason, String SQLState, int vendorCode) {
super(reason, SQLState, vendorCode);
}
public NoSuchRecordException(String reason, String SQLState) {
super(reason, SQLState);
}
public NoSuchRecordException(String reason) {
super(reason);
}
public NoSuchRecordException() {
}
public NoSuchRecordException(Throwable cause) {
super(cause);
}
public NoSuchRecordException(String reason, Throwable cause) {
super(reason, cause);
}
public NoSuchRecordException(String reason, String sqlState, Throwable cause) {
super(reason, sqlState, cause);
}
public NoSuchRecordException(String reason, String sqlState, int vendorCode, Throwable cause) {
super(reason, sqlState, vendorCode, cause);
}
}

27
src/main/java/org/leolo/nrdatad/model/AssociationCategory.java

@ -0,0 +1,27 @@
package org.leolo.nrdatad.model;
public enum AssociationCategory {
JOIN("JJ"),
DIVIDE("VV"),
NEXT("NP");
private String code;
private AssociationCategory(String code){
this.code = code;
}
public String getCode() {
return code;
}
public static AssociationCategory parseCode(String code){
if(code==null)
return null;
for(AssociationCategory ac: AssociationCategory.values()){
if(code.equalsIgnoreCase(ac.code))
return ac;
}
return null;
}
}

125
src/main/java/org/leolo/nrdatad/model/Corpus.java

@ -0,0 +1,125 @@
package org.leolo.nrdatad.model;
import org.json.JSONObject;
import java.util.Objects;
public class Corpus {
private String stanoxCode;
private String uicCode;
private String crsCode;
private String tiplocCode;
private String nlcCode;
private String longDescription;
private String shortDescription;
private DataSource dataSource;
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public String getStanoxCode() {
return stanoxCode;
}
public void setStanoxCode(String stanoxCode) {
this.stanoxCode = stanoxCode;
}
public String getUicCode() {
return uicCode;
}
public void setUicCode(String uicCode) {
this.uicCode = uicCode;
}
public String getCrsCode() {
return crsCode;
}
public void setCrsCode(String crsCode) {
this.crsCode = crsCode;
}
public String getTiplocCode() {
return tiplocCode;
}
public void setTiplocCode(String tiplocCode) {
this.tiplocCode = tiplocCode;
}
public String getNlcCode() {
return nlcCode;
}
public void setNlcCode(String nlcCode) {
this.nlcCode = nlcCode;
}
public String getLongDescription() {
return longDescription;
}
public void setLongDescription(String longDescription) {
this.longDescription = longDescription;
}
public String getShortDescription() {
return shortDescription;
}
public void setShortDescription(String shortDescription) {
this.shortDescription = shortDescription;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Corpus corpus = (Corpus) o;
return Objects.equals(stanoxCode, corpus.stanoxCode) && Objects.equals(uicCode, corpus.uicCode) && Objects.equals(crsCode, corpus.crsCode) && Objects.equals(tiplocCode, corpus.tiplocCode) && Objects.equals(nlcCode, corpus.nlcCode) && Objects.equals(longDescription, corpus.longDescription) && Objects.equals(shortDescription, corpus.shortDescription);
}
@Override
public int hashCode() {
return Objects.hash(stanoxCode, uicCode, crsCode, tiplocCode, nlcCode, longDescription, shortDescription);
}
@Override
public String toString() {
return "CORPUS{" +
"stanoxCode='" + stanoxCode + '\'' +
", uicCode='" + uicCode + '\'' +
", crsCode='" + crsCode + '\'' +
", tiplocCode='" + tiplocCode + '\'' +
", nlcCode='" + nlcCode + '\'' +
", longDescription='" + longDescription + '\'' +
", shortDescription='" + shortDescription + '\'' +
'}';
}
public static Corpus parseJSON(JSONObject obj){
Corpus corpus = new Corpus();
corpus.setStanoxCode(obj.optString("STANOX"));
corpus.setUicCode(obj.optString("UIC"));
corpus.setCrsCode(obj.optString("3ALPHA"));
corpus.setTiplocCode(obj.optString("TIPLOC"));
corpus.setNlcCode(obj.optString("NLC"));
corpus.setLongDescription(obj.optString("NLCDESC"));
corpus.setShortDescription(obj.optString("NLCDESC16"));
return corpus;
}
public static Corpus parseJSON(String json){
return parseJSON(new JSONObject(json));
}
}

98
src/main/java/org/leolo/nrdatad/model/DataSource.java

@ -0,0 +1,98 @@
package org.leolo.nrdatad.model;
import org.leolo.nrdatad.util.UUIDUtil;
import java.util.Date;
import java.util.UUID;
public class DataSource {
private UUID uuid;
private String provider;
private String streamName;
private boolean isMessage;
private Date startDate;
private String messageId;
public DataSource(){
setRandomUuid();
}
public DataSource(String provider, String streamName, boolean isMessage, Date startDate, String messageId) {
setRandomUuid();
this.provider = provider;
this.streamName = streamName;
this.isMessage = isMessage;
this.startDate = startDate;
this.messageId = messageId;
}
public DataSource(String provider, String streamName, boolean isMessage) {
setRandomUuid();
this.provider = provider;
this.streamName = streamName;
this.isMessage = isMessage;
this.startDate = new Date();
}
public DataSource(String provider, String streamName, boolean isMessage, String messageId) {
setRandomUuid();
this.provider = provider;
this.streamName = streamName;
this.isMessage = isMessage;
this.startDate = new Date();
this.messageId = messageId;
}
public String getProvider() {
return provider;
}
public void setProvider(String provider) {
this.provider = provider;
}
public String getStreamName() {
return streamName;
}
public void setStreamName(String streamName) {
this.streamName = streamName;
}
public boolean isMessage() {
return isMessage;
}
public void setMessage(boolean message) {
isMessage = message;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public String getMessageId() {
return messageId;
}
public void setMessageId(String messageId) {
this.messageId = messageId;
}
public UUID getUuid() {
return uuid;
}
public void setUuid(UUID uuid) {
this.uuid = uuid;
}
public void setRandomUuid(){
this.uuid = UUIDUtil.getNextUUID();
}
}

192
src/main/java/org/leolo/nrdatad/model/ScheduleAssociation.java

@ -0,0 +1,192 @@
package org.leolo.nrdatad.model;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONException;
import org.json.JSONObject;
import org.leolo.nrdatad.util.TUIDDateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ScheduleAssociation {
private static Logger log = LogManager.getLogger();
private String mainUid;
private String assoUid;
private Date startDate;
private Date endDate;
private String assoDays;
private AssociationCategory associationCategory;
private int associationDate;
private String associationLocation;
private String baseLocationSuffix;
private String assocLocationSuffix;
private ShortTermPlanningIndicator stpIndicator;
private TUIDDateFormat tuidDateFormat = new TUIDDateFormat();
public String getAuid(){
return mainUid+assoUid+associationLocation+tuidDateFormat.format(startDate)+tuidDateFormat.format(endDate)+stpIndicator.getCode();
}
public String getMainUid() {
return mainUid;
}
public void setMainUid(String mainUid) {
this.mainUid = mainUid;
}
public String getAssoUid() {
return assoUid;
}
public void setAssoUid(String assoUid) {
this.assoUid = assoUid;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public String getAssoDays() {
return assoDays;
}
public void setAssoDays(String assoDays) {
this.assoDays = assoDays;
}
public AssociationCategory getAssociationCategory() {
return associationCategory;
}
public void setAssociationCategory(AssociationCategory associationCategory) {
this.associationCategory = associationCategory;
}
public int getAssociationDate() {
return associationDate;
}
public void setAssociationDate(int associationDate) {
this.associationDate = associationDate;
}
public String getAssociationLocation() {
return associationLocation;
}
public void setAssociationLocation(String associationLocation) {
this.associationLocation = associationLocation;
}
public String getBaseLocationSuffix() {
return baseLocationSuffix;
}
public void setBaseLocationSuffix(String baseLocationSuffix) {
this.baseLocationSuffix = baseLocationSuffix;
}
public String getAssocLocationSuffix() {
return assocLocationSuffix;
}
public void setAssocLocationSuffix(String assocLocationSuffix) {
this.assocLocationSuffix = assocLocationSuffix;
}
public ShortTermPlanningIndicator getStpIndicator() {
return stpIndicator;
}
public void setStpIndicator(ShortTermPlanningIndicator stpIndicator) {
this.stpIndicator = stpIndicator;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ScheduleAssociation that = (ScheduleAssociation) o;
if (associationDate != that.associationDate) return false;
if (!mainUid.equals(that.mainUid)) return false;
if (!assoUid.equals(that.assoUid)) return false;
if (!startDate.equals(that.startDate)) return false;
if (!endDate.equals(that.endDate)) return false;
if (!assoDays.equals(that.assoDays)) return false;
if (associationCategory != that.associationCategory) return false;
if (!associationLocation.equals(that.associationLocation)) return false;
if (baseLocationSuffix != null ? !baseLocationSuffix.equals(that.baseLocationSuffix) : that.baseLocationSuffix != null)
return false;
if (assocLocationSuffix != null ? !assocLocationSuffix.equals(that.assocLocationSuffix) : that.assocLocationSuffix != null)
return false;
return stpIndicator == that.stpIndicator;
}
@Override
public int hashCode() {
int result = mainUid.hashCode();
result = 31 * result + assoUid.hashCode();
result = 31 * result + startDate.hashCode();
result = 31 * result + endDate.hashCode();
result = 31 * result + assoDays.hashCode();
result = 31 * result + (associationCategory==null?0:associationCategory.name().hashCode());
result = 31 * result + associationDate;
result = 31 * result + associationLocation.hashCode();
result = 31 * result + (baseLocationSuffix != null ? baseLocationSuffix.hashCode() : 0);
result = 31 * result + (assocLocationSuffix != null ? assocLocationSuffix.hashCode() : 0);
result = 31 * result + (stpIndicator==null?0:stpIndicator.name().hashCode());
return result;
}
public static ScheduleAssociation parseJSON(String obj) throws JSONException {
return parseJSON(new JSONObject(obj));
}
public static ScheduleAssociation parseJSON(JSONObject obj) throws JSONException {
ScheduleAssociation scheduleAssociation = new ScheduleAssociation();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
scheduleAssociation.mainUid=obj.optString("main_train_uid").strip();
scheduleAssociation.assoUid=obj.optString("assoc_train_uid").strip();
try {
scheduleAssociation.startDate=sdf.parse(obj.optString("assoc_start_date"));
scheduleAssociation.endDate=sdf.parse(obj.optString("assoc_end_date"));
} catch (ParseException e) {
log.atWarn().withThrowable(e).log("Unable to parse date");
}
scheduleAssociation.assoDays=obj.optString("assoc_days");
scheduleAssociation.associationCategory = AssociationCategory.parseCode(obj.optString("category"));
String dateIndicator = obj.optString("date_indicator");
if("N".equals(dateIndicator)){
scheduleAssociation.associationDate = 1;
} else if ("P".equals(dateIndicator)) {
scheduleAssociation.associationDate = -1;
}else {
scheduleAssociation.associationDate = 0;
}
scheduleAssociation.associationLocation = obj.optString("location").strip();
scheduleAssociation.baseLocationSuffix = obj.optString("base_location_suffix");
scheduleAssociation.assocLocationSuffix = obj.optString("assoc_location_suffix");
scheduleAssociation.stpIndicator = ShortTermPlanningIndicator.parseCode(obj.optString("CIF_stp_indicator"));
return scheduleAssociation;
}
}

30
src/main/java/org/leolo/nrdatad/model/ShortTermPlanningIndicator.java

@ -0,0 +1,30 @@
package org.leolo.nrdatad.model;
public enum ShortTermPlanningIndicator {
CANCELLATION("C"),
NEW("N"),
OVERLAY("O"),
PERMANENT("P");
private String code;
private ShortTermPlanningIndicator(String code){
this.code = code;
}
public String getCode() {
return code;
}
public static ShortTermPlanningIndicator parseCode(String code){
if(code==null)
return null;
for(ShortTermPlanningIndicator stpi: ShortTermPlanningIndicator.values()){
if(code.equalsIgnoreCase(stpi.code))
return stpi;
}
return null;
}
}

190
src/main/java/org/leolo/nrdatad/model/Smart.java

@ -0,0 +1,190 @@
package org.leolo.nrdatad.model;
import org.json.JSONObject;
import org.leolo.nrdatad.util.JSONUtil;
public class Smart {
private int smartId;
private String td;
private String fromBerth;
private String toBerth;
private String fromLine;
private String toLine;
private int berthOffset;
private String platform;
private SmartEvent event;
private String route;
private String stanox;
private String stationName;
private SmartStepType stepType;
private String comment;
private DataSource dataSource;
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public int getSmartId() {
return smartId;
}
public void setSmartId(int smartId) {
this.smartId = smartId;
}
public String getTd() {
return td;
}
public void setTd(String td) {
this.td = td;
}
public String getFromBerth() {
return fromBerth;
}
public void setFromBerth(String fromBerth) {
this.fromBerth = fromBerth;
}
public String getToBerth() {
return toBerth;
}
public void setToBerth(String toBerth) {
this.toBerth = toBerth;
}
public String getFromLine() {
return fromLine;
}
public void setFromLine(String fromLine) {
this.fromLine = fromLine;
}
public String getToLine() {
return toLine;
}
public void setToLine(String toLine) {
this.toLine = toLine;
}
public int getBerthOffset() {
return berthOffset;
}
public void setBerthOffset(int berthOffset) {
this.berthOffset = berthOffset;
}
public String getPlatform() {
return platform;
}
public void setPlatform(String platform) {
this.platform = platform;
}
public SmartEvent getEvent() {
return event;
}
public void setEvent(SmartEvent event) {
this.event = event;
}
public String getRoute() {
return route;
}
public void setRoute(String route) {
this.route = route;
}
public String getStanox() {
return stanox;
}
public void setStanox(String stanox) {
this.stanox = stanox;
}
public String getStationName() {
return stationName;
}
public void setStationName(String stationName) {
this.stationName = stationName;
}
public SmartStepType getStepType() {
return stepType;
}
public void setStepType(SmartStepType stepType) {
this.stepType = stepType;
}
public String getComment() {
return comment;
}
public void setComment(String comment) {
this.comment = comment;
}
public static Smart parseJSON(String json){
return parseJSON(new JSONObject(json));
}
public static Smart parseJSON(JSONObject obj) {
Smart smart = new Smart();
smart.td = obj.optString("TD");
smart.fromBerth = obj.optString("FROMBERTH");
smart.toBerth = obj.optString("TOBERTH");
smart.fromLine = obj.optString("FROMLINE");
smart.toLine = obj.optString("TOLINE");
smart.berthOffset = JSONUtil.parseInt(obj.optString("BERTHOFFSET"));
smart.platform = obj.optString("PLATFORM");
smart.event = SmartEvent.parseCode(obj.optString("EVENT"));
smart.route = obj.optString("ROUTE");
smart.stanox = obj.optString("STANOX");
smart.stationName = obj.optString("STANME");
smart.stepType = SmartStepType.parseCode(obj.optString("STEPTYPE"));
smart.comment = obj.optString("COMMENT");
return smart;
}
public boolean checkEnums(){
return event!=null && stepType!=null;
}
@Override
public String toString() {
return "Smart{" +
"smartId=" + smartId +
", td='" + td + '\'' +
", fromBerth='" + fromBerth + '\'' +
", toBerth='" + toBerth + '\'' +
", fromLine='" + fromLine + '\'' +
", toLine='" + toLine + '\'' +
", berthOffset=" + berthOffset +
", platform='" + platform + '\'' +
", event=" + event +
", route='" + route + '\'' +
", stanox='" + stanox + '\'' +
", stationName='" + stationName + '\'' +
", stepType=" + stepType +
", comment='" + comment + '\'' +
'}';
}
}

33
src/main/java/org/leolo/nrdatad/model/SmartEvent.java

@ -0,0 +1,33 @@
package org.leolo.nrdatad.model;
import org.jetbrains.annotations.NotNull;
public enum SmartEvent {
ARRIVE_UP ("A"),
DEPART_UP ("B"),
ARRIVE_DOWN("C"),
DEPART_DOWN("D");
private String code;
private SmartEvent(String code){
this.code = code;
}
public String getCode() {
return code;
}
public static SmartEvent parseCode(@NotNull String code){
if(code==null)
return null;
for(SmartEvent se: SmartEvent.values()){
if(code.equalsIgnoreCase(se.code)){
return se;
}
}
return null;
}
}

65
src/main/java/org/leolo/nrdatad/model/SmartStepType.java

@ -0,0 +1,65 @@
package org.leolo.nrdatad.model;
import org.jetbrains.annotations.NotNull;
public enum SmartStepType {
/**
* This is a move between directly adjacent berths, and is the preferred type of movement. The time reported to
* TRUST is the time that the train enters the 'to' berth.
*/
BETWEEN("B"),
/**
* This is used to record a time for a train going in either direction (up or down) from the specified berth to any
* other berth. The time reported to TRUST is the time that the train leaves the 'from' berth.
*/
FROM("F"),
/**
* This is the opposite of the 'F' step type. It is used to record a time for a train from any berth to the
* specified berth. The time reported to TRUST is the time that the train enters the 'to' berth.
*/
TO("T"),
/**
* This is used to specify the route a train is taking, usually when departing a station or junction.
*
* For example, if a 'D' move is specified as 0101 to 0407, and a train moves between berths 0101, 0203, 0305 and
* 0407, the move will be reported for the time the train left the first berth.
*/
INTERMEDIATE_FIRST("D"),
/**
* This is used to report on a movement where the only indication is a cancel message. For example, when a train
* leaves Network Rail infrastructure and moves in to a siding or area not covered by a train describer, the only
* message that will be received is a Clearout (CB) message.
*/
CLEAROUT("C"),
/**
* This is the opposite of the 'C' step type. It is used to report on a movement where the only indication is
* an interpose message. For example, when a train arrives on Network Rail infrastructure from a siding. The time
* reported to TRUST is the time that the interpose happened.
*/
INTERPOSE("I"),
/**
* This is similar to the 'D' move, but usually used for arrivals. The time reported to TRUST is the time the last
* berth step was made.
*/
INTERMEDIATE("E");
private String code;
private SmartStepType(String code){
this.code = code;
}
public String getCode() {
return code;
}
public static SmartStepType parseCode(@NotNull String code){
if(code==null)
return null;
for(SmartStepType sst: SmartStepType.values()){
if(code.equalsIgnoreCase(sst.code))
return sst;
}
return null;
}
}

75
src/main/java/org/leolo/nrdatad/model/Tiploc.java

@ -0,0 +1,75 @@
package org.leolo.nrdatad.model;
import org.json.JSONObject;
public class Tiploc {
private String tiplocCode;
private String nalco;
private String stanox;
private String description;
private String tpsDescription;
private String crsCode;
public String getTiplocCode() {
return tiplocCode;
}
public void setTiplocCode(String tiplocCode) {
this.tiplocCode = tiplocCode;
}
public String getNalco() {
return nalco;
}
public void setNalco(String nalco) {
this.nalco = nalco;
}
public String getStanox() {
return stanox;
}
public void setStanox(String stanox) {
this.stanox = stanox;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getTpsDescription() {
return tpsDescription;
}
public void setTpsDescription(String tpsDescription) {
this.tpsDescription = tpsDescription;
}
public String getCrsCode() {
return crsCode;
}
public void setCrsCode(String crsCode) {
this.crsCode = crsCode;
}
public static Tiploc parseJSON(String json){
return parseJSON(new JSONObject(json));
}
public static Tiploc parseJSON(JSONObject obj){
Tiploc tiploc = new Tiploc();
tiploc.tpsDescription = obj.optString("tps_description");
tiploc.crsCode = obj.optString("crs_code");
tiploc.description = obj.optString("description");
tiploc.stanox = obj.optString("stanox");
tiploc.tiplocCode = obj.optString("tiploc_code");
tiploc.nalco = obj.optString("nalco");
return tiploc;
}
}

155
src/main/java/org/leolo/nrdatad/model/TrainSchedule.java

@ -0,0 +1,155 @@
package org.leolo.nrdatad.model;
import org.json.JSONObject;
import org.leolo.nrdatad.util.DateUtil;
import org.leolo.nrdatad.util.TUIDDateFormat;
import java.util.Date;
public class TrainSchedule {
private int scheduleVersion = UNKNOWN_VERSION_NUMBER;
public static final int UNKNOWN_VERSION_NUMBER = -1;
private String runsOnBankHoliday;
private String trainStatus;//TODO: change it into enum
private String trainUid;
private String runsOn;
private ShortTermPlanningIndicator shortTermPlanningIndicator;
private String atocCode;
private String uicCode;
private String tractionClass;
private Date startDate;
private Date endDate;
private TrainScheduleSector sector;
private String schedeuleType;
public String getSUID(){
TUIDDateFormat tuiddf = new TUIDDateFormat();
return trainUid+tuiddf.format(startDate)+tuiddf.format(endDate)+shortTermPlanningIndicator.getCode();
}
public String getRunsOnBankHoliday() {
return runsOnBankHoliday;
}
public void setRunsOnBankHoliday(String runsOnBankHoliday) {
this.runsOnBankHoliday = runsOnBankHoliday;
}
public String getTrainStatus() {
return trainStatus;
}
public void setTrainStatus(String trainStatus) {
this.trainStatus = trainStatus;
}
public String getTrainUid() {
return trainUid;
}
public void setTrainUid(String trainUid) {
this.trainUid = trainUid;
}
public String getRunsOn() {
return runsOn;
}
public void setRunsOn(String runsOn) {
this.runsOn = runsOn;
}
public ShortTermPlanningIndicator getShortTermPlanningIndicator() {
return shortTermPlanningIndicator;
}
public void setShortTermPlanningIndicator(ShortTermPlanningIndicator shortTermPlanningIndicator) {
this.shortTermPlanningIndicator = shortTermPlanningIndicator;
}
public String getAtocCode() {
return atocCode;
}
public void setAtocCode(String atocCode) {
this.atocCode = atocCode;
}
public String getUicCode() {
return uicCode;
}
public void setUicCode(String uicCode) {
this.uicCode = uicCode;
}
public String getTractionClass() {
return tractionClass;
}
public void setTractionClass(String tractionClass) {
this.tractionClass = tractionClass;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public TrainScheduleSector getSector() {
return sector;
}
public void setSector(TrainScheduleSector sector) {
this.sector = sector;
}
public static TrainSchedule parseJSON(String json){
return parseJSON(new JSONObject(json));
}
public static TrainSchedule parseJSON(JSONObject object){
TrainSchedule ts = new TrainSchedule();
ts.runsOnBankHoliday = object.optString("CIF_bank_holiday_running");
ts.trainUid = object.optString("CIF_train_uid");
ts.runsOn = object.optString("schedule_days_runs");
ts.shortTermPlanningIndicator = ShortTermPlanningIndicator.parseCode(object.optString("CIF_stp_indicator"));
ts.atocCode = object.optString("atoc_code");
ts.startDate = DateUtil.parseDate(object.optString("schedule_start_date"));
ts.endDate = DateUtil.parseDate(object.optString("schedule_end_date"));
ts.sector = TrainScheduleSector.parseJSON(object.getJSONObject("schedule_segment"));
ts.trainStatus = object.optString("train_status");
return ts;
}
public int getScheduleVersion() {
return scheduleVersion;
}
public void setScheduleVersion(int scheduleVersion) {
this.scheduleVersion = scheduleVersion;
}
public String getSchedeuleType() {
return schedeuleType;
}
public void setSchedeuleType(String schedeuleType) {
this.schedeuleType = schedeuleType;
}
}

207
src/main/java/org/leolo/nrdatad/model/TrainScheduleLocation.java

@ -0,0 +1,207 @@
package org.leolo.nrdatad.model;
import org.json.JSONObject;
import org.leolo.nrdatad.util.TimeUtil;
public class TrainScheduleLocation {
public static final int UNKNOWN_RECORD_SEQUENCE = -1;
private int recordSequence = UNKNOWN_RECORD_SEQUENCE;
private TrainScheduleSector recordSector;
private TrainScheduleLocationRecordIdentity recordIdentity;
private String tiploc;
private int tiplocInstance = UNKNOWN_RECORD_SEQUENCE;
private long wttArrival;
private long wttDeparture;
private long wttPass;
private long publicArrival;
private long publicDeparture;
private String platform;
/**
* Line used for departure
*/
private String line;
/**
* Path used for arrival
*/
private String path;
/**
* This is extra time to allow for a speed restriction on the railway.
*
* It may also be called 'Box Time', as the Working Timetable encloses engineering allowances in a box or square brackets.
*/
private long engineeringAllowance;
/**
* This is extra time to ensure the train path doesn't conflict with others at a junction, or where trains running at different speeds are operating over a route.
*
* It may also be called 'Circle Time', as the Working Timetable encloses pathing allowances in a circle or brackets.
*/
private long pathingAllowance;
/**
* This is extra time to provide a margin or buffer for late running on a day-to-day basis.
*
* It may also be called 'Diamond Time', as the Working Timetable encloses performance allowances in a diamond or angle-brackets.
*/
private long performanceAllowance;
public int getRecordSequence() {
return recordSequence;
}
public void setRecordSequence(int recordSequence) {
this.recordSequence = recordSequence;
}
public TrainScheduleLocationRecordIdentity getRecordIdentity() {
return recordIdentity;
}
public void setRecordIdentity(TrainScheduleLocationRecordIdentity recordIdentity) {
this.recordIdentity = recordIdentity;
}
public String getTiploc() {
return tiploc;
}
public void setTiploc(String tiploc) {
this.tiploc = tiploc;
}
public int getTiplocInstance() {
return tiplocInstance;
}
public void setTiplocInstance(int tiplocInstance) {
this.tiplocInstance = tiplocInstance;
}
public long getWttArrival() {
return wttArrival;
}
public void setWttArrival(long wttArrival) {
this.wttArrival = wttArrival;
}
public long getWttDeparture() {
return wttDeparture;
}
public void setWttDeparture(long wttDeparture) {
this.wttDeparture = wttDeparture;
}
public long getWttPass() {
return wttPass;
}
public void setWttPass(long wttPass) {
this.wttPass = wttPass;
}
public long getPublicArrival() {
return publicArrival;
}
public void setPublicArrival(long publicArrival) {
this.publicArrival = publicArrival;
}
public long getPublicDeparture() {
return publicDeparture;
}
public void setPublicDeparture(long publicDeparture) {
this.publicDeparture = publicDeparture;
}
public String getPlatform() {
return platform;
}
public void setPlatform(String platform) {
this.platform = platform;
}
public String getLine() {
return line;
}
public void setLine(String line) {
this.line = line;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public long getEngineeringAllowance() {
return engineeringAllowance;
}
public void setEngineeringAllowance(long engineeringAllowance) {
this.engineeringAllowance = engineeringAllowance;
}
public long getPathingAllowance() {
return pathingAllowance;
}
public void setPathingAllowance(long pathingAllowance) {
this.pathingAllowance = pathingAllowance;
}
public long getPerformanceAllowance() {
return performanceAllowance;
}
public void setPerformanceAllowance(long performanceAllowance) {
this.performanceAllowance = performanceAllowance;
}
public TrainScheduleSector getRecordSector() {
return recordSector;
}
public void setRecordSector(TrainScheduleSector recordSector) {
this.recordSector = recordSector;
}
public static TrainScheduleLocation parseJSON(String json){
return parseJSON(new JSONObject(json), UNKNOWN_RECORD_SEQUENCE);
}
public static TrainScheduleLocation parseJSON(String json, int recordSequence){
return parseJSON(new JSONObject(json), recordSequence);
}
public static TrainScheduleLocation parseJSON(JSONObject object){
return parseJSON(object, UNKNOWN_RECORD_SEQUENCE);
}
public static TrainScheduleLocation parseJSON(JSONObject object, int recordSequence){
TrainScheduleLocation tsl = new TrainScheduleLocation();
tsl.recordSequence = recordSequence;
tsl.tiploc = object.optString("tiploc_code");
tsl.tiplocInstance = object.optInt("tiploc_instance", UNKNOWN_RECORD_SEQUENCE);
tsl.wttArrival = TimeUtil.parseTime(object.optString("arrival"));
tsl.wttDeparture = TimeUtil.parseTime(object.optString("departure"));
tsl.wttPass = TimeUtil.parseTime(object.optString("pass"));
tsl.publicArrival = TimeUtil.parseTime(object.optString("public_arrival"));
tsl.publicDeparture = TimeUtil.parseTime(object.optString("public_departure"));
tsl.platform = object.optString("platform");
tsl.line = object.optString("line");
tsl.path = object.optString("path");
tsl.engineeringAllowance = TimeUtil.parseTime(object.optString("engineering_allowance"));
tsl.pathingAllowance = TimeUtil.parseTime(object.optString("pathing_allowance"));
tsl.performanceAllowance = TimeUtil.parseTime(object.optString("performance_allowance"));
tsl.recordIdentity = TrainScheduleLocationRecordIdentity.parseCode(object.optString("location_type"));
return tsl;
}
}

27
src/main/java/org/leolo/nrdatad/model/TrainScheduleLocationRecordIdentity.java

@ -0,0 +1,27 @@
package org.leolo.nrdatad.model;
public enum TrainScheduleLocationRecordIdentity {
ORIGINATE("LO"),
INTERMEDIATE("LI"),
TERMINATE("LT");
private String code;
private TrainScheduleLocationRecordIdentity(String code) {
this.code = code;
}
public static TrainScheduleLocationRecordIdentity parseCode(String code) {
if (code == null) {
return null;
}
for (TrainScheduleLocationRecordIdentity e : TrainScheduleLocationRecordIdentity.values()) {
if (e.code.equalsIgnoreCase(code)) {
return e;
}
}
return null;
}
}

252
src/main/java/org/leolo/nrdatad/model/TrainScheduleSector.java

@ -0,0 +1,252 @@
package org.leolo.nrdatad.model;
import org.json.JSONArray;
import org.json.JSONObject;
import org.leolo.nrdatad.util.JSONUtil;
import java.util.List;
import java.util.Vector;
public class TrainScheduleSector {
/**
* A sequence number of the schedule sector which are belongs to same schedule
*/
private int sectorId;
/**
* Service brand
*/
private String serviceBranding;
/**
* Train category
*/
private String trainCategory;
/**
* Formerly used to denote the business sector running the service; now represent the Portion ID for services which join or split
*/
private String businessSector;
/**
* Planned speed of the train service
*/
private int speed;
/**
* Reservation recommendations:
* <ul>
* <li>A - Reservations compulsory</li>
* <li>E - Reservations for bicycles essential</li>
* <li>R - Reservations recommended</li>
* <li>S - Reservations possible from any station</li>
* </ul>
*/
private String reservation;
/**
* Up to two characters from the following:
* <ul>
* <li>C - Buffet Service</li>
* <li>F - Restaurant Car available for First Class passengers</li>
* <li>H - Hot food available</li>
* <li>M - Meal included for First Class passengers</li>
* <li>P - Wheelchair only reservations</li>
* <li>R - Restaurant</li>
* <li>T - Trolley service</li>
* </ul>
*/
private String catering;
/**
* Power type
*/
private String powerType;
/**
* Timing load
*/
private String timingLoad;
/**
* Signalling ID, also referred to as headcode, not to be confused with below. For passenger services, this is the headcode of the service. For anonymous freight services this will be blank.
*/
private String signalId;
/**
* National Reservation System headcode, designated by train operator, not to be confused with signal ID.
*/
private String headcode;
/**
* Operating characteristics
*/
private String operatingCharacteristic;
/**
* Sleeping accommodation available:
* <ul>
* <li>B - First and standard class</li>
* <li>F - First Class only</li>
* <li>S - Standard class only</li>
* </ul>
*/
private String sleeper;
/**
* Divides trains into service groups and used for revenue reasons
*/
private String trainServiceCode;
/**
* Seating classes available:
* <ul>
* <li><span style="font-style: italic;">Blank</span> or B - First and standard</li>
* <li>S - Standard class only</li>
* </ul>
*/
private String trainClass;
private List<TrainScheduleLocation> scheduleLocations = new Vector<>();
public int getSectorId() {
return sectorId;
}
public void setSectorId(int sectorId) {
this.sectorId = sectorId;
}
public String getServiceBranding() {
return serviceBranding;
}
public void setServiceBranding(String serviceBranding) {
this.serviceBranding = serviceBranding;
}
public String getTrainCategory() {
return trainCategory;
}
public void setTrainCategory(String trainCategory) {
this.trainCategory = trainCategory;
}
public String getBusinessSector() {
return businessSector;
}
public void setBusinessSector(String businessSector) {
this.businessSector = businessSector;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public String getReservation() {
return reservation;
}
public void setReservation(String reservation) {
this.reservation = reservation;
}
public String getCatering() {
return catering;
}
public void setCatering(String catering) {
this.catering = catering;
}
public String getPowerType() {
return powerType;
}
public void setPowerType(String powerType) {
this.powerType = powerType;
}
public String getTimingLoad() {
return timingLoad;
}
public void setTimingLoad(String timingLoad) {
this.timingLoad = timingLoad;
}
public String getSignalId() {
return signalId;
}
public void setSignalId(String signalId) {
this.signalId = signalId;
}
public String getHeadcode() {
return headcode;
}
public void setHeadcode(String headcode) {
this.headcode = headcode;
}
public String getOperatingCharacteristic() {
return operatingCharacteristic;
}
public void setOperatingCharacteristic(String operatingCharacteristic) {
this.operatingCharacteristic = operatingCharacteristic;
}
public String getSleeper() {
return sleeper;
}
public void setSleeper(String sleeper) {
this.sleeper = sleeper;
}
public String getTrainServiceCode() {
return trainServiceCode;
}
public void setTrainServiceCode(String trainServiceCode) {
this.trainServiceCode = trainServiceCode;
}
public String getTrainClass() {
return trainClass;
}
public void setTrainClass(String trainClass) {
this.trainClass = trainClass;
}
public List<TrainScheduleLocation> getScheduleLocations() {
return scheduleLocations;
}
public static TrainScheduleSector parseJSON(String json){
return parseJSON(new JSONObject(json));
}
public static TrainScheduleSector parseJSON(JSONObject object){
TrainScheduleSector tss = new TrainScheduleSector();
tss.trainCategory = object.optString("CIF_train_category");
tss.signalId = object.optString("signalling_id");
tss.headcode = object.optString("CIF_headcode");
tss.trainServiceCode = object.optString("CIF_train_service_code");
tss.businessSector = object.optString("CIF_business_sector");
tss.powerType = object.optString("CIF_power_type");
tss.timingLoad = object.optString("CIF_timing_load");
tss.speed = JSONUtil.parseInt(object.optString("CIF_speed"));
tss.operatingCharacteristic = object.optString("CIF_operating_characteristics");
tss.trainClass = object.optString("CIF_train_class");
tss.sleeper = object.optString("CIF_sleepers");
tss.reservation = object.optString("COF_reservations");
tss.catering = object.optString("CIF_catering_code");
tss.serviceBranding = object.optString("CIF_service_branding");
JSONArray locations = object.getJSONArray("schedule_location");
for(int i=0;i<locations.length();i++){
JSONObject location = locations.getJSONObject(i);
TrainScheduleLocation tsl = TrainScheduleLocation.parseJSON(location, i+1);
tsl.setRecordSector(tss);
tss.scheduleLocations.add(tsl);
}
return tss;
}
}

39
src/main/java/org/leolo/nrdatad/util/DateUtil.java

@ -0,0 +1,39 @@
package org.leolo.nrdatad.util;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
private static Logger log = LogManager.getLogger();
public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
public static Date parseDate(String date){
return parseDate(date, DEFAULT_DATE_FORMAT, true);
}
public static Date parseDate(String date, boolean suppressException){
return parseDate(date, DEFAULT_DATE_FORMAT, suppressException);
}
public static Date parseDate(String data, String dateFormat){
return parseDate(data, dateFormat, true);
}
public static Date parseDate(String data, String dateFormat, boolean suppressException){
try{
return new SimpleDateFormat(dateFormat).parse(data);
} catch (ParseException e) {
log.atWarn().withThrowable(e).log("Unable to parse date");
if(suppressException){
return null;
}
throw new RuntimeException(e.getMessage(), e);
}
}
}

76
src/main/java/org/leolo/nrdatad/util/HttpUtil.java

@ -0,0 +1,76 @@
package org.leolo.nrdatad.util;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.leolo.nrdatad.ConfigurationManager;
import org.leolo.nrdatad.Constants;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Base64;
import java.util.zip.GZIPInputStream;
public class HttpUtil {
Logger log = LogManager.getLogger();
public static String sendHttpRequest(URL url, String userName, String password) throws IOException {
URLConnection conn = url.openConnection();
String userpwd = userName+":"+password;
conn.addRequestProperty("Authorization", "Basic "+ Base64.getEncoder().encodeToString(userpwd.getBytes()));
conn.connect();
StringBuilder sb = new StringBuilder();
try(BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()))){
while(true){
String line = br.readLine();
if(line == null) break;
sb.append(line).append("\n");
}
}
return sb.toString();
}
public static String sendHttpRequest(String url, String userName, String password) throws IOException, URISyntaxException {
return sendHttpRequest(new URL(url), userName, password);
}
public static String sendHttpRequestForGZipFile(URL url, String userName, String password) throws IOException {
URLConnection conn = url.openConnection();
String userpwd = userName+":"+password;
conn.addRequestProperty("Authorization", "Basic "+ Base64.getEncoder().encodeToString(userpwd.getBytes()));
conn.connect();
StringBuilder sb = new StringBuilder();
try(
GZIPInputStream gis = new GZIPInputStream(conn.getInputStream());
BufferedReader br = new BufferedReader(new InputStreamReader(gis))
){
while(true){
String line = br.readLine();
if(line == null) break;
sb.append(line).append("\n");
}
}
return sb.toString();
}
public static InputStream getHttpGZipStream(URL url, String userName, String password) throws IOException, URISyntaxException {
URLConnection conn = url.openConnection();
String userpwd = userName+":"+password;
conn.addRequestProperty("Authorization", "Basic "+ Base64.getEncoder().encodeToString(userpwd.getBytes()));
conn.connect();
return new GZIPInputStream(conn.getInputStream());
}
public static InputStream getHttpGZipStream(String url, String userName, String password) throws IOException, URISyntaxException {
return getHttpGZipStream(new URL(url), userName, password);
}
public static String sendHttpRequestForGZipFile(String url, String userName, String password) throws IOException, URISyntaxException {
return sendHttpRequestForGZipFile(new URL(url), userName, password);
}
}

32
src/main/java/org/leolo/nrdatad/util/JSONUtil.java

@ -0,0 +1,32 @@
package org.leolo.nrdatad.util;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class JSONUtil {
static Logger log = LogManager.getLogger();
public static int parseInt(String val){
//Throw exception for empty string, or just a plus sign to maintenance backward
//compatibility with previous version.
if(val.length()==0 || "+".equals(val)){
throw new NumberFormatException();
}
if (val.startsWith("+")) {
return _parseInt(val.substring(1));
}
return _parseInt(val);
}
private static int _parseInt(String val) {
if(val.length()==0){
return 0;
}
if (val.startsWith("0")) {
return _parseInt(val.substring(1));
}
return Integer.parseInt(val);
}
}

25
src/main/java/org/leolo/nrdatad/util/Range.java

@ -0,0 +1,25 @@
package org.leolo.nrdatad.util;
public class Range<E extends Comparable> {
private E lowerBound;
private E upperBound;
public Range(E bound1, E bound2){
if(bound1.compareTo(bound2)==-1){
lowerBound = bound1;
upperBound = bound2;
}else{
lowerBound = bound2;
upperBound = bound1;
}
}
public E getLowerBound() {
return lowerBound;
}
public E getUpperBound() {
return upperBound;
}
}

32
src/main/java/org/leolo/nrdatad/util/TUIDDateFormat.java

@ -0,0 +1,32 @@
package org.leolo.nrdatad.util;
import java.text.DateFormat;
import java.text.FieldPosition;
import java.text.ParsePosition;
import java.util.Date;
public class TUIDDateFormat extends DateFormat {
/**
*
*/
private static final long serialVersionUID = 7274736087586430881L;
public static final String MONTH_ID = "MBTQPHSONDUE";
public static final String DAY_ID = "0123456789ABCDEFGHJKLMNPRSTUVWX";
@Override
public StringBuffer format(Date arg0, StringBuffer arg1, FieldPosition arg2) {
// TODO Auto-generated method stub
arg1.append(MONTH_ID.charAt(arg0.getMonth()));
arg1.append(DAY_ID.charAt(arg0.getDate()-1));
return arg1;
}
@Override
public Date parse(String arg0, ParsePosition arg1) {
throw new UnsupportedOperationException();
}
}

58
src/main/java/org/leolo/nrdatad/util/TimeUtil.java

@ -0,0 +1,58 @@
package org.leolo.nrdatad.util;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Date;
public class TimeUtil {
public static final long ONE_SECOND = 1_000;
public static final long ONE_MINUTE = 60_000;
public static final long ONE_HOUR = 3_600_000;
public static final long ONE_DAY = 86_400_000;
private static Logger log = LogManager.getLogger();
public static long extractTime(Date date){
return date.getTime()%ONE_DAY;
}
public static long parseTime(String time){
return parseTime(time, 0);
}
public static long parseTime(String time, int dayOffSet){
time = time.strip().toUpperCase();
long result;
//This given string can be 1, 2, 4 or 5 characters long
int strLen = time.length();
if (strLen == 0 ) {
result = 0;
} else if (strLen==1||strLen==2) {
if("H".equals(time)) {
result = 30 * ONE_SECOND;
} else if(time.endsWith("H")){
result = Integer.parseInt(time.substring(0, 1))*ONE_MINUTE + 30*ONE_SECOND;
}else{
result = Integer.parseInt(time)*ONE_MINUTE;
}
} else if (strLen==4||strLen==5) {
if(time.endsWith("H")){
result = Integer.parseInt(time.substring(0, 2))*ONE_HOUR +
Integer.parseInt(time.substring(2, 4))*ONE_MINUTE +
30*ONE_SECOND;
}else{
result = Integer.parseInt(time.substring(0, 2))*ONE_HOUR +
Integer.parseInt(time.substring(2, 4))*ONE_MINUTE;
}
} else {
log.atWarn().log("String \"{}\" is not a legal value.", time);
throw new RuntimeException(time+" is Illegal value for time");
}
result += dayOffSet*ONE_DAY;
return result;
}
}

26
src/main/java/org/leolo/nrdatad/util/UUIDUtil.java

@ -0,0 +1,26 @@
package org.leolo.nrdatad.util;
import com.fasterxml.uuid.EthernetAddress;
import com.fasterxml.uuid.Generators;
import org.leolo.nrdatad.ConfigurationManager;
import java.util.UUID;
public class UUIDUtil {
private static EthernetAddress ethernetAddress;
public static UUID getNextUUID(){
return Generators.timeBasedGenerator(getEthernetAddress()).generate();
}
private static synchronized EthernetAddress getEthernetAddress() {
if(ethernetAddress==null){
//Get it from conf
String ethAddr = ConfigurationManager.getInstance().getEthAddress();
ethernetAddress = new EthernetAddress(ethAddr);
}
return ethernetAddress;
}
}

18
src/main/resources/log4j2.xml

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout
pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="Console" />
</Root>
<Logger name="org.leolo.nrdd">
<AppenderRef ref="Console" />
</Logger>
</Loggers>
</Configuration>

5
src/test/java/org/leolo/AppTest.java → src/test/java/org/leolo/nrdatad/AppTest.java

@ -1,4 +1,4 @@
package org.leolo;
package org.leolo.nrdatad;
import static org.junit.Assert.assertTrue;
@ -7,8 +7,7 @@ import org.junit.Test;
/**
* Unit test for simple App.
*/
public class AppTest
{
public class AppTest {
/**
* Rigorous Test :-)
*/

92
src/test/java/org/leolo/nrdatad/ConfigurationTest.java

@ -0,0 +1,92 @@
package org.leolo.nrdatad;
import org.apache.logging.log4j.LogBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.*;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.util.Random;
import static org.junit.Assert.*;
public class ConfigurationTest {
public static final char[] randomChars = "1234567890qwertyuiopasdfghjklzxcvbnm".toCharArray();
private static Logger log = LogManager.getLogger();
private static String fileName;
@Before public void setUpClass(){
int round = 0;
while(true) {
fileName = "test-"+getRandomFileName()+".tmp";
log.always().log("File name is {}", fileName);
if(!new File(fileName).exists()){
break;
}
if(++round>48){
assertTrue("Cannot get a file name", false);
}
}
}
private static String getRandomFileName(){
StringBuilder sb = new StringBuilder();
Random r = new Random();
for(int i=0;i<16;i++){
sb.append(randomChars[r.nextInt(randomChars.length)]);
}
return sb.toString();
}
@Test public void basicLoad() throws Exception{
assertEquals("Dirty configs", 0, ConfigurationManager.getInstance().size());
//Step 1: Create a simple one
try(PrintWriter out = new PrintWriter(new File(fileName))){
out.println("key1=value1");
out.println("key2=value2");
out.println("key3=value3");
out.println("key4=value4");
out.println("#key5=value5");
}catch(IOException e){
log.always().withThrowable(e).log("Unable to test");
assert false;
}
ConfigurationManager.getInstance().loadConfiguration(fileName);
assertEquals("Config key count mismatch", 4, ConfigurationManager.getInstance().size());
assertEquals("Config key count mismatch", "value1", ConfigurationManager.getInstance().getProperty("key1"));
assertEquals("Config key count mismatch", "value3", ConfigurationManager.getInstance().getProperty("key3"));
assertEquals("Config key count mismatch", "value4", ConfigurationManager.getInstance().getProperty("key4"));
assertEquals("Config key count mismatch", "value2", ConfigurationManager.getInstance().getProperty("key2"));
assertEquals("Config key count mismatch", null, ConfigurationManager.getInstance().getProperty("key5"));
}
@Test public void notExistFile(){
assertEquals("Dirty configs", 0, ConfigurationManager.getInstance().size());
try {
ConfigurationManager.getInstance().loadConfiguration(fileName);
}catch(IOException e){
return;
}
assertFalse("Exception not thrown", true);
}
@After public void cleanUpEach(){
try {
Files.deleteIfExists(new File(fileName).toPath());
log.atInfo().log("Removed file {}", fileName);
} catch (IOException e) {
log.always().withThrowable(e).log("Unable to remove file");
}
ConfigurationManager.getInstance().clear();
}
@AfterClass public static void cleanup(){
new File(fileName).deleteOnExit();
}
}

270
src/test/java/org/leolo/nrdatad/ReferenceDataParserTest.java

@ -0,0 +1,270 @@
package org.leolo.nrdatad;
import org.json.JSONObject;
import org.junit.Test;
import org.leolo.nrdatad.model.Corpus;
import org.leolo.nrdatad.model.Smart;
import org.leolo.nrdatad.model.SmartEvent;
import org.leolo.nrdatad.model.SmartStepType;
import static org.junit.Assert.*;
public class ReferenceDataParserTest {
@Test
public void testCorpusNormalCase1(){
String json = "{\"NLC\":\"535500\",\"STANOX\":\"87701\",\"TIPLOC\":\"ECROYDN\",\"3ALPHA\":\"ECR\",\"UIC\":\"53550\",\"NLCDESC\":\"EAST CROYDON\",\"NLCDESC16\":\"\"}";
Corpus corpus = Corpus.parseJSON(json);
assertEquals("535500", corpus.getNlcCode());
assertEquals("87701", corpus.getStanoxCode());
assertEquals("ECROYDN", corpus.getTiplocCode());
assertEquals("ECR", corpus.getCrsCode());
assertEquals("53550", corpus.getUicCode());
assertEquals("EAST CROYDON", corpus.getLongDescription());
}
@Test
public void testCorpusNormalCase2(){
String json = "{\"NLC\":\"535500\",\"STANOX\":\"87701\",\"TIPLOC\":\"ECROYDN\",\"3ALPHA\":\"ECR\",\"UIC\":\"53550\",\"NLCDESC\":\"EAST CROYDON\",\"NLCDESC16\":\"\"}";
Corpus corpus = Corpus.parseJSON(new JSONObject(json));
assertEquals("535500", corpus.getNlcCode());
assertEquals("87701", corpus.getStanoxCode());
assertEquals("ECROYDN", corpus.getTiplocCode());
assertEquals("ECR", corpus.getCrsCode());
assertEquals("53550", corpus.getUicCode());
assertEquals("EAST CROYDON", corpus.getLongDescription());
}
@Test
public void testSmartNormalCase1(){
String json = "{\"STEPTYPE\":\"B\",\"FROMBERTH\":\"7910\",\"TOBERTH\":\"7904\",\"STANOX\":\"01125\",\"EVENT\":\"A\",\"PLATFORM\":\"1\"," +
"\"TOLINE\":\"\",\"BERTHOFFSET\":\"+59\",\"ROUTE\":\"\",\"FROMLINE\":\"\",\"TD\":\"IH\",\"COMMENT\":\"02/12/2018\",\"STANME\":\"NAIRN\"}";
Smart smart = Smart.parseJSON(json);
assertEquals(SmartStepType.BETWEEN, smart.getStepType());
assertEquals("7910", smart.getFromBerth());
assertEquals("7904", smart.getToBerth());
assertEquals("01125", smart.getStanox());
assertEquals(SmartEvent.ARRIVE_UP, smart.getEvent());
assertEquals("1", smart.getPlatform());
assertEquals("", smart.getToLine());
assertEquals(59, smart.getBerthOffset());
assertEquals("", smart.getRoute());
assertEquals("", smart.getFromLine());
assertEquals("IH", smart.getTd());
assertEquals("02/12/2018", smart.getComment());
assertEquals("NAIRN", smart.getStationName());
}
@Test
public void testSmartInvalidEnumVal1(){
String json = "{\"STEPTYPE\":\"B\",\"FROMBERTH\":\"7910\",\"TOBERTH\":\"7904\",\"STANOX\":\"01125\",\"EVENT\":\"Z\",\"PLATFORM\":\"1\"," +
"\"TOLINE\":\"\",\"BERTHOFFSET\":\"+59\",\"ROUTE\":\"\",\"FROMLINE\":\"\",\"TD\":\"IH\",\"COMMENT\":\"02/12/2018\",\"STANME\":\"NAIRN\"}";
Smart smart = Smart.parseJSON(json);
assertEquals(SmartStepType.BETWEEN, smart.getStepType());
assertEquals("7910", smart.getFromBerth());
assertEquals("7904", smart.getToBerth());
assertEquals("01125", smart.getStanox());
assertNull(smart.getEvent());
assertEquals("1", smart.getPlatform());
assertEquals("", smart.getToLine());
assertEquals(59, smart.getBerthOffset());
assertEquals("", smart.getRoute());
assertEquals("", smart.getFromLine());
assertEquals("IH", smart.getTd());
assertEquals("02/12/2018", smart.getComment());
assertEquals("NAIRN", smart.getStationName());
}
@Test
public void testSmartNormalCase2(){
String json = "{\"STEPTYPE\":\"B\",\"FROMBERTH\":\"7910\",\"TOBERTH\":\"7904\",\"STANOX\":\"01125\",\"EVENT\":\"A\",\"PLATFORM\":\"1\"," +
"\"TOLINE\":\"\",\"BERTHOFFSET\":\"+59\",\"ROUTE\":\"\",\"FROMLINE\":\"\",\"TD\":\"IH\",\"COMMENT\":\"02/12/2018\",\"STANME\":\"NAIRN\"}";
Smart smart = Smart.parseJSON(new JSONObject(json));
assertEquals(SmartStepType.BETWEEN, smart.getStepType());
assertEquals("7910", smart.getFromBerth());
assertEquals("7904", smart.getToBerth());
assertEquals("01125", smart.getStanox());
assertEquals(SmartEvent.ARRIVE_UP, smart.getEvent());
assertEquals("1", smart.getPlatform());
assertEquals("", smart.getToLine());
assertEquals(59, smart.getBerthOffset());
assertEquals("", smart.getRoute());
assertEquals("", smart.getFromLine());
assertEquals("IH", smart.getTd());
assertEquals("02/12/2018", smart.getComment());
assertEquals("NAIRN", smart.getStationName());
}
@Test
public void testSmartInvalidEnumVal2(){
String json = "{\"STEPTYPE\":\"B\",\"FROMBERTH\":\"7910\",\"TOBERTH\":\"7904\",\"STANOX\":\"01125\",\"EVENT\":\"Z\",\"PLATFORM\":\"1\"," +
"\"TOLINE\":\"\",\"BERTHOFFSET\":\"+59\",\"ROUTE\":\"\",\"FROMLINE\":\"\",\"TD\":\"IH\",\"COMMENT\":\"02/12/2018\",\"STANME\":\"NAIRN\"}";
Smart smart = Smart.parseJSON(new JSONObject(json));
assertEquals(SmartStepType.BETWEEN, smart.getStepType());
assertEquals("7910", smart.getFromBerth());
assertEquals("7904", smart.getToBerth());
assertEquals("01125", smart.getStanox());
assertNull(smart.getEvent());
assertEquals("1", smart.getPlatform());
assertEquals("", smart.getToLine());
assertEquals(59, smart.getBerthOffset());
assertEquals("", smart.getRoute());
assertEquals("", smart.getFromLine());
assertEquals("IH", smart.getTd());
assertEquals("02/12/2018", smart.getComment());
assertEquals("NAIRN", smart.getStationName());
}
@Test
public void testSmartNormalCase3(){
String json = "{\"STEPTYPE\":\"B\",\"FROMBERTH\":\"7910\",\"TOBERTH\":\"7904\",\"STANOX\":\"01125\",\"EVENT\":\"A\",\"PLATFORM\":\"1\"," +
"\"TOLINE\":\"\",\"BERTHOFFSET\":\"-59\",\"ROUTE\":\"\",\"FROMLINE\":\"\",\"TD\":\"IH\",\"COMMENT\":\"02/12/2018\",\"STANME\":\"NAIRN\"}";
Smart smart = Smart.parseJSON(json);
assertEquals(SmartStepType.BETWEEN, smart.getStepType());
assertEquals("7910", smart.getFromBerth());
assertEquals("7904", smart.getToBerth());
assertEquals("01125", smart.getStanox());
assertEquals(SmartEvent.ARRIVE_UP, smart.getEvent());
assertEquals("1", smart.getPlatform());
assertEquals("", smart.getToLine());
assertEquals(-59, smart.getBerthOffset());
assertEquals("", smart.getRoute());
assertEquals("", smart.getFromLine());
assertEquals("IH", smart.getTd());
assertEquals("02/12/2018", smart.getComment());
assertEquals("NAIRN", smart.getStationName());
}
@Test
public void testSmartInvalidEnumVal3(){
String json = "{\"STEPTYPE\":\"B\",\"FROMBERTH\":\"7910\",\"TOBERTH\":\"7904\",\"STANOX\":\"01125\",\"EVENT\":\"Z\",\"PLATFORM\":\"1\"," +
"\"TOLINE\":\"\",\"BERTHOFFSET\":\"-59\",\"ROUTE\":\"\",\"FROMLINE\":\"\",\"TD\":\"IH\",\"COMMENT\":\"02/12/2018\",\"STANME\":\"NAIRN\"}";
Smart smart = Smart.parseJSON(json);
assertEquals(SmartStepType.BETWEEN, smart.getStepType());
assertEquals("7910", smart.getFromBerth());
assertEquals("7904", smart.getToBerth());
assertEquals("01125", smart.getStanox());
assertNull(smart.getEvent());
assertEquals("1", smart.getPlatform());
assertEquals("", smart.getToLine());
assertEquals(-59, smart.getBerthOffset());
assertEquals("", smart.getRoute());
assertEquals("", smart.getFromLine());
assertEquals("IH", smart.getTd());
assertEquals("02/12/2018", smart.getComment());
assertEquals("NAIRN", smart.getStationName());
}
@Test
public void testSmartNormalCase4(){
String json = "{\"STEPTYPE\":\"B\",\"FROMBERTH\":\"7910\",\"TOBERTH\":\"7904\",\"STANOX\":\"01125\",\"EVENT\":\"A\",\"PLATFORM\":\"1\"," +
"\"TOLINE\":\"\",\"BERTHOFFSET\":\"-59\",\"ROUTE\":\"\",\"FROMLINE\":\"\",\"TD\":\"IH\",\"COMMENT\":\"02/12/2018\",\"STANME\":\"NAIRN\"}";
Smart smart = Smart.parseJSON(new JSONObject(json));
assertEquals(SmartStepType.BETWEEN, smart.getStepType());
assertEquals("7910", smart.getFromBerth());
assertEquals("7904", smart.getToBerth());
assertEquals("01125", smart.getStanox());
assertEquals(SmartEvent.ARRIVE_UP, smart.getEvent());
assertEquals("1", smart.getPlatform());
assertEquals("", smart.getToLine());
assertEquals(-59, smart.getBerthOffset());
assertEquals("", smart.getRoute());
assertEquals("", smart.getFromLine());
assertEquals("IH", smart.getTd());
assertEquals("02/12/2018", smart.getComment());
assertEquals("NAIRN", smart.getStationName());
}
@Test
public void testSmartInvalidEnumVal4(){
String json = "{\"STEPTYPE\":\"B\",\"FROMBERTH\":\"7910\",\"TOBERTH\":\"7904\",\"STANOX\":\"01125\",\"EVENT\":\"Z\",\"PLATFORM\":\"1\"," +
"\"TOLINE\":\"\",\"BERTHOFFSET\":\"-59\",\"ROUTE\":\"\",\"FROMLINE\":\"\",\"TD\":\"IH\",\"COMMENT\":\"02/12/2018\",\"STANME\":\"NAIRN\"}";
Smart smart = Smart.parseJSON(new JSONObject(json));
assertEquals(SmartStepType.BETWEEN, smart.getStepType());
assertEquals("7910", smart.getFromBerth());
assertEquals("7904", smart.getToBerth());
assertEquals("01125", smart.getStanox());
assertNull(smart.getEvent());
assertEquals("1", smart.getPlatform());
assertEquals("", smart.getToLine());
assertEquals(-59, smart.getBerthOffset());
assertEquals("", smart.getRoute());
assertEquals("", smart.getFromLine());
assertEquals("IH", smart.getTd());
assertEquals("02/12/2018", smart.getComment());
assertEquals("NAIRN", smart.getStationName());
}
@Test
public void testSmartNormalCase6(){
String json = "{\"STEPTYPE\":\"B\",\"FROMBERTH\":\"7910\",\"TOBERTH\":\"7904\",\"STANOX\":\"01125\",\"EVENT\":\"A\",\"PLATFORM\":\"1\"," +
"\"TOLINE\":\"\",\"BERTHOFFSET\":\"59\",\"ROUTE\":\"\",\"FROMLINE\":\"\",\"TD\":\"IH\",\"COMMENT\":\"02/12/2018\",\"STANME\":\"NAIRN\"}";
Smart smart = Smart.parseJSON(json);
assertEquals(SmartStepType.BETWEEN, smart.getStepType());
assertEquals("7910", smart.getFromBerth());
assertEquals("7904", smart.getToBerth());
assertEquals("01125", smart.getStanox());
assertEquals(SmartEvent.ARRIVE_UP, smart.getEvent());
assertEquals("1", smart.getPlatform());
assertEquals("", smart.getToLine());
assertEquals(59, smart.getBerthOffset());
assertEquals("", smart.getRoute());
assertEquals("", smart.getFromLine());
assertEquals("IH", smart.getTd());
assertEquals("02/12/2018", smart.getComment());
assertEquals("NAIRN", smart.getStationName());
}
@Test
public void testSmartInvalidEnumVal6(){
String json = "{\"STEPTYPE\":\"B\",\"FROMBERTH\":\"7910\",\"TOBERTH\":\"7904\",\"STANOX\":\"01125\",\"EVENT\":\"Z\",\"PLATFORM\":\"1\"," +
"\"TOLINE\":\"\",\"BERTHOFFSET\":\"59\",\"ROUTE\":\"\",\"FROMLINE\":\"\",\"TD\":\"IH\",\"COMMENT\":\"02/12/2018\",\"STANME\":\"NAIRN\"}";
Smart smart = Smart.parseJSON(json);
assertEquals(SmartStepType.BETWEEN, smart.getStepType());
assertEquals("7910", smart.getFromBerth());
assertEquals("7904", smart.getToBerth());
assertEquals("01125", smart.getStanox());
assertNull(smart.getEvent());
assertEquals("1", smart.getPlatform());
assertEquals("", smart.getToLine());
assertEquals(59, smart.getBerthOffset());
assertEquals("", smart.getRoute());
assertEquals("", smart.getFromLine());
assertEquals("IH", smart.getTd());
assertEquals("02/12/2018", smart.getComment());
assertEquals("NAIRN", smart.getStationName());
}
@Test
public void testSmartNormalCase5(){
String json = "{\"STEPTYPE\":\"B\",\"FROMBERTH\":\"7910\",\"TOBERTH\":\"7904\",\"STANOX\":\"01125\",\"EVENT\":\"A\",\"PLATFORM\":\"1\"," +
"\"TOLINE\":\"\",\"BERTHOFFSET\":\"59\",\"ROUTE\":\"\",\"FROMLINE\":\"\",\"TD\":\"IH\",\"COMMENT\":\"02/12/2018\",\"STANME\":\"NAIRN\"}";
Smart smart = Smart.parseJSON(new JSONObject(json));
assertEquals(SmartStepType.BETWEEN, smart.getStepType());
assertEquals("7910", smart.getFromBerth());
assertEquals("7904", smart.getToBerth());
assertEquals("01125", smart.getStanox());
assertEquals(SmartEvent.ARRIVE_UP, smart.getEvent());
assertEquals("1", smart.getPlatform());
assertEquals("", smart.getToLine());
assertEquals(59, smart.getBerthOffset());
assertEquals("", smart.getRoute());
assertEquals("", smart.getFromLine());
assertEquals("IH", smart.getTd());
assertEquals("02/12/2018", smart.getComment());
assertEquals("NAIRN", smart.getStationName());
}
@Test
public void testSmartInvalidEnumVal5(){
String json = "{\"STEPTYPE\":\"B\",\"FROMBERTH\":\"7910\",\"TOBERTH\":\"7904\",\"STANOX\":\"01125\",\"EVENT\":\"Z\",\"PLATFORM\":\"1\"," +
"\"TOLINE\":\"\",\"BERTHOFFSET\":\"59\",\"ROUTE\":\"\",\"FROMLINE\":\"\",\"TD\":\"IH\",\"COMMENT\":\"02/12/2018\",\"STANME\":\"NAIRN\"}";
Smart smart = Smart.parseJSON(new JSONObject(json));
assertEquals(SmartStepType.BETWEEN, smart.getStepType());
assertEquals("7910", smart.getFromBerth());
assertEquals("7904", smart.getToBerth());
assertEquals("01125", smart.getStanox());
assertNull(smart.getEvent());
assertEquals("1", smart.getPlatform());
assertEquals("", smart.getToLine());
assertEquals(59, smart.getBerthOffset());
assertEquals("", smart.getRoute());
assertEquals("", smart.getFromLine());
assertEquals("IH", smart.getTd());
assertEquals("02/12/2018", smart.getComment());
assertEquals("NAIRN", smart.getStationName());
}
}

31
src/test/java/org/leolo/nrdatad/ScheduleEnumTest.java

@ -0,0 +1,31 @@
package org.leolo.nrdatad;
import org.junit.Test;
import org.leolo.nrdatad.model.AssociationCategory;
import org.leolo.nrdatad.model.ShortTermPlanningIndicator;
import static org.junit.Assert.*;
public class ScheduleEnumTest {
@Test
public void testAssociationCategory(){
assertEquals(AssociationCategory.DIVIDE, AssociationCategory.parseCode("VV"));
assertEquals(AssociationCategory.JOIN, AssociationCategory.parseCode("JJ"));
assertEquals(AssociationCategory.NEXT, AssociationCategory.parseCode("NP"));
assertNull(AssociationCategory.parseCode("XX"));
assertNull(AssociationCategory.parseCode(null));
assertNull(AssociationCategory.parseCode(""));
}
@Test public void testShortTermPlanningIndicator(){
assertEquals(ShortTermPlanningIndicator.CANCELLATION, ShortTermPlanningIndicator.parseCode("C"));
assertEquals(ShortTermPlanningIndicator.NEW, ShortTermPlanningIndicator.parseCode("N"));
assertEquals(ShortTermPlanningIndicator.PERMANENT, ShortTermPlanningIndicator.parseCode("P"));
assertEquals(ShortTermPlanningIndicator.OVERLAY, ShortTermPlanningIndicator.parseCode("O"));
assertNull(ShortTermPlanningIndicator.parseCode("X"));
assertNull(ShortTermPlanningIndicator.parseCode(null));
assertNull(ShortTermPlanningIndicator.parseCode(""));
}
}

29
src/test/java/org/leolo/nrdatad/SmartEnumTest.java

@ -0,0 +1,29 @@
package org.leolo.nrdatad;
import org.junit.Test;
import org.leolo.nrdatad.model.SmartEvent;
import org.leolo.nrdatad.model.SmartStepType;
import static org.junit.Assert.*;
public class SmartEnumTest {
@Test
public void testSmartStepType(){
assertEquals(SmartStepType.BETWEEN, SmartStepType.parseCode("B"));
assertEquals(SmartStepType.BETWEEN, SmartStepType.parseCode("b"));
assertNull(SmartStepType.parseCode("Q"));
assertNull(SmartStepType.parseCode("X"));
assertNull(SmartStepType.parseCode(""));
}
@Test
public void testSmartEvent(){
assertEquals(SmartEvent.ARRIVE_UP, SmartEvent.parseCode("A"));
assertEquals(SmartEvent.ARRIVE_UP, SmartEvent.parseCode("a"));
assertNull(SmartEvent.parseCode("Q"));
assertNull(SmartEvent.parseCode("X"));
assertNull(SmartEvent.parseCode(""));
}
}

59
src/test/java/org/leolo/nrdatad/db/test/DatabaseManager.java

@ -0,0 +1,59 @@
package org.leolo.nrdatad.db.test;
import org.leolo.nrdatad.db.*;
import org.leolo.nrdatad.db.MetadataDao;
import java.sql.Connection;
import java.sql.SQLException;
public class DatabaseManager implements org.leolo.nrdatad.db.DatabaseManager {
public void initPool() {
}
@Override
public boolean checkConnection() {
return true;
}
@Override
public Connection getConnection() throws SQLException {
throw new RuntimeException("Function not supported by stub");
}
@Override
public SmartDao getSmartDao() {
return null;
}
@Override
public TiplocDao getTiplocDao() {
return null;
}
@Override
public TrainAssociationDao getTrainAssociationDao() {
return null;
}
@Override
public TrainScheduleDao getTrainScheduleDao() {
return null;
}
@Override
public MetadataDao getMetadataDao() {
return null;
}
@Override
public CorpusDao getCORPUSDao() {
return null;
}
@Override
public DataSourceDao getDataSourceDao() {
return null;
}
}

40
src/test/java/org/leolo/nrdatad/db/test/MetadataDao.java

@ -0,0 +1,40 @@
package org.leolo.nrdatad.db.test;
import org.jetbrains.annotations.NotNull;
import org.leolo.nrdatad.db.DatabaseManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Set;
import java.util.function.BiConsumer;
public class MetadataDao extends org.leolo.nrdatad.db.MetadataDao {
private HashMap<String, String> dataMap = new HashMap<>();
public MetadataDao(DatabaseManager manager) {
super(manager);
}
@Override
public String getMetadata(String key, String defaultValue) throws SQLException {
if(dataMap.containsKey(key))
return dataMap.get(key);
return defaultValue;
}
@Override
public void updateMetadata(String key, @NotNull String value) throws SQLException {
dataMap.put(key, value);
}
@Override
public Set<String> getKeys() throws SQLException {
return dataMap.keySet();
}
@Override
public void forEach(BiConsumer<String, String> action) throws SQLException {
dataMap.forEach(action);
}
}

105
src/test/java/org/leolo/nrdatad/model/ScheduleAssociationTest.java

@ -0,0 +1,105 @@
package org.leolo.nrdatad.model;
import org.junit.Test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import static org.junit.Assert.*;
public class ScheduleAssociationTest {
@Test public void basicCase1() throws ParseException {
String json = "{\"diagram_type\":\"T\",\"assoc_end_date\":\"2022-12-05T00:00:00Z\",\"assoc_location_suffix\":null," +
"\"assoc_days\":\"1000000\",\"assoc_train_uid\":\"L77547\",\"transaction_type\":\"Create\"," +
"\"date_indicator\":\"S\",\"base_location_suffix\":null,\"main_train_uid\":\"L77935\"," +
"\"CIF_stp_indicator\":\"P\",\"assoc_start_date\":\"2022-05-16T00:00:00Z\"," +
"\"location\":\"KNGX\",\"category\":\"NP\"}";
ScheduleAssociation sa = ScheduleAssociation.parseJSON(json);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
assertEquals(sdf.parse("20221205"), sa.getEndDate());
assertEquals("", sa.getAssocLocationSuffix());
assertEquals("1000000", sa.getAssoDays());
assertEquals("L77547", sa.getAssoUid());
assertEquals(0, sa.getAssociationDate());
assertEquals("", sa.getBaseLocationSuffix());
assertEquals("L77935", sa.getMainUid());
assertEquals(ShortTermPlanningIndicator.PERMANENT, sa.getStpIndicator());
assertEquals(sdf.parse("20220516"), sa.getStartDate());
assertEquals("KNGX", sa.getAssociationLocation());
assertEquals(AssociationCategory.NEXT, sa.getAssociationCategory());
assertEquals("L77935L77547KNGXPFE4P", sa.getAuid());
assertEquals(790828983, sa.hashCode());
}
@Test public void vstpAssoc1() throws ParseException {
String json = "{\"diagram_type\":\"T\",\"assoc_end_date\":\"2022-12-05T00:00:00Z\",\"assoc_location_suffix\":null," +
"\"assoc_days\":\"1000000\",\"assoc_train_uid\":\" 77547\",\"transaction_type\":\"Create\"," +
"\"date_indicator\":\"S\",\"base_location_suffix\":null,\"main_train_uid\":\" 77935\"," +
"\"CIF_stp_indicator\":\"P\",\"assoc_start_date\":\"2022-05-16T00:00:00Z\"," +
"\"location\":\"KNGX\",\"category\":\"NP\"}";
ScheduleAssociation sa = ScheduleAssociation.parseJSON(json);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
assertEquals(sdf.parse("20221205"), sa.getEndDate());
assertEquals("", sa.getAssocLocationSuffix());
assertEquals("1000000", sa.getAssoDays());
assertEquals("77547", sa.getAssoUid());
assertEquals(0, sa.getAssociationDate());
assertEquals("", sa.getBaseLocationSuffix());
assertEquals("77935", sa.getMainUid());
assertEquals(ShortTermPlanningIndicator.PERMANENT, sa.getStpIndicator());
assertEquals(sdf.parse("20220516"), sa.getStartDate());
assertEquals("KNGX", sa.getAssociationLocation());
assertEquals(AssociationCategory.NEXT, sa.getAssociationCategory());
assertEquals("7793577547KNGXPFE4P", sa.getAuid());
assertEquals(-1968067017, sa.hashCode());
}
@Test public void basicCase2() throws ParseException {
String json = "{\"diagram_type\":\"T\",\"assoc_end_date\":\"2022-12-05T00:00:00Z\"," +
"\"assoc_days\":\"1000000\",\"assoc_train_uid\":\"L77547\",\"transaction_type\":\"Create\"," +
"\"date_indicator\":\"S\",\"main_train_uid\":\"L77935\"," +
"\"CIF_stp_indicator\":\"P\",\"assoc_start_date\":\"2022-05-16T00:00:00Z\"," +
"\"location\":\"KNGX\",\"category\":\"NP\"}";
ScheduleAssociation sa = ScheduleAssociation.parseJSON(json);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
assertEquals(sdf.parse("20221205"), sa.getEndDate());
assertEquals("", sa.getAssocLocationSuffix());
assertEquals("1000000", sa.getAssoDays());
assertEquals("L77547", sa.getAssoUid());
assertEquals(0, sa.getAssociationDate());
assertEquals("", sa.getBaseLocationSuffix());
assertEquals("L77935", sa.getMainUid());
assertEquals(ShortTermPlanningIndicator.PERMANENT, sa.getStpIndicator());
assertEquals(sdf.parse("20220516"), sa.getStartDate());
assertEquals("KNGX", sa.getAssociationLocation());
assertEquals(AssociationCategory.NEXT, sa.getAssociationCategory());
assertEquals("L77935L77547KNGXPFE4P", sa.getAuid());
assertEquals(790828983, sa.hashCode());
}
@Test public void vstpAssoc2() throws ParseException {
String json = "{\"diagram_type\":\"T\",\"assoc_end_date\":\"2022-12-05T00:00:00Z\"," +
"\"assoc_days\":\"1000000\",\"assoc_train_uid\":\" 77547\",\"transaction_type\":\"Create\"," +
"\"date_indicator\":\"S\",\"main_train_uid\":\" 77935\"," +
"\"CIF_stp_indicator\":\"P\",\"assoc_start_date\":\"2022-05-16T00:00:00Z\"," +
"\"location\":\"KNGX\",\"category\":\"NP\"}";
ScheduleAssociation sa = ScheduleAssociation.parseJSON(json);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
assertEquals(sdf.parse("20221205"), sa.getEndDate());
assertEquals("", sa.getAssocLocationSuffix());
assertEquals("1000000", sa.getAssoDays());
assertEquals("77547", sa.getAssoUid());
assertEquals(0, sa.getAssociationDate());
assertEquals("", sa.getBaseLocationSuffix());
assertEquals("77935", sa.getMainUid());
assertEquals(ShortTermPlanningIndicator.PERMANENT, sa.getStpIndicator());
assertEquals(sdf.parse("20220516"), sa.getStartDate());
assertEquals("KNGX", sa.getAssociationLocation());
assertEquals(AssociationCategory.NEXT, sa.getAssociationCategory());
assertEquals("7793577547KNGXPFE4P", sa.getAuid());
assertEquals(-1968067017, sa.hashCode());
}
}

30
src/test/java/org/leolo/nrdatad/model/TiplocTest.java

@ -0,0 +1,30 @@
package org.leolo.nrdatad.model;
import org.junit.Test;
import static org.junit.Assert.*;
public class TiplocTest {
@Test public void basicTest(){
String json = "{\"tps_description\":\"ABERDEEN\",\"crs_code\":\"ABD\",\"description\":\"ABERDEEN\"," +
"\"stanox\":\"02071\",\"tiploc_code\":\"ABRDEEN\",\"transaction_type\":\"Create\",\"nalco\":\"897600\"}";
Tiploc tiploc = Tiploc.parseJSON(json);
assertEquals("ABERDEEN", tiploc.getTpsDescription());
assertEquals("ABD", tiploc.getCrsCode());
assertEquals("ABERDEEN", tiploc.getDescription());
assertEquals("02071", tiploc.getStanox());
assertEquals("ABRDEEN", tiploc.getTiplocCode());
assertEquals("897600", tiploc.getNalco());
}
@Test public void basicTest2(){
String json = "{\"tps_description\":\"ABERDEEN\",\"description\":\"ABERDEEN\"," +
"\"stanox\":\"02071\",\"tiploc_code\":\"ABRDEEN\",\"transaction_type\":\"Create\",\"nalco\":\"897600\"}";
Tiploc tiploc = Tiploc.parseJSON(json);
assertEquals("ABERDEEN", tiploc.getTpsDescription());
assertEquals("", tiploc.getCrsCode());
assertEquals("ABERDEEN", tiploc.getDescription());
assertEquals("02071", tiploc.getStanox());
assertEquals("ABRDEEN", tiploc.getTiplocCode());
assertEquals("897600", tiploc.getNalco());
}
}

210
src/test/java/org/leolo/nrdatad/model/TrainScheduleTest.java

@ -0,0 +1,210 @@
package org.leolo.nrdatad.model;
import org.junit.Test;
import org.leolo.nrdatad.util.TestUtil;
import org.leolo.nrdatad.util.TimeUtil;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import static org.junit.Assert.*;
public class TrainScheduleTest {
@Test public void testSchedule1() throws IOException, ParseException {
String json = TestUtil.openResourceFileAsString("org/leolo/nrdatad/test/V56331_N.json");
TrainSchedule ts = TrainSchedule.parseJSON(json);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
//Schedule Info
assertEquals("V56331", ts.getTrainUid());
assertEquals("V56331M3M4N", ts.getSUID());
assertEquals("AW", ts.getAtocCode());
assertEquals(ShortTermPlanningIndicator.NEW, ts.getShortTermPlanningIndicator());
assertEquals(sdf.parse("20230104"), ts.getStartDate());
assertEquals(sdf.parse("20230105"), ts.getEndDate());
assertEquals("0011000", ts.getRunsOn());
assertEquals("",ts.getRunsOnBankHoliday());
assertEquals("1", ts.getSchedeuleType());
//Sector Info
TrainScheduleSector tss = ts.getSector();
assertEquals("", tss.getServiceBranding());
assertEquals("OO", tss.getTrainCategory());
assertEquals("??", tss.getBusinessSector());
assertEquals(75, tss.getSpeed());
assertEquals("", tss.getReservation());
assertEquals("", tss.getCatering());
assertEquals("DMU", tss.getPowerType());
assertEquals("S", tss.getTimingLoad());
assertEquals("2F60", tss.getSignalId());
assertEquals("", tss.getHeadcode());
assertEquals("", tss.getOperatingCharacteristic());
assertEquals("", tss.getSleeper());
assertEquals("25441000", tss.getTrainServiceCode());
assertEquals("S", tss.getTrainClass());
//Check each location
//1 - PTYPRID
TrainScheduleLocation tsl = tss.getScheduleLocations().get(0);
assertEquals(0, tsl.getPathingAllowance());
assertEquals(TrainScheduleLocationRecordIdentity.ORIGINATE, tsl.getRecordIdentity());
assertEquals(0, tsl.getEngineeringAllowance());
assertEquals("U", tsl.getLine());
assertEquals("", tsl.getPath());
assertEquals(TrainScheduleLocation.UNKNOWN_RECORD_SEQUENCE, tsl.getTiplocInstance());
assertEquals(tss, tsl.getRecordSector());
assertEquals(1, tsl.getRecordSequence());
assertEquals("1", tsl.getPlatform());
assertEquals(TimeUtil.parseTime("1654"), tsl.getWttDeparture());
assertEquals(TimeUtil.parseTime("1654"), tsl.getPublicDeparture());
assertEquals(0, tsl.getWttArrival());
assertEquals(0, tsl.getPublicArrival());
assertEquals(0, tsl.getWttPass());
//2 - TREFRST
tsl = tss.getScheduleLocations().get(1);
assertEquals(180_000, tsl.getPathingAllowance());
assertEquals(TrainScheduleLocationRecordIdentity.INTERMEDIATE, tsl.getRecordIdentity());
assertEquals(60_000, tsl.getEngineeringAllowance());
assertEquals("UL", tsl.getLine());
assertEquals("UP", tsl.getPath());
assertEquals(TrainScheduleLocation.UNKNOWN_RECORD_SEQUENCE, tsl.getTiplocInstance());
assertEquals(tss, tsl.getRecordSector());
assertEquals(2, tsl.getRecordSequence());
assertEquals("A", tsl.getPlatform());
assertEquals(TimeUtil.parseTime("1657H"), tsl.getWttDeparture());
assertEquals(TimeUtil.parseTime("1657"), tsl.getPublicDeparture());
assertEquals(TimeUtil.parseTime("1656H"), tsl.getWttArrival());
assertEquals(TimeUtil.parseTime("1656"), tsl.getPublicArrival());
assertEquals(0, tsl.getWttPass());
//8 - CVLESBY
tsl = tss.getScheduleLocations().get(7);
assertEquals(0, tsl.getPathingAllowance());
assertEquals(TrainScheduleLocationRecordIdentity.INTERMEDIATE, tsl.getRecordIdentity());
assertEquals(0, tsl.getEngineeringAllowance());
assertEquals("", tsl.getLine());
assertEquals("", tsl.getPath());
assertEquals(TrainScheduleLocation.UNKNOWN_RECORD_SEQUENCE, tsl.getTiplocInstance());
assertEquals(tss, tsl.getRecordSector());
assertEquals(8, tsl.getRecordSequence());
assertEquals("", tsl.getPlatform());
assertEquals(0, tsl.getWttDeparture());
assertEquals(0, tsl.getPublicDeparture());
assertEquals(0, tsl.getWttArrival());
assertEquals(0, tsl.getPublicArrival());
assertEquals(TimeUtil.parseTime("1723"), tsl.getWttPass());
//9 - CRDFCEN
tsl = tss.getScheduleLocations().get(8);
assertEquals(0, tsl.getPathingAllowance());
assertEquals(TrainScheduleLocationRecordIdentity.TERMINATE, tsl.getRecordIdentity());
assertEquals(0, tsl.getEngineeringAllowance());
assertEquals("", tsl.getLine());
assertEquals("", tsl.getPath());
assertEquals(TrainScheduleLocation.UNKNOWN_RECORD_SEQUENCE, tsl.getTiplocInstance());
assertEquals(tss, tsl.getRecordSector());
assertEquals(9, tsl.getRecordSequence());
assertEquals("7", tsl.getPlatform());
assertEquals(0, tsl.getWttDeparture());
assertEquals(0, tsl.getPublicDeparture());
assertEquals(TimeUtil.parseTime("1724"), tsl.getWttArrival());
assertEquals(TimeUtil.parseTime("1725"), tsl.getPublicArrival());
assertEquals(0, tsl.getWttPass());
}
@Test public void testSchedule2() throws IOException, ParseException {
String json = TestUtil.openResourceFileAsString("org/leolo/nrdatad/test/V56332_N.json");
TrainSchedule ts = TrainSchedule.parseJSON(json);
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
//Schedule Info
assertEquals("V56332", ts.getTrainUid());
assertEquals("V56332M3M4N", ts.getSUID());
assertEquals("AW", ts.getAtocCode());
assertEquals(ShortTermPlanningIndicator.NEW, ts.getShortTermPlanningIndicator());
assertEquals(sdf.parse("20230104"), ts.getStartDate());
assertEquals(sdf.parse("20230105"), ts.getEndDate());
assertEquals("0011000", ts.getRunsOn());
assertEquals("",ts.getRunsOnBankHoliday());
assertEquals("1", ts.getSchedeuleType());
//Sector Info
TrainScheduleSector tss = ts.getSector();
assertEquals("", tss.getServiceBranding());
assertEquals("OO", tss.getTrainCategory());
assertEquals("??", tss.getBusinessSector());
assertEquals(75, tss.getSpeed());
assertEquals("", tss.getReservation());
assertEquals("", tss.getCatering());
assertEquals("DMU", tss.getPowerType());
assertEquals("S", tss.getTimingLoad());
assertEquals("2F60", tss.getSignalId());
assertEquals("", tss.getHeadcode());
assertEquals("", tss.getOperatingCharacteristic());
assertEquals("", tss.getSleeper());
assertEquals("25441000", tss.getTrainServiceCode());
assertEquals("S", tss.getTrainClass());
//Check each location
//1 - PTYPRID
TrainScheduleLocation tsl = tss.getScheduleLocations().get(0);
assertEquals(0, tsl.getPathingAllowance());
assertEquals(TrainScheduleLocationRecordIdentity.ORIGINATE, tsl.getRecordIdentity());
assertEquals(0, tsl.getEngineeringAllowance());
assertEquals("U", tsl.getLine());
assertEquals("", tsl.getPath());
assertEquals(TrainScheduleLocation.UNKNOWN_RECORD_SEQUENCE, tsl.getTiplocInstance());
assertEquals(tss, tsl.getRecordSector());
assertEquals(1, tsl.getRecordSequence());
assertEquals("1", tsl.getPlatform());
assertEquals(TimeUtil.parseTime("1654"), tsl.getWttDeparture());
assertEquals(TimeUtil.parseTime("1654"), tsl.getPublicDeparture());
assertEquals(0, tsl.getWttArrival());
assertEquals(0, tsl.getPublicArrival());
assertEquals(0, tsl.getWttPass());
//2 - TREFRST
tsl = tss.getScheduleLocations().get(1);
assertEquals(180_000, tsl.getPathingAllowance());
assertEquals(TrainScheduleLocationRecordIdentity.INTERMEDIATE, tsl.getRecordIdentity());
assertEquals(60_000, tsl.getEngineeringAllowance());
assertEquals("UL", tsl.getLine());
assertEquals("UP", tsl.getPath());
assertEquals(TrainScheduleLocation.UNKNOWN_RECORD_SEQUENCE, tsl.getTiplocInstance());
assertEquals(tss, tsl.getRecordSector());
assertEquals(2, tsl.getRecordSequence());
assertEquals("A", tsl.getPlatform());
assertEquals(TimeUtil.parseTime("1657H"), tsl.getWttDeparture());
assertEquals(TimeUtil.parseTime("1657"), tsl.getPublicDeparture());
assertEquals(TimeUtil.parseTime("1656H"), tsl.getWttArrival());
assertEquals(TimeUtil.parseTime("1656"), tsl.getPublicArrival());
assertEquals(0, tsl.getWttPass());
//8 - CVLESBY
tsl = tss.getScheduleLocations().get(7);
assertEquals(0, tsl.getPathingAllowance());
assertEquals(TrainScheduleLocationRecordIdentity.INTERMEDIATE, tsl.getRecordIdentity());
assertEquals(0, tsl.getEngineeringAllowance());
assertEquals("", tsl.getLine());
assertEquals("", tsl.getPath());
assertEquals(TrainScheduleLocation.UNKNOWN_RECORD_SEQUENCE, tsl.getTiplocInstance());
assertEquals(tss, tsl.getRecordSector());
assertEquals(8, tsl.getRecordSequence());
assertEquals("", tsl.getPlatform());
assertEquals(0, tsl.getWttDeparture());
assertEquals(0, tsl.getPublicDeparture());
assertEquals(0, tsl.getWttArrival());
assertEquals(0, tsl.getPublicArrival());
assertEquals(TimeUtil.parseTime("1723"), tsl.getWttPass());
//9 - CRDFCEN
tsl = tss.getScheduleLocations().get(8);
assertEquals(0, tsl.getPathingAllowance());
assertEquals(TrainScheduleLocationRecordIdentity.TERMINATE, tsl.getRecordIdentity());
assertEquals(0, tsl.getEngineeringAllowance());
assertEquals("", tsl.getLine());
assertEquals("", tsl.getPath());
assertEquals(TrainScheduleLocation.UNKNOWN_RECORD_SEQUENCE, tsl.getTiplocInstance());
assertEquals(tss, tsl.getRecordSector());
assertEquals(9, tsl.getRecordSequence());
assertEquals("7", tsl.getPlatform());
assertEquals(0, tsl.getWttDeparture());
assertEquals(0, tsl.getPublicDeparture());
assertEquals(TimeUtil.parseTime("1724"), tsl.getWttArrival());
assertEquals(TimeUtil.parseTime("1725"), tsl.getPublicArrival());
assertEquals(0, tsl.getWttPass());
}
}

41
src/test/java/org/leolo/nrdatad/util/DateUtilTest.java

@ -0,0 +1,41 @@
package org.leolo.nrdatad.util;
import org.junit.Test;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import static org.junit.Assert.*;
public class DateUtilTest {
@Test public void testDate1(){
// Second day of January 2022
Date d1 = DateUtil.parseDate("2022-01-02");
Calendar c = GregorianCalendar.getInstance();
c.setTime(d1);
assertEquals(2022, c.get(Calendar.YEAR));
assertEquals(Calendar.JANUARY, c.get(Calendar.MONTH));
assertEquals(2, c.get(Calendar.DAY_OF_MONTH));
}
@Test public void testDate2(){
// Second day of January 2022
Date d1 = DateUtil.parseDate("2022-01-02", "yyyy-dd-MM");
Calendar c = GregorianCalendar.getInstance();
c.setTime(d1);
assertEquals(2022, c.get(Calendar.YEAR));
assertEquals(Calendar.FEBRUARY, c.get(Calendar.MONTH));
assertEquals(1, c.get(Calendar.DAY_OF_MONTH));
}
@Test public void testDateInvalidSuppressed(){
assertNull(DateUtil.parseDate("This is not a date"));
}
@Test(expected = RuntimeException.class) public void testDateException(){
DateUtil.parseDate("This is not a date", false);
}
}

48
src/test/java/org/leolo/nrdatad/util/JSONUtilTest.java

@ -0,0 +1,48 @@
package org.leolo.nrdatad.util;
import org.junit.Test;
import static org.junit.Assert.*;
public class JSONUtilTest {
@Test public void testJSONUtilParseIntNormal(){
assertEquals(10, JSONUtil.parseInt("10"));
assertEquals(0, JSONUtil.parseInt("0"));
assertEquals(10, JSONUtil.parseInt("+10"));
assertEquals(-10, JSONUtil.parseInt("-10"));
}
@Test public void testZeroPrefix(){
assertEquals(75, JSONUtil.parseInt("075"));
assertEquals(5, JSONUtil.parseInt("005"));
assertEquals(75, JSONUtil.parseInt("+075"));
assertEquals(5, JSONUtil.parseInt("+005"));
assertEquals(-75, JSONUtil.parseInt("-075"));
assertEquals(-5, JSONUtil.parseInt("-005"));
}
@Test public void testAllZero(){
assertEquals(0, JSONUtil.parseInt("00000000"));
}
@Test(expected = NumberFormatException.class) public void testJSONUtilParseIntError1(){
JSONUtil.parseInt("abcd");
}
@Test(expected = NumberFormatException.class) public void testJSONUtilParseIntError2(){
JSONUtil.parseInt("");
}
@Test(expected = NullPointerException.class) public void testJSONUtilParseIntError3(){
JSONUtil.parseInt(null);
}
@Test(expected = NumberFormatException.class) public void testJSONUtilParseIntError4(){
JSONUtil.parseInt("+");
}
@Test(expected = NumberFormatException.class) public void testJSONUtilParseIntError5(){
JSONUtil.parseInt("+abcd");
}
@Test(expected = NumberFormatException.class) public void testJSONUtilParseIntError6(){
JSONUtil.parseInt("+2200000000");
}
}

20
src/test/java/org/leolo/nrdatad/util/RangeTest.java

@ -0,0 +1,20 @@
package org.leolo.nrdatad.util;
import org.junit.Test;
import static org.junit.Assert.*;
public class RangeTest {
@Test
public void basicInteger1(){
Range<Integer> range = new Range<>(1,2);
assertEquals(1, range.getLowerBound().intValue());
assertEquals(2, range.getUpperBound().intValue());
range = new Range<>(2,1);
assertEquals(1, range.getLowerBound().intValue());
assertEquals(2, range.getUpperBound().intValue());
range = new Range<>(2,2);
assertEquals(2, range.getLowerBound().intValue());
assertEquals(2, range.getUpperBound().intValue());
}
}

34
src/test/java/org/leolo/nrdatad/util/TUIDDateFormatTest.java

@ -0,0 +1,34 @@
package org.leolo.nrdatad.util;
import org.junit.Test;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import static org.junit.Assert.*;
public class TUIDDateFormatTest {
@Test
public void basicConvert() throws ParseException {
assertEquals("M0", convert("2022-01-01"));
assertEquals("MX", convert("2022-01-31"));
assertEquals("M0", convert("2023-01-01"));
assertEquals("MX", convert("2023-01-31"));
assertEquals("P0", convert("2022-05-01"));
assertEquals("PX", convert("2022-05-31"));
assertEquals("P0", convert("2023-05-01"));
assertEquals("PX", convert("2023-05-31"));
assertEquals("E0", convert("2022-12-01"));
assertEquals("EX", convert("2022-12-31"));
assertEquals("E0", convert("2023-12-01"));
assertEquals("EX", convert("2023-12-31"));
}
private String convert(String date) throws ParseException {
return new TUIDDateFormat().format(new SimpleDateFormat("yyyy-MM-dd").parse(date));
}
}

31
src/test/java/org/leolo/nrdatad/util/TestUtil.java

@ -0,0 +1,31 @@
package org.leolo.nrdatad.util;
import java.io.*;
public class TestUtil {
public static BufferedReader openResourceFileAsBufferedReader(String fileName) throws IOException {
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
InputStream is = classloader.getResourceAsStream(fileName);
if(is==null){
throw new FileNotFoundException("Resource file "+fileName+" cannot be found");
}else{
return new BufferedReader(new InputStreamReader(is));
}
}
public static String openResourceFileAsString(String fileName) throws IOException {
ClassLoader classloader = Thread.currentThread().getContextClassLoader();
InputStream is = classloader.getResourceAsStream(fileName);
if(is==null){
throw new FileNotFoundException("Resource file "+fileName+" cannot be found");
}
ByteArrayOutputStream baos = new ByteArrayOutputStream();
is.transferTo(baos);
String result = new String(baos.toByteArray());
baos.close();
is.close();
return result;
}
}

75
src/test/java/org/leolo/nrdatad/util/TimeUtilTest.java

@ -0,0 +1,75 @@
package org.leolo.nrdatad.util;
import org.junit.Test;
import static org.junit.Assert.*;
public class TimeUtilTest {
@Test public void testAllowance(){
assertEquals(60000, TimeUtil.parseTime("1"));
assertEquals(0, TimeUtil.parseTime(""));
assertEquals(0, TimeUtil.parseTime(" "));
assertEquals(0, TimeUtil.parseTime(" "));
assertEquals(60000, TimeUtil.parseTime("1 "));
assertEquals(90000, TimeUtil.parseTime("1H"));
assertEquals(30000, TimeUtil.parseTime("H"));
assertEquals(30000, TimeUtil.parseTime("H "));
assertEquals(30000, TimeUtil.parseTime(" H"));
assertEquals(600000, TimeUtil.parseTime("10"));
assertEquals(900000, TimeUtil.parseTime("15"));
assertEquals(1200000, TimeUtil.parseTime("20"));
}
@Test public void testWTTTime(){
assertEquals(3_660_000, TimeUtil.parseTime("0101 "));
assertEquals(3_690_000, TimeUtil.parseTime("0101H"));
assertEquals(36_000_000, TimeUtil.parseTime("1000 "));
assertEquals(37_800_000, TimeUtil.parseTime("1030 "));
assertEquals(36_030_000, TimeUtil.parseTime("1000H"));
assertEquals(37_830_000, TimeUtil.parseTime("1030H"));
assertEquals(72_000_000, TimeUtil.parseTime("2000 "));
assertEquals(73_800_000, TimeUtil.parseTime("2030 "));
assertEquals(72_030_000, TimeUtil.parseTime("2000H"));
assertEquals(73_830_000, TimeUtil.parseTime("2030H"));
assertEquals(3_660_000, TimeUtil.parseTime("0101 ", 0));
assertEquals(3_690_000, TimeUtil.parseTime("0101H", 0));
assertEquals(36_000_000, TimeUtil.parseTime("1000 ", 0));
assertEquals(37_800_000, TimeUtil.parseTime("1030 ", 0));
assertEquals(36_030_000, TimeUtil.parseTime("1000H", 0));
assertEquals(37_830_000, TimeUtil.parseTime("1030H", 0));
assertEquals(72_000_000, TimeUtil.parseTime("2000 ", 0));
assertEquals(73_800_000, TimeUtil.parseTime("2030 ", 0));
assertEquals(72_030_000, TimeUtil.parseTime("2000H", 0));
assertEquals(73_830_000, TimeUtil.parseTime("2030H", 0));
// 86_400_000
assertEquals( 90_060_000, TimeUtil.parseTime("0101 ", 1));
assertEquals( 90_090_000, TimeUtil.parseTime("0101H", 1));
assertEquals(122_400_000, TimeUtil.parseTime("1000 ", 1));
assertEquals(124_200_000, TimeUtil.parseTime("1030 ", 1));
assertEquals(122_430_000, TimeUtil.parseTime("1000H", 1));
assertEquals(124_230_000, TimeUtil.parseTime("1030H", 1));
assertEquals(158_400_000, TimeUtil.parseTime("2000 ", 1));
assertEquals(160_200_000, TimeUtil.parseTime("2030 ", 1));
assertEquals(158_430_000, TimeUtil.parseTime("2000H", 1));
assertEquals(160_230_000, TimeUtil.parseTime("2030H", 1));
}
@Test public void testGBTTTime(){
assertEquals(3_660_000, TimeUtil.parseTime("0101"));
assertEquals(36_000_000, TimeUtil.parseTime("1000"));
assertEquals(37_800_000, TimeUtil.parseTime("1030"));
assertEquals(72_000_000, TimeUtil.parseTime("2000"));
assertEquals(73_800_000, TimeUtil.parseTime("2030"));
assertEquals(3_660_000, TimeUtil.parseTime("0101", 0));
assertEquals(36_000_000, TimeUtil.parseTime("1000", 0));
assertEquals(37_800_000, TimeUtil.parseTime("1030", 0));
assertEquals(72_000_000, TimeUtil.parseTime("2000", 0));
assertEquals(73_800_000, TimeUtil.parseTime("2030", 0));
assertEquals( 90_060_000, TimeUtil.parseTime("0101", 1));
assertEquals(122_400_000, TimeUtil.parseTime("1000", 1));
assertEquals(124_200_000, TimeUtil.parseTime("1030", 1));
assertEquals(158_400_000, TimeUtil.parseTime("2000", 1));
assertEquals(160_200_000, TimeUtil.parseTime("2030", 1));
}
}

178
src/test/resources/org/leolo/nrdatad/test/V56331_N.json

@ -0,0 +1,178 @@
{
"CIF_bank_holiday_running": null,
"train_status": "1",
"CIF_train_uid": "V56331",
"schedule_days_runs": "0011000",
"CIF_stp_indicator": "N",
"applicable_timetable": "Y",
"atoc_code": "AW",
"schedule_start_date": "2023-01-04",
"new_schedule_segment": {
"uic_code": "",
"traction_class": ""
},
"transaction_type": "Create",
"schedule_end_date": "2023-01-05",
"schedule_segment": {
"CIF_service_branding": "",
"CIF_train_category": "OO",
"CIF_business_sector": "??",
"CIF_connection_indicator": null,
"CIF_speed": "075",
"CIF_reservations": null,
"CIF_catering_code": null,
"CIF_power_type": "DMU",
"CIF_course_indicator": 1,
"CIF_timing_load": "S",
"signalling_id": "2F60",
"CIF_headcode": "",
"CIF_operating_characteristics": null,
"schedule_location": [
{
"pathing_allowance": null,
"record_identity": "LO",
"engineering_allowance": null,
"line": "U",
"public_departure": "1654",
"tiploc_code": "PTYPRID",
"tiploc_instance": null,
"departure": "1654",
"performance_allowance": null,
"location_type": "LO",
"platform": "1"
},
{
"engineering_allowance": "1",
"arrival": "1656H",
"pass": null,
"line": "UL",
"public_arrival": "1656",
"performance_allowance": "2",
"location_type": "LI",
"platform": "A",
"pathing_allowance": "3",
"record_identity": "LI",
"path": "UP",
"public_departure": "1657",
"tiploc_code": "TREFRST",
"tiploc_instance": null,
"departure": "1657H"
},
{
"engineering_allowance": null,
"arrival": "1703",
"pass": null,
"line": null,
"public_arrival": "1704",
"performance_allowance": null,
"location_type": "LI",
"platform": null,
"pathing_allowance": null,
"record_identity": "LI",
"path": null,
"public_departure": "1704",
"tiploc_code": "TAFFSWL",
"tiploc_instance": null,
"departure": "1704"
},
{
"engineering_allowance": null,
"arrival": "1707",
"pass": null,
"line": null,
"public_arrival": "1708",
"performance_allowance": null,
"location_type": "LI",
"platform": "1",
"pathing_allowance": null,
"record_identity": "LI",
"path": null,
"public_departure": "1708",
"tiploc_code": "RADYR",
"tiploc_instance": null,
"departure": "1708"
},
{
"engineering_allowance": null,
"arrival": "1710",
"pass": null,
"line": null,
"public_arrival": "1710",
"performance_allowance": null,
"location_type": "LI",
"platform": null,
"pathing_allowance": null,
"record_identity": "LI",
"path": null,
"public_departure": "1711",
"tiploc_code": "LLANDAF",
"tiploc_instance": null,
"departure": "1711"
},
{
"engineering_allowance": "1",
"arrival": "1714H",
"pass": null,
"line": null,
"public_arrival": "1715",
"performance_allowance": null,
"location_type": "LI",
"platform": null,
"pathing_allowance": "H",
"record_identity": "LI",
"path": null,
"public_departure": "1715",
"tiploc_code": "CATHAYS",
"tiploc_instance": null,
"departure": "1715H"
},
{
"engineering_allowance": null,
"arrival": "1719",
"pass": null,
"line": null,
"public_arrival": "1720",
"performance_allowance": null,
"location_type": "LI",
"platform": "3",
"pathing_allowance": null,
"record_identity": "LI",
"path": null,
"public_departure": "1721",
"tiploc_code": "CARDFQS",
"tiploc_instance": null,
"departure": "1721"
},
{
"engineering_allowance": null,
"arrival": null,
"pass": "1723",
"line": null,
"public_arrival": null,
"performance_allowance": null,
"location_type": "LI",
"platform": null,
"pathing_allowance": null,
"record_identity": "LI",
"path": null,
"public_departure": null,
"tiploc_code": "CVLESBY",
"tiploc_instance": null,
"departure": null
},
{
"record_identity": "LT",
"path": null,
"arrival": "1724",
"public_arrival": "1725",
"tiploc_code": "CRDFCEN",
"tiploc_instance": null,
"location_type": "LT",
"platform": "7"
}
],
"CIF_sleepers": null,
"CIF_train_service_code": "25441000",
"CIF_train_class": "S"
}
}

113
src/test/resources/org/leolo/nrdatad/test/V56332_N.json

@ -0,0 +1,113 @@
{
"train_status": "1",
"CIF_train_uid": "V56332",
"schedule_days_runs": "0011000",
"CIF_stp_indicator": "N",
"applicable_timetable": "Y",
"atoc_code": "AW",
"schedule_start_date": "2023-01-04",
"new_schedule_segment": {
},
"transaction_type": "Create",
"schedule_end_date": "2023-01-05",
"schedule_segment": {
"CIF_train_category": "OO",
"CIF_business_sector": "??",
"CIF_speed": "075",
"CIF_power_type": "DMU",
"CIF_course_indicator": 1,
"CIF_timing_load": "S",
"signalling_id": "2F60",
"schedule_location": [
{
"record_identity": "LO",
"line": "U",
"public_departure": "1654",
"tiploc_code": "PTYPRID",
"departure": "1654",
"location_type": "LO",
"platform": "1"
},
{
"engineering_allowance": "1",
"arrival": "1656H",
"line": "UL",
"public_arrival": "1656",
"performance_allowance": "2",
"location_type": "LI",
"platform": "A",
"pathing_allowance": "3",
"record_identity": "LI",
"path": "UP",
"public_departure": "1657",
"tiploc_code": "TREFRST",
"departure": "1657H"
},
{
"arrival": "1703",
"public_arrival": "1704",
"location_type": "LI",
"record_identity": "LI",
"public_departure": "1704",
"tiploc_code": "TAFFSWL",
"departure": "1704"
},
{
"arrival": "1707",
"public_arrival": "1708",
"location_type": "LI",
"platform": "1",
"record_identity": "LI",
"public_departure": "1708",
"tiploc_code": "RADYR",
"departure": "1708"
},
{
"arrival": "1710",
"public_arrival": "1710",
"location_type": "LI",
"record_identity": "LI",
"public_departure": "1711",
"tiploc_code": "LLANDAF",
"departure": "1711"
},
{
"engineering_allowance": "1",
"arrival": "1714H",
"public_arrival": "1715",
"location_type": "LI",
"pathing_allowance": "H",
"record_identity": "LI",
"public_departure": "1715",
"tiploc_code": "CATHAYS",
"departure": "1715H"
},
{
"arrival": "1719",
"public_arrival": "1720",
"location_type": "LI",
"platform": "3",
"record_identity": "LI",
"public_departure": "1721",
"tiploc_code": "CARDFQS",
"departure": "1721"
},
{
"pass": "1723",
"location_type": "LI",
"record_identity": "LI",
"tiploc_code": "CVLESBY"
},
{
"record_identity": "LT",
"arrival": "1724",
"public_arrival": "1725",
"tiploc_code": "CRDFCEN",
"location_type": "LT",
"platform": "7"
}
],
"CIF_train_service_code": "25441000",
"CIF_train_class": "S"
}
}
Loading…
Cancel
Save