View Javadoc
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 }