1   /* Copyright 2022-2025 Luc Maisonobe
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.files.sinex;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  import java.util.function.Predicate;
22  import java.util.regex.Matcher;
23  import java.util.regex.Pattern;
24  
25  /** Parser for SINEX block.
26   * @param <T> type of the sinex files parse info
27   * @author Luc Maisonobe
28   * @since 13.0
29   */
30  class BlockParser<T extends ParseInfo<?>> implements LineParser<T> {
31  
32      /** Start block pattern. */
33      private final Pattern startPattern;
34  
35      /** End block pattern. */
36      private Pattern endPattern;
37  
38      /** Allowed parsers when dealing within this block (content + end marker). */
39      private final List<LineParser<T>> inBlockParsers;
40  
41      /** Allowed parsers when leaving this block. */
42      private List<LineParser<T>> siblingParsers;
43  
44      /** Simple constructor.
45       * @param blockId    regular expression for block name
46       * @param predicates predicates for parsing block content lines
47       */
48      protected BlockParser(final String blockId, final List<Predicate<T>> predicates) {
49          this.startPattern   = Pattern.compile("^\\+(" + blockId + ") *$");
50          this.endPattern     = null;
51          this.inBlockParsers = new ArrayList<>(1 + predicates.size());
52          for (final Predicate<T> predicate : predicates) {
53              inBlockParsers.add(new LineParser<T>() {
54  
55                  /** {@inheritDoc} */
56                  @Override
57                  public boolean parseIfRecognized(final T parseInfo) {
58                      return predicate.test(parseInfo);
59                  }
60  
61                  /** {@inheritDoc} */
62                  @Override
63                  public Iterable<LineParser<T>> allowedNextParsers(final T parseInfo) {
64                      return inBlockParsers;
65                  }
66  
67              });
68          }
69          this.inBlockParsers.add(this);
70      }
71  
72      /** Set allowed parsers when leaving this block.
73       * @param siblingParsers allowed parsers when leaving this block
74       */
75      public void setSiblingParsers(final List<LineParser<T>> siblingParsers) {
76          this.siblingParsers = siblingParsers;
77      }
78  
79      /** {@inheritDoc} */
80      @Override
81      public boolean parseIfRecognized(final T parseInfo) {
82          return outsideBlock() ? checkEntering(parseInfo) : checkLeaving(parseInfo);
83      }
84  
85      /** {@inheritDoc} */
86      @Override
87      public Iterable<LineParser<T>> allowedNextParsers(final T parseInfo) {
88          return outsideBlock()  ? siblingParsers : inBlockParsers;
89      }
90  
91      /** Check if we are outside block.
92       * @return true if we are at block end
93       */
94      protected boolean outsideBlock() {
95          return endPattern == null;
96      }
97  
98      /** Check if we are at the start marker.
99       * @param parseInfo holder for transient data
100      * @return true if we are at block start
101      */
102     protected boolean checkEntering(final T parseInfo) {
103         final Matcher matcher = startPattern.matcher(parseInfo.getLine());
104         if (matcher.matches()) {
105             // we are entering the block
106             endPattern = Pattern.compile("^-" + matcher.group(1) + " *$");
107             return true;
108         } else {
109             return false;
110         }
111     }
112 
113     /** Check if we are at the end marker.
114      * @param parseInfo holder for transient data
115      * @return true if we are at block end
116      */
117     protected boolean checkLeaving(final T parseInfo) {
118         if (endPattern != null && endPattern.matcher(parseInfo.getLine()).matches()) {
119             endPattern = null;
120             return true;
121         } else {
122             return false;
123         }
124     }
125 
126 }
127