NdmParser.java

/* Copyright 2002-2024 CS GROUP
 * Licensed to CS GROUP (CS) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * CS licenses this file to You 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 org.orekit.files.ccsds.ndm;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;

import org.orekit.errors.OrekitException;
import org.orekit.errors.OrekitMessages;
import org.orekit.files.ccsds.section.CommentsContainer;
import org.orekit.files.ccsds.utils.FileFormat;
import org.orekit.files.ccsds.utils.lexical.ParseToken;
import org.orekit.files.ccsds.utils.lexical.XmlTokenBuilder;
import org.orekit.files.ccsds.utils.parsing.AbstractMessageParser;

/** A parser for the CCSDS NDM (Navigation Data Message).
 * @author Luc Maisonobe
 * @since 11.0
 */
public class NdmParser extends AbstractMessageParser<Ndm> {

    /** Builder for the constituents parsers. */
    private final ParserBuilder builder;

    /** Current constituent parser. */
    private AbstractMessageParser<? extends NdmConstituent<?, ?>> constituentParser;

    /** Container for comments. */
    private CommentsContainer comments;

    /** Container for constituents. */
    private List<NdmConstituent<?, ?>> constituents;

    /** Simple constructor.
     * <p>
     * Calling this constructor directly is not recommended. Users should rather use
     * {@link org.orekit.files.ccsds.ndm.ParserBuilder#buildNdmParser()
     * parserBuilder.buildNdmParser()}.
     * </p>
     * @param builder builder for the constituents parsers
     * @param filters filters to apply to parse tokens
     * @since 12.0
     */
    public NdmParser(final ParserBuilder builder,
                     final Function<ParseToken, List<ParseToken>>[] filters) {
        super(NdmStructureKey.ndm.name(), null, filters);
        this.builder = builder;
    }

    /** {@inheritDoc} */
    @Override
    public Map<String, XmlTokenBuilder> getSpecialXmlElementsBuilders() {
        final Map<String, XmlTokenBuilder> builders = super.getSpecialXmlElementsBuilders();

        // special handling of root elements for all constituents
        builders.putAll(builder.buildTdmParser().getSpecialXmlElementsBuilders());
        builders.putAll(builder.buildOpmParser().getSpecialXmlElementsBuilders());
        builders.putAll(builder.buildOmmParser().getSpecialXmlElementsBuilders());
        builders.putAll(builder.buildOemParser().getSpecialXmlElementsBuilders());
        builders.putAll(builder.buildOcmParser().getSpecialXmlElementsBuilders());
        builders.putAll(builder.buildApmParser().getSpecialXmlElementsBuilders());
        builders.putAll(builder.buildAemParser().getSpecialXmlElementsBuilders());
        builders.putAll(builder.buildAcmParser().getSpecialXmlElementsBuilders());
        builders.putAll(builder.buildCdmParser().getSpecialXmlElementsBuilders());

        return builders;

    }

    /** {@inheritDoc} */
    @Override
    public void reset(final FileFormat fileFormat) {
        reset(fileFormat, this::processToken);
        constituentParser = null;
        comments          = new CommentsContainer();
        constituents      = new ArrayList<>();
    }

    /** {@inheritDoc} */
    @Override
    public Ndm build() {
        // build the file from parsed comments and constituents
        return new Ndm(comments.getComments(), constituents);
    }

    /**
     * Add comment.
     * <p>
     * Comments are accepted only at start. Once
     * other content is stored in the same section, comments are refused.
     * </p>
     * @param comment comment line
     * @return true if comment was accepted
     */
    public boolean addComment(final String comment) {
        return comments.addComment(comment);
    }

    /** Prepare parsing of a TDM constituent.
     * @return always return true
     */
    boolean manageTdmConstituent() {
        return manageConstituent(builder::buildTdmParser);
    }

    /** Prepare parsing of an OPM constituent.
     * @return always return true
     */
    boolean manageOpmConstituent() {
        return manageConstituent(builder::buildOpmParser);
    }

    /** Prepare parsing of an OMM constituent.
     * @return always return true
     */
    boolean manageOmmConstituent() {
        return manageConstituent(builder::buildOmmParser);
    }

    /** Prepare parsing of an OEM constituent.
     * @return always return true
     */
    boolean manageOemConstituent() {
        return manageConstituent(builder::buildOemParser);
    }

    /** Prepare parsing of an OCM constituent.
     * @return always return true
     */
    boolean manageOcmConstituent() {
        return manageConstituent(builder::buildOcmParser);
    }

    /** Prepare parsing of an APM constituent.
     * @return always return true
     */
    boolean manageApmConstituent() {
        return manageConstituent(builder::buildApmParser);
    }

    /** Prepare parsing of a AEM constituent.
     * @return always return true
     */
    boolean manageAemConstituent() {
        return manageConstituent(builder::buildAemParser);
    }

    /** Prepare parsing of a ACM constituent.
     * @return always return true
     * @since 12.0
     */
    boolean manageAcmConstituent() {
        return manageConstituent(builder::buildAcmParser);
    }

    /** Prepare parsing of a CDM constituent.
     * @return always return true
     */
    boolean manageCdmConstituent() {
        return manageConstituent(builder::buildCdmParser);
    }

    /** Prepare parsing of a constituent.
     * @param parserSupplier supplier for constituent parser
     * @return always return true
     */
    boolean manageConstituent(final Supplier<AbstractMessageParser<? extends NdmConstituent<?, ?>>> parserSupplier) {

        // as we have started parsing constituents, we cannot accept any further comments
        comments.refuseFurtherComments();

        // create a parser for the constituent
        constituentParser = parserSupplier.get();
        constituentParser.reset(getFileFormat());

        return true;

    }

    /** Process one token.
     * @param token token to process
     * @return true if token was processed, false otherwise
     */
    private boolean processToken(final ParseToken token) {

        if (getFileFormat() == FileFormat.KVN) {
            // NDM combined instantiation can only be formatted as XML messages
            throw new OrekitException(OrekitMessages.UNSUPPORTED_FILE_FORMAT, token.getFileName());
        }

        if (constituentParser == null) {
            // we are in the global NDM structure
            try {
                return NdmStructureKey.valueOf(token.getName()).process(token, this);
            } catch (IllegalArgumentException iae) {
                // token has not been recognized
                return false;
            }
        } else {
            // we are inside one constituent
            constituentParser.process(token);
            if (constituentParser.wasEndTagSeen()) {
                // we have seen the end tag, we must go back global structure parsing
                constituents.add(constituentParser.build());
                constituentParser = null;
            }
            return true;
        }

    }

}