1   /* Copyright 2002-2025 CS GROUP
2    * Licensed to CS GROUP (CS) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * CS licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy 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,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.orekit.propagation.sampling;
18  
19  import java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.Iterator;
22  import java.util.List;
23  
24  import org.hipparchus.CalculusFieldElement;
25  import org.orekit.propagation.FieldSpacecraftState;
26  import org.orekit.time.FieldAbsoluteDate;
27  
28  /** This class gathers several {@link OrekitStepHandler} instances into one.
29   *
30   * @author Luc Maisonobe
31   * @param <T> type of the field elements
32   */
33  public class FieldStepHandlerMultiplexer<T extends CalculusFieldElement<T>> implements FieldOrekitStepHandler<T> {
34  
35      /** Underlying step handlers. */
36      private final List<FieldOrekitStepHandler<T>> handlers;
37  
38      /** Target time. */
39      private FieldAbsoluteDate<T> target;
40  
41      /** Last known state. */
42      private FieldSpacecraftState<T> last;
43  
44      /** Simple constructor.
45       */
46      public FieldStepHandlerMultiplexer() {
47          handlers = new ArrayList<>();
48      }
49  
50      /** Add a handler for variable size step.
51       * <p>
52       * If propagation is ongoing (i.e. global {@link #init(FieldSpacecraftState, FieldAbsoluteDate)
53       * init} already called and global {@link #finish(FieldSpacecraftState) finish} not called
54       * yet), then the local {@link FieldOrekitStepHandler#init(FieldSpacecraftState, FieldAbsoluteDate)
55       * FieldOrekitStepHandler.init} method of the added handler will be called with the last
56       * known state, so the handler starts properly.
57       * </p>
58       * @param handler step handler to add
59       */
60      public void add(final FieldOrekitStepHandler<T> handler) {
61          handlers.add(handler);
62          if (last != null) {
63              // propagation is ongoing, we need to call init now for this handler
64              handler.init(last, target);
65          }
66      }
67  
68      /** Add a handler for fixed size step.
69       * <p>
70       * If propagation is ongoing (i.e. global {@link #init(FieldSpacecraftState, FieldAbsoluteDate)
71       * init} already called and global {@link #finish(FieldSpacecraftState) finish} not called
72       * yet), then the local {@link FieldOrekitFixedStepHandler#init(FieldSpacecraftState, FieldAbsoluteDate,
73       * CalculusFieldElement) FieldOrekitStepHandler.init} method of the added handler will be
74       * called with the last known state, so the handler starts properly.
75       * </p>
76       * @param h fixed stepsize (s)
77       * @param handler handler called at the end of each finalized step
78       * @since 11.0
79       */
80      public void add(final T h, final FieldOrekitFixedStepHandler<T> handler) {
81          final FieldOrekitStepHandler<T> normalized = new FieldOrekitStepNormalizer<>(h, handler);
82          handlers.add(normalized);
83          if (last != null) {
84              // propagation is ongoing, we need to call init now for this handler
85              normalized.init(last, target);
86          }
87      }
88  
89      /** Get an unmodifiable view of all handlers.
90       * <p>
91       * Note that if {@link FieldOrekitFixedStepHandler fixed step handlers} have
92       * been {@link #add(CalculusFieldElement, FieldOrekitFixedStepHandler)}, then they will
93       * show up wrapped within {@link FieldOrekitStepNormalizer step normalizers}.
94       * </p>
95       * @return an unmodifiable view of all handlers
96       * @since 11.0
97       */
98      public List<FieldOrekitStepHandler<T>> getHandlers() {
99          return Collections.unmodifiableList(handlers);
100     }
101 
102     /** Remove a handler.
103      * <p>
104      * If propagation is ongoing (i.e. global {@link #init(FieldSpacecraftState, FieldAbsoluteDate)
105      * init} already called and global {@link #finish(FieldSpacecraftState) finish} not called
106      * yet), then the local {@link FieldOrekitStepHandler#finish(FieldSpacecraftState)
107      * FieldOrekitStepHandler.finish} method of the removed handler will be called with the last
108      * known state, so the handler stops properly.
109      * </p>
110      * @param handler step handler to remove
111      * @since 11.0
112      */
113     public void remove(final FieldOrekitStepHandler<T> handler) {
114         final Iterator<FieldOrekitStepHandler<T>> iterator = handlers.iterator();
115         while (iterator.hasNext()) {
116             if (iterator.next() == handler) {
117                 if (last != null) {
118                     // propagation is ongoing, we need to call finish now for this handler
119                     handler.finish(last);
120                 }
121                 iterator.remove();
122                 return;
123             }
124         }
125     }
126 
127     /** Remove a handler.
128      * <p>
129      * If propagation is ongoing (i.e. global {@link #init(FieldSpacecraftState, FieldAbsoluteDate)
130      * init} already called and global {@link #finish(FieldSpacecraftState) finish} not called
131      * yet), then the local {@link FieldOrekitFixedStepHandler#finish(FieldSpacecraftState)
132      * FieldOrekitFixedStepHandler.finish} method of the removed handler will be called with the last
133      * known state, so the handler stops properly.
134      * </p>
135      * @param handler step handler to remove
136      * @since 11.0
137      */
138     public void remove(final FieldOrekitFixedStepHandler<T> handler) {
139         final Iterator<FieldOrekitStepHandler<T>> iterator = handlers.iterator();
140         while (iterator.hasNext()) {
141             final FieldOrekitStepHandler<T> current = iterator.next();
142             if (current instanceof FieldOrekitStepNormalizer &&
143                 ((FieldOrekitStepNormalizer<T>) current).getFixedStepHandler() == handler) {
144                 if (last != null) {
145                     // propagation is ongoing, we need to call finish now for this handler
146                     current.finish(last);
147                 }
148                 iterator.remove();
149                 return;
150             }
151         }
152     }
153 
154     /** Remove all handlers managed by this multiplexer.
155      * <p>
156      * If propagation is ongoing (i.e. global {@link #init(FieldSpacecraftState, FieldAbsoluteDate)
157      * init} already called and global {@link #finish(FieldSpacecraftState) finish} not called
158      * yet), then the local {@link FieldOrekitStepHandler#finish(FieldSpacecraftState)
159      * FieldOrekitStepHandler.finish} and {@link FieldOrekitFixedStepHandler#finish(FieldSpacecraftState)
160      * FieldOrekitFixedStepHandler.finish} methods of the removed handlers will be called with the last
161      * known state, so the handlers stop properly.
162      * </p>
163      * @since 11.0
164      */
165     public void clear() {
166         if (last != null) {
167             // propagation is ongoing, we need to call finish now for all handlers
168             handlers.forEach(h -> h.finish(last));
169         }
170         handlers.clear();
171     }
172 
173     /** {@inheritDoc} */
174     public void init(final FieldSpacecraftState<T> s0, final FieldAbsoluteDate<T> t) {
175         this.target = t;
176         this.last   = s0;
177         for (final FieldOrekitStepHandler<T> handler : handlers) {
178             handler.init(s0, t);
179         }
180     }
181 
182     /** {@inheritDoc} */
183     public void handleStep(final FieldOrekitStepInterpolator<T> interpolator) {
184         this.last = interpolator.getCurrentState();
185         for (final FieldOrekitStepHandler<T> handler : handlers) {
186             handler.handleStep(interpolator);
187         }
188     }
189 
190     /** {@inheritDoc} */
191     public void finish(final FieldSpacecraftState<T> finalState) {
192         this.target = null;
193         this.last   = null;
194         for (final FieldOrekitStepHandler<T> handler : handlers) {
195             handler.finish(finalState);
196         }
197     }
198 
199 }