1 package com.github.davidmoten.rx2.internal.flowable;
2
3 import com.github.davidmoten.guavamini.annotations.VisibleForTesting;
4
5
6
7
8
9 public final class DelimitedStringLinkedList {
10
11 private final String delimiter;
12 private final StringBuilder b = new StringBuilder();
13
14 private Node head;
15 private Node tail;
16 private int headPosition;
17 private Node searchNode;
18 private int searchPosition;
19 private int nextLength;
20 private boolean added;
21
22 public DelimitedStringLinkedList(String delimiter) {
23 this.delimiter = delimiter;
24 }
25
26 @VisibleForTesting
27 int searchPosition() {
28 return searchPosition;
29 }
30
31 private static final class Node {
32 final String value;
33 Node next;
34
35 Node(String value, Node next) {
36 this.value = value;
37 this.next = next;
38 }
39
40 @Override
41 public String toString() {
42 return "Node [value=" + value + ", next=" + next + "]";
43 }
44 }
45
46 public boolean addCalled() {
47 return added;
48 }
49
50 public void add(String s) {
51 added = true;
52 if (s.length() == 0) {
53 return;
54 }
55 if (head == null) {
56 head = new Node(s, null);
57 tail = head;
58 headPosition = 0;
59 searchPosition = 0;
60 searchNode = head;
61 nextLength = 0;
62 } else {
63 Node node = new Node(s, null);
64 tail.next = node;
65 tail = node;
66 if (searchNode == null) {
67 searchNode = node;
68 searchPosition = 0;
69 }
70 }
71 }
72
73 public String remaining() {
74 if (head == null) {
75 return null;
76 } else {
77 b.setLength(0);
78 Node n = head;
79 do {
80 if (n == head) {
81 b.append(n.value.substring(headPosition, n.value.length()));
82 } else {
83 b.append(n.value);
84 }
85 n = n.next;
86 } while (n != null);
87 return b.toString();
88 }
89 }
90
91 public String next() {
92 while (searchNode != null) {
93 if (searchNode.value.charAt(searchPosition) == delimiter.charAt(0)) {
94 Node nd = searchNode;
95 int pos = searchPosition + 1;
96 int j = 1;
97 while (j < delimiter.length()) {
98 if (pos == nd.value.length()) {
99 if (nd.next == null) {
100 break;
101 }
102 nd = nd.next;
103 pos = 0;
104 }
105 if (nd.value.charAt(pos) != delimiter.charAt(j)) {
106 break;
107 }
108 j++;
109 pos++;
110 }
111 boolean found = j == delimiter.length();
112 if (found) {
113
114
115
116
117 String result = extractFromHeadPositionToSearchPosition();
118
119 resetPositionsAfterExtract(nd, pos);
120 return result;
121 }
122 }
123 nextLength++;
124 searchPosition += 1;
125 if (searchPosition == searchNode.value.length()) {
126 if (searchNode.next == null) {
127 searchNode = null;
128 searchPosition = 0;
129 break;
130 } else {
131 searchNode = searchNode.next;
132 searchPosition = 0;
133 }
134 }
135 }
136 return null;
137 }
138
139 private String extractFromHeadPositionToSearchPosition() {
140
141 b.setLength(0);
142 b.ensureCapacity(nextLength);
143 Node n = head;
144 while (true) {
145 if (n == searchNode && n == head) {
146 b.append(n.value.substring(headPosition, searchPosition));
147 break;
148 } else if (n == head) {
149 b.append(n.value.substring(headPosition, n.value.length()));
150 } else if (n == searchNode) {
151 b.append(n.value.substring(0, searchPosition));
152 break;
153 } else {
154 b.append(n.value);
155 }
156 n = n.next;
157 }
158 if (nextLength != b.length()) {
159 throw new RuntimeException("unexpected");
160 }
161 return b.toString();
162 }
163
164 private void resetPositionsAfterExtract(Node nd, int pos) {
165 nextLength = 0;
166 if (pos == nd.value.length()) {
167 if (tail == nd) {
168 tail = nd.next;
169 }
170
171 head = nd.next;
172 headPosition = 0;
173 searchPosition = 0;
174 searchNode = head;
175 } else {
176 head = nd;
177 headPosition = pos;
178 if (headPosition == head.value.length()) {
179 dropFirst();
180 }
181 searchNode = head;
182 searchPosition = headPosition;
183 }
184 }
185
186 private void dropFirst() {
187 if (head.next == null) {
188 tail = null;
189 head = null;
190 headPosition = 0;
191 } else {
192 if (tail == head) {
193 tail = head.next;
194 }
195 Node h = head;
196 head = head.next;
197 h.next = null;
198 headPosition = 0;
199 }
200 }
201
202 public void clear() {
203 head = null;
204 tail = null;
205 searchNode = null;
206 headPosition = 0;
207 searchPosition = 0;
208 }
209
210 }