OceanusProfile.java

/*******************************************************************************
 * Oceanus: Java Utilities
 * Copyright 2012,2025 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 net.sourceforge.joceanus.oceanus.profile;

import net.sourceforge.joceanus.oceanus.decimal.OceanusDecimal;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * Profile data.
 */
public class OceanusProfile {
    /**
     * number of decimals for elapsed.
     */
    private static final int NUM_DECIMALS = 6;

    /**
     * Step name.
     */
    private final String theName;

    /**
     * Status.
     */
    private OceanusProfileStatus theStatus;

    /**
     * Start time.
     */
    private final long theStart;

    /**
     * Elapsed (in milliseconds).
     */
    private OceanusDecimal theElapsed;

    /**
     * Hidden Elapsed (in milliseconds).
     */
    private OceanusDecimal theHidden;

    /**
     * Current subTask.
     */
    private OceanusProfile theCurrentTask;

    /**
     * List of subTasks.
     */
    private List<OceanusProfile> theSubTasks;

    /**
     * Constructor.
     * @param pName the name of the step
     */
    public OceanusProfile(final String pName) {
        /* Record the name and start the timer */
        theName = pName;
        theStart = System.nanoTime();
        theStatus = OceanusProfileStatus.RUNNING;
    }

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

    /**
     * Obtain the status of the profile.
     * @return the status
     */
    public OceanusProfileStatus getStatus() {
        return theStatus.isRunning()
                ? theStatus
                : null;
    }

    /**
     * Obtain the elapsed time of the profile.
     * @return the elapsedTime
     */
    public OceanusDecimal getElapsed() {
        return theStatus.isRunning()
                ? null
                : theElapsed;
    }

    /**
     * Obtain the hidden time of the profile.
     * @return the hiddenTime
     */
    public OceanusDecimal getHidden() {
        return theHidden;
    }

    /**
     * Obtain the subtask iterator.
     * @return the iterator
     */
    public Iterator<OceanusProfile> subTaskIterator() {
        return theSubTasks == null ? Collections.emptyIterator() : theSubTasks.iterator();
    }

    /**
     * Start a new subTask.
     * @param pName the name of the subTask
     * @return the new task
     */
    public OceanusProfile startTask(final String pName) {
        /* If we are currently running */
        if (theStatus.isRunning()) {
            /* Prepare for the task */
            prepareForTask();

            /* Loop through the subTasks */
            for (OceanusProfile myProfile : theSubTasks) {
                /* Check for duplicate name */
                if (pName.equals(myProfile.getName())) {
                    throw new IllegalArgumentException("Duplicate Task - " + pName);
                }
            }

            /* Create the new task */
            final OceanusProfile myTask = new OceanusProfile(pName);
            theSubTasks.add(myTask);
            theCurrentTask = myTask;
        }

        /* Return the current task */
        return theCurrentTask;
    }

    /**
     * Prepare for the task.
     */
    private void prepareForTask() {
        /* End any subTask */
        endSubTask();

        /* If we do not currently have a subTask list */
        if (theSubTasks == null) {
            /* Create the list */
            theSubTasks = new ArrayList<>();
        }
    }

    /**
     * End any subTask.
     */
    private void endSubTask() {
        /* If we have a subTask */
        if (theCurrentTask != null) {
            /* End the current task */
            theCurrentTask.end();
            theCurrentTask = null;
        }
    }

    /**
     * End the task.
     */
    public void end() {
        /* If we are currently running */
        if (theStatus.isRunning()) {
            /* End any subTasks */
            endSubTask();

            /* Stop the task and calculate the elapsed time */
            final long myEnd = System.nanoTime();
            theElapsed = new OceanusDecimal(myEnd - theStart, NUM_DECIMALS);
            theHidden = theSubTasks == null
                    ? null
                    : calculateHidden();

            /* Mark time as stopped */
            theStatus = OceanusProfileStatus.STOPPED;
        }
    }

    /**
     * Calculate the hidden time.
     * @return the hidden time
     */
    private OceanusDecimal calculateHidden() {
        /* Initialise hidden value */
        final OceanusDecimal myHidden = new OceanusDecimal(theElapsed);

        /* Loop through the subTasks */
        for (OceanusProfile myProfile : theSubTasks) {
            /* Subtract child time */
            myHidden.subtractValue(myProfile.theElapsed);
        }

        /* Return calculated value */
        return myHidden;
    }

    /**
     * is the task running?
     * @return true/false.
     */
    public boolean isRunning() {
        /* return status */
        return theStatus.isRunning();
    }

    /**
     * Obtain the currently active task.
     * @return the task
     */
    public OceanusProfile getActiveTask() {
        /* If we are not currently running */
        if (!isRunning()) {
            return null;
        }

        /* Return self is no active and running subTask else ask subTask */
        return theCurrentTask == null
                || !theCurrentTask.isRunning()
                ? this
                : theCurrentTask.getActiveTask();
    }

    /**
     * Status of timer.
     */
    public enum OceanusProfileStatus {
        /**
         * Running.
         */
        RUNNING,

        /**
         * Stopped.
         */
        STOPPED;

        /**
         * is the timer running?
         * @return true/false
         */
        private boolean isRunning() {
            switch (this) {
                case RUNNING:
                    return true;
                case STOPPED:
                default:
                    return false;
            }
        }
    }
}