ThemisStatsParser.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.statistics;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisAnnotation;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisAnonClass;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisBlank;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisBlock;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisCase;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisCatch;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisClass;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisComment;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisContainer;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisDoWhile;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisElement;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisElse;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisEmbedded;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisEnum;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisField;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisFile;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisFile.ThemisAnalysisObject;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisFinally;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisFor;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisIf;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisImports;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisInterface;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisLambda;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisMethod;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisModule;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisPackage;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisProject;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisStatement;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisSwitch;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisTry;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisWhile;
/**
* Package Parser.
*/
public class ThemisStatsParser {
/**
* The cached documentation comment.
*/
private ThemisAnalysisComment theCachedDocComment;
/**
* The cached annotation.
*/
private ThemisAnalysisAnnotation theCachedAnnotation;
/**
* parse a project.
*
* @param pProject the project to parse
* @return the stats
*/
public ThemisStatsProject parseProject(final ThemisAnalysisProject pProject) {
/* Create the stats */
final ThemisStatsProject myStats = new ThemisStatsProject(pProject);
/* Loop through the modules */
for (ThemisAnalysisModule myModule : pProject.getModules()) {
/* Process the module */
myStats.addModule(parseModule(myModule));
}
/* Return the projectStats */
return myStats;
}
/**
* parse a module.
*
* @param pModule the module to parse
* @return the stats
*/
public ThemisStatsModule parseModule(final ThemisAnalysisModule pModule) {
/* Create the stats */
final ThemisStatsModule myStats = new ThemisStatsModule(pModule);
/* Loop through the packages */
for (ThemisAnalysisPackage myPackage : pModule.getPackages()) {
/* Process the package */
myStats.addPackage(parsePackage(myPackage));
}
/* Return the moduleStats */
return myStats;
}
/**
* parse a package.
*
* @param pPackage the package to parse
* @return the stats
*/
public ThemisStatsPackage parsePackage(final ThemisAnalysisPackage pPackage) {
/* Create the stats */
final ThemisStatsPackage myStats = new ThemisStatsPackage(pPackage);
/* Loop through the files */
for (ThemisAnalysisFile myFile : pPackage.getFiles()) {
/* Process the file */
myStats.addFile(parseFile(myStats, myFile));
}
/* Return the packageStats */
return myStats;
}
/**
* parse a file.
*
* @param pPackage the owning package
* @param pFile the file to parse
* @return the stats
*/
public ThemisStatsFile parseFile(final ThemisStatsPackage pPackage,
final ThemisAnalysisFile pFile) {
/* Create the stats */
final ThemisStatsFile myStats = new ThemisStatsFile(pFile);
/* process the container */
processContainer(myStats, pFile);
/* Return the fileStats */
return myStats;
}
/**
* parse a class.
*
* @param pOwner the owner
* @param pClass the class to parse
* @return the stats
*/
public ThemisStatsClass parseClass(final ThemisStatsBase pOwner,
final ThemisAnalysisObject pClass) {
/* Create the stats */
final ThemisStatsClass myStats = new ThemisStatsClass(pClass);
/* process the container */
processCachedItems(pOwner, myStats);
processContainer(myStats, pClass);
/* Return the classStats */
return myStats;
}
/**
* parse a method.
*
* @param pOwner the owner
* @param pMethod the method to parse
* @return the stats
*/
public ThemisStatsMethod parseMethod(final ThemisStatsBase pOwner,
final ThemisAnalysisMethod pMethod) {
/* Create the stats */
final ThemisStatsMethod myStats = new ThemisStatsMethod(pMethod);
/* process the container */
processCachedItems(pOwner, myStats);
processContainer(myStats, pMethod);
/* Return the classStats */
return myStats;
}
/**
* process container.
*
* @param pOwner the owner
* @param pContainer the container
*/
public void processContainer(final ThemisStatsBase pOwner,
final ThemisAnalysisContainer pContainer) {
/* Loop through the contents */
for (ThemisAnalysisElement myElement : pContainer.getContents()) {
/* If this is an annotation */
if (myElement instanceof ThemisAnalysisAnnotation myAnnotation) {
processAnnotation(pOwner, myAnnotation, true);
/* If this is an anonymous class */
} else if (myElement instanceof ThemisAnalysisAnonClass myAnon) {
processAnonClass(pOwner, myAnon);
/* If this is a blank */
} else if (myElement instanceof ThemisAnalysisBlank myBlank) {
processBlank(pOwner, myBlank);
/* If this is a block */
} else if (myElement instanceof ThemisAnalysisBlock myBlock) {
processBlock(pOwner, myBlock);
/* If this is a case */
} else if (myElement instanceof ThemisAnalysisCase myCase) {
processCase(pOwner, myCase);
/* If this is a class */
} else if (myElement instanceof ThemisAnalysisClass myClass) {
processClass(pOwner, myClass);
/* If this is a comment */
} else if (myElement instanceof ThemisAnalysisComment myComment) {
processComment(pOwner, myComment, true);
/* If this is a doWhile */
} else if (myElement instanceof ThemisAnalysisDoWhile myDoWhile) {
processDoWhile(pOwner, myDoWhile);
/* If this is an embedded */
} else if (myElement instanceof ThemisAnalysisEmbedded myEmbedded) {
processEmbedded(pOwner, myEmbedded);
/* If this is an enum */
} else if (myElement instanceof ThemisAnalysisEnum myEnum) {
processEnum(pOwner, myEnum);
/* If this is a field */
} else if (myElement instanceof ThemisAnalysisField myField) {
processField(pOwner, myField);
/* If this is a for */
} else if (myElement instanceof ThemisAnalysisFor myFor) {
processFor(pOwner, myFor);
/* If this is an if */
} else if (myElement instanceof ThemisAnalysisIf myIf) {
processIf(pOwner, myIf);
/* If this is an imports */
} else if (myElement instanceof ThemisAnalysisImports myImports) {
processImports(pOwner, myImports);
/* If this is an interface */
} else if (myElement instanceof ThemisAnalysisInterface myInterface) {
processInterface(pOwner, myInterface);
/* If this is a lambda */
} else if (myElement instanceof ThemisAnalysisLambda myLambda) {
processLambda(pOwner, myLambda);
/* If this is a method */
} else if (myElement instanceof ThemisAnalysisMethod myMethod) {
processMethod(pOwner, myMethod);
/* If this is a package */
} else if (myElement instanceof ThemisAnalysisPackage) {
processPackage(pOwner);
/* If this is a statement */
} else if (myElement instanceof ThemisAnalysisStatement myStatement) {
processStatement(pOwner, myStatement);
/* If this is a switch */
} else if (myElement instanceof ThemisAnalysisSwitch mySwitch) {
processSwitch(pOwner, mySwitch);
/* If this is a try */
} else if (myElement instanceof ThemisAnalysisTry myTry) {
processTry(pOwner, myTry);
/* If this is a while */
} else if (myElement instanceof ThemisAnalysisWhile myWhile) {
processWhile(pOwner, myWhile);
}
/* Adjust the cached items */
adjustCachedItems(myElement);
}
}
/**
* process annotation.
*
* @param pOwner the owner
* @param pAnnotation the annotation
* @param pAddToStats addTo stats or remove from Stats?
*/
private static void processAnnotation(final ThemisStatsBase pOwner,
final ThemisAnalysisAnnotation pAnnotation,
final boolean pAddToStats) {
/* Adjust statistics */
final int myAdjust = pAddToStats
? pAnnotation.getNumLines()
: -pAnnotation.getNumLines();
adjustLinesOfCode(pOwner, myAdjust);
}
/**
* process anonymous class.
*
* @param pOwner the owner
* @param pAnon the anonymous class
*/
private void processAnonClass(final ThemisStatsBase pOwner,
final ThemisAnalysisAnonClass pAnon) {
/* Parse the class */
final ThemisStatsClass myClass = parseClass(pOwner, pAnon);
/* Adjust statistics */
pOwner.incrementStat(ThemisStat.NCL);
/* Adjust the stats and add to owner */
adjustLinesOfCode(myClass, pAnon.getNumLines());
pOwner.addClass(myClass);
}
/**
* process blank.
*
* @param pOwner the owner
* @param pBlank the blank
*/
private static void processBlank(final ThemisStatsBase pOwner,
final ThemisAnalysisBlank pBlank) {
/* Adjust statistics */
final int myBlanks = pBlank.getNumLines();
pOwner.adjustStat(ThemisStat.LOC, myBlanks);
}
/**
* process block.
*
* @param pOwner the owner
* @param pBlock the block
*/
private void processBlock(final ThemisStatsBase pOwner,
final ThemisAnalysisBlock pBlock) {
/* process the container */
processContainer(pOwner, pBlock);
/* Adjust the owner stats */
adjustLinesOfCode(pOwner, pBlock.getNumLines());
if (pBlock.isSynchronized()) {
adjustNumberOfStatements(pOwner, 1);
}
}
/**
* process class.
*
* @param pOwner the owner
* @param pClass the class
*/
private void processClass(final ThemisStatsBase pOwner,
final ThemisAnalysisClass pClass) {
/* Parse the class */
final ThemisStatsClass myClass = parseClass(pOwner, pClass);
/* Adjust statistics */
pOwner.incrementStat(ThemisStat.NCL);
/* Adjust the stats and add to owner */
adjustLinesOfCode(myClass, pClass.getNumLines());
adjustNumberOfStatements(pOwner, 1);
pOwner.addClass(myClass);
}
/**
* process comment.
*
* @param pOwner the owner
* @param pComment the comment
* @param pAddToStats addTo stats or remove from Stats?
*/
private static void processComment(final ThemisStatsBase pOwner,
final ThemisAnalysisComment pComment,
final boolean pAddToStats) {
/* Adjust the stats */
final int myAdjust = pAddToStats
? pComment.getNumLines()
: -pComment.getNumLines();
pOwner.adjustStat(ThemisStat.LOC, myAdjust);
pOwner.adjustStat(ThemisStat.CLOC, myAdjust);
if (pComment.isJavaDoc()) {
pOwner.adjustStat(ThemisStat.DLOC, myAdjust);
}
}
/**
* process doWhile.
*
* @param pOwner the owner
* @param pDoWhile the doWhile
*/
private void processDoWhile(final ThemisStatsBase pOwner,
final ThemisAnalysisDoWhile pDoWhile) {
/* process the container */
processContainer(pOwner, pDoWhile);
/* Adjust the owner stats */
adjustLinesOfCode(pOwner, pDoWhile.getNumLines());
adjustNumberOfStatements(pOwner, 1);
}
/**
* process embedded element.
*
* @param pOwner the owner
* @param pEmbedded the embedded
*/
private void processEmbedded(final ThemisStatsBase pOwner,
final ThemisAnalysisEmbedded pEmbedded) {
/* process the container */
processContainer(pOwner, pEmbedded);
/* Adjust statistics */
adjustNumberOfStatements(pOwner, 1);
adjustLinesOfCode(pOwner, pEmbedded.getNumLines());
}
/**
* process enum.
*
* @param pOwner the owner
* @param pEnum the enum
*/
private void processEnum(final ThemisStatsBase pOwner,
final ThemisAnalysisEnum pEnum) {
/* Parse the enum */
final ThemisStatsClass myEnum = parseClass(pOwner, pEnum);
/* Adjust owner statistics */
pOwner.incrementStat(ThemisStat.NEN);
/* Adjust the stats and add to owner */
adjustLinesOfCode(myEnum, pEnum.getNumLines());
adjustNumberOfAttributes(myEnum, pEnum.getNumEnums());
adjustNumberOfStatements(pOwner, 1);
pOwner.addClass(myEnum);
}
/**
* process field.
*
* @param pOwner the owner
* @param pField the field
*/
private static void processField(final ThemisStatsBase pOwner,
final ThemisAnalysisField pField) {
/* Adjust the stats */
adjustLinesOfCode(pOwner, pField.getNumLines());
/* Counts as a statement if there is an initializer */
if (pField.statementIterator().hasNext()) {
adjustNumberOfStatements(pOwner, 1);
}
/* Attributes are only relevant to classes */
if (pOwner instanceof ThemisStatsClass) {
adjustNumberOfAttributes(pOwner, 1);
}
}
/**
* process for.
*
* @param pOwner the owner
* @param pFor the for
*/
private void processFor(final ThemisStatsBase pOwner,
final ThemisAnalysisFor pFor) {
/* process the container */
processContainer(pOwner, pFor);
/* Adjust the owner stats */
adjustLinesOfCode(pOwner, pFor.getNumLines());
adjustNumberOfStatements(pOwner, 1);
}
/**
* process if.
*
* @param pOwner the owner
* @param pIf the if
*/
private void processIf(final ThemisStatsBase pOwner,
final ThemisAnalysisIf pIf) {
/* process the container */
processContainer(pOwner, pIf);
/* Adjust the owner stats */
adjustLinesOfCode(pOwner, pIf.getNumLines());
adjustNumberOfStatements(pOwner, 1);
/* Handle else */
final ThemisAnalysisElse myElse = pIf.getElse();
if (myElse != null) {
processElse(pOwner, myElse);
}
}
/**
* process else.
*
* @param pOwner the owner
* @param pElse the else
*/
private void processElse(final ThemisStatsBase pOwner,
final ThemisAnalysisElse pElse) {
/* process the container */
processContainer(pOwner, pElse);
/* Adjust the owner stats */
adjustLinesOfCode(pOwner, pElse.getNumLines());
adjustNumberOfStatements(pOwner, 1);
/* Handle additional else */
final ThemisAnalysisElse myElse = pElse.getElse();
if (myElse != null) {
processElse(pOwner, myElse);
}
}
/**
* process imports.
*
* @param pOwner the owner
* @param pImports the imports
*/
private static void processImports(final ThemisStatsBase pOwner,
final ThemisAnalysisImports pImports) {
/* Adjust the stats */
adjustLinesOfCode(pOwner, pImports.getNumLines());
adjustNumberOfStatements(pOwner, pImports.getNumLines());
}
/**
* process interface.
*
* @param pOwner the owner
* @param pInterface the interface
*/
private void processInterface(final ThemisStatsBase pOwner,
final ThemisAnalysisInterface pInterface) {
/* Parse the interface */
final ThemisStatsClass myIFace = parseClass(pOwner, pInterface);
/* Adjust owner statistics */
pOwner.incrementStat(ThemisStat.NIN);
/* Adjust the stats and add to owner */
adjustLinesOfCode(myIFace, pInterface.getNumLines());
adjustNumberOfStatements(pOwner, 1);
pOwner.addClass(myIFace);
}
/**
* process lambda.
*
* @param pOwner the owner
* @param pLambda the lambda
*/
private void processLambda(final ThemisStatsBase pOwner,
final ThemisAnalysisLambda pLambda) {
/* process the container */
processContainer(pOwner, pLambda);
/* Adjust the owner stats */
adjustLinesOfCode(pOwner, pLambda.getNumLines());
}
/**
* process method.
*
* @param pOwner the owner
* @param pMethod the method
*/
private void processMethod(final ThemisStatsBase pOwner,
final ThemisAnalysisMethod pMethod) {
/* Parse the method */
final ThemisStatsMethod myMethod = parseMethod(pOwner, pMethod);
/* Adjust owner statistics */
pOwner.incrementStat(ThemisStat.NM);
/* Adjust the lines of code and add to owner */
adjustLinesOfCode(myMethod, pMethod.getNumLines());
adjustNumberOfStatements(pOwner, 1);
pOwner.addMethod(myMethod);
}
/**
* process package.
*
* @param pOwner the owner
*/
private static void processPackage(final ThemisStatsBase pOwner) {
/* Adjust the stats */
adjustLinesOfCode(pOwner, 1);
}
/**
* process statement.
*
* @param pOwner the owner
* @param pStatement the statement
*/
private static void processStatement(final ThemisStatsBase pOwner,
final ThemisAnalysisStatement pStatement) {
/* Adjust the owner stats */
adjustLinesOfCode(pOwner, pStatement.getNumLines());
adjustNumberOfStatements(pOwner, 1);
}
/**
* process switch.
*
* @param pOwner the owner
* @param pSwitch the switch
*/
private void processSwitch(final ThemisStatsBase pOwner,
final ThemisAnalysisSwitch pSwitch) {
/* process the container */
processContainer(pOwner, pSwitch);
/* Adjust the owner stats */
adjustLinesOfCode(pOwner, pSwitch.getNumLines());
adjustNumberOfStatements(pOwner, 1);
}
/**
* process case.
*
* @param pOwner the owner
* @param pCase the case
*/
private void processCase(final ThemisStatsBase pOwner,
final ThemisAnalysisCase pCase) {
/* process the container */
processContainer(pOwner, pCase);
/* Adjust the owner stats */
adjustLinesOfCode(pOwner, pCase.getNumLines());
}
/**
* process try.
*
* @param pOwner the owner
* @param pTry the try
*/
private void processTry(final ThemisStatsBase pOwner,
final ThemisAnalysisTry pTry) {
/* process the container */
processContainer(pOwner, pTry);
/* Adjust the owner stats */
adjustLinesOfCode(pOwner, pTry.getNumLines());
/* Handle catch */
final ThemisAnalysisCatch myCatch = pTry.getCatch();
if (myCatch != null) {
processCatch(pOwner, myCatch);
}
/* Handle finally */
final ThemisAnalysisFinally myFinally = pTry.getFinally();
if (myFinally != null) {
processFinally(pOwner, myFinally);
}
}
/**
* process catch.
*
* @param pOwner the owner
* @param pCatch the catch
*/
private void processCatch(final ThemisStatsBase pOwner,
final ThemisAnalysisCatch pCatch) {
/* process the container */
processContainer(pOwner, pCatch);
/* Adjust the owner stats */
adjustLinesOfCode(pOwner, pCatch.getNumLines());
adjustNumberOfStatements(pOwner, 1);
/* Handle additional catch */
final ThemisAnalysisCatch myCatch = pCatch.getCatch();
if (myCatch != null) {
processCatch(pOwner, myCatch);
}
}
/**
* process finally.
*
* @param pOwner the owner
* @param pFinally the finally
*/
private void processFinally(final ThemisStatsBase pOwner,
final ThemisAnalysisFinally pFinally) {
/* process the container */
processContainer(pOwner, pFinally);
/* Adjust the owner stats */
adjustLinesOfCode(pOwner, pFinally.getNumLines());
adjustNumberOfStatements(pOwner, 1);
}
/**
* process while.
*
* @param pOwner the owner
* @param pWhile the while
*/
private void processWhile(final ThemisStatsBase pOwner,
final ThemisAnalysisWhile pWhile) {
/* process the container */
processContainer(pOwner, pWhile);
/* Adjust the owner stats */
adjustLinesOfCode(pOwner, pWhile.getNumLines());
adjustNumberOfStatements(pOwner, 1);
}
/**
* adjust lines of Code.
*
* @param pOwner the owner
* @param pCount the line count
*/
private static void adjustLinesOfCode(final ThemisStatsBase pOwner,
final int pCount) {
/* Adjust the stats */
pOwner.adjustStat(ThemisStat.LOC, pCount);
pOwner.adjustStat(ThemisStat.LLOC, pCount);
}
/**
* adjust number of statements.
*
* @param pOwner the owner
* @param pCount the statement count
*/
private static void adjustNumberOfStatements(final ThemisStatsBase pOwner,
final int pCount) {
/* Adjust the stats */
pOwner.adjustStat(ThemisStat.NOS, pCount);
}
/**
* adjust number of attributes.
*
* @param pOwner the owner
* @param pCount the attribute count
*/
private static void adjustNumberOfAttributes(final ThemisStatsBase pOwner,
final int pCount) {
/* Adjust the stats */
pOwner.adjustStat(ThemisStat.NA, pCount);
adjustNumberOfStatements(pOwner, pCount);
}
/**
* adjust cached items.
*
* @param pElement the item
*/
private void adjustCachedItems(final ThemisAnalysisElement pElement) {
/* Adjust for Annotation */
if (pElement instanceof ThemisAnalysisAnnotation myAnnotation) {
theCachedAnnotation = myAnnotation;
/* Adjust for documentation comment */
} else if (pElement instanceof ThemisAnalysisComment myComment
&& myComment.isJavaDoc()) {
theCachedDocComment = myComment;
theCachedAnnotation = null;
/* else reset cache */
} else {
resetCache();
}
}
/**
* process cached items.
*
* @param pParent the parent
* @param pOwner the stats owner
*/
private void processCachedItems(final ThemisStatsBase pParent,
final ThemisStatsBase pOwner) {
/* If we have an annotation */
if (theCachedAnnotation != null) {
/* transfer stats from parent */
processAnnotation(pParent, theCachedAnnotation, false);
processAnnotation(pOwner, theCachedAnnotation, true);
}
/* If we have a documentation comment */
if (theCachedDocComment != null) {
/* transfer stats from parent */
processComment(pParent, theCachedDocComment, false);
processComment(pOwner, theCachedDocComment, true);
}
/* reset cache */
resetCache();
}
/**
* resetCache.
*/
private void resetCache() {
theCachedDocComment = null;
theCachedAnnotation = null;
}
}