ThemisXAnalysisExprAnonClass.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.xanalysis.parser.expr;

import com.github.javaparser.JavaToken;
import com.github.javaparser.Range;
import com.github.javaparser.TokenRange;
import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.NodeList;
import com.github.javaparser.ast.body.BodyDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.expr.ObjectCreationExpr;
import com.github.javaparser.ast.expr.SimpleName;
import com.github.javaparser.ast.type.ClassOrInterfaceType;

/**
 * Anonymous class JavaParser representation.
 */
public class ThemisXAnalysisExprAnonClass
        extends ClassOrInterfaceDeclaration {
    /**
     * The anonymous class name.
     */
    private static final String ANON = "$Anon$";

    /**
     * Constructor.
     *
     * @param pExpression the object creation instance
     */
    ThemisXAnalysisExprAnonClass(final ObjectCreationExpr pExpression) {
        /* Set the anonymous class name */
        setName(new SimpleName(ANON));

        /* Set the expression type as the extended type */
        final NodeList<ClassOrInterfaceType> myExtended = new NodeList<>();
        myExtended.add(pExpression.getType().clone());
        setExtendedTypes(myExtended);

        /* Initialise ranges */
        Range myRange = null;
        TokenRange myTokens = TokenRange.INVALID;

        /* Set the members */
        for (BodyDeclaration<?> myBody : pExpression.getAnonymousClassBody().orElse(new NodeList<>())) {
            /* Add clone to list and adjust ranges */
            addMember(myBody.clone());
            myRange = adjustRange(myRange, myBody);
            myTokens = adjustTokenRange(myTokens, myBody);
        }

        /* Record range details */
        if (myRange != null) {
            setRange(myRange);
        }
        if (!TokenRange.INVALID.equals(myTokens)) {
            setTokenRange(myTokens);
        }
    }

    /**
     * Adjust range.
     *
     * @param pRange existing range
     * @param pNode  the node to add
     * @return the new range
     */
    private Range adjustRange(final Range pRange,
                              final Node pNode) {
        /* Handle the case where the new node does not have a range */
        final Range myRange = pNode.getRange().orElse(null);
        if (myRange == null) {
            return pRange;
        }

        /* Handle initial range */
        if (pRange == null) {
            return myRange;
        }

        /* Extend range as necessary */
        Range myResult = pRange;
        if (myResult.begin.isAfter(myRange.begin)) {
            myResult = myResult.withBegin(myRange.begin);
        }
        if (myResult.end.isBefore(myRange.end)) {
            myResult = myResult.withEnd(myRange.end);
        }
        return myResult;
    }

    /**
     * Adjust token range.
     *
     * @param pRange existing range
     * @param pNode  the node to add
     * @return the new range
     */
    private TokenRange adjustTokenRange(final TokenRange pRange,
                                        final Node pNode) {
        /* Handle the case where the new node does not have a tokenRange */
        final TokenRange myRange = pNode.getTokenRange().orElse(TokenRange.INVALID);
        if (TokenRange.INVALID.equals(myRange)) {
            return pRange;
        }

        /* Handle initial range */
        if (TokenRange.INVALID.equals(pRange)) {
            return myRange;
        }

        /* Extend range as necessary */
        TokenRange myResult = pRange;
        if (badOrder(myResult.getBegin(), myRange.getBegin())) {
            myResult = myResult.withBegin(myRange.getBegin());
        }
        if (badOrder(myRange.getEnd(), myResult.getEnd())) {
            myResult = myResult.withEnd(myRange.getEnd());
        }
        return myResult;
    }

    /**
     * Compare two tokens for order.
     *
     * @param pFirst  the first token
     * @param pSecond the second token
     * @return are the two tokens out of order true/false
     */
    private boolean badOrder(final JavaToken pFirst,
                             final JavaToken pSecond) {
        final Range myFirst = pFirst.getRange().orElse(null);
        final Range mySecond = pSecond.getRange().orElse(null);
        if (mySecond == null) {
            return true;
        } else if (myFirst == null) {
            return false;
        }
        return myFirst.isAfter(mySecond);
    }
}