View Javadoc
1   /*
2    * Metis: Java Data 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.metis.field;
18  
19  import io.github.tonywasher.joceanus.metis.data.MetisDataDifference;
20  import io.github.tonywasher.joceanus.metis.data.MetisDataResource;
21  import io.github.tonywasher.joceanus.metis.field.MetisFieldVersion.MetisFieldVersionHistoryCtl;
22  import io.github.tonywasher.joceanus.oceanus.format.OceanusDataFormatter;
23  
24  import java.util.ArrayDeque;
25  import java.util.Deque;
26  import java.util.Iterator;
27  
28  /**
29   * Data Version History.
30   */
31  public class MetisFieldVersionHistory
32          implements MetisFieldVersionHistoryCtl {
33      /*
34       * FieldSet definitions.
35       */
36      static {
37          MetisFieldSet.newFieldSet(MetisFieldVersionHistory.class);
38      }
39  
40      /**
41       * The stack of valueSet changes.
42       */
43      private final Deque<MetisFieldVersionValues> theStack;
44  
45      /**
46       * The stack of valueSetDelta fields.
47       */
48      private final Deque<MetisFieldVersionDelta> theDeltas;
49  
50      /**
51       * The current set of values for this object.
52       */
53      private MetisFieldVersionValues theCurr;
54  
55      /**
56       * The original set of values if any changes have been made.
57       */
58      private MetisFieldVersionValues theOriginal;
59  
60      /**
61       * Constructor.
62       *
63       * @param pCurr the current values
64       */
65      protected MetisFieldVersionHistory(final MetisFieldVersionValues pCurr) {
66          /* Allocate the values */
67          theCurr = pCurr;
68          theOriginal = theCurr;
69  
70          /* Allocate the stack */
71          theStack = new ArrayDeque<>();
72          theDeltas = new ArrayDeque<>();
73      }
74  
75      @Override
76      public MetisFieldSetDef getDataFieldSet() {
77          /* Allocate new local fields */
78          final MetisFieldSet<MetisFieldVersionHistory> myLocal = MetisFieldSet.newFieldSet(this);
79          final String myVersion = MetisDataResource.DATA_VERSION.getValue();
80  
81          /* Loop through the fields */
82          final Iterator<MetisFieldVersionDelta> myIterator = theDeltas.descendingIterator();
83          while (myIterator.hasNext()) {
84              /* Access the Delta */
85              final MetisFieldVersionDelta myDelta = myIterator.next();
86  
87              /* Declare the field */
88              myLocal.declareLocalField(myVersion + "(" + myDelta.getVersion() + ")", f -> myDelta);
89          }
90  
91          /* Return the fieldSet */
92          return myLocal;
93      }
94  
95      @Override
96      public String formatObject(final OceanusDataFormatter pFormatter) {
97          return MetisFieldVersionHistory.class.getSimpleName() + "(" + theStack.size() + ")";
98      }
99  
100     /**
101      * Initialise the current values.
102      *
103      * @param pValues the current values
104      */
105     protected void setValues(final MetisFieldVersionValues pValues) {
106         /* Store details and clear the stack */
107         theCurr = pValues;
108         theOriginal = theCurr;
109         theStack.clear();
110         theDeltas.clear();
111     }
112 
113     /**
114      * Get the changeable values object for this item.
115      *
116      * @return the object
117      */
118     protected MetisFieldVersionValues getValueSet() {
119         return theCurr;
120     }
121 
122     /**
123      * Get original values.
124      *
125      * @return original values
126      */
127     protected MetisFieldVersionValues getOriginalValues() {
128         return theOriginal;
129     }
130 
131     /**
132      * Push Item to the history.
133      *
134      * @param pVersion the new version
135      */
136     public void pushHistory(final int pVersion) {
137         /* Create a new ValueSet */
138         final MetisFieldVersionValues mySet = theCurr.cloneIt();
139         mySet.setVersion(pVersion);
140 
141         /* Add the delta to the stack */
142         theDeltas.push(new MetisFieldVersionDelta(mySet, theCurr));
143 
144         /* Add old values to the stack and record new values */
145         theStack.push(theCurr);
146         theCurr = mySet;
147     }
148 
149     /**
150      * popItem from the history and remove from history.
151      */
152     public void popTheHistory() {
153         /* If we have an item on the stack */
154         if (hasHistory()) {
155             /* Remove it from the list */
156             theCurr = theStack.pop();
157             theDeltas.pop();
158         }
159     }
160 
161     /**
162      * popItem from the history if equal to current.
163      *
164      * @return was a change made
165      */
166     public boolean maybePopHistory() {
167         /* If there is no change */
168         if (!theStack.isEmpty()
169                 && theCurr.differs(theStack.peek()).isIdentical()) {
170             /* Just pop the history */
171             popTheHistory();
172             return false;
173         }
174 
175         /* Return that we have made a change */
176         return true;
177     }
178 
179     /**
180      * Is there any history?
181      *
182      * @return whether there are entries in the history list
183      */
184     public boolean hasHistory() {
185         return !theStack.isEmpty();
186     }
187 
188     /**
189      * Obtain the last values.
190      *
191      * @return the last values (or null)
192      */
193     public MetisFieldVersionValues getLastValues() {
194         return theStack.peek();
195     }
196 
197     /**
198      * Clear history.
199      */
200     protected void clearHistory() {
201         /* Remove all history */
202         theStack.clear();
203         theDeltas.clear();
204         theOriginal = theCurr;
205         theOriginal.setVersion(0);
206     }
207 
208     /**
209      * Reset history.
210      */
211     protected void resetHistory() {
212         /* Remove all history */
213         theStack.clear();
214         theDeltas.clear();
215         theCurr = theOriginal;
216     }
217 
218     /**
219      * Set history explicitly.
220      *
221      * @param pBase the base item
222      */
223     public void setHistory(final MetisFieldVersionValues pBase) {
224         theStack.clear();
225         theDeltas.clear();
226         theOriginal = theCurr.cloneIt();
227         theOriginal.copyFrom(pBase);
228         theStack.push(theOriginal);
229         theCurr.setVersion(1);
230 
231         /* Add the delta to the stack */
232         theDeltas.push(new MetisFieldVersionDelta(theCurr, theOriginal));
233     }
234 
235     /**
236      * Condense history.
237      *
238      * @param pNewVersion the new maximum version
239      */
240     protected void condenseHistory(final int pNewVersion) {
241         /* If we need to condense history */
242         if (theCurr.getVersion() > pNewVersion) {
243             /* While we have unnecessary stack entries */
244             boolean bNewDelta = false;
245             while (!theStack.isEmpty()
246                     && theStack.peek().getVersion() >= pNewVersion) {
247                 /* Clear them */
248                 theStack.pop();
249                 theDeltas.pop();
250                 bNewDelta = true;
251             }
252 
253             /* Set the desired version */
254             theCurr.setVersion(pNewVersion);
255 
256             /* If we need to adjust the delta */
257             if (bNewDelta && !theStack.isEmpty()) {
258                 /* remove old delta */
259                 theDeltas.pop();
260 
261                 /* Add the new delta to the stack */
262                 theDeltas.push(new MetisFieldVersionDelta(theCurr, theStack.peek()));
263             }
264         }
265     }
266 
267     /**
268      * Determines whether a particular field has changed.
269      *
270      * @param pField the field
271      * @return the difference
272      */
273     public MetisDataDifference fieldChanged(final MetisFieldDef pField) {
274         /* Handle irrelevant cases */
275         if (!(pField instanceof MetisFieldVersionedDef)) {
276             return MetisDataDifference.IDENTICAL;
277         }
278         if (!((MetisFieldVersionedDef) pField).isEquality()) {
279             return MetisDataDifference.IDENTICAL;
280         }
281 
282         /* Call the function from the interface */
283         return theCurr.fieldChanged(pField, theOriginal);
284     }
285 }