ThemisSourcePanel.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.ui;

import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
import io.github.tonywasher.joceanus.tethys.api.base.TethysUIComponent;
import io.github.tonywasher.joceanus.tethys.api.control.TethysUIHTMLManager;
import io.github.tonywasher.joceanus.tethys.api.control.TethysUISplitTreeManager;
import io.github.tonywasher.joceanus.tethys.api.control.TethysUITreeManager;
import io.github.tonywasher.joceanus.tethys.api.control.TethysUITreeManager.TethysUITreeItem;
import io.github.tonywasher.joceanus.tethys.api.factory.TethysUIFactory;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisAnonClass;
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.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.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.ThemisAnalysisIf.ThemisIteratorChain;
import io.github.tonywasher.joceanus.themis.lethe.analysis.ThemisAnalysisIf.ThemisReducedIterator;
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;

import java.util.Collections;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Source Panel.
 */
public class ThemisSourcePanel {
    /**
     * The Next entryId.
     */
    private static final AtomicInteger NEXT_ENTRY_ID = new AtomicInteger(1);

    /**
     * The Allowed File children.
     */
    private static final Class<?>[] FILE_CHILDREN = {
            ThemisAnalysisClass.class,
            ThemisAnalysisInterface.class,
            ThemisAnalysisEnum.class
    };

    /**
     * The Allowed Class children.
     */
    private static final Class<?>[] CLASS_CHILDREN = {
            ThemisAnalysisClass.class,
            ThemisAnalysisInterface.class,
            ThemisAnalysisEnum.class,
            ThemisAnalysisMethod.class,
            ThemisAnalysisField.class,
            ThemisAnalysisBlock.class
    };

    /**
     * The Allowed Code children.
     */
    private static final Class<?>[] CODE_CHILDREN = {
            ThemisAnalysisClass.class,
            ThemisAnalysisInterface.class,
            ThemisAnalysisEnum.class,
            ThemisAnalysisIf.class,
            ThemisAnalysisFor.class,
            ThemisAnalysisDoWhile.class,
            ThemisAnalysisWhile.class,
            ThemisAnalysisSwitch.class,
            ThemisAnalysisTry.class,
            ThemisAnalysisStatement.class,
            ThemisAnalysisField.class,
            ThemisAnalysisBlock.class
    };

    /**
     * The Switch Code children.
     */
    private static final Class<?>[] SWITCH_CHILDREN = {
            ThemisAnalysisCase.class
    };

    /**
     * The SplitTree Manager.
     */
    private final TethysUISplitTreeManager<ThemisSourceEntry> theSplitTree;

    /**
     * The Tree Manager.
     */
    private final TethysUITreeManager<ThemisSourceEntry> theTree;

    /**
     * The HTML Manager.
     */
    private final TethysUIHTMLManager theHTML;

    /**
     * The Current root.
     */
    private TethysUITreeItem<ThemisSourceEntry> theRoot;

    /**
     * Constructor.
     *
     * @param pFactory the GuiFactory
     * @throws OceanusException on error
     */
    public ThemisSourcePanel(final TethysUIFactory<?> pFactory) throws OceanusException {
        /* Create the splitTree Manager */
        theSplitTree = pFactory.controlFactory().newSplitTreeManager();
        theTree = theSplitTree.getTreeManager();
        theHTML = theSplitTree.getHTMLManager();
        theHTML.setCSSContent(ThemisDSMStyleSheet.CSS_DSM);
        theTree.setVisible(true);
    }

    /**
     * Obtain the component.
     *
     * @return the component
     */
    public TethysUIComponent getComponent() {
        return theSplitTree;
    }

    /**
     * Get NextId.
     *
     * @return the next id
     */
    static int getNextId() {
        return NEXT_ENTRY_ID.getAndIncrement();
    }

