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