1 /*
2 * Prometheus: Application Framework
3 * Copyright 2012-2026. Tony Washer
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 * use this file except in compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations under
15 * the License.
16 */
17 package io.github.tonywasher.joceanus.prometheus.database;
18
19 import java.util.ArrayList;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.ListIterator;
23
24 import io.github.tonywasher.joceanus.metis.data.MetisDataState;
25 import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataItem;
26 import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataList;
27
28 /**
29 * Batch control class. This controls updating data lists after the commit of the batch.
30 */
31 public class PrometheusBatchControl {
32 /**
33 * Capacity of Batch Control (0=Unlimited).
34 */
35 private final int theCapacity;
36
37 /**
38 * Number of items in this batch.
39 */
40 private int theItems;
41
42 /**
43 * The List of tables associated with this batch.
44 */
45 private List<PrometheusBatchTable> theList;
46
47 /**
48 * The Currently active Database table.
49 */
50 private PrometheusTableDataItem<?> theCurrTable;
51
52 /**
53 * The Currently active Mode.
54 */
55 private MetisDataState theCurrMode;
56
57 /**
58 * Is the current table in use.
59 */
60 private boolean isTableActive;
61
62 /**
63 * Constructor.
64 *
65 * @param pBatchSize the batch size
66 */
67 protected PrometheusBatchControl(final Integer pBatchSize) {
68 /* Create the batch table list */
69 theList = new ArrayList<>();
70
71 /* Store capacity and capacity */
72 theCapacity = pBatchSize;
73 }
74
75 /**
76 * Is the batch full.
77 *
78 * @return true/false is the batch full
79 */
80 protected boolean isFull() {
81 return theCapacity != 0
82 && theItems >= theCapacity;
83 }
84
85 /**
86 * Is the batch active.
87 *
88 * @return true/false is the batch active
89 */
90 protected boolean isActive() {
91 return theItems >= 0;
92 }
93
94 /**
95 * Set the currently active state.
96 *
97 * @param pTable the Table being operated on
98 * @param pMode the Mode that is in operation
99 */
100 protected void setCurrentTable(final PrometheusTableDataItem<?> pTable,
101 final MetisDataState pMode) {
102 /* Store details */
103 theCurrTable = pTable;
104 theCurrMode = pMode;
105 isTableActive = false;
106 }
107
108 /**
109 * Add item to the batch.
110 */
111 protected void addBatchItem() {
112 /* Increment batch count */
113 theItems++;
114
115 /* If the current table is not active */
116 if (!isTableActive) {
117 /* Create the batch entry */
118 final PrometheusBatchTable myTable = new PrometheusBatchTable();
119
120 /* Add to the batch list */
121 theList.add(myTable);
122 isTableActive = true;
123 }
124 }
125
126 /**
127 * Commit the batch.
128 */
129 protected void commitItems() {
130 /* Access iterator for the list */
131 final Iterator<PrometheusBatchTable> myIterator = theList.iterator();
132
133 /* Loop through the items */
134 while (myIterator.hasNext()) {
135 /* Access the next entry */
136 final PrometheusBatchTable myTable = myIterator.next();
137
138 /* Commit batch items in the table */
139 switch (myTable.theState) {
140 case DELETED:
141 myTable.commitDeleteBatch();
142 break;
143 default:
144 myTable.commitBatch();
145 break;
146 }
147 }
148
149 /* Clear the list */
150 theList.clear();
151 isTableActive = false;
152 theItems = 0;
153 }
154
155 /**
156 * Table step.
157 */
158 private final class PrometheusBatchTable {
159 /**
160 * The table that is being controlled.
161 */
162 private final PrometheusTableDataItem<?> theTable;
163
164 /**
165 * The State of the table.
166 */
167 private final MetisDataState theState;
168
169 /**
170 * Constructor.
171 */
172 private PrometheusBatchTable() {
173 /* Store the details */
174 theTable = theCurrTable;
175 theState = theCurrMode;
176 }
177
178 /**
179 * Mark updates in the table as committed for insert/change.
180 */
181 private void commitBatch() {
182 /* Access the iterator */
183 final Iterator<?> myIterator = theTable.getList().iterator();
184
185 /* Loop through the list */
186 while (myIterator.hasNext()) {
187 final PrometheusDataItem myCurr = (PrometheusDataItem) myIterator.next();
188
189 /* Ignore items that are not this type */
190 if (myCurr.getState() != theState) {
191 continue;
192 }
193
194 /* Commit the item and break loop if required */
195 if (commitItem(myCurr)) {
196 break;
197 }
198 }
199 }
200
201 /**
202 * Mark updates in the table as committed for delete.
203 */
204 private void commitDeleteBatch() {
205 /* Access the iterator */
206 final PrometheusDataList<?> myList = theTable.getList();
207 final ListIterator<?> myIterator = myList.listIterator(myList.size());
208
209 /* Loop through the list */
210 while (myIterator.hasPrevious()) {
211 final PrometheusDataItem myCurr = (PrometheusDataItem) myIterator.previous();
212
213 /* Ignore items that are not this type */
214 final MetisDataState myState = myCurr.getState();
215 if ((myState != MetisDataState.DELETED)
216 && (myState != MetisDataState.DELNEW)) {
217 continue;
218 }
219
220 /* Commit the item and break loop if required */
221 if (commitItem(myCurr)) {
222 break;
223 }
224 }
225 }
226
227 /**
228 * Mark an item in the table as committed.
229 *
230 * @param pItem the item to commit
231 * @return have we reached the end of the batch?
232 */
233 private boolean commitItem(final PrometheusDataItem pItem) {
234 /* Access the underlying element */
235 final PrometheusDataItem myBase = pItem.getBase();
236
237 /* If we are handling deletions */
238 if (theState == MetisDataState.DELETED) {
239 /* Unlink the underlying item */
240 myBase.unLink();
241
242 /* Remove any registration */
243 myBase.deRegister();
244
245 /* else we are handling new/changed items */
246 } else {
247 /* Clear the history */
248 myBase.clearHistory();
249 }
250
251 /* Mark this item as clean */
252 pItem.clearHistory();
253
254 /* If we have to worry about batch space */
255 /* Adjust batch and note if we are finished */
256 return theCapacity > 0
257 && --theItems == 0;
258 }
259 }
260 }