fixed parameterized type transitive lookup in NMEA AbstractSentenceListener

Change-Id: Iba08f867257a8ae707f8c1c8a11dae865da52c25
This commit is contained in:
Axel Uhl committed 2017-07-01 12:58:25 +02:00
1 parent 123777280d
commit bcdc8a260c
2 files changed
+151 -117

No files matched your search

@@ -1,113 +1,113 @@
package com.sap.sailing.nmeaconnector.test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.nmeaconnector.NmeaFactory;
import com.sap.sse.common.Util;
import net.sf.marineapi.nmea.event.AbstractSentenceListener;
import net.sf.marineapi.nmea.event.SentenceListener;
import net.sf.marineapi.nmea.io.SentenceReader;
import net.sf.marineapi.nmea.parser.DataNotAvailableException;
import net.sf.marineapi.nmea.sentence.PositionSentence;
import net.sf.marineapi.nmea.sentence.Sentence;
import net.sf.marineapi.nmea.sentence.TimeSentence;
import net.sf.marineapi.nmea.util.Position;
import net.sf.marineapi.nmea.util.Time;
public class GeneralizedSentenceListenerTest {
private SentenceReader sentenceReader;
private SentenceReaderSupport readerSupport;
@Before
public void setUp() throws FileNotFoundException {
sentenceReader = new SentenceReader(new FileInputStream("resources/LogSS.txt"));
readerSupport = new SentenceReaderSupport(sentenceReader);
}
@Test
public void testRegisterPositionListener() throws InterruptedException {
final List<Position> positions = new ArrayList<>();
SentenceListener positionListener = new AbstractSentenceListener<PositionSentence>() {
@Override
public void sentenceRead(PositionSentence sentence) {
try {
final Position position = sentence.getPosition();
positions.add(position);
} catch (DataNotAvailableException e) {
// this can happen and simply means we don't have a position; so we
// can safely ignore this exception
}
}
};
sentenceReader.addSentenceListener(positionListener);
readerSupport.startReading();
readerSupport.waitUntilAllMessagesHaveBeenRead();
assertFalse(positions.isEmpty());
}
@Test
public void testRegisterTimeListener() throws InterruptedException {
final List<Time> times = new ArrayList<>();
SentenceListener timeListener = new AbstractSentenceListener<TimeSentence>() {
@Override
public void sentenceRead(TimeSentence sentence) {
try {
final Time time = sentence.getTime();
times.add(time);
} catch (DataNotAvailableException e) {
// this can happen and simply means we don't have a position; so we
// can safely ignore this exception
}
}
};
sentenceReader.addSentenceListener(timeListener);
readerSupport.startReading();
readerSupport.waitUntilAllMessagesHaveBeenRead();
assertFalse(times.isEmpty());
}
@Test
public void testSimpleNmeaWindReceiverScenario() throws FileNotFoundException, InterruptedException {
final Iterable<Wind> wind = NmeaFactory.INSTANCE.readWind(new FileInputStream("resources/LogSS.txt"));
assertFalse(Util.isEmpty(wind));
}
private static class MyGenericSentenceListener<A, B extends Sentence> extends AbstractSentenceListener<B> {
private B lastSentence;
@Override
public void sentenceRead(B sentence) {
lastSentence = sentence;
}
public B getLastSentence() {
return lastSentence;
}
}
private static class MyOtherGenericSentenceListener<A> extends MyGenericSentenceListener<A, TimeSentence> {}
private static class MyConcreteSentenceListener extends MyOtherGenericSentenceListener<String> {}
private static class MyConcreteSentenceListenerSubclass extends MyConcreteSentenceListener {}
@Test
public void testComplicatedGenericSentenceListenerSubclass() throws InterruptedException {
MyConcreteSentenceListenerSubclass listener = new MyConcreteSentenceListenerSubclass();
sentenceReader.addSentenceListener(listener);
readerSupport.startReading();
readerSupport.waitUntilAllMessagesHaveBeenRead();
assertNotNull(listener.getLastSentence());
}
package com.sap.sailing.nmeaconnector.test;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.nmeaconnector.NmeaFactory;
import com.sap.sse.common.Util;
import net.sf.marineapi.nmea.event.AbstractSentenceListener;
import net.sf.marineapi.nmea.event.SentenceListener;
import net.sf.marineapi.nmea.io.SentenceReader;
import net.sf.marineapi.nmea.parser.DataNotAvailableException;
import net.sf.marineapi.nmea.sentence.PositionSentence;
import net.sf.marineapi.nmea.sentence.Sentence;
import net.sf.marineapi.nmea.sentence.TimeSentence;
import net.sf.marineapi.nmea.util.Position;
import net.sf.marineapi.nmea.util.Time;
public class GeneralizedSentenceListenerTest {
private SentenceReader sentenceReader;
private SentenceReaderSupport readerSupport;
@Before
public void setUp() throws FileNotFoundException {
sentenceReader = new SentenceReader(new FileInputStream("resources/LogSS.txt"));
readerSupport = new SentenceReaderSupport(sentenceReader);
}
@Test
public void testRegisterPositionListener() throws InterruptedException {
final List<Position> positions = new ArrayList<>();
SentenceListener positionListener = new AbstractSentenceListener<PositionSentence>() {
@Override
public void sentenceRead(PositionSentence sentence) {
try {
final Position position = sentence.getPosition();
positions.add(position);
} catch (DataNotAvailableException e) {
// this can happen and simply means we don't have a position; so we
// can safely ignore this exception
}
}
};
sentenceReader.addSentenceListener(positionListener);
readerSupport.startReading();
readerSupport.waitUntilAllMessagesHaveBeenRead();
assertFalse(positions.isEmpty());
}
@Test
public void testRegisterTimeListener() throws InterruptedException {
final List<Time> times = new ArrayList<>();
SentenceListener timeListener = new AbstractSentenceListener<TimeSentence>() {
@Override
public void sentenceRead(TimeSentence sentence) {
try {
final Time time = sentence.getTime();
times.add(time);
} catch (DataNotAvailableException e) {
// this can happen and simply means we don't have a position; so we
// can safely ignore this exception
}
}
};
sentenceReader.addSentenceListener(timeListener);
readerSupport.startReading();
readerSupport.waitUntilAllMessagesHaveBeenRead();
assertFalse(times.isEmpty());
}
@Test
public void testSimpleNmeaWindReceiverScenario() throws FileNotFoundException, InterruptedException {
final Iterable<Wind> wind = NmeaFactory.INSTANCE.readWind(new FileInputStream("resources/LogSS.txt"));
assertFalse(Util.isEmpty(wind));
}
private static class MyGenericSentenceListener<A, B extends Sentence> extends AbstractSentenceListener<B> {
private B lastSentence;
@Override
public void sentenceRead(B sentence) {
lastSentence = sentence;
}
public B getLastSentence() {
return lastSentence;
}
}
private static class MyOtherGenericSentenceListener<A> extends MyGenericSentenceListener<A, TimeSentence> {}
private static class MyConcreteSentenceListener extends MyOtherGenericSentenceListener<String> {}
private static class MyConcreteSentenceListenerSubclass extends MyConcreteSentenceListener {}
@Test
public void testComplicatedGenericSentenceListenerSubclass() throws InterruptedException {
MyConcreteSentenceListenerSubclass listener = new MyConcreteSentenceListenerSubclass();
sentenceReader.addSentenceListener(listener);
readerSupport.startReading();
readerSupport.waitUntilAllMessagesHaveBeenRead();
assertNotNull(listener.getLastSentence());
}
}
@@ -22,7 +22,10 @@ package net.sf.marineapi.nmea.event;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import net.sf.marineapi.nmea.sentence.Sentence;
@@ -48,13 +51,44 @@ public abstract class AbstractSentenceListener<T extends Sentence>
private final Type expectedType;
public AbstractSentenceListener() {
ParameterizedType superClass =
(ParameterizedType) getClass().getGenericSuperclass();
Type[] superClassTypeArgs = superClass.getActualTypeArguments();
this.expectedType = superClassTypeArgs[0];
// if during super class traversal we found a parameterized type we remember the
// concrete parameter types that were used during the type instantiation keyed by
// the type parameters
final Map<TypeVariable<?>, Class<?>> concreteTypeForTypeVariable = new HashMap<>();
ParameterizedType superClass = null;
Class<?> c = getClass();
while (superClass == null && c != null) {
if (c.getGenericSuperclass() instanceof ParameterizedType) {
final ParameterizedType pt = (ParameterizedType) c.getGenericSuperclass();
if (pt.getRawType() == AbstractSentenceListener.class) {
superClass = pt;
} else {
c = (Class<?>) pt.getRawType();
int i=0;
for (TypeVariable<?> tv : c.getTypeParameters()) {
concreteTypeForTypeVariable.put(tv, resolve(pt.getActualTypeArguments()[i++], concreteTypeForTypeVariable));
}
}
} else {
c = (c.getGenericSuperclass() instanceof Class<?>) ? (Class<?>) c.getGenericSuperclass() : null;
}
}
assert superClass == null || superClass.getRawType() == AbstractSentenceListener.class;
// we now assume that this class has exactly one type parameter [0] that is the expected type:
this.expectedType = resolve(superClass.getActualTypeArguments()[0], concreteTypeForTypeVariable);
}
/**
private Class<?> resolve(Type type, Map<TypeVariable<?>, Class<?>> concreteTypeForTypeVariable) {
if (type instanceof Class<?>) {
return (Class<?>) type;
} else if (type instanceof TypeVariable<?>) {
return concreteTypeForTypeVariable.get((TypeVariable<?>) type);
} else {
return null;
}
}
/**
* Empty implementation.
* @see net.sf.marineapi.nmea.event.SentenceListener#readingPaused()
*/