Skip to content

Commit

Permalink
Implemented SIGMET Bulletin TAC serialization (for the placeholder SI…
Browse files Browse the repository at this point in the history
…GMET version), added message type package structure, fixes #23
  • Loading branch information
ilkkarinne committed Nov 15, 2018
1 parent 985eb48 commit ffdb29c
Show file tree
Hide file tree
Showing 25 changed files with 281 additions and 81 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<dependency>
<groupId>fi.fmi.avi.converter</groupId>
<artifactId>fmi-avi-messageconverter</artifactId>
<version>3.1.0</version>
<version>3.1.1-SNAPSHOT</version>
</dependency>

<!-- For JUnit tests -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,20 @@
import fi.fmi.avi.converter.tac.lexer.LexemeSequenceBuilder;
import fi.fmi.avi.converter.tac.lexer.SerializingException;
import fi.fmi.avi.converter.tac.lexer.impl.ReconstructorContext;
import fi.fmi.avi.model.AviationWeatherMessage;
import fi.fmi.avi.model.AviationWeatherMessageOrCollection;
import fi.fmi.avi.model.taf.TAF;
import fi.fmi.avi.model.taf.TAFBulletin;
import fi.fmi.avi.model.BulletinHeading;
import fi.fmi.avi.model.MeteorologicalBulletin;

