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 io.github.tonywasher.joceanus.metis.data.MetisDataState;
20 import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataItem;
21 import io.github.tonywasher.joceanus.prometheus.data.PrometheusDataList;
22
23 import java.util.ArrayList;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.ListIterator;
27 import java.util.Objects;
28
29 /**
30 * Batch control class. This controls updating data lists after the commit of the batch.
31 */
32 public class PrometheusBatchControl {
33 /**
34 * Capacity of Batch Control (0=Unlimited).
35 */
36 private final int theCapacity;
37
38 /**
39 * Number of items in this batch.
40 */
41 private int theItems;
42
43 /**
44 * The List of tables associated with this batch.
45 */
46 private final List<PrometheusBatchTable> theList;
47
48 /**
49 * The Currently active Database table.
50 */
51 private PrometheusTableInstance<?> theCurrTable;
52
53 /**
54 * The Currently active Mode.
55 */
56 private MetisDataState theCurrMode;
57
58 /**
59 * Is the current table in use.
60 */
61 private boolean isTableActive;
62
63 /**
64 * Constructor.
65 *
66 * @param pBatchSize the batch size
67 */
68 protected PrometheusBatchControl(final Integer pBatchSize) {
69 /* Create the batch table list */
70 theList = new ArrayList<>();
71
72 /* Store capacity and capacity */
73 theCapacity = pBatchSize;
74 }
75
76 /**
77 * Is the batch full.
78 *
79 * @return true/false is the batch full
80 */
81 protected boolean isFull() {
82 return theCapacity != 0
83 && theItems >= theCapacity;
84 }
85
86 /**
87 * Is the batch active.
88 *
89 * @return true/false is the batch active
90 */
91 protected boolean isActive() {
92 return theItems >= 0;
93 }
94
95 /**
96 * Set the currently active state.
97 *
98 * @param pTable the Table being operated on
99 * @param pMode the Mode that is in operation
100 */
101 protected void setCurrentTable(final PrometheusTableInstance<?> pTable,
102 final MetisDataState pMode) {
103 /* Store details */
104 theCurrTable = pTable;
105 theCurrMode = pMode;
106 isTableActive = false;
107 }
108
109 /**
110 * Add item to the batch.
111 */
112 protected void addBatchItem() {
113 /* Increment batch count */
114 theItems++;
115
116 /* If the current table is not active */
117 if (!isTableActive) {
118 /* Create the batch entry */
119 final PrometheusBatchTable myTable = new PrometheusBatchTable();
120
121 /* Add to the batch list */
122 theList.add(myTable);
123 isTableActive = true;
124 }
125 }
126
127 /**
128 * Commit the batch.
129 */
130 protected void commitItems() {
131 /* Access iterator for the list */
132
133 /* Loop through the items */
134 for (PrometheusBatchTable myTable : theList) {
135 /* Access the next entry */
136 /* Commit batch items in the table */
137 if (Objects.requireNonNull(myTable.theState) == MetisDataState.DELETED) {
138 myTable.commitDeleteBatch();
139 } else {
140 myTable.commitBatch();
141 }
142 }
143
144 /* Clear the list */
145 theList.clear();
146 isTableActive = false;
147 theItems = 0;
148 }
149
150 /**
151 * Table step.
152 */
153 private final class PrometheusBatchTable {
154 /**
155 * The table that is being controlled.
156 */
157 private final PrometheusTableInstance<?> theTable;
158
159 /**
160 * The State of the table.
161 */
162 private final MetisDataState theState;
163
164 /**
165 * Constructor.
166 */
167 private PrometheusBatchTable() {
168 /* Store the details */
169 theTable = theCurrTable;
170 theState = theCurrMode;
171 }
172
173 /**
174 * Mark updates in the table as committed for insert/change.
175 */
176 private void commitBatch() {
177 /* Access the iterator */
178 final Iterator<?> myIterator = theTable.getList().iterator();
179
180 /* Loop through the list */
181 while (myIterator.hasNext()) {
182 final PrometheusDataItem myCurr = (PrometheusDataItem) myIterator.next();
183
184 /* Ignore items that are not this type, otherwise commit and break if required */
185 if (myCurr.getState() == theState
186 && commitItem(myCurr)) {
187 break;
188 }
189 }
190 }
191
192 /**
193 * Mark updates in the table as committed for delete.
194 */
195 private void commitDeleteBatch() {
196 /* Access the iterator */
197 final PrometheusDataList<?> myList = theTable.getList();
198 final ListIterator<?> myIterator = myList.listIterator(myList.size());
199
200 /* Loop through the list */
201 while (myIterator.hasPrevious()) {
202 final PrometheusDataItem myCurr = (PrometheusDataItem) myIterator.previous();
203
204 /* Ignore items that are not this type, otherwise commit and break if necessary */
205 final MetisDataState myState = myCurr.getState();
206 if ((myState == MetisDataState.DELETED
207 || myState == MetisDataState.DELNEW)
208 && commitItem(myCurr)) {
209 break;
210 }
211 }
212 }
213
214 /**
215 * Mark an item in the table as committed.
216 *
217 * @param pItem the item to commit
218 * @return have we reached the end of the batch?
219 */
220 private boolean commitItem(final PrometheusDataItem pItem) {
221 /* Access the underlying element */
222 final PrometheusDataItem myBase = pItem.getBase();
223
224 /* If we are handling deletions */
225 if (theState == MetisDataState.DELETED) {
226 /* Unlink the underlying item */
227 myBase.unLink();
228
229 /* Remove any registration */
230 myBase.deRegister();
231
232 /* else we are handling new/changed items */
233 } else {
234 /* Clear the history */
235 myBase.clearHistory();
236 }
237
238 /* Mark this item as clean */
239 pItem.clearHistory();
240
241 /* If we have to worry about batch space */
242 /* Adjust batch and note if we are finished */
243 return theCapacity > 0
244 && --theItems == 0;
245 }
246 }
247 }