    /**
     * Initialise tree.
     *
     * @param pProject the project
     */
    void initialiseTree(final ThemisAnalysisProject pProject) {
        /* If there is currently a root item */
        if (theRoot != null) {
            /* Hide it */
            theRoot.setVisible(false);
        }

        /* Create root item for project */
        final ThemisSourceEntry myEntry = new ThemisSourceEntry(pProject);
        theRoot = theTree.addRootItem(myEntry.getUniqueName(), myEntry);
        theRoot.setIcon(ThemisSourceIcon.getElementIcon(pProject));
        theRoot.setVisible(true);

        /* Create child entries */
        createChildEntries(theRoot, pProject);
    }

    /**
     * Create child entries tree.
     *
     * @param pParent the parent
     * @param pChild  the child
     */
    void createChildEntries(final TethysUITreeItem<ThemisSourceEntry> pParent,
                            final ThemisAnalysisElement pChild) {
        /* Loop through the root children */
        final Iterator<? extends ThemisAnalysisElement> myIterator = childIterator(pChild);
        while (myIterator.hasNext()) {
            final ThemisAnalysisElement myChild = myIterator.next();

            /* Create a new root entry */
            final ThemisSourceEntry myEntry = new ThemisSourceEntry(pParent.getItem(), myChild);
            final TethysUITreeItem<ThemisSourceEntry> myTreeItem = theTree.addChildItem(pParent, myEntry.getUniqueName(), myEntry);
            myTreeItem.setIcon(ThemisSourceIcon.getElementIcon(myChild));
            myTreeItem.setVisible(true);

            /* Create child entries */
            createChildEntries(myTreeItem, myChild);
        }
    }

    /**
     * Derive list of children for an element.
     *
     * @param pElement the project
     * @return the list of children
     */
    private static Iterator<ThemisAnalysisElement> childIterator(final ThemisAnalysisElement pElement) {
        if (pElement instanceof ThemisAnalysisProject myProject) {
            return projectIterator(myProject);
        }
        if (pElement instanceof ThemisAnalysisModule myModule) {
            return moduleIterator(myModule);
        }
        if (pElement instanceof ThemisAnalysisPackage myPackage) {
            return packageIterator(myPackage);
        }
        if (pElement instanceof ThemisAnalysisFile myFile) {
            return allowedIterator(myFile, FILE_CHILDREN);
        }
        if (pElement instanceof ThemisAnalysisClass
                || pElement instanceof ThemisAnalysisAnonClass
                || pElement instanceof ThemisAnalysisInterface
                || pElement instanceof ThemisAnalysisEnum) {
            return allowedIterator((ThemisAnalysisObject) pElement, CLASS_CHILDREN);
        }
        if (pElement instanceof ThemisAnalysisMethod
                || pElement instanceof ThemisAnalysisLambda
                || pElement instanceof ThemisAnalysisBlock) {
            return allowedIterator((ThemisAnalysisContainer) pElement, CODE_CHILDREN);
        }
        if (pElement instanceof ThemisAnalysisFor
                || pElement instanceof ThemisAnalysisWhile
                || pElement instanceof ThemisAnalysisDoWhile) {
            return allowedIterator((ThemisAnalysisContainer) pElement, CODE_CHILDREN);
        }
        if (pElement instanceof ThemisAnalysisTry myTry) {
            return tryIterator(myTry);
        }
        if (pElement instanceof ThemisAnalysisIf myIf) {
            return ifIterator(myIf);
        }
        if (pElement instanceof ThemisAnalysisSwitch) {
            return allowedIterator((ThemisAnalysisContainer) pElement, SWITCH_CHILDREN);
        }
        if (pElement instanceof ThemisAnalysisCase
                || pElement instanceof ThemisAnalysisElse) {
            return allowedIterator((ThemisAnalysisContainer) pElement, CODE_CHILDREN);
        }
        if (pElement instanceof ThemisAnalysisCatch
                || pElement instanceof ThemisAnalysisFinally) {
            return allowedIterator((ThemisAnalysisContainer) pElement, CODE_CHILDREN);
        }
        if (pElement instanceof ThemisAnalysisField myField) {
            return fieldIterator(myField);
        }
        if (pElement instanceof ThemisAnalysisStatement myStatement) {
            return statementIterator(myStatement);
        }
        return Collections.emptyIterator();
    }

