ThemisAnalysisMethod.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 java.util.ArrayDeque;
import java.util.Deque;

/**
 * Method Representation.
 */
public class ThemisAnalysisMethod
        implements ThemisAnalysisContainer {
    /**
     * The name of the class.
     */
    private final String theName;

    /**
     * The reference of the class.
     */
    private final ThemisAnalysisReference theReference;

    /**
     * The parent.
     */
    private ThemisAnalysisContainer theParent;

    /**
     * The properties.
     */
    private final ThemisAnalysisProperties theProperties;

    /**
     * The contents.
     */
    private final Deque<ThemisAnalysisElement> theContents;

    /**
     * The dataMap.
     */
    private ThemisAnalysisDataMap theDataMap;

    /**
     * The parameters.
     */
    private final ThemisAnalysisParameters theParameters;

    /**
     * The number of lines.
     */
    private final int theNumLines;

    /**
     * Is this an initialiser?
     */
    private final boolean isInitializer;

    /**
     * Constructor.
     *
     * @param pParser    the parser
     * @param pName      the method name
     * @param pReference the reference
     * @param pLine      the initial method line
     * @throws OceanusException on error
     */
    ThemisAnalysisMethod(final ThemisAnalysisParser pParser,
                         final String pName,
                         final ThemisAnalysisReference pReference,
                         final ThemisAnalysisLine pLine) throws OceanusException {
        /* Store parameters */
        isInitializer = pName.length() == 0;
        theName = isInitializer ? pReference.toString() : pName;
        theReference = pReference;
        theProperties = pLine.getProperties();

        /* Access details from parser */
        theParent = pParser.getParent();
        theDataMap = new ThemisAnalysisDataMap(theParent.getDataMap());

        /* Determine whether this method is abstract */
        final boolean isInterface = theParent instanceof ThemisAnalysisInterface;
        final boolean markedDefault = theProperties.hasModifier(ThemisAnalysisModifier.DEFAULT);
        final boolean markedStatic = theProperties.hasModifier(ThemisAnalysisModifier.STATIC);
        final boolean markedAbstract = theProperties.hasModifier(ThemisAnalysisModifier.ABSTRACT);
        final boolean isAbstract = markedAbstract || (isInterface && !markedDefault && !markedStatic);

        /* Parse the headers */
        theContents = new ArrayDeque<>();
        final Deque<ThemisAnalysisElement> myHeaders = isAbstract
                ? ThemisAnalysisBuilder.parseTrailers(pParser, pLine)
                : parseHeaders(pParser, pLine);
        theNumLines = myHeaders.size() + (isAbstract ? 0 : 1);

        /* Create a parser */
        final ThemisAnalysisParser myParser = new ThemisAnalysisParser(myHeaders, theContents, this);

        /* Resolve the generics */
        theProperties.resolveGeneric(myParser);
        theReference.resolveGeneric(myParser);

        /* resolve the parameters */
        theParameters = new ThemisAnalysisParameters(myParser, myHeaders);

        /* Post process the lines */
        postProcessLines();
    }

    /**
     * Constructor.
     *
     * @param pParser    the parser
     * @param pName      the method name
     * @param pReference the reference
     * @param pBody      the method body
     * @throws OceanusException on error
     */
    ThemisAnalysisMethod(final ThemisAnalysisParser pParser,
                         final String pName,
                         final ThemisAnalysisReference pReference,
                         final ThemisAnalysisMethodBody pBody) throws OceanusException {
        /* Store parameters */
        final ThemisAnalysisLine myLine = pBody.getHeader();
        isInitializer = pName.length() == 0;
        theName = isInitializer ? pReference.toString() : pName;
        theReference = pReference;
        theProperties = myLine.getProperties();

        /* Access details from body */
        theParent = pBody.getParent();
        theDataMap = pBody.getDataMap();

        /* Record the headers */
        final Deque<ThemisAnalysisElement> myHeaders = new ArrayDeque<>();
        myHeaders.add(myLine);
        theNumLines = 2;

        /* Access the contents */
        theContents = pBody.getContents();

        /* Create a parser */
        final ThemisAnalysisParser myParser = new ThemisAnalysisParser(myHeaders, theContents, this);

        /* Resolve the generics */
        theProperties.resolveGeneric(myParser);
        theReference.resolveGeneric(myParser);

        /* resolve the parameters */
        theParameters = new ThemisAnalysisParameters(myParser, myHeaders);

        /* Post process the lines */
        postProcessLines();
    }

    /**
     * Parse Headers.
     *
     * @param pParser the parser
     * @param pLine   the line
     * @return the headers
     * @throws OceanusException on error
     */
    private Deque<ThemisAnalysisElement> parseHeaders(final ThemisAnalysisParser pParser,
                                                      final ThemisAnalysisLine pLine) throws OceanusException {
        /* Initialise details */
        final Deque<ThemisAnalysisElement> myHeaders = new ArrayDeque<>();
        ThemisAnalysisElement myElement = pLine;

        /* Add lines to header */
        while (myElement instanceof ThemisAnalysisLine) {
            myHeaders.add(myElement);
            myElement = pParser.popNextLine();
        }

        /* Must end with a method body */
        if (!(myElement instanceof ThemisAnalysisMethodBody)) {
            throw new ThemisDataException("Unexpected dataType");
        }

        /* Copy details from method body */
        final ThemisAnalysisMethodBody myBody = (ThemisAnalysisMethodBody) myElement;
        myHeaders.add(myBody.getHeader());
        theContents.addAll(myBody.getContents());
        theParent = myBody.getParent();
        theDataMap = myBody.getDataMap();

        /* return the headers */
        return myHeaders;
    }

    /**
     * Obtain the name.
     *
     * @return the name
     */
    public String getName() {
        return theName;
    }

    @Override
    public Deque<ThemisAnalysisElement> getContents() {
        return theContents;
    }

    @Override
    public ThemisAnalysisContainer getParent() {
        return this;
    }

    @Override
    public ThemisAnalysisDataMap getDataMap() {
        return theDataMap;
    }

    @Override
    public int getNumLines() {
        return theNumLines;
    }

    @Override
    public String toString() {
        return isInitializer
                ? getName() + theParameters.toString()
                : theReference.toString() + " " + getName() + theParameters.toString();
    }
}