public class TAFBulletinTACSerializer extends AbstractTACSerializer<TAFBulletin> {
public abstract class AbstractTACBulletinSerializer<U extends BulletinHeading, S extends AviationWeatherMessage, T extends MeteorologicalBulletin<S, U>>
extends AbstractTACSerializer<T> {

public static final int MAX_ROW_LENGTH = 60;
public static final int WRAPPED_LINE_INDENT = 5;
public static final CharSequence NEW_LINE = "\r\n";

private TAFTACSerializer tafSerializer;

public void setTafSerializer(final TAFTACSerializer serializer) {
this.tafSerializer = serializer;
}

@Override
public ConversionResult<String> convertMessage(final TAFBulletin input, final ConversionHints hints) {
public ConversionResult<String> convertMessage(final T input, final ConversionHints hints) {
ConversionResult<String> result = new ConversionResult<>();
try {
LexemeSequence seq = tokenizeMessage(input, hints);
Expand All @@ -43,33 +39,33 @@ public LexemeSequence tokenizeMessage(final AviationWeatherMessageOrCollection m
return tokenizeMessage(msg, null);
}

protected abstract T accepts(final AviationWeatherMessageOrCollection message) throws SerializingException;

protected abstract Class<T> getBulletinClass();

protected abstract LexemeSequence tokenizeSingleMessage(final S message, final ConversionHints hints) throws SerializingException;

@Override
public LexemeSequence tokenizeMessage(final AviationWeatherMessageOrCollection msg, final ConversionHints hints) throws SerializingException {
if (!(msg instanceof TAFBulletin)) {
throw new SerializingException("I can only tokenize TAFBulletins!");
}
if (this.tafSerializer == null) {
throw new IllegalStateException("No TafSerializer set");
}
TAFBulletin input = (TAFBulletin) msg;
T input = accepts(msg);
LexemeSequenceBuilder retval = this.getLexingFactory().createLexemeSequenceBuilder();
ReconstructorContext<TAFBulletin> baseCtx = new ReconstructorContext<>(input, hints);
appendToken(retval, Lexeme.Identity.BULLETIN_HEADING_DATA_DESIGNATORS, input, TAFBulletin.class, baseCtx);
ReconstructorContext<T> baseCtx = new ReconstructorContext<>(input, hints);
appendToken(retval, Lexeme.Identity.BULLETIN_HEADING_DATA_DESIGNATORS, input, getBulletinClass(), baseCtx);
appendWhitespace(retval, ' ');
appendToken(retval, Lexeme.Identity.BULLETIN_HEADING_LOCATION_INDICATOR, input, TAFBulletin.class, baseCtx);
appendToken(retval, Lexeme.Identity.BULLETIN_HEADING_LOCATION_INDICATOR, input, getBulletinClass(), baseCtx);
appendWhitespace(retval, ' ');
appendToken(retval, Lexeme.Identity.ISSUE_TIME, input, TAFBulletin.class, baseCtx);
appendToken(retval, Lexeme.Identity.ISSUE_TIME, input, getBulletinClass(), baseCtx);
appendWhitespace(retval, ' ');
if (appendToken(retval, Lexeme.Identity.BULLETIN_HEADING_BBB_INDICATOR, input, TAFBulletin.class, baseCtx) == 0) {
if (appendToken(retval, Lexeme.Identity.BULLETIN_HEADING_BBB_INDICATOR, input, getBulletinClass(), baseCtx) == 0) {
retval.removeLast();
}
List<TAF> messages = input.getMessages();
LexemeSequence tafSequence;
for (TAF message : messages) {
List<S> messages = input.getMessages();
LexemeSequence messageSequence;
for (S message : messages) {
appendWhitespace(retval, NEW_LINE);
tafSequence = tafSerializer.tokenizeMessage(message, hints);
messageSequence = tokenizeSingleMessage(message, hints);
int charsOnRow = 0;
List<Lexeme> lexemes = tafSequence.getLexemes();
List<Lexeme> lexemes = messageSequence.getLexemes();
for (Lexeme l : lexemes) {
if (l.getIdentity() != Lexeme.Identity.WHITE_SPACE && l.getIdentity() != Lexeme.Identity.END_TOKEN) {
int length = l.getTACToken().length();
Expand All @@ -89,9 +85,9 @@ public LexemeSequence tokenizeMessage(final AviationWeatherMessageOrCollection m
while (retval.getLast().isPresent() && retval.getLast().get().getIdentity() == Lexeme.Identity.WHITE_SPACE) {
retval.removeLast();
}
Lexeme endToken = tafSequence.getLastLexeme();
Lexeme endToken = messageSequence.getLastLexeme();
if (endToken.getIdentity() != Lexeme.Identity.END_TOKEN) {
throw new SerializingException("TAF does not end in end token '='");
throw new SerializingException("Contained message does not end in end token '='");
}
retval.append(endToken);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ protected boolean checkAndReportLexingResult(final LexemeSequence lexed, final C
* {@link #findNext(Identity, Lexeme, Consumer, LexemeParsingNotifyer)}.
*/
@FunctionalInterface
interface LexemeParsingNotifyer {
public interface LexemeParsingNotifyer {
void ping();
}
}
50 changes: 34 additions & 16 deletions src/main/java/fi/fmi/avi/converter/tac/conf/TACConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,7 @@
import fi.fmi.avi.converter.AviMessageSpecificConverter;
import fi.fmi.avi.converter.ConversionSpecification;
import fi.fmi.avi.converter.tac.AbstractTACSerializer;
import fi.fmi.avi.converter.tac.ImmutableMETARTACParser;
import fi.fmi.avi.converter.tac.ImmutableTAFTACParser;
import fi.fmi.avi.converter.tac.METARTACParser;
import fi.fmi.avi.converter.tac.METARTACSerializer;
import fi.fmi.avi.converter.tac.SPECITACParser;
import fi.fmi.avi.converter.tac.SPECITACSerializer;
import fi.fmi.avi.converter.tac.TACParser;
import fi.fmi.avi.converter.tac.TAFBulletinTACSerializer;
import fi.fmi.avi.converter.tac.TAFTACParser;
import fi.fmi.avi.converter.tac.TAFTACSerializer;
import fi.fmi.avi.converter.tac.lexer.AviMessageLexer;
import fi.fmi.avi.converter.tac.lexer.AviMessageTACTokenizer;
import fi.fmi.avi.converter.tac.lexer.Lexeme;
Expand Down Expand Up @@ -67,9 +58,20 @@
import fi.fmi.avi.converter.tac.lexer.impl.token.Weather;
import fi.fmi.avi.converter.tac.lexer.impl.token.Whitespace;
import fi.fmi.avi.converter.tac.lexer.impl.token.WindShear;
import fi.fmi.avi.converter.tac.metar.ImmutableMETARTACParser;
import fi.fmi.avi.converter.tac.metar.METARTACParser;
import fi.fmi.avi.converter.tac.metar.METARTACSerializer;
import fi.fmi.avi.converter.tac.metar.SPECITACParser;
import fi.fmi.avi.converter.tac.metar.SPECITACSerializer;
import fi.fmi.avi.converter.tac.sigmet.SIGMETBulletinTACSerializer;
import fi.fmi.avi.converter.tac.taf.ImmutableTAFTACParser;
import fi.fmi.avi.converter.tac.taf.TAFBulletinTACSerializer;
import fi.fmi.avi.converter.tac.taf.TAFTACParser;
import fi.fmi.avi.converter.tac.taf.TAFTACSerializer;
import fi.fmi.avi.model.metar.METAR;
import fi.fmi.avi.model.metar.SPECI;
import fi.fmi.avi.model.metar.immutable.METARImpl;
import fi.fmi.avi.model.sigmet.SIGMETBulletin;
import fi.fmi.avi.model.taf.TAF;
import fi.fmi.avi.model.taf.TAFBulletin;
import fi.fmi.avi.model.taf.immutable.TAFImpl;
Expand Down Expand Up @@ -133,6 +135,12 @@ public class TACConverter {
public static final ConversionSpecification<TAFBulletin, String> TAF_BULLETIN_POJO_TO_TAC = new ConversionSpecification<>(TAFBulletin.class, String.class,
null, "WMO GTS TAF Bulletin");

/**
* Pre-configured spec for {@link SIGMETBulletin} to TAC encoded TAF bulletin
*/
public static final ConversionSpecification<SIGMETBulletin, String> SIGMET_BULLETIN_POJO_TO_TAC = new ConversionSpecification<>(SIGMETBulletin.class,
String.class, null, "WMO GTS SIGMET Bulletin");

@Bean
AviMessageSpecificConverter<String, METAR> metarTACParser() {
TACParser<METAR> p = new METARTACParser();
Expand Down Expand Up @@ -258,17 +266,19 @@ private TAFTACSerializer spawnTAFTACSerializer() {
@Bean
AviMessageSpecificConverter<TAFBulletin, String> tafBulletinTACSerializer() {
TAFBulletinTACSerializer s = new TAFBulletinTACSerializer();
s.setLexingFactory(lexingFactory());
addCommonBulletinReconstructors(s);
TAFTACSerializer tafSerializer = (TAFTACSerializer) bulletinTAFTACSerializer();

s.setTafSerializer(tafSerializer);
s.addReconstructor(Lexeme.Identity.BULLETIN_HEADING_DATA_DESIGNATORS, new BulletinHeaderDataDesignators.Reconstructor());
s.addReconstructor(Lexeme.Identity.BULLETIN_HEADING_LOCATION_INDICATOR, new BulletinLocationIndicator.Reconstructor());
s.addReconstructor(Lexeme.Identity.ISSUE_TIME, new IssueTime.Reconstructor());
s.addReconstructor(Lexeme.Identity.BULLETIN_HEADING_BBB_INDICATOR, new BulletinHeadingBBBIndicator.Reconstructor());
return s;
}


@Bean
AviMessageSpecificConverter<SIGMETBulletin, String> sigmetBulletinTACSerializer() {
SIGMETBulletinTACSerializer s = new SIGMETBulletinTACSerializer();
addCommonBulletinReconstructors(s);
return s;
}

@Bean
public AviMessageLexer aviMessageLexer() {
AviMessageLexerImpl l = new AviMessageLexerImpl();
Expand All @@ -294,6 +304,14 @@ public LexingFactory lexingFactory() {
return new LexingFactoryImpl();
}

private void addCommonBulletinReconstructors(final AbstractTACSerializer<?> s) {
s.setLexingFactory(lexingFactory());
s.addReconstructor(Lexeme.Identity.BULLETIN_HEADING_DATA_DESIGNATORS, new BulletinHeaderDataDesignators.Reconstructor());
s.addReconstructor(Lexeme.Identity.BULLETIN_HEADING_LOCATION_INDICATOR, new BulletinLocationIndicator.Reconstructor());
s.addReconstructor(Lexeme.Identity.ISSUE_TIME, new IssueTime.Reconstructor());
s.addReconstructor(Lexeme.Identity.BULLETIN_HEADING_BBB_INDICATOR, new BulletinHeadingBBBIndicator.Reconstructor());
}

private RecognizingAviMessageTokenLexer metarTokenLexer() {
RecognizingAviMessageTokenLexer l = new RecognizingAviMessageTokenLexer();
l.teach(new MetarStart(Priority.HIGH));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package fi.fmi.avi.converter.tac.lexer.impl;

import fi.fmi.avi.converter.ConversionHints;
import fi.fmi.avi.converter.tac.METARTACSerializer;
import fi.fmi.avi.converter.tac.SPECITACSerializer;
import fi.fmi.avi.converter.tac.TAFBulletinTACSerializer;
import fi.fmi.avi.converter.tac.TAFTACSerializer;
import fi.fmi.avi.converter.tac.lexer.AviMessageTACTokenizer;
import fi.fmi.avi.converter.tac.lexer.LexemeSequence;
import fi.fmi.avi.converter.tac.lexer.SerializingException;
import fi.fmi.avi.converter.tac.metar.METARTACSerializer;
import fi.fmi.avi.converter.tac.metar.SPECITACSerializer;
import fi.fmi.avi.converter.tac.taf.TAFBulletinTACSerializer;
import fi.fmi.avi.converter.tac.taf.TAFTACSerializer;
import fi.fmi.avi.model.AviationWeatherMessageOrCollection;
import fi.fmi.avi.model.metar.METAR;
import fi.fmi.avi.model.metar.SPECI;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
import fi.fmi.avi.converter.tac.lexer.impl.ReconstructorContext;
import fi.fmi.avi.converter.tac.lexer.impl.RegexMatchingLexemeVisitor;
import fi.fmi.avi.model.AviationWeatherMessageOrCollection;
import fi.fmi.avi.model.taf.TAFBulletin;
import fi.fmi.avi.model.BulletinHeading;
import fi.fmi.avi.model.MeteorologicalBulletin;
import fi.fmi.avi.model.sigmet.SIGMETBulletinHeading;
import fi.fmi.avi.model.taf.TAFBulletinHeading;

public class BulletinHeaderDataDesignators extends RegexMatchingLexemeVisitor {
Expand All @@ -30,15 +32,34 @@ public static class Reconstructor extends FactoryBasedReconstructor {
public <T extends AviationWeatherMessageOrCollection> Optional<Lexeme> getAsLexeme(T msg, Class<T> clz, ReconstructorContext<T> ctx)
throws SerializingException {
Optional<Lexeme> retval = Optional.empty();
if (TAFBulletin.class.isAssignableFrom(clz)) {
TAFBulletinHeading heading = ((TAFBulletin) msg).getHeading();
if (MeteorologicalBulletin.class.isAssignableFrom(clz)) {
BulletinHeading heading = ((MeteorologicalBulletin) msg).getHeading();
if (heading != null) {
StringBuilder sb = new StringBuilder("F");
if (heading.isValidLessThan12Hours()) {
sb.append('C');
} else {
sb.append('T');
StringBuilder sb = new StringBuilder();
if (heading instanceof TAFBulletinHeading) {
TAFBulletinHeading tbh = (TAFBulletinHeading) heading;
sb.append('F');
if (tbh.isValidLessThan12Hours()) {
sb.append('C');
} else {
sb.append('T');
}
} else if (heading instanceof SIGMETBulletinHeading) {
sb.append('W');
SIGMETBulletinHeading sbh = (SIGMETBulletinHeading) heading;
switch (sbh.getSIGMETType()) {
case SEVERE_WEATHER:
sb.append('S');
break;
case TROPICAL_CYCLONE:
sb.append('Y');
break;
case VOLCANIC_ASH:
sb.append('V');
break;
}
}

if (heading.getGeographicalDesignator() == null || heading.getGeographicalDesignator().length() != 2) {
throw new SerializingException("Invalid geographical location code '" + heading.getGeographicalDesignator() + "' in TAF bulletin");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
import fi.fmi.avi.converter.tac.lexer.impl.ReconstructorContext;
import fi.fmi.avi.converter.tac.lexer.impl.RegexMatchingLexemeVisitor;
import fi.fmi.avi.model.AviationWeatherMessageOrCollection;
import fi.fmi.avi.model.taf.TAFBulletin;
import fi.fmi.avi.model.taf.TAFBulletinHeading;
import fi.fmi.avi.model.BulletinHeading;
import fi.fmi.avi.model.MeteorologicalBulletin;

public class BulletinHeadingBBBIndicator extends RegexMatchingLexemeVisitor {

Expand All @@ -31,8 +31,8 @@ public static class Reconstructor extends FactoryBasedReconstructor {
public <T extends AviationWeatherMessageOrCollection> Optional<Lexeme> getAsLexeme(T msg, Class<T> clz, ReconstructorContext<T> ctx)
throws SerializingException {
Optional<Lexeme> retval = Optional.empty();
if (TAFBulletin.class.isAssignableFrom(clz)) {
TAFBulletinHeading heading = ((TAFBulletin) msg).getHeading();
if (MeteorologicalBulletin.class.isAssignableFrom(clz)) {
BulletinHeading heading = ((MeteorologicalBulletin) msg).getHeading();
if (heading != null) {
OptionalInt augNumber = heading.getBulletinAugmentationNumber();
if (augNumber.isPresent()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
import fi.fmi.avi.converter.tac.lexer.impl.ReconstructorContext;
import fi.fmi.avi.converter.tac.lexer.impl.RegexMatchingLexemeVisitor;
import fi.fmi.avi.model.AviationWeatherMessageOrCollection;
import fi.fmi.avi.model.taf.TAFBulletin;
import fi.fmi.avi.model.taf.TAFBulletinHeading;
import fi.fmi.avi.model.BulletinHeading;
import fi.fmi.avi.model.MeteorologicalBulletin;

public class BulletinLocationIndicator extends RegexMatchingLexemeVisitor {

Expand All @@ -30,8 +30,8 @@ public static class Reconstructor extends FactoryBasedReconstructor {
public <T extends AviationWeatherMessageOrCollection> Optional<Lexeme> getAsLexeme(T msg, Class<T> clz, ReconstructorContext<T> ctx)
throws SerializingException {
Optional<Lexeme> retval = Optional.empty();
if (TAFBulletin.class.isAssignableFrom(clz)) {
TAFBulletinHeading heading = ((TAFBulletin) msg).getHeading();
if (MeteorologicalBulletin.class.isAssignableFrom(clz)) {
BulletinHeading heading = ((MeteorologicalBulletin) msg).getHeading();
if (heading != null) {
if (heading.getLocationIndicator() == null || heading.getLocationIndicator().length() != 4) {
throw new SerializingException("Invalid location indicator '" + heading.getLocationIndicator() + "' in TAF bulletin");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import fi.fmi.avi.converter.tac.lexer.Lexeme;
import fi.fmi.avi.converter.tac.lexer.impl.FactoryBasedReconstructor;
import fi.fmi.avi.converter.tac.lexer.impl.ReconstructorContext;
import fi.fmi.avi.model.AerodromeWeatherMessage;
import fi.fmi.avi.model.AviationWeatherMessageOrCollection;
import fi.fmi.avi.model.MeteorologicalBulletin;
import fi.fmi.avi.model.PartialOrCompleteTimeInstant;
Expand Down Expand Up @@ -49,7 +50,14 @@ public static class Reconstructor extends FactoryBasedReconstructor {

@Override
public <T extends AviationWeatherMessageOrCollection> Optional<Lexeme> getAsLexeme(T msg, Class<T> clz, final ReconstructorContext<T> ctx) {
PartialOrCompleteTimeInstant time = msg.getIssueTime();
PartialOrCompleteTimeInstant time;
if (AerodromeWeatherMessage.class.isAssignableFrom(clz)) {
time = ((AerodromeWeatherMessage) msg).getIssueTime();
} else if (MeteorologicalBulletin.class.isAssignableFrom(clz)) {
time = ((MeteorologicalBulletin) msg).getIssueTime();
} else {
return Optional.empty();
}
String format;
if (MeteorologicalBulletin.class.isAssignableFrom(clz)) {
format = "%02d%02d%02d";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package fi.fmi.avi.converter.tac;
package fi.fmi.avi.converter.tac.metar;

import fi.fmi.avi.converter.tac.lexer.Lexeme;
import fi.fmi.avi.model.metar.immutable.METARImpl;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package fi.fmi.avi.converter.tac;
package fi.fmi.avi.converter.tac.metar;

import fi.fmi.avi.converter.tac.lexer.Lexeme;
import fi.fmi.avi.model.metar.METAR;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package fi.fmi.avi.converter.tac;
package fi.fmi.avi.converter.tac.metar;

import static fi.fmi.avi.converter.tac.lexer.Lexeme.Identity;
import static fi.fmi.avi.converter.tac.lexer.impl.RecognizingAviMessageTokenLexer.RelationalOperator;
Expand All @@ -15,6 +15,7 @@
import fi.fmi.avi.converter.ConversionIssue;
import fi.fmi.avi.converter.ConversionIssue.Type;
import fi.fmi.avi.converter.ConversionResult;
import fi.fmi.avi.converter.tac.AbstractTACParser;
import fi.fmi.avi.converter.tac.lexer.AviMessageLexer;
import fi.fmi.avi.converter.tac.lexer.Lexeme;
import fi.fmi.avi.converter.tac.lexer.Lexeme.ParsedValueName;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package fi.fmi.avi.converter.tac;
package fi.fmi.avi.converter.tac.metar;

import fi.fmi.avi.converter.ConversionHints;
import fi.fmi.avi.converter.tac.lexer.Lexeme;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package fi.fmi.avi.converter.tac;
package fi.fmi.avi.converter.tac.metar;

import static fi.fmi.avi.converter.tac.lexer.Lexeme.Identity;

Expand All @@ -8,6 +8,7 @@
import fi.fmi.avi.converter.ConversionIssue;
import fi.fmi.avi.converter.ConversionIssue.Type;
import fi.fmi.avi.converter.ConversionResult;
import fi.fmi.avi.converter.tac.AbstractTACSerializer;
import fi.fmi.avi.converter.tac.lexer.Lexeme;
import fi.fmi.avi.converter.tac.lexer.LexemeSequence;
import fi.fmi.avi.converter.tac.lexer.LexemeSequenceBuilder;
Expand Down
Loading

0 comments on commit ffdb29c

Please sign in to comment.