    /**
     * Derive list of children for a project.
     *
     * @param pProject the project
     * @return the list of children
     */
    private static Iterator<ThemisAnalysisElement> projectIterator(final ThemisAnalysisProject pProject) {
        return new ThemisReducedIterator<>(pProject.getModules().iterator());
    }

    /**
     * Derive list of children for a module.
     *
     * @param pModule the module
     * @return the list of children
     */
    private static Iterator<ThemisAnalysisElement> moduleIterator(final ThemisAnalysisModule pModule) {
        return new ThemisReducedIterator<>(pModule.getPackages().iterator());
    }

    /**
     * Derive list of children for a package.
     *
     * @param pPackage the package
     * @return the list of children
     */
    private static Iterator<ThemisAnalysisElement> packageIterator(final ThemisAnalysisPackage pPackage) {
        return new ThemisReducedIterator<>(pPackage.getFiles().iterator());
    }

    /**
     * Derive list of children for a container.
     *
     * @param pContainer the container
     * @param pAllowed   the allowed array.
     * @return the list of children
     */
    private static Iterator<ThemisAnalysisElement> allowedIterator(final ThemisAnalysisContainer pContainer,
                                                                   final Class<?>[] pAllowed) {
        return pContainer.getContents().stream().filter(e -> filterAllowed(e, pAllowed)).iterator();
    }

    /**
     * Derive list of children for a try.
     *
     * @param pTry the try
     * @return the list of children
     */
    private static Iterator<ThemisAnalysisElement> tryIterator(final ThemisAnalysisTry pTry) {
        Iterator<ThemisAnalysisElement> myIterator = pTry.getContents().stream().filter(e -> filterAllowed(e, CODE_CHILDREN)).iterator();
        ThemisAnalysisCatch myCatch = pTry.getCatch();
        while (myCatch != null) {
            myIterator = new ThemisIteratorChain<>(myIterator, Collections.singleton((ThemisAnalysisElement) myCatch).iterator());
            myCatch = myCatch.getCatch();
        }
        final ThemisAnalysisFinally myFinally = pTry.getFinally();
        if (myFinally != null) {
            myIterator = new ThemisIteratorChain<>(myIterator, Collections.singleton((ThemisAnalysisElement) myFinally).iterator());
        }
        return myIterator;
    }

    /**
     * Derive list of children for an if.
     *
     * @param pIf the if
     * @return the list of children
     */
    private static Iterator<ThemisAnalysisElement> ifIterator(final ThemisAnalysisIf pIf) {
        Iterator<ThemisAnalysisElement> myIterator = pIf.getContents().stream().filter(e -> filterAllowed(e, CODE_CHILDREN)).iterator();
        ThemisAnalysisElse myElse = pIf.getElse();
        while (myElse != null) {
            myIterator = new ThemisIteratorChain<>(myIterator, Collections.singleton((ThemisAnalysisElement) myElse).iterator());
            myElse = myElse.getElse();
        }
        return myIterator;
    }

    /**
     * Derive list of children for a field.
     *
     * @param pField the field
     * @return the list of children
     */
    private static Iterator<ThemisAnalysisElement> fieldIterator(final ThemisAnalysisField pField) {
        return new ThemisReducedIterator<>(pField.statementIterator());
    }

    /**
     * Derive list of children for a statement.
     *
     * @param pStatement the statement
     * @return the list of children
     */
    private static Iterator<ThemisAnalysisElement> statementIterator(final ThemisAnalysisStatement pStatement) {
        final ThemisAnalysisElement myEmbedded = pStatement.getEmbedded();
        return myEmbedded != null
                ? Collections.singleton(myEmbedded).iterator()
                : Collections.emptyIterator();
    }

    /**
     * Filter allowed objects.
     *
     * @param pElement the element
     * @param pAllowed the allowed array.
     * @return allowedd true/false
     */
    private static boolean filterAllowed(final ThemisAnalysisElement pElement,
                                         final Class<?>[] pAllowed) {
        /* Loop through the allowed list */
        for (Class<?> myClazz : pAllowed) {
            /* If the child is allowed */
            if (myClazz.isInstance(pElement)) {
                return true;
            }
        }

        /* Not allowed */
        return false;
    }
}