1 /*
2 * Oceanus: Java Utilities
3 * Copyright 2012-2026. Tony Washer
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6 * use this file except in compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 * License for the specific language governing permissions and limitations under
15 * the License.
16 */
17 package io.github.tonywasher.joceanus.oceanus.profile;
18
19 import io.github.tonywasher.joceanus.oceanus.decimal.OceanusDecimal;
20
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.Iterator;
24 import java.util.List;
25
26 /**
27 * Profile data.
28 */
29 public class OceanusProfile {
30 /**
31 * number of decimals for elapsed.
32 */
33 private static final int NUM_DECIMALS = 6;
34
35 /**
36 * Step name.
37 */
38 private final String theName;
39
40 /**
41 * Status.
42 */
43 private OceanusProfileStatus theStatus;
44
45 /**
46 * Start time.
47 */
48 private final long theStart;
49
50 /**
51 * Elapsed (in milliseconds).
52 */
53 private OceanusDecimal theElapsed;
54
55 /**
56 * Hidden Elapsed (in milliseconds).
57 */
58 private OceanusDecimal theHidden;
59
60 /**
61 * Current subTask.
62 */
63 private OceanusProfile theCurrentTask;
64
65 /**
66 * List of subTasks.
67 */
68 private List<OceanusProfile> theSubTasks;
69
70 /**
71 * Constructor.
72 *
73 * @param pName the name of the step
74 */
75 public OceanusProfile(final String pName) {
76 /* Record the name and start the timer */
77 theName = pName;
78 theStart = System.nanoTime();
79 theStatus = OceanusProfileStatus.RUNNING;
80 }
81
82 /**
83 * Obtain the name of the profile.
84 *
85 * @return the name
86 */
87 public String getName() {
88 return theName;
89 }
90
91 /**
92 * Obtain the status of the profile.
93 *
94 * @return the status
95 */
96 public OceanusProfileStatus getStatus() {
97 return theStatus.isRunning()
98 ? theStatus
99 : null;
100 }
101
102 /**
103 * Obtain the elapsed time of the profile.
104 *
105 * @return the elapsedTime
106 */
107 public OceanusDecimal getElapsed() {
108 return theStatus.isRunning()
109 ? null
110 : theElapsed;
111 }
112
113 /**
114 * Obtain the hidden time of the profile.
115 *
116 * @return the hiddenTime
117 */
118 public OceanusDecimal getHidden() {
119 return theHidden;
120 }
121
122 /**
123 * Obtain the subtask iterator.
124 *
125 * @return the iterator
126 */
127 public Iterator<OceanusProfile> subTaskIterator() {
128 return theSubTasks == null ? Collections.emptyIterator() : theSubTasks.iterator();
129 }
130
131 /**
132 * Start a new subTask.
133 *
134 * @param pName the name of the subTask
135 * @return the new task
136 */
137 public OceanusProfile startTask(final String pName) {
138 /* If we are currently running */
139 if (theStatus.isRunning()) {
140 /* Prepare for the task */
141 prepareForTask();
142
143 /* Loop through the subTasks */
144 for (OceanusProfile myProfile : theSubTasks) {
145 /* Check for duplicate name */
146 if (pName.equals(myProfile.getName())) {
147 throw new IllegalArgumentException("Duplicate Task - " + pName);
148 }
149 }
150
151 /* Create the new task */
152 final OceanusProfile myTask = new OceanusProfile(pName);
153 theSubTasks.add(myTask);
154 theCurrentTask = myTask;
155 }
156
157 /* Return the current task */
158 return theCurrentTask;
159 }
160
161 /**
162 * Prepare for the task.
163 */
164 private void prepareForTask() {
165 /* End any subTask */
166 endSubTask();
167
168 /* If we do not currently have a subTask list */
169 if (theSubTasks == null) {
170 /* Create the list */
171 theSubTasks = new ArrayList<>();
172 }
173 }
174
175 /**
176 * End any subTask.
177 */
178 private void endSubTask() {
179 /* If we have a subTask */
180 if (theCurrentTask != null) {
181 /* End the current task */
182 theCurrentTask.end();
183 theCurrentTask = null;
184 }
185 }
186
187 /**
188 * End the task.
189 */
190 public void end() {
191 /* If we are currently running */
192 if (theStatus.isRunning()) {
193 /* End any subTasks */
194 endSubTask();
195
196 /* Stop the task and calculate the elapsed time */
197 final long myEnd = System.nanoTime();
198 theElapsed = new OceanusDecimal(myEnd - theStart, NUM_DECIMALS);
199 theHidden = theSubTasks == null
200 ? null
201 : calculateHidden();
202
203 /* Mark time as stopped */
204 theStatus = OceanusProfileStatus.STOPPED;
205 }
206 }
207
208 /**
209 * Calculate the hidden time.
210 *
211 * @return the hidden time
212 */
213 private OceanusDecimal calculateHidden() {
214 /* Initialise hidden value */
215 final OceanusDecimal myHidden = new OceanusDecimal(theElapsed);
216
217 /* Loop through the subTasks */
218 for (OceanusProfile myProfile : theSubTasks) {
219 /* Subtract child time */
220 myHidden.subtractValue(myProfile.theElapsed);
221 }
222
223 /* Return calculated value */
224 return myHidden;
225 }
226
227 /**
228 * is the task running?
229 *
230 * @return true/false.
231 */
232 public boolean isRunning() {
233 /* return status */
234 return theStatus.isRunning();
235 }
236
237 /**
238 * Obtain the currently active task.
239 *
240 * @return the task
241 */
242 public OceanusProfile getActiveTask() {
243 /* If we are not currently running */
244 if (!isRunning()) {
245 return null;
246 }
247
248 /* Return self is no active and running subTask else ask subTask */
249 return theCurrentTask == null
250 || !theCurrentTask.isRunning()
251 ? this
252 : theCurrentTask.getActiveTask();
253 }
254
255 /**
256 * Status of timer.
257 */
258 public enum OceanusProfileStatus {
259 /**
260 * Running.
261 */
262 RUNNING,
263
264 /**
265 * Stopped.
266 */
267 STOPPED;
268
269 /**
270 * is the timer running?
271 *
272 * @return true/false
273 */
274 private boolean isRunning() {
275 return this == RUNNING;
276 }
277 }
278 }
279