1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.core5.http;
29
30 import org.apache.hc.core5.annotation.Internal;
31 import org.apache.hc.core5.util.TextUtils;
32 import org.apache.hc.core5.util.Tokenizer;
33
34 @Internal
35 public class ProtocolVersionParser {
36
37 public final static ProtocolVersionParser INSTANCE = new ProtocolVersionParser();
38
39 private static final char SLASH = '/';
40 private static final char FULL_STOP = '.';
41 private static final Tokenizer.Delimiter PROTO_DELIMITER = Tokenizer.delimiters(SLASH);
42 private static final Tokenizer.Delimiter FULL_STOP_OR_BLANK = Tokenizer.delimiters(FULL_STOP, ' ', '\t');
43 private static final Tokenizer.Delimiter BLANK = Tokenizer.delimiters(' ', '\t');
44 private final Tokenizer tokenizer;
45
46 public ProtocolVersionParser() {
47 this.tokenizer = Tokenizer.INSTANCE;
48 }
49
50 @Internal
51 @FunctionalInterface
52 public interface Factory {
53
54 ProtocolVersion create(int major, int minor);
55
56 }
57
58 public ProtocolVersion parse(
59 final String protocol,
60 final Factory factory,
61 final CharSequence buffer,
62 final Tokenizer.Cursor cursor,
63 final Tokenizer.Delimiter delimiterPredicate) throws ParseException {
64 final int lowerBound = cursor.getLowerBound();
65 final int upperBound = cursor.getUpperBound();
66 final String token1 = tokenizer.parseToken(buffer, cursor,
67 delimiterPredicate != null ? (ch) -> delimiterPredicate.test(ch) || FULL_STOP_OR_BLANK.test(ch) : FULL_STOP_OR_BLANK);
68 final int major;
69 try {
70 major = Integer.parseInt(token1);
71 } catch (final NumberFormatException e) {
72 throw new ParseException("Invalid " + protocol + " major version number",
73 buffer, lowerBound, upperBound, cursor.getPos());
74 }
75 if (cursor.atEnd()) {
76 return factory != null ? factory.create(major, major) : new ProtocolVersion(protocol, major, 0);
77 }
78 if (buffer.charAt(cursor.getPos()) != FULL_STOP) {
79 return factory != null ? factory.create(major, major) : new ProtocolVersion(protocol, major, 0);
80 } else {
81 cursor.updatePos(cursor.getPos() + 1);
82 final String token2 = tokenizer.parseToken(buffer, cursor,
83 delimiterPredicate != null ? (ch) -> delimiterPredicate.test(ch) || BLANK.test(ch) : BLANK);
84 final int minor;
85 try {
86 minor = Integer.parseInt(token2);
87 } catch (final NumberFormatException e) {
88 throw new ParseException("Invalid " + protocol + " minor version number",
89 buffer, lowerBound, upperBound, cursor.getPos());
90 }
91 return factory != null ? factory.create(major, minor) : new ProtocolVersion(protocol, major, minor);
92 }
93 }
94
95 public ProtocolVersion parse(
96 final String protocol,
97 final CharSequence buffer,
98 final Tokenizer.Cursor cursor,
99 final Tokenizer.Delimiter delimiterPredicate) throws ParseException {
100 return parse(protocol, null, buffer, cursor, delimiterPredicate);
101 }
102
103 public ProtocolVersion parse(
104 final String protocol,
105 final CharSequence buffer,
106 final Tokenizer.Cursor cursor) throws ParseException {
107 return parse(protocol, null, buffer, cursor, null);
108 }
109
110 public ProtocolVersion parse(
111 final CharSequence buffer,
112 final Tokenizer.Cursor cursor,
113 final Tokenizer.Delimiter delimiterPredicate) throws ParseException {
114 tokenizer.skipWhiteSpace(buffer, cursor);
115 final String proto = tokenizer.parseToken(buffer, cursor, PROTO_DELIMITER);
116 if (TextUtils.isBlank(proto)) {
117 throw new ParseException("Invalid protocol name");
118 }
119 if (!cursor.atEnd() && buffer.charAt(cursor.getPos()) == SLASH) {
120 cursor.updatePos(cursor.getPos() + 1);
121 return parse(proto, null, buffer, cursor, delimiterPredicate);
122 } else {
123 throw new ParseException("Invalid protocol name");
124 }
125 }
126
127 }