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.MetisDataItem.MetisDataFieldId;
21  
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  /**
26   * Records errors relating to an item.
27   */
28  public class MetisFieldValidation
29          implements MetisFieldItem {
30      /*
31       * FieldSet definitions.
32       */
33      static {
34          MetisFieldSet.newFieldSet(MetisFieldValidation.class);
35      }
36  
37      /**
38       * The local fields.
39       */
40      private MetisFieldSet<MetisFieldValidation> theLocalFields;
41  
42      /**
43       * The first error in the list.
44       */
45      private final List<MetisFieldError> theErrors;
46  
47      /**
48       * Constructor.
49       */
50      public MetisFieldValidation() {
51          /* Store details */
52          theErrors = new ArrayList<>();
53          allocateNewFields();
54      }
55  
56      /**
57       * Allocate new DataFields.
58       */
59      private void allocateNewFields() {
60          theLocalFields = MetisFieldSet.newFieldSet(this);
61      }
62  
63      @Override
64      public MetisFieldSetDef getDataFieldSet() {
65          return theLocalFields;
66      }
67  
68      @Override
69      public String formatObject(final OceanusDataFormatter pFormatter) {
70          final int mySize = theErrors.size();
71          if (mySize != 1) {
72              return mySize
73                      + " Errors";
74          }
75          final MetisFieldError myError = theErrors.get(0);
76          return myError.formatError();
77      }
78  
79      /**
80       * Do we have any errors?
81       *
82       * @return true/false
83       */
84      public boolean hasErrors() {
85          return !theErrors.isEmpty();
86      }
87  
88      /**
89       * Get the first error in the list.
90       *
91       * @return the first error or <code>null</code>
92       */
93      public MetisFieldError getFirst() {
94          return theErrors.isEmpty()
95                  ? null
96                  : theErrors.get(0);
97      }
98  
99      /**
100      * add error to the list.
101      *
102      * @param pText  the text for the error
103      * @param pField the field for the error
104      */
105     public void addError(final String pText,
106                          final MetisDataFieldId pField) {
107         /* Create a new error element */
108         final MetisFieldError myError = new MetisFieldError(pText, pField);
109 
110         /* Declare error field */
111         final int myCount = countFieldErrors(pField);
112         final String myName = pField.getId() + "-" + myCount;
113         theLocalFields.declareLocalField(myName, f -> myError);
114 
115         /* Add to the end of the list */
116         theErrors.add(myError);
117     }
118 
119     /**
120      * Determine whether there are any errors for a particular field.
121      *
122      * @param pField - the field
123      * @return <code>true</code> if there are any errors <code>false</code> otherwise
124      */
125     public boolean hasErrors(final MetisDataFieldId pField) {
126         /* Loop through the elements */
127         for (MetisFieldError myCurr : theErrors) {
128             /* Access the element and return if related to required field */
129             if (myCurr.getField().equals(pField)) {
130                 return true;
131             }
132         }
133         return false;
134     }
135 
136     /**
137      * Get the error details for a particular field.
138      *
139      * @param pField - the field
140      * @return the error text
141      */
142     public String getFieldErrors(final MetisDataFieldId pField) {
143         final StringBuilder myErrors = new StringBuilder();
144 
145         /* Loop through the elements */
146         for (MetisFieldError myCurr : theErrors) {
147             /* Access the element */
148             /* If the field matches */
149             if (myCurr.getField().equals(pField)) {
150                 /* Add the error */
151                 addErrorText(myErrors, myCurr.getError());
152             }
153         }
154 
155         /* If we have errors */
156         if (!myErrors.isEmpty()) {
157             /* Complete the format and return it */
158             endErrors(myErrors);
159             return myErrors.toString();
160         }
161 
162         /* Return null */
163         return null;
164     }
165 
166     /**
167      * Count the errors for this field.
168      *
169      * @param pField - the field number to check
170      * @return the error count
171      */
172     private int countFieldErrors(final MetisDataFieldId pField) {
173         int myCount = 0;
174 
175         /* Loop through the elements */
176         for (MetisFieldError myCurr : theErrors) {
177             /* Access the element */
178             /* If the field matches */
179             if (myCurr.getField().equals(pField)) {
180                 /* Increment the count */
181                 myCount++;
182             }
183         }
184 
185         /* Return the count */
186         return myCount;
187     }
188 
189     /**
190      * Get the error text for fields outside a set of fields.
191      *
192      * @param aFields the set of fields
193      * @return the error text
194      */
195     public String getFieldErrors(final MetisFieldDef[] aFields) {
196         final StringBuilder myErrors = new StringBuilder();
197 
198         /* Loop through the elements */
199         for (MetisFieldError myCurr : theErrors) {
200             /* Access the element and field */
201             final MetisDataFieldId myField = myCurr.getField();
202 
203             /* Search the field set */
204             boolean bFound = false;
205             for (MetisFieldDef field : aFields) {
206                 /* If we have found the field note it and break loop */
207                 if (field != null && field.equals(myField)) {
208                     bFound = true;
209                     break;
210                 }
211             }
212 
213             /* Skip error if the field was found */
214             if (bFound) {
215                 continue;
216             }
217 
218             /* Add the error */
219             addErrorText(myErrors, myCurr.getError());
220         }
221 
222         /* If we have errors */
223         if (!myErrors.isEmpty()) {
224             /* Complete the format and return it */
225             endErrors(myErrors);
226             return myErrors.toString();
227         }
228 
229         /* Return null */
230         return null;
231     }
232 
233     /**
234      * Add error text.
235      *
236      * @param pBuilder the string builder
237      * @param pError   new error text
238      */
239     private static void addErrorText(final StringBuilder pBuilder,
240                                      final String pError) {
241         /* Add relevant prefix */
242         pBuilder.append((pBuilder.isEmpty())
243                 ? "<html>"
244                 : "<br>");
245 
246         /* Add error text */
247         pBuilder.append(pError);
248     }
249 
250     /**
251      * End error text.
252      *
253      * @param pErrors the error builder
254      */
255     public void endErrors(final StringBuilder pErrors) {
256         pErrors.append("</html>");
257     }
258 
259     /**
260      * Clear errors.
261      */
262     public void clearErrors() {
263         /* Remove all errors */
264         theErrors.clear();
265         allocateNewFields();
266     }
267 
268     /**
269      * represents an instance of an error for an object.
270      */
271     public static final class MetisFieldError {
272         /**
273          * The text of the error.
274          */
275         private final String theError;
276 
277         /**
278          * The field for the error.
279          */
280         private final MetisDataFieldId theField;
281 
282         /**
283          * Constructor for the error.
284          *
285          * @param pError the error text
286          * @param pField the field
287          */
288         private MetisFieldError(final String pError,
289                                 final MetisDataFieldId pField) {
290             theError = pError;
291             theField = pField;
292         }
293 
294         /**
295          * Get the text for the error.
296          *
297          * @return the text
298          */
299         public String getError() {
300             return theError;
301         }
302 
303         /**
304          * Get the field for the error.
305          *
306          * @return the field
307          */
308         public MetisDataFieldId getField() {
309             return theField;
310         }
311 
312         /**
313          * Format the error.
314          *
315          * @return the formatted error
316          */
317         private String formatError() {
318             return theField.getId()
319                     + ": "
320                     + theError;
321         }
322     }
323 }