[Request ID], E, <Error Text>
31 | *
32 | * @param csv the CSV
33 | * @param requestID the request ID
34 | *
35 | * @return true if the CSV represents an {@link FeedMessageType#ERROR} message
36 | */
37 | public boolean isRequestErrorMessage(String[] csv, String requestID) {
38 | return valueEquals(csv, 0, requestID) && valueEquals(csv, 1, FeedMessageType.ERROR.value());
39 | }
40 |
41 | /**
42 | * Check if a message matches the following format:
43 | * [Request ID], {@link FeedSpecialMessage#END_OF_MESSAGE}
45 | *
46 | * @param csv the CSV
47 | * @param requestID the request ID
48 | *
49 | * @return true if the message represents an {@link FeedSpecialMessage#END_OF_MESSAGE} message
50 | */
51 | public boolean isRequestEndOfMessage(String[] csv, String requestID) {
52 | return valueEquals(csv, 0, requestID) && valueEquals(csv, 1, FeedSpecialMessage.END_OF_MESSAGE.value());
53 | }
54 |
55 | /**
56 | * Check if a message matches the following format:
57 | * [Request ID], E, {@link FeedSpecialMessage#NO_DATA_ERROR}
59 | *
60 | * @param csv the CSV
61 | *
62 | * @return true if the message represents a {@link FeedSpecialMessage#NO_DATA_ERROR} message
63 | */
64 | public boolean isRequestNoDataError(String[] csv) {
65 | return valueEquals(csv, 2, FeedSpecialMessage.NO_DATA_ERROR.value());
66 | }
67 |
68 | /**
69 | * Check if a message matches the following format:
70 | * [Request ID], E, {@link FeedSpecialMessage#SYNTAX_ERROR}
72 | *
73 | * @param csv the CSV
74 | *
75 | * @return true if the message represents a {@link FeedSpecialMessage#SYNTAX_ERROR} message
76 | */
77 | public boolean isRequestSyntaxError(String[] csv) {
78 | return valueEquals(csv, 2, FeedSpecialMessage.SYNTAX_ERROR.value());
79 | }
80 |
81 | /**
82 | * Gets a new Request ID. This method is thread safe.
83 | *
84 | * @return a new request ID
85 | */
86 | public String getNewRequestID() {
87 | int maxRequestID;
88 | synchronized (requestIDs) {
89 | maxRequestID = requestIDs.stream().max(Integer::compareTo).orElse(0) + 1;
90 | }
91 | requestIDs.add(maxRequestID);
92 | return String.valueOf(maxRequestID);
93 | }
94 |
95 | /**
96 | * Removes a Request ID. This method is thread safe.
97 | *
98 | * @param requestID the request ID
99 | */
100 | public void removeRequestID(String requestID) {
101 | requestIDs.remove(Integer.parseInt(requestID));
102 | }
103 |
104 | /**
105 | * Clears all Request IDs so that {@link #getNewRequestID()} starts at 0.
106 | */
107 | public void clearRequestIDs() {
108 | requestIDs.clear();
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/feed/exception/IQFeedException.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.feed.exception;
2 |
3 | /**
4 | * {@link IQFeedException} represents IQFeed (checked) {@link Exception}s resembling a recoverable error due to an issue
5 | * outside the control of this program.
6 | */
7 | public class IQFeedException extends Exception {
8 |
9 | /**
10 | * Instantiates a new {@link IQFeedException}.
11 | */
12 | public IQFeedException() {}
13 |
14 | /**
15 | * Instantiates a new {@link IQFeedException}.
16 | *
17 | * @param message the message
18 | */
19 | public IQFeedException(String message) {
20 | super(message);
21 | }
22 |
23 | /**
24 | * Instantiates a new {@link IQFeedException}.
25 | *
26 | * @param message the message
27 | * @param cause the cause
28 | */
29 | public IQFeedException(String message, Throwable cause) {
30 | super(message, cause);
31 | }
32 |
33 | /**
34 | * Instantiates a new {@link IQFeedException}
35 | *
36 | * @param cause the cause
37 | */
38 | public IQFeedException(Throwable cause) {
39 | super(cause);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/feed/exception/IQFeedRuntimeException.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.feed.exception;
2 |
3 | /**
4 | * {@link IQFeedRuntimeException} represents (unchecked) IQFeed {@link RuntimeException}s resembling an unrecoverable
5 | * error due to faulty program logic.
6 | */
7 | public class IQFeedRuntimeException extends RuntimeException {
8 |
9 | /**
10 | * Instantiates a new {@link IQFeedRuntimeException}.
11 | */
12 | public IQFeedRuntimeException() {}
13 |
14 | /**
15 | * Instantiates a new {@link IQFeedRuntimeException}.
16 | *
17 | * @param message the message
18 | */
19 | public IQFeedRuntimeException(String message) {
20 | super(message);
21 | }
22 |
23 | /**
24 | * Instantiates a new {@link IQFeedRuntimeException}.
25 | *
26 | * @param message the message
27 | * @param cause the cause
28 | */
29 | public IQFeedRuntimeException(String message, Throwable cause) {
30 | super(message, cause);
31 | }
32 |
33 | /**
34 | * Instantiates a new {@link IQFeedRuntimeException}
35 | *
36 | * @param cause the cause
37 | */
38 | public IQFeedRuntimeException(Throwable cause) {
39 | super(cause);
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/feed/exception/NoDataException.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.feed.exception;
2 |
3 | /**
4 | * {@link NoDataException} is an {@link IQFeedException} for when no data is present.
5 | */
6 | public class NoDataException extends IQFeedException {
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/feed/exception/SyntaxException.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.feed.exception;
2 |
3 | /**
4 | * {@link SyntaxException} is an {@link IQFeedRuntimeException} for when a syntax error in a request occurs.
5 | */
6 | public class SyntaxException extends IQFeedRuntimeException {
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/feed/lookup/AbstractLookupFeed.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.feed.lookup;
2 |
3 | import com.google.common.base.Splitter;
4 | import net.jacobpeterson.iqfeed4j.feed.AbstractFeed;
5 | import net.jacobpeterson.iqfeed4j.feed.RequestIDFeedHelper;
6 | import net.jacobpeterson.iqfeed4j.feed.exception.IQFeedRuntimeException;
7 | import net.jacobpeterson.iqfeed4j.feed.exception.NoDataException;
8 | import net.jacobpeterson.iqfeed4j.feed.exception.SyntaxException;
9 | import net.jacobpeterson.iqfeed4j.feed.message.MultiMessageListener;
10 | import net.jacobpeterson.iqfeed4j.model.feed.common.enums.FeedMessageType;
11 | import net.jacobpeterson.iqfeed4j.model.feed.common.enums.FeedSpecialMessage;
12 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.index.AbstractIndexCSVMapper;
13 | import org.slf4j.Logger;
14 |
15 | import java.util.Arrays;
16 | import java.util.Map;
17 |
18 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueEquals;
19 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace;
20 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valuePresent;
21 |
22 | /**
23 | * {@link AbstractLookupFeed} represents a {@link AbstractFeed} for Lookup data.
24 | */
25 | public abstract class AbstractLookupFeed extends AbstractFeed {
26 |
27 | private static final String FEED_NAME_SUFFIX = " Lookup Feed";
28 |
29 | protected final RequestIDFeedHelper requestIDFeedHelper;
30 |
31 | /**
32 | * Instantiates a new {@link AbstractLookupFeed}.
33 | *
34 | * @param logger the {@link Logger}
35 | * @param lookupFeedName the lookup feed name
36 | * @param hostname the host name
37 | * @param port the port
38 | * @param csvSplitter the CSV {@link Splitter}
39 | */
40 | public AbstractLookupFeed(Logger logger, String lookupFeedName, String hostname, int port, Splitter csvSplitter) {
41 | super(logger, lookupFeedName + FEED_NAME_SUFFIX, hostname, port, csvSplitter, true, true);
42 |
43 | requestIDFeedHelper = new RequestIDFeedHelper();
44 | }
45 |
46 | /**
47 | * Handles a standard message for a {@link MultiMessageListener} by: checking for request error messages, handling
48 | * {@link FeedSpecialMessage#END_OF_MESSAGE} messages, and performing
49 | * {@link AbstractIndexCSVMapper#map(String[], int)} on the csv
to call
50 | * {@link MultiMessageListener#onMessageReceived(Object)}.
51 | *
52 | * @param requestID
was a key inside listenersOfRequestIDs
, false otherwise
61 | */
62 | protected null
) and to block the current thread until the resulting
9 | * message is available.
10 | * S,SERVER
11 | * CONNECTED>LF<
or S,SERVER DISCONNECTED>LF<
messages.
12 | */
13 | public abstract class AbstractServerConnectionFeed extends AbstractFeed {
14 |
15 | protected ServerConnectionStatus serverConnectionStatus;
16 |
17 | /**
18 | * Instantiates a new {@link AbstractServerConnectionFeed}.
19 | *
20 | * @param logger the {@link Logger}
21 | * @param feedName the feed name
22 | * @param hostname the hostname
23 | * @param port the port
24 | * @param csvSplitter the CSV {@link Splitter}
25 | * @param validateProtocolVersion true to send and validate the {@link #CURRENTLY_SUPPORTED_PROTOCOL_VERSION}
26 | * @param sendClientName true to send the client feedName
27 | */
28 | public AbstractServerConnectionFeed(Logger logger, String feedName, String hostname, int port, Splitter csvSplitter,
29 | boolean validateProtocolVersion, boolean sendClientName) {
30 | super(logger, feedName, hostname, port, csvSplitter, validateProtocolVersion, sendClientName);
31 | }
32 |
33 | /**
34 | * Checks and sets {@link #serverConnectionStatus} if a {@link ServerConnectionStatus} is present. Note this will
35 | * not check if the CSV message is a {@link FeedMessageType#SYSTEM} message so that should be done before calling
36 | * this method.
37 | *
38 | * @param systemMessageType the {@link FeedMessageType#SYSTEM} message type
39 | *
40 | * @return true if the message was a {@link ServerConnectionStatus} message, false otherwise
41 | */
42 | protected boolean checkServerConnectionStatusMessage(String systemMessageType) {
43 | try {
44 | serverConnectionStatus = ServerConnectionStatus.fromValue(systemMessageType);
45 |
46 | if (serverConnectionStatus == ServerConnectionStatus.SERVER_CONNECTED) {
47 | logger.info("Server is connected.");
48 | } else {
49 | logger.warn("Server is disconnected!");
50 | }
51 |
52 | return true;
53 | } catch (IllegalArgumentException ignored) {
54 | return false;
55 | }
56 | }
57 |
58 | /**
59 | * Gets {@link #serverConnectionStatus}.
60 | *
61 | * @return the {@link ServerConnectionStatus}
62 | */
63 | public ServerConnectionStatus getServerConnectionStatus() {
64 | return serverConnectionStatus;
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/feed/streaming/StreamingCSVMappers.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.feed.streaming;
2 |
3 | import net.jacobpeterson.iqfeed4j.model.feed.streaming.common.FeedStatistics;
4 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper;
5 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.index.IndexCSVMapper;
6 |
7 | import static net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper.DateTimeConverters.MONTH3_DAY_TIME_AM_PM;
8 | import static net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper.PrimitiveConvertors.DOUBLE;
9 | import static net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper.PrimitiveConvertors.INTEGER;
10 | import static net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper.PrimitiveConvertors.STRING;
11 |
12 | /**
13 | * {@link StreamingCSVMappers} contains {@link AbstractCSVMapper}s for streaming feeds.
14 | */
15 | public final class StreamingCSVMappers {
16 |
17 | /**
18 | * A {@link IndexCSVMapper} for {@link FeedStatistics}.
19 | */
20 | public static final IndexCSVMapperchar
type.
5 | */
6 | public final class CharUtil {
7 |
8 | /**
9 | * Finds the last index of a non-number character in a {@link String}, or -1
if none was found.
10 | *
11 | * @param string the string to search
12 | * @param qualifyDecimalPoint true to qualify a decimal point as a number
13 | * @param qualifyPlusOrMinus true to qualify a '+' or a '-' character
14 | *
15 | * @return the index
16 | */
17 | public static int lastIndexOfNonNumber(String string, boolean qualifyDecimalPoint, boolean qualifyPlusOrMinus) {
18 | if (string.isEmpty()) {
19 | return -1;
20 | }
21 |
22 | // Start at end of string
23 | for (int index = string.length() - 1; index >= 0; index--) {
24 | char ch = string.charAt(index);
25 | if (!(ch == '0' || ch == '1' || ch == '2' || ch == '3' || ch == '4' || ch == '5' || ch == '6' ||
26 | ch == '7' || ch == '8' || ch == '9' || (qualifyDecimalPoint && ch == '.') ||
27 | (qualifyPlusOrMinus && (ch == '+' || ch == '-')))) {
28 | return index;
29 | }
30 | }
31 |
32 | return -1;
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/util/csv/CSVUtil.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.util.csv;
2 |
3 | /**
4 | * {@link CSVUtil} defines utility methods for CSVs.
5 | */
6 | public final class CSVUtil {
7 |
8 | /**
9 | * Tests if the csv
array has index
.
10 | *
11 | * @param csv the CSV
12 | * @param index the index
13 | *
14 | * @return a boolean
15 | */
16 | public static boolean valueExists(String[] csv, int index) {
17 | return index < csv.length;
18 | }
19 |
20 | /**
21 | * Tests if the csv
array has index
and if the value at index
is not empty.
22 | *
23 | * @param csv the CSV
24 | * @param index the index
25 | *
26 | * @return a boolean
27 | */
28 | public static boolean valuePresent(String[] csv, int index) {
29 | return index < csv.length && !csv[index].isEmpty();
30 | }
31 |
32 | /**
33 | * Tests if the csv
array has index
and if the value at index
is not empty
34 | * and not whitespace.
35 | *
36 | * @param csv the CSV
37 | * @param index the index
38 | *
39 | * @return a boolean
40 | */
41 | public static boolean valueNotWhitespace(String[] csv, int index) {
42 | return index < csv.length && !csv[index].isEmpty() && !csv[index].trim().isEmpty();
43 | }
44 |
45 | /**
46 | * Tests if the csv
array has match
at index
.
47 | *
48 | * @param csv the CSV
49 | * @param index the index
50 | * @param match the string to check
51 | *
52 | * @return the boolean
53 | */
54 | public static boolean valueEquals(String[] csv, int index, String match) {
55 | return valueExists(csv, index) && csv[index].equals(match);
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/AbstractCSVMapper.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper;
2 |
3 | import java.time.LocalDate;
4 | import java.time.LocalDateTime;
5 | import java.time.LocalTime;
6 | import java.time.format.DateTimeFormatter;
7 | import java.time.format.DateTimeFormatterBuilder;
8 | import java.time.temporal.ChronoField;
9 | import java.util.Locale;
10 | import java.util.function.Function;
11 | import java.util.function.Supplier;
12 |
13 | /**
14 | * {@link AbstractCSVMapper} maps CSV {@link String} values to various types/objects.
15 | *
16 | * @param HHmmss
*/
43 | public static final DateTimeFormatter TIME = DateTimeFormatter.ofPattern("HHmmss");
44 |
45 | /** Format of: yyyyMMdd
*/
46 | public static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("yyyyMMdd");
47 |
48 | /** Format of: yyyyMMdd HHmmss
*/
49 | public static final DateTimeFormatter DATE_SPACE_TIME =
50 | DateTimeFormatter.ofPattern("yyyyMMdd HHmmss");
51 |
52 | /** Format of: MMM dd h:mma
*/
53 | public static final DateTimeFormatter MONTH3_DAY_TIME_AM_PM =
54 | new DateTimeFormatterBuilder()
55 | .appendPattern("MMM dd h:mma")
56 | .parseDefaulting(ChronoField.YEAR, LocalDate.now().getYear())
57 | .toFormatter(Locale.ENGLISH);
58 |
59 | /** Format of: yyyy-MM-dd HH:mm:ss.nnnnnn
*/
60 | public static final DateTimeFormatter DASHED_DATE_SPACE_TIME_FRACTIONAL =
61 | new DateTimeFormatterBuilder()
62 | .parseCaseInsensitive()
63 | .append(DateTimeFormatter.ISO_LOCAL_DATE)
64 | .appendLiteral(' ')
65 | .append(DateTimeFormatter.ISO_LOCAL_TIME) // Optionally includes micro/nanoseconds
66 | .toFormatter(Locale.ENGLISH);
67 |
68 | /** Format of: yyyy-MM-dd
*/
69 | public static final DateTimeFormatter DASHED_DATE = DateTimeFormatter.ISO_LOCAL_DATE;
70 |
71 | /** Format of: MM/dd/yyyy
*/
72 | public static final DateTimeFormatter SLASHED_DATE = DateTimeFormatter.ofPattern("MM/dd/yyyy");
73 |
74 | /** Format of: HH:mm:ss[.nnnnnn]
*/
75 | public static final DateTimeFormatter COLON_TIME = DateTimeFormatter.ISO_LOCAL_TIME;
76 |
77 | /** Format of: yyyyMMdd HH:mm:ss
*/
78 | public static final DateTimeFormatter DATE_SPACE_COLON_TIME = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss");
79 |
80 | /** Format of: [Hmmss][HHmmss]
*/
81 | public static final DateTimeFormatter OPTIONAL_1_OR_2_DIGIT_HOUR_TIME =
82 | DateTimeFormatter.ofPattern("[Hmmss][HHmmss]");
83 | }
84 |
85 | /**
86 | * {@link DateTimeConverters} contains common {@link Function}s with the argument being the CSV {@link String} value
87 | * and the return value being the converted CSV date/time value.
88 | */
89 | public static class DateTimeConverters {
90 |
91 | /** Convertor using {@link DateTimeFormatters#TIME} */
92 | public static final Functionnull
on an {@link Exception}. */
135 | public static final Function the type of the POJO field
11 | */
12 | public final class CSVMapping the type of the POJO field
34 | * @param fieldSetter see {@link CSVMapping} constructor doc
35 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc
36 | */
37 | public void addMapping(BiConsumer the type of the POJO field
72 | * @param csvIndex the CSV index
73 | * @param fieldSetter see {@link CSVMapping} constructor doc
74 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc
75 | */
76 | public void setMapping(int csvIndex, BiConsumer the type parameter
84 | * @param csvIndex the CSV index
85 | * @param csvValueConsumer see {@link CSVMapping} constructor doc
86 | */
87 | public void setMapping(int csvIndex, BiConsumer the type of the POJO field
40 | * @param fieldSetter see {@link CSVMapping} constructor doc
41 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc
42 | */
43 | public void addMapping(BiConsumer the type of the POJO field
52 | * @param csvIndex the CSV index
53 | * @param fieldSetter see {@link CSVMapping} constructor doc
54 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc
55 | */
56 | @SuppressWarnings("OptionalGetWithoutIsPresent") // Optional will always be present here
57 | public void setMapping(int csvIndex, BiConsumer the type of the POJO field
76 | * @param fieldSetter see {@link CSVMapping} constructor doc
77 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc
78 | */
79 | public void setTrailingMapping(BiConsumer the type of the POJO field
46 | * @param fieldSetter see {@link CSVMapping} constructor doc
47 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc
48 | */
49 | public void addMapping(BiConsumer the type of the POJO field
58 | * @param csvIndex the CSV index
59 | * @param fieldSetter see {@link CSVMapping} constructor doc
60 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc
61 | */
62 | public void setMapping(int csvIndex, BiConsumer the type of the POJO field
37 | * @param csvIndexName the CSV index name
38 | * @param fieldSetter see {@link CSVMapping} constructor doc
39 | * @param stringToFieldConverter see {@link CSVMapping} constructor doc
40 | */
41 | public void setMapping(String csvIndexName, BiConsumer
34 | * Note: this will map to a type using the {@link #stringToTypeConverter}.
35 | */
36 | @Override
37 | public T map(String[] csv, int offset) {
38 | if (!valueNotWhitespace(csv, csvIndex + offset)) { // Don't map empty CSV values
39 | return null;
40 | }
41 |
42 | // apply() could throw a variety of exceptions
43 | try {
44 | return stringToTypeConverter.apply(csv[csvIndex + offset]);
45 | } catch (Exception exception) {
46 | throw new CSVMappingException(csvIndex, offset, exception);
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/index/IndexCSVMapper.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.index;
2 |
3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMapping;
4 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException;
5 |
6 | import java.util.HashMap;
7 | import java.util.function.BiConsumer;
8 | import java.util.function.Function;
9 | import java.util.function.Supplier;
10 |
11 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace;
12 |
13 | /**
14 | */
15 | public class IndexCSVMapper
112 | * Note this will map with the mappings added via {@link #setMapping(int, BiConsumer, Function)}.
113 | */
114 | @Override
115 | public T map(String[] csv, int offset) {
116 | T instance = pojoInstantiator.get();
117 |
118 | // Loop through all added 'CSVMappings' and apply them
119 | for (int csvIndex : csvMappingsOfCSVIndices.keySet()) {
120 | if (!valueNotWhitespace(csv, csvIndex + offset)) { // Don't map empty CSV values
121 | continue;
122 | }
123 |
124 | // apply() could throw a variety of exceptions
125 | try {
126 | csvMappingsOfCSVIndices.get(csvIndex).apply(instance, csv[csvIndex + offset]);
127 | } catch (Exception exception) {
128 | throw new CSVMappingException(csvIndex, offset, exception);
129 | }
130 | }
131 |
132 | return instance;
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/index/TrailingIndexCSVMapper.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.index;
2 |
3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMapping;
4 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException;
5 |
6 | import java.util.HashMap;
7 | import java.util.function.BiConsumer;
8 | import java.util.function.Function;
9 | import java.util.function.Supplier;
10 |
11 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueExists;
12 | import static net.jacobpeterson.iqfeed4j.util.csv.CSVUtil.valueNotWhitespace;
13 |
14 | /**
15 | * {@link TrailingIndexCSVMapper} mappings are based off of predefined CSV indices, but with the ability to add a CSV
16 | * mapping that accumulates all CSV values after a certain index into one mapping.
17 | */
18 | public class TrailingIndexCSVMapper
85 | * Note this will map with the mappings added via {@link #setMapping(int, BiConsumer, Function)} and
86 | * {@link #setTrailingMapping(BiConsumer, Function)}.
87 | */
88 | @Override
89 | public T map(String[] csv, int offset) {
90 | T instance = pojoInstantiator.get();
91 |
92 | // Loop through all added 'CSVMapping's and apply them
93 | for (int csvIndex : csvMappingsOfCSVIndices.keySet()) {
94 | if (!valueNotWhitespace(csv, csvIndex + offset)) { // Don't map empty CSV values
95 | continue;
96 | }
97 |
98 | // apply() could throw a variety of exceptions
99 | try {
100 | csvMappingsOfCSVIndices.get(csvIndex).apply(instance, csv[csvIndex + offset]);
101 | } catch (Exception exception) {
102 | throw new CSVMappingException(csvIndex, offset, exception);
103 | }
104 | }
105 |
106 | // Now apply 'trailingCSVMapping' on trailing CSV values
107 | if (valueExists(csv, trailingCSVIndex + offset)) {
108 | StringBuilder trailingCSVBuilder = new StringBuilder();
109 | for (int csvIndex = trailingCSVIndex + offset; csvIndex < csv.length; csvIndex++) {
110 | trailingCSVBuilder.append(csv[csvIndex]);
111 |
112 | if (csvIndex < csv.length - 1) { // Don't append comma on last CSV value
113 | trailingCSVBuilder.append(",");
114 | }
115 | }
116 |
117 | String trailingCSVString = trailingCSVBuilder.toString();
118 | // apply() could throw a variety of exceptions
119 | try {
120 | trailingCSVMapping.apply(instance, trailingCSVString);
121 | } catch (Exception exception) {
122 | throw new CSVMappingException(trailingCSVIndex, offset, exception);
123 | }
124 | }
125 |
126 | return instance;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/util/csv/mapper/list/AbstractListCSVMapper.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.util.csv.mapper.list;
2 |
3 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.AbstractCSVMapper;
4 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMapping;
5 | import net.jacobpeterson.iqfeed4j.util.csv.mapper.CSVMappingException;
6 |
7 | import java.util.List;
8 | import java.util.function.Supplier;
9 |
10 | /**
11 | * {@link AbstractListCSVMapper} maps a CSV list.
12 | */
13 | public abstract class AbstractListCSVMappercsv
list to a {@link List}.
26 | *
27 | * @param csv the CSV
28 | * @param offset offset to add to CSV indices when applying {@link CSVMapping}s
29 | *
30 | * @return a new {@link List} of mapped POJOs
31 | *
32 | * @throws CSVMappingException thrown for {@link CSVMappingException}s
33 | */
34 | public abstract List
36 | * Note: this will map to a list with the {@link #stringToTypeConverter} applied.
37 | */
38 | @Override
39 | public Listnull
to not check)
31 | */
32 | public ListCSVMapper(Supplier extends List
43 | * Note: this will map to a list of POJOs with the {@link #csvValueConsumer} applied.
44 | */
45 | @Override
46 | public List
77 | * Note: this will map to a list of POJOs using the mappings added via
78 | * {@link #setMapping(int, BiConsumer, Function)}. This will iterate through the list at {@link #nestedListLength}
79 | * length.
80 | */
81 | @Override
82 | public ListcsvIndexName
s that were added
61 | * via {@link #setMapping(String, BiConsumer, Function)} and the values being which
62 | * CSV indices they correspond to in the given csv
.
63 | *
64 | * @return a new POJO
65 | *
66 | * @throws CSVMappingException thrown for {@link CSVMappingException}s
67 | */
68 | public T map(String[] csv, int offset, Mapnull
if none were found
22 | *
23 | * @see Stackoverflow
24 | */
25 | public static propertyFile
, then the
38 | * defaultPropertyFile
is searched.
39 | *
40 | * @param propertyFile the property file
41 | * @param defaultPropertyFile the default property file
42 | * @param key the key
43 | *
44 | * @return the string
45 | */
46 | public static String getProperty(String propertyFile, String defaultPropertyFile, String key) {
47 | return getProperty(propertyFile, defaultPropertyFile, key, null);
48 | }
49 |
50 | /**
51 | * Gets a string property from a property file. Will try to IO load the properties in the property file if not
52 | * cached already. If the desired property is not found in the propertyFile
, then the
53 | * defaultPropertyFile
is searched, and if it's not there, then defaultValue
is returned.
54 | *
55 | * @param propertyFile the property file
56 | * @param defaultPropertyFile the default property file
57 | * @param key the key
58 | * @param defaultValue the default value (if the desired property wasn't found, then this is returned)
59 | *
60 | * @return the string
61 | */
62 | public static String getProperty(String propertyFile, String defaultPropertyFile, String key, String defaultValue) {
63 | Properties properties;
64 |
65 | if (!CACHED_PROPERTIES.containsKey(propertyFile)) {
66 | properties = loadPropertyFile(propertyFile, defaultPropertyFile);
67 | CACHED_PROPERTIES.put(propertyFile, properties);
68 | } else {
69 | properties = CACHED_PROPERTIES.get(propertyFile);
70 | }
71 |
72 | return properties == null ? defaultValue : properties.getProperty(key, defaultValue);
73 | }
74 |
75 | /**
76 | * Loads property file {@link Properties}.
77 | *
78 | * @param propertyFile the property file name
79 | * @param defaultPropertyFile the default property file name
80 | *
81 | * @return the properties
82 | */
83 | public synchronized static Properties loadPropertyFile(String propertyFile, String defaultPropertyFile) {
84 | ClassLoader classLoader = PropertyUtil.class.getClassLoader();
85 | if (classLoader == null) {
86 | classLoader = ClassLoader.getSystemClassLoader();
87 | }
88 |
89 | // Load the default property file if exists
90 | Properties defaultProperties = null;
91 | InputStream defaultPropertyStream = classLoader.getResourceAsStream(defaultPropertyFile);
92 |
93 | if (defaultPropertyStream != null) {
94 | defaultProperties = new Properties();
95 |
96 | // Load the properties
97 | try {
98 | defaultProperties.load(defaultPropertyStream);
99 | LOGGER.debug("Loaded default properties file: {}", defaultPropertyFile);
100 | } catch (IOException exception) {
101 | LOGGER.error("Could not load default property file: {}\n{}", defaultPropertyFile, exception);
102 | }
103 |
104 | // Close the InputStream
105 | try {
106 | defaultPropertyStream.close();
107 | } catch (IOException exception) {
108 | LOGGER.error("Could not close default property file stream: {}\n{}", defaultPropertyFile, exception);
109 | }
110 |
111 | } else {
112 | LOGGER.warn("No default property file found for: {}", propertyFile);
113 | }
114 |
115 | // Load the property file
116 | Properties properties = null;
117 | InputStream propertyStream = classLoader.getResourceAsStream(propertyFile);
118 |
119 | if (propertyStream != null) {
120 | // Add default properties if they were found
121 | properties = defaultProperties == null ? new Properties() : new Properties(defaultProperties);
122 |
123 | // Load the properties
124 | try {
125 | properties.load(propertyStream);
126 | LOGGER.info("Loaded properties file: {}", propertyFile);
127 | } catch (IOException exception) {
128 | LOGGER.error("Could not load property file: {}\n{}", propertyFile, exception);
129 | }
130 |
131 | // Close the InputStream
132 | try {
133 | propertyStream.close();
134 | } catch (IOException exception) {
135 | LOGGER.error("Could not close property file stream: {}\n{}", propertyFile, exception);
136 | }
137 | } else {
138 | LOGGER.debug("Could not find property file: {}", propertyFile);
139 |
140 | if (defaultProperties != null) {
141 | LOGGER.info("Using default properties for: {}", propertyFile);
142 | properties = defaultProperties;
143 | }
144 | }
145 |
146 | return properties;
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/main/java/net/jacobpeterson/iqfeed4j/util/split/SplitUtil.java:
--------------------------------------------------------------------------------
1 | package net.jacobpeterson.iqfeed4j.util.split;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.regex.Matcher;
6 | import java.util.regex.Pattern;
7 |
8 | /**
9 | * {@link SplitUtil} contains utility functions for splitting {@link String}s.
10 | */
11 | public class SplitUtil {
12 |
13 | private static final Pattern QUOTE_ESCAPED_SPACE_PATTERN = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'");
14 |
15 | /**
16 | * Splits a {@link String} using the space key as a delimiter, but ignores spaces surrounded in quotes.
17 | *
18 | * @param toSplit the {@link String} to split
19 | *
20 | * @return a {@link List} of {@link String}s
21 | *
22 | * @see "Regex for splitting a string using space" reference
23 | */
24 | public static Listnull
if no digits were converted
18 | */
19 | public static List