ArduinoSystem.java

/*******************************************************************************
 * jArduino: Arduino C++ Code Generation From Java
 * Copyright 2020 Tony Washer
 *
 * Licensed 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 net.sourceforge.jarduino.message;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import net.sourceforge.jarduino.ArduinoException;
import net.sourceforge.jarduino.message.ArduinoAttribute.ArduinoAttrObject;
import net.sourceforge.jarduino.message.ArduinoParser.ArduinoParserException;

/**
 * The system.
 */
public final class ArduinoSystem
        implements ArduinoNamedObject, ArduinoAttrObject {
    /**
     * The name of the system.
     */
    private final String theName;

    /**
     * The Cname of the system.
     */
    private final String theCName;

    /**
     * The list of nodes.
     */
    private final List<ArduinoNode> theNodes;

    /**
     * The core attributes.
     */
    private final ArduinoAttributes theCoreAttributes;

    /**
     * the Map of explicit attributes.
     */
    private final Map<ArduinoAttribute, Object> theAttributes;

    /**
     * The comments.
     */
    private final ArduinoComments theComments;

    /**
     * Constructor.
     * @param pName the name
     */
    private ArduinoSystem(final String pName) {
        /* Store parameters */
        theName = pName;

        /* Remove problematical characters for the CName */
        theCName = theName.replace(ArduinoChar.BLANK, ArduinoChar.UNDERSCORE)
                .replace(ArduinoChar.DEC, ArduinoChar.UNDERSCORE);

        /* Create the lists and comments */
        theNodes = new ArrayList<>();
        theAttributes = new HashMap<>();
        theCoreAttributes = new ArduinoAttributes();
        theComments = new ArduinoComments();
    }

    /**
     * Parse the nodes.
     * @param pNodes the nodes string
     */
    private void parseNodes(final String pNodes) {
        /* Loop through the tokens */
        String myNodes = pNodes;
        while (myNodes.length() > 0) {
            final String myNode = ArduinoParser.nextToken(myNodes);
            myNodes = ArduinoParser.stripToken(myNodes, myNode);
            theNodes.add(new ArduinoNode(this, myNode));
        }
    }

    @Override
    public String getName() {
        return theName;
    }

    /**
     * Obtain the CName.
     * @return the CName
     */
    public String getCName() {
        return theCName;
    }

    /**
     * Obtain the nodes in this system.
     * @return the nodes
     */
    public List<ArduinoNode> getNodes() {
        return theNodes;
    }

    /**
     * Obtain a list of all messages in the system.
     * @return the messages
     */
    public List<ArduinoMessage> getMessages() {
        /* Create an empty list */
        final List<ArduinoMessage> myList = new ArrayList<>();

        /* Loop through the nodes */
        for (ArduinoNode myNode : theNodes) {
            /* Add all the messages */
            myList.addAll(myNode.getMessages());
        }

        /* return the list */
         return myList;
    }

    /**
     * Obtain a list of all attributes in the system.
     * @return the messages
     */
    public List<ArduinoAttribute> getAttributes() {
        /* return the list */
        return theCoreAttributes.getAttributes();
    }

    /**
     * Set value for attribute.
     * @param pAttr the attribute
     * @param pValue the value
     * @throws ArduinoParserException on error
     */
    void setAttrValue(final ArduinoAttribute pAttr,
                      final Object pValue) throws ArduinoParserException {
        /* Check that this is not a duplicate */
        if (theAttributes.containsKey(pAttr)) {
            throw new ArduinoParserException("Duplicate Attribute",  pAttr.getName());
        }

        /* Store the value */
        theAttributes.put(pAttr, pValue);
    }

    @Override
    public Object getAttrValue(final ArduinoAttribute pAttr) {
        final Object myValue = theAttributes.get(pAttr);
        return myValue == null ? pAttr.getDefault() : myValue;
    }

    /**
     * find node by name.
     * @param pName the node name.
     * @return the node
     * @throws ArduinoParserException on error
     */
    public ArduinoNode findNodeByName(final String pName) throws ArduinoParserException {
        /* Loop through the list */
        for (ArduinoNode myNode : theNodes) {
            if (pName.equals(myNode.getName())) {
                return myNode;
            }
        }

        /* If this is the null Node */
        if (ArduinoNode.NULL_NODE.equals(pName)) {
            /* Add it to the list of nodes and return it */
            final ArduinoNode myNode = new ArduinoNode(this, pName);
            theNodes.add(myNode);
            return myNode;
        }

        /* Throw error */
        throw new ArduinoParserException("Unknown node", pName);
    }

    /**
     * find message by id.
     * @param pId the message Id.
     * @return the message
     * @throws ArduinoParserException on error
     */
    ArduinoMessage findMessageById(final String pId) throws ArduinoParserException {
        /* Loop through the list */
        for (ArduinoNode myNode : theNodes) {
            final ArduinoMessage myMessage = myNode.findMessageById(pId);
            if (myMessage != null) {
                return myMessage;
            }
        }

        /* Not found */
        throw new ArduinoParserException("Unknown id: ", pId);
    }

    /**
     * find signal by Id and name.
     * @param pId the message Id.
     * @param pName the signal name
     * @return the signal
     * @throws ArduinoParserException on error
     */
    ArduinoSignal findSignalByIdAndName(final String pId,
                                        final String pName) throws ArduinoParserException {
        /* Find the message */
        final ArduinoMessage myMessage = findMessageById(pId);

        /* Find the signal */
        return myMessage.findSignalByName(pName);
    }

    /**
     * find attribute by name.
     * @param pName the signal name
     * @return the attribute
     * @throws ArduinoParserException on error
     */
    ArduinoAttribute findAttributeByName(final String pName) throws ArduinoParserException {
        /* Find the attribute */
        final ArduinoAttribute myAttr = theCoreAttributes.getAttributeForName(pName);
        if (myAttr == null) {
            throw new ArduinoParserException("Unknown attribute: ", pName);
        }
        return myAttr;
    }

    /**
     * Obtain the comment for an object (if any).
     * @param pObject the object
     * @return the comment (or null)
     */
    public String getCommentForObject(final ArduinoNamedObject pObject) {
        return theComments.getCommentForObject(pObject);
    }

    /**
     * Store the comment for an object.
     * @param pObject the object
     * @param pComment the comment
     */
    void storeCommentForObject(final ArduinoNamedObject pObject,
                               final String pComment) {
        theComments.storeCommentForObject(pObject, pComment);
    }

    /**
     * Store the attribute.
     * @param pAttr the attribute
     * @throws ArduinoException on error
     */
    void storeAttribute(final ArduinoAttribute pAttr) throws ArduinoException {
        theCoreAttributes.storeAttribute(pAttr);
    }

    /**
     * Parse system.
     * @param pName the system name
     * @param pSystemDef the system representation
     * @return the system
     * @throws ArduinoException on error
     */
    static ArduinoSystem parseSystem(final String pName,
                                     final String pSystemDef) throws ArduinoException {
        /* Marker is first token in header */
        final String myMarker = ArduinoParser.nextToken(pSystemDef);
        final String myNodes = ArduinoParser.stripToken(pSystemDef, myMarker);
        if (!myMarker.equals(ArduinoNode.MARKER + ArduinoChar.COLON)) {
            throw new ArduinoException("Invalid marker", pSystemDef);
        }

        /* Create the system */
        final ArduinoSystem mySystem = new ArduinoSystem(pName);

        /* Parse the nodes */
        mySystem.parseNodes(myNodes);

        /* Return the parsed system */
        return mySystem;
    }

    @Override
    public boolean equals(final Object pThat) {
        /* Handle trivial cases */
        if (pThat == this) {
            return true;
        } else if (!(pThat instanceof ArduinoSystem)) {
            return false;
        }

        /* Access correctly */
        final ArduinoSystem myThat = (ArduinoSystem) pThat;

        /* Check name */
        return theName.equals(myThat.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(theName);
    }

    @Override
    public String toString() {
        return theName;
    }
}