View Javadoc
1   /*
2    * Themis: Java Project 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.themis.lethe.analysis;
18  
19  import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
20  import io.github.tonywasher.joceanus.themis.exc.ThemisDataException;
21  import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisDataMap.ThemisAnalysisDataType;
22  
23  import java.util.ArrayList;
24  import java.util.Deque;
25  import java.util.List;
26  import java.util.ListIterator;
27  
28  /**
29   * Generic construct.
30   */
31  public interface ThemisAnalysisGeneric {
32      /**
33       * Is the line a generic?
34       *
35       * @param pLine the line
36       * @return true/false
37       */
38      static boolean isGeneric(final ThemisAnalysisLine pLine) {
39          /* If we are started with a GENERIC_OPEN */
40          return pLine.startsWithChar(ThemisAnalysisChar.GENERIC_OPEN);
41      }
42  
43      /**
44       * Generic Base.
45       */
46      class ThemisAnalysisGenericBase
47              implements ThemisAnalysisGeneric {
48          /**
49           * The contents of the generic.
50           */
51          private final ThemisAnalysisLine theContents;
52  
53          /**
54           * Constructor.
55           *
56           * @param pLine the line
57           * @throws OceanusException on error
58           */
59          ThemisAnalysisGenericBase(final ThemisAnalysisLine pLine) throws OceanusException {
60              /* Find the end of the generic sequence */
61              final int myEnd = pLine.findEndOfNestedSequence(0, 0, ThemisAnalysisChar.GENERIC_CLOSE, ThemisAnalysisChar.GENERIC_OPEN);
62              if (myEnd < 0) {
63                  throw new ThemisDataException("End character not found");
64              }
65  
66              /* Obtain the contents */
67              theContents = pLine.stripUpToPosition(myEnd);
68              theContents.stripStartChar(ThemisAnalysisChar.GENERIC_OPEN);
69              theContents.stripEndChar(ThemisAnalysisChar.GENERIC_CLOSE);
70          }
71  
72          /**
73           * Constructor.
74           *
75           * @param pParser the parser
76           * @param pLine   the line
77           * @throws OceanusException on error
78           */
79          ThemisAnalysisGenericBase(final ThemisAnalysisParser pParser,
80                                    final ThemisAnalysisLine pLine) throws OceanusException {
81              /* Create a scanner */
82              final ThemisAnalysisScanner myScanner = new ThemisAnalysisScanner(pParser);
83  
84              /* Scan for the end of the generic sequence */
85              final Deque<ThemisAnalysisElement> myLines = myScanner.scanForGeneric(pLine);
86  
87              /* Obtain the contents */
88              theContents = new ThemisAnalysisLine(myLines);
89              theContents.stripStartChar(ThemisAnalysisChar.GENERIC_OPEN);
90              theContents.stripEndChar(ThemisAnalysisChar.GENERIC_CLOSE);
91          }
92  
93          /**
94           * Obtain the line.
95           *
96           * @return the line
97           */
98          ThemisAnalysisLine getLine() {
99              return theContents;
100         }
101 
102         @Override
103         public String toString() {
104             return "" + ThemisAnalysisChar.GENERIC_OPEN + theContents + ThemisAnalysisChar.GENERIC_CLOSE;
105         }
106     }
107 
108     /**
109      * Generic Reference.
110      */
111     class ThemisAnalysisGenericRef
112             implements ThemisAnalysisGeneric {
113         /**
114          * The base generic.
115          */
116         private final ThemisAnalysisGenericBase theBase;
117 
118         /**
119          * The list of references.
120          */
121         private final List<ThemisAnalysisReference> theReferences;
122 
123         /**
124          * Constructor.
125          *
126          * @param pParser the parser
127          * @param pBase   the base generic line
128          * @throws OceanusException on error
129          */
130         ThemisAnalysisGenericRef(final ThemisAnalysisParser pParser,
131                                  final ThemisAnalysisGenericBase pBase) throws OceanusException {
132             /* Create the list */
133             theBase = pBase;
134             theReferences = new ArrayList<>();
135             final ThemisAnalysisDataMap myDataMap = pParser.getDataMap();
136 
137             /* Take a copy of the buffer */
138             final ThemisAnalysisLine myLine = new ThemisAnalysisLine(theBase.getLine());
139 
140             /* Loop through the line */
141             while (true) {
142                 /* Strip leading comma */
143                 if (myLine.startsWithChar(ThemisAnalysisChar.COMMA)) {
144                     myLine.stripStartChar(ThemisAnalysisChar.COMMA);
145                 }
146 
147                 /* Access first token */
148                 final String myToken = myLine.peekNextToken();
149                 if (myToken.isEmpty()) {
150                     return;
151                 }
152 
153                 /* Handle generic wildcard */
154                 if (myToken.length() == 1 && myToken.charAt(0) == ThemisAnalysisChar.QUESTION) {
155                     /* Strip the wildcard */
156                     myLine.stripNextToken();
157 
158                     /* Create reference list */
159                     final List<ThemisAnalysisReference> myRefs = new ArrayList<>();
160 
161                     /* If we have an extension/subtype */
162                     final String myNext = myLine.peekNextToken();
163                     if (myNext.equals(ThemisAnalysisKeyWord.EXTENDS.getKeyWord())
164                             || myNext.equals(ThemisAnalysisKeyWord.SUPER.getKeyWord())) {
165                         /* Access data type */
166                         myLine.stripNextToken();
167                         ThemisAnalysisReference myRef = ThemisAnalysisParser.parseDataType(myDataMap, myLine);
168                         myRefs.add(myRef);
169 
170                         /* Loop for additional extends */
171                         while (myLine.getLength() > 0 && myLine.startsWithChar(ThemisAnalysisChar.AND)) {
172                             myLine.stripStartChar(ThemisAnalysisChar.AND);
173                             myRef = ThemisAnalysisParser.parseDataType(myDataMap, myLine);
174                             myRefs.add(myRef);
175                         }
176                     }
177 
178                     final ThemisAnalysisGenericVar myVar = new ThemisAnalysisGenericVar(myToken, myRefs);
179                     theReferences.add(new ThemisAnalysisReference(myVar, null, null));
180 
181                     /* else handle standard generic */
182                 } else {
183                     final ThemisAnalysisReference myReference = ThemisAnalysisParser.parseDataType(myDataMap, myLine);
184                     myReference.resolveGeneric(pParser);
185                     theReferences.add(myReference);
186                 }
187             }
188         }
189 
190         /**
191          * Obtain the list of references.
192          *
193          * @return the list
194          */
195         public List<ThemisAnalysisReference> getReferences() {
196             return theReferences;
197         }
198 
199         @Override
200         public String toString() {
201             return theBase.toString();
202         }
203     }
204 
205     /**
206      * Generic Variable List.
207      */
208     class ThemisAnalysisGenericVarList
209             implements ThemisAnalysisGeneric {
210         /**
211          * The base generic.
212          */
213         private final ThemisAnalysisGenericBase theBase;
214 
215         /**
216          * The list of variables.
217          */
218         private final List<ThemisAnalysisGenericVar> theVariables;
219 
220         /**
221          * Constructor.
222          *
223          * @param pParser the parser
224          * @param pBase   the base generic line
225          * @throws OceanusException on error
226          */
227         ThemisAnalysisGenericVarList(final ThemisAnalysisParser pParser,
228                                      final ThemisAnalysisGenericBase pBase) throws OceanusException {
229             /* Create the list */
230             theBase = pBase;
231             theVariables = new ArrayList<>();
232             final ThemisAnalysisDataMap myDataMap = pParser.getDataMap();
233 
234             /* Take a copy of the buffer */
235             final ThemisAnalysisLine myLine = new ThemisAnalysisLine(theBase.getLine());
236 
237             /* Loop through the lines */
238             while (true) {
239                 /* Strip leading comma */
240                 if (myLine.startsWithChar(ThemisAnalysisChar.COMMA)) {
241                     myLine.stripStartChar(ThemisAnalysisChar.COMMA);
242                 }
243 
244                 /* Access name of variable */
245                 final String myName = myLine.stripNextToken();
246                 if (myName.length() == 0) {
247                     addToDataList(pParser);
248                     return;
249                 }
250 
251                 /* Create list */
252                 final List<ThemisAnalysisReference> myRefs = new ArrayList<>();
253 
254                 /* If we have an extension */
255                 final String myNext = myLine.peekNextToken();
256                 if (myNext.equals(ThemisAnalysisKeyWord.EXTENDS.getKeyWord())) {
257                     /* Access data type */
258                     myLine.stripNextToken();
259                     ThemisAnalysisReference myRef = ThemisAnalysisParser.parseDataType(myDataMap, myLine);
260                     myRefs.add(myRef);
261 
262                     /* Loop for additional extends */
263                     while (myLine.getLength() > 0 && myLine.startsWithChar(ThemisAnalysisChar.AND)) {
264                         myLine.stripStartChar(ThemisAnalysisChar.AND);
265                         myRef = ThemisAnalysisParser.parseDataType(myDataMap, myLine);
266                         myRefs.add(myRef);
267                     }
268                 }
269 
270                 /* Create the variable */
271                 final ThemisAnalysisGenericVar myVar = new ThemisAnalysisGenericVar(myName, myRefs);
272                 theVariables.add(myVar);
273             }
274         }
275 
276         /**
277          * Add to dataList.
278          *
279          * @param pParser the parser
280          * @throws OceanusException on error
281          */
282         private void addToDataList(final ThemisAnalysisParser pParser) throws OceanusException {
283             /* Access the dataMap */
284             final ThemisAnalysisDataMap myMap = pParser.getDataMap();
285 
286             /* Loop through the variables in reverse order */
287             final ListIterator<ThemisAnalysisGenericVar> myIterator = theVariables.listIterator(theVariables.size());
288             while (myIterator.hasPrevious()) {
289                 final ThemisAnalysisGenericVar myVar = myIterator.previous();
290 
291                 /* Resolve generic details and add to dataMap */
292                 myMap.declareGenericVar(myVar);
293                 myVar.resolveGeneric(pParser);
294             }
295         }
296 
297         @Override
298         public String toString() {
299             return theBase.toString();
300         }
301     }
302 
303     /**
304      * Generic Variable.
305      */
306     class ThemisAnalysisGenericVar
307             implements ThemisAnalysisGeneric, ThemisAnalysisDataType {
308         /**
309          * The name of the variable.
310          */
311         private final String theName;
312 
313         /**
314          * The list of references.
315          */
316         private final List<ThemisAnalysisReference> theReferences;
317 
318         /**
319          * Constructor.
320          *
321          * @param pName       the name
322          * @param pReferences the references
323          */
324         ThemisAnalysisGenericVar(final String pName,
325                                  final List<ThemisAnalysisReference> pReferences) {
326             /* Record parameters */
327             theName = pName;
328             theReferences = pReferences;
329         }
330 
331         /**
332          * Obtain the name.
333          *
334          * @return the name
335          */
336         String getName() {
337             return theName;
338         }
339 
340         /**
341          * Resolve the generic references.
342          *
343          * @param pParser the parser
344          * @throws OceanusException on error
345          */
346         public void resolveGeneric(final ThemisAnalysisParser pParser) throws OceanusException {
347             /* Loop through resolving the references */
348             for (ThemisAnalysisReference myRef : theReferences) {
349                 myRef.resolveGeneric(pParser);
350             }
351         }
352 
353         @Override
354         public String toString() {
355             return theName;
356         }
357     }
358 }