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