ThemisAnalysisGeneric.java
/*
* Themis: Java Project Framework
* Copyright 2012-2026. Tony Washer
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package io.github.tonywasher.joceanus.themis.lethe.analysis;
import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
import io.github.tonywasher.joceanus.themis.exc.ThemisDataException;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisDataMap.ThemisAnalysisDataType;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;
import java.util.ListIterator;
/**
* Generic construct.
*/
public interface ThemisAnalysisGeneric {
/**
* Is the line a generic?
*
* @param pLine the line
* @return true/false
*/
static boolean isGeneric(final ThemisAnalysisLine pLine) {
/* If we are started with a GENERIC_OPEN */
return pLine.startsWithChar(ThemisAnalysisChar.GENERIC_OPEN);
}
/**
* Generic Base.
*/
class ThemisAnalysisGenericBase
implements ThemisAnalysisGeneric {
/**
* The contents of the generic.
*/
private final ThemisAnalysisLine theContents;
/**
* Constructor.
*
* @param pLine the line
* @throws OceanusException on error
*/
ThemisAnalysisGenericBase(final ThemisAnalysisLine pLine) throws OceanusException {
/* Find the end of the generic sequence */
final int myEnd = pLine.findEndOfNestedSequence(0, 0, ThemisAnalysisChar.GENERIC_CLOSE, ThemisAnalysisChar.GENERIC_OPEN);
if (myEnd < 0) {
throw new ThemisDataException("End character not found");
}
/* Obtain the contents */
theContents = pLine.stripUpToPosition(myEnd);
theContents.stripStartChar(ThemisAnalysisChar.GENERIC_OPEN);
theContents.stripEndChar(ThemisAnalysisChar.GENERIC_CLOSE);
}
/**
* Constructor.
*
* @param pParser the parser
* @param pLine the line
* @throws OceanusException on error
*/
ThemisAnalysisGenericBase(final ThemisAnalysisParser pParser,
final ThemisAnalysisLine pLine) throws OceanusException {
/* Create a scanner */
final ThemisAnalysisScanner myScanner = new ThemisAnalysisScanner(pParser);
/* Scan for the end of the generic sequence */
final Deque<ThemisAnalysisElement> myLines = myScanner.scanForGeneric(pLine);
/* Obtain the contents */
theContents = new ThemisAnalysisLine(myLines);
theContents.stripStartChar(ThemisAnalysisChar.GENERIC_OPEN);
theContents.stripEndChar(ThemisAnalysisChar.GENERIC_CLOSE);
}
/**
* Obtain the line.
*
* @return the line
*/
ThemisAnalysisLine getLine() {
return theContents;
}
@Override
public String toString() {
return "" + ThemisAnalysisChar.GENERIC_OPEN + theContents + ThemisAnalysisChar.GENERIC_CLOSE;
}
}
/**
* Generic Reference.
*/
class ThemisAnalysisGenericRef
implements ThemisAnalysisGeneric {
/**
* The base generic.
*/
private final ThemisAnalysisGenericBase theBase;
/**
* The list of references.
*/
private final List<ThemisAnalysisReference> theReferences;
/**
* Constructor.
*
* @param pParser the parser
* @param pBase the base generic line
* @throws OceanusException on error
*/
ThemisAnalysisGenericRef(final ThemisAnalysisParser pParser,
final ThemisAnalysisGenericBase pBase) throws OceanusException {
/* Create the list */
theBase = pBase;
theReferences = new ArrayList<>();
final ThemisAnalysisDataMap myDataMap = pParser.getDataMap();
/* Take a copy of the buffer */
final ThemisAnalysisLine myLine = new ThemisAnalysisLine(theBase.getLine());
/* Loop through the line */
while (true) {
/* Strip leading comma */
if (myLine.startsWithChar(ThemisAnalysisChar.COMMA)) {
myLine.stripStartChar(ThemisAnalysisChar.COMMA);
}
/* Access first token */
final String myToken = myLine.peekNextToken();
if (myToken.isEmpty()) {
return;
}
/* Handle generic wildcard */
if (myToken.length() == 1 && myToken.charAt(0) == ThemisAnalysisChar.QUESTION) {
/* Strip the wildcard */
myLine.stripNextToken();
/* Create reference list */
final List<ThemisAnalysisReference> myRefs = new ArrayList<>();
/* If we have an extension/subtype */
final String myNext = myLine.peekNextToken();
if (myNext.equals(ThemisAnalysisKeyWord.EXTENDS.getKeyWord())
|| myNext.equals(ThemisAnalysisKeyWord.SUPER.getKeyWord())) {
/* Access data type */
myLine.stripNextToken();
ThemisAnalysisReference myRef = ThemisAnalysisParser.parseDataType(myDataMap, myLine);
myRefs.add(myRef);
/* Loop for additional extends */
while (myLine.getLength() > 0 && myLine.startsWithChar(ThemisAnalysisChar.AND)) {
myLine.stripStartChar(ThemisAnalysisChar.AND);
myRef = ThemisAnalysisParser.parseDataType(myDataMap, myLine);
myRefs.add(myRef);
}
}
final ThemisAnalysisGenericVar myVar = new ThemisAnalysisGenericVar(myToken, myRefs);
theReferences.add(new ThemisAnalysisReference(myVar, null, null));
/* else handle standard generic */
} else {
final ThemisAnalysisReference myReference = ThemisAnalysisParser.parseDataType(myDataMap, myLine);
myReference.resolveGeneric(pParser);
theReferences.add(myReference);
}
}
}
/**
* Obtain the list of references.
*
* @return the list
*/
public List<ThemisAnalysisReference> getReferences() {
return theReferences;
}
@Override
public String toString() {
return theBase.toString();
}
}
/**
* Generic Variable List.
*/
class ThemisAnalysisGenericVarList
implements ThemisAnalysisGeneric {
/**
* The base generic.
*/
private final ThemisAnalysisGenericBase theBase;
/**
* The list of variables.
*/
private final List<ThemisAnalysisGenericVar> theVariables;
/**
* Constructor.
*
* @param pParser the parser
* @param pBase the base generic line
* @throws OceanusException on error
*/
ThemisAnalysisGenericVarList(final ThemisAnalysisParser pParser,
final ThemisAnalysisGenericBase pBase) throws OceanusException {
/* Create the list */
theBase = pBase;
theVariables = new ArrayList<>();
final ThemisAnalysisDataMap myDataMap = pParser.getDataMap();
/* Take a copy of the buffer */
final ThemisAnalysisLine myLine = new ThemisAnalysisLine(theBase.getLine());
/* Loop through the lines */
while (true) {
/* Strip leading comma */
if (myLine.startsWithChar(ThemisAnalysisChar.COMMA)) {
myLine.stripStartChar(ThemisAnalysisChar.COMMA);
}
/* Access name of variable */
final String myName = myLine.stripNextToken();
if (myName.length() == 0) {
addToDataList(pParser);
return;
}
/* Create list */
final List<ThemisAnalysisReference> myRefs = new ArrayList<>();
/* If we have an extension */
final String myNext = myLine.peekNextToken();
if (myNext.equals(ThemisAnalysisKeyWord.EXTENDS.getKeyWord())) {
/* Access data type */
myLine.stripNextToken();
ThemisAnalysisReference myRef = ThemisAnalysisParser.parseDataType(myDataMap, myLine);
myRefs.add(myRef);
/* Loop for additional extends */
while (myLine.getLength() > 0 && myLine.startsWithChar(ThemisAnalysisChar.AND)) {
myLine.stripStartChar(ThemisAnalysisChar.AND);
myRef = ThemisAnalysisParser.parseDataType(myDataMap, myLine);
myRefs.add(myRef);
}
}
/* Create the variable */
final ThemisAnalysisGenericVar myVar = new ThemisAnalysisGenericVar(myName, myRefs);
theVariables.add(myVar);
}
}
/**
* Add to dataList.
*
* @param pParser the parser
* @throws OceanusException on error
*/
private void addToDataList(final ThemisAnalysisParser pParser) throws OceanusException {
/* Access the dataMap */
final ThemisAnalysisDataMap myMap = pParser.getDataMap();
/* Loop through the variables in reverse order */
final ListIterator<ThemisAnalysisGenericVar> myIterator = theVariables.listIterator(theVariables.size());
while (myIterator.hasPrevious()) {
final ThemisAnalysisGenericVar myVar = myIterator.previous();
/* Resolve generic details and add to dataMap */
myMap.declareGenericVar(myVar);
myVar.resolveGeneric(pParser);
}
}
@Override
public String toString() {
return theBase.toString();
}
}
/**
* Generic Variable.
*/
class ThemisAnalysisGenericVar
implements ThemisAnalysisGeneric, ThemisAnalysisDataType {
/**
* The name of the variable.
*/
private final String theName;
/**
* The list of references.
*/
private final List<ThemisAnalysisReference> theReferences;
/**
* Constructor.
*
* @param pName the name
* @param pReferences the references
*/
ThemisAnalysisGenericVar(final String pName,
final List<ThemisAnalysisReference> pReferences) {
/* Record parameters */
theName = pName;
theReferences = pReferences;
}
/**
* Obtain the name.
*
* @return the name
*/
String getName() {
return theName;
}
/**
* Resolve the generic references.
*
* @param pParser the parser
* @throws OceanusException on error
*/
public void resolveGeneric(final ThemisAnalysisParser pParser) throws OceanusException {
/* Loop through resolving the references */
for (ThemisAnalysisReference myRef : theReferences) {
myRef.resolveGeneric(pParser);
}
}
@Override
public String toString() {
return theName;
}
}
}