ArduinoAttributes.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 net.sourceforge.jarduino.ArduinoException;
import net.sourceforge.jarduino.message.ArduinoAttribute.ArduinoAttrClass;
import net.sourceforge.jarduino.message.ArduinoAttribute.ArduinoAttrType;
import net.sourceforge.jarduino.message.ArduinoParser.ArduinoParserException;
/**
* Attributes.
*/
public class ArduinoAttributes {
/**
* The Attribute Marker.
*/
static final String MARKER = "BA_";
/**
* The Definition Marker.
*/
static final String MARKER_DEF = "BA_DEF_";
/**
* The Default Marker.
*/
static final String MARKER_DEFAULT = "BA_DEF_DEF_";
/**
* The Attribute Marker.
*/
static final String MARKER_REL = MARKER + ArduinoAttribute.REL_MARKER;
/**
* The Definition Marker.
*/
static final String MARKER_REL_DEF = MARKER_DEF + ArduinoAttribute.REL_MARKER;
/**
* The Default Marker.
*/
static final String MARKER_REL_DEFAULT = MARKER_DEFAULT + ArduinoAttribute.REL_MARKER;
/**
* Attributes Map.
*/
private final Map<String, ArduinoAttribute> theAttributes;
/**
* Attributes.
*/
ArduinoAttributes() {
/* Create the map */
theAttributes = new HashMap<>();
}
/**
* Obtain a list of all attributes in the system.
* @return the attributes
*/
public List<ArduinoAttribute> getAttributes() {
return new ArrayList<>(theAttributes.values());
}
/**
* Obtain attribute for name.
* @param pName the name
* @return the attribute
*/
public ArduinoAttribute getAttributeForName(final String pName) {
return theAttributes.get(pName);
}
/**
* Store the attribute.
* @param pAttr the attribute
* @throws ArduinoException on error
*/
void storeAttribute(final ArduinoAttribute pAttr) throws ArduinoException {
/* Check that this is not a duplicate */
if (theAttributes.containsKey(pAttr.getName())) {
throw new ArduinoException("Duplicate Attribute " + pAttr.getName());
}
/* Store the attribute */
theAttributes.put(pAttr.getName(), pAttr);
}
/**
* parse the attribute definition.
* @param pSystem the system
* @param pMarker the marker
* @param pAttrDef the attribute definition
* @throws ArduinoException on error
*/
public static void parseAttributeDef(final ArduinoSystem pSystem,
final String pMarker,
final String pAttrDef) throws ArduinoException {
/* Marker is first token */
final String myMarker = ArduinoParser.nextToken(pAttrDef);
String myDef = ArduinoParser.stripToken(pAttrDef, myMarker);
if (!myMarker.equals(pMarker)) {
throw new ArduinoException("Invalid AttrDef marker", pAttrDef);
}
/* Attribute class is next token (if present) */
final boolean isRelation = MARKER_REL_DEF.equals(pMarker);
final String myClassDef = ArduinoParser.nextToken(myDef);
final ArduinoAttrClass myClass = ArduinoAttrClass.parseAttrClass(myClassDef);
if (myClass.isRelation() != isRelation) {
throw new ArduinoException("Relation mismatch for AttrDef", pAttrDef);
}
if (myClass != ArduinoAttrClass.SYSTEM) {
myDef = ArduinoParser.stripToken(myDef, myClassDef);
}
/* Protect against exceptions */
try {
/* Access name */
final String myName = ArduinoParser.nextQuotedToken(myDef);
myDef = ArduinoParser.stripQuotedToken(myDef, myName);
/* Check that the end character is semicolon */
final int myLen = myDef.length();
if (myDef.length() == 0 || myDef.charAt(myLen - 1) != ArduinoChar.SEMICOLON) {
throw new ArduinoParserException("Missing AttrDef terminator", myDef);
}
myDef = myDef.substring(0, myLen - 1);
/* Attribute type is next token */
final String myTypeDef = ArduinoParser.nextToken(myDef);
final ArduinoAttrType myType = ArduinoAttrType.parseAttrType(myTypeDef);
myDef = ArduinoParser.stripToken(myDef, myTypeDef);
/* Create the new attribute and add to the map */
final ArduinoAttribute myAttr = new ArduinoAttribute(myName, myClass, myType);
myAttr.setConstraints(ArduinoAttrConstraints.parseConstraints(myAttr, myDef));
pSystem.storeAttribute(myAttr);
/* Handle parser exceptions */
} catch (ArduinoParserException e) {
throw new ArduinoException(e.getMessage() + ArduinoChar.COLON + e.getDetail(), pAttrDef);
}
}
/**
* parse the attribute default.
* @param pSystem the system
* @param pMarker the marker
* @param pAttrDef the attribute default definition
* @throws ArduinoException on error
*/
public static void parseAttributeDefault(final ArduinoSystem pSystem,
final String pMarker,
final String pAttrDef) throws ArduinoException {
/* Marker is first token */
final String myMarker = ArduinoParser.nextToken(pAttrDef);
String myDef = ArduinoParser.stripToken(pAttrDef, myMarker);
if (!myMarker.equals(pMarker)) {
throw new ArduinoException("Invalid AttrDefault marker", pAttrDef);
}
/* Protect against exceptions */
try {
/* Access name */
final boolean isRelation = MARKER_REL_DEFAULT.equals(pMarker);
final String myName = ArduinoParser.nextQuotedToken(myDef);
myDef = ArduinoParser.stripQuotedToken(myDef, myName);
/* Check that the end character is semicolon */
final int myLen = myDef.length();
if (myDef.length() == 0 || myDef.charAt(myLen - 1) != ArduinoChar.SEMICOLON) {
throw new ArduinoException("Missing AttrDefault terminator", pAttrDef);
}
myDef = myDef.substring(0, myLen - 1);
/* Access the attribute */
final ArduinoAttribute myAttr = pSystem.findAttributeByName(myName);
if (myAttr.getAttrClass().isRelation() != isRelation) {
throw new ArduinoException("Relation mismatch for AttrDefault", pAttrDef);
}
final Object myDefault = parseAttributeValue(myAttr, myDef);
/* Set the default */
myAttr.setDefault(myDefault);
/* Handle parser exceptions */
} catch (ArduinoParserException e) {
throw new ArduinoException(e.getMessage() + ArduinoChar.COLON + e.getDetail(), pAttrDef);
}
}
/**
* parse the attribute value.
* @param pAttr the attribute
* @param pValueDef the attribute definition
* @throws ArduinoParserException on error
* @return the parsed value
*/
private static Object parseAttributeValue(final ArduinoAttribute pAttr,
final String pValueDef) throws ArduinoParserException {
/* Switch on attribute type */
Object myValue;
switch (pAttr.getAttrType()) {
case FLOAT:
case INT:
case HEX:
myValue = ArduinoParser.parseNumber(pValueDef);
break;
case STRING:
myValue = ArduinoParser.nextQuotedToken(pValueDef);
break;
case ENUM:
default:
myValue = pValueDef.charAt(0) == ArduinoChar.QUOTE
? ArduinoParser.nextQuotedToken(pValueDef)
: ArduinoParser.parseNumber(ArduinoParser.nextToken(pValueDef));
break;
}
/* If there are constraints */
final ArduinoAttrConstraints myConstraints = pAttr.getConstraints();
if (myConstraints != null) {
/* Check against the constraints */
myValue = myConstraints.checkValue(myValue);
}
/* Return the value */
return myValue;
}
/**
* parse the attribute.
* @param pSystem the system
* @param pMarker the marker
* @param pAttrDef the attribute definition
* @throws ArduinoException on error
*/
public static void parseAttribute(final ArduinoSystem pSystem,
final String pMarker,
final String pAttrDef) throws ArduinoException {
/* Marker is first token */
final String myMarker = ArduinoParser.nextToken(pAttrDef);
String myDef = ArduinoParser.stripToken(pAttrDef, myMarker);
if (!myMarker.equals(pMarker)) {
throw new ArduinoException("Invalid Attr marker", pAttrDef);
}
/* Protect against exceptions */
try {
/* Access name */
final boolean isRelation = MARKER_REL.equals(pMarker);
final String myName = ArduinoParser.nextQuotedToken(myDef);
myDef = ArduinoParser.stripQuotedToken(myDef, myName);
/* Check that the end character is semicolon */
final int myLen = myDef.length();
if (myDef.length() == 0 || myDef.charAt(myLen - 1) != ArduinoChar.SEMICOLON) {
throw new ArduinoException("Missing Attr terminator", pAttrDef);
}
myDef = myDef.substring(0, myLen - 1);
/* Access the attribute */
final ArduinoAttribute myAttr = pSystem.findAttributeByName(myName);
if (myAttr.getAttrClass().isRelation() != isRelation) {
throw new ArduinoException("Relation mismatch for Attr", pAttrDef);
}
/* split processing based on class */
switch (myAttr.getAttrClass()) {
case NODE:
parseNodeAttribute(pSystem, myAttr, myDef);
break;
case MESSAGE:
parseMessageAttribute(pSystem, myAttr, myDef);
break;
case SIGNAL:
parseSignalAttribute(pSystem, myAttr, myDef);
break;
case NODE2MSG:
parseNode2MsgAttribute(pSystem, myAttr, myDef);
break;
case NODE2SIGNAL:
parseNode2SignalAttribute(pSystem, myAttr, myDef);
break;
case SYSTEM:
default:
parseSystemAttribute(pSystem, myAttr, myDef);
break;
}
/* Handle parser exceptions */
} catch (ArduinoParserException e) {
throw new ArduinoException(e.getMessage() + ArduinoChar.COLON + e.getDetail(), pAttrDef);
}
}
/**
* parse the system attribute.
* @param pSystem the system
* @param pAttr the attribute
* @param pAttrDef the attribute definition
* @throws ArduinoParserException on error
*/
private static void parseSystemAttribute(final ArduinoSystem pSystem,
final ArduinoAttribute pAttr,
final String pAttrDef) throws ArduinoParserException {
/* Parse and set value */
final Object myValue = parseAttributeValue(pAttr, pAttrDef);
pSystem.setAttrValue(pAttr, myValue);
}
/**
* parse the node attribute.
* @param pSystem the system
* @param pAttr the attribute
* @param pAttrDef the attribute definition
* @throws ArduinoParserException on error
*/
private static void parseNodeAttribute(final ArduinoSystem pSystem,
final ArduinoAttribute pAttr,
final String pAttrDef) throws ArduinoParserException {
/* Marker is first token */
final String myMarker = ArduinoParser.nextToken(pAttrDef);
String myDef = ArduinoParser.stripToken(pAttrDef, myMarker);
if (!myMarker.equals(ArduinoNode.MARKER)) {
throw new ArduinoParserException("Invalid Node marker", pAttrDef);
}
/* Access name */
final String myName = ArduinoParser.nextToken(myDef);
myDef = ArduinoParser.stripToken(myDef, myName);
/* Locate the node and set the value */
final ArduinoNode myNode = pSystem.findNodeByName(myName);
final Object myValue = parseAttributeValue(pAttr, myDef);
myNode.setAttrValue(pAttr, myValue);
}
/**
* parse the message attribute.
* @param pSystem the system
* @param pAttr the attribute
* @param pAttrDef the attribute definition
* @throws ArduinoParserException on error
*/
private static void parseMessageAttribute(final ArduinoSystem pSystem,
final ArduinoAttribute pAttr,
final String pAttrDef) throws ArduinoParserException {
/* Marker is first token */
final String myMarker = ArduinoParser.nextToken(pAttrDef);
String myDef = ArduinoParser.stripToken(pAttrDef, myMarker);
if (!myMarker.equals(ArduinoMessage.MARKER)) {
throw new ArduinoParserException("Invalid Message marker", pAttrDef);
}
/* Access msgId */
final String myMsgId = ArduinoParser.nextToken(myDef);
myDef = ArduinoParser.stripToken(myDef, myMsgId);
/* Locate the message and set the value */
final ArduinoMessage myMessage = pSystem.findMessageById(myMsgId);
final Object myValue = parseAttributeValue(pAttr, myDef);
myMessage.setAttrValue(pAttr, myValue);
}
/**
* parse the node2message attribute.
* @param pSystem the system
* @param pAttr the attribute
* @param pAttrDef the attribute definition
* @throws ArduinoParserException on error
*/
private static void parseNode2MsgAttribute(final ArduinoSystem pSystem,
final ArduinoAttribute pAttr,
final String pAttrDef) throws ArduinoParserException {
/* Marker is first token */
String myMarker = ArduinoParser.nextToken(pAttrDef);
String myDef = ArduinoParser.stripToken(pAttrDef, myMarker);
if (!myMarker.equals(ArduinoAttribute.NODE2MSG_MARKER)) {
throw new ArduinoParserException("Invalid Node2Message marker", pAttrDef);
}
/* Access node */
final String myName = ArduinoParser.nextToken(myDef);
myDef = ArduinoParser.stripToken(myDef, myName);
final ArduinoNode myNode = pSystem.findNodeByName(myName);
/* Access messagel marker */
myMarker = ArduinoParser.nextToken(myDef);
myDef = ArduinoParser.stripToken(myDef, myMarker);
if (!myMarker.equals(ArduinoMessage.MARKER)) {
throw new ArduinoParserException("Invalid Node2Msg message marker", pAttrDef);
}
/* Access msgId */
final String myMsgId = ArduinoParser.nextToken(myDef);
myDef = ArduinoParser.stripToken(myDef, myMsgId);
/* Locate the message and set the value */
final ArduinoMessage myMessage = pSystem.findMessageById(myMsgId);
final Object myValue = parseAttributeValue(pAttr, myDef);
if (!myNode.receivesMessage(myMessage)) {
throw new ArduinoParserException("Impossible Node2Msg relationship", pAttrDef);
}
myNode.setRelationValue(pAttr, myMessage, myValue);
}
/**
* parse the signal attribute.
* @param pSystem the system
* @param pAttr the attribute
* @param pAttrDef the attribute definition
* @throws ArduinoParserException on error
*/
private static void parseSignalAttribute(final ArduinoSystem pSystem,
final ArduinoAttribute pAttr,
final String pAttrDef) throws ArduinoParserException {
/* Marker is first token */
final String myMarker = ArduinoParser.nextToken(pAttrDef);
String myDef = ArduinoParser.stripToken(pAttrDef, myMarker);
if (!myMarker.equals(ArduinoSignal.MARKER)) {
throw new ArduinoParserException("Invalid Signal marker", pAttrDef);
}
/* Access msgId */
final String myMsgId = ArduinoParser.nextToken(myDef);
myDef = ArduinoParser.stripToken(myDef, myMsgId);
/* Access signal name */
final String myName = ArduinoParser.nextToken(myDef);
myDef = ArduinoParser.stripToken(myDef, myName);
/* Locate the signal */
final ArduinoSignal mySignal = pSystem.findSignalByIdAndName(myMsgId, myName);
final Object myValue = parseAttributeValue(pAttr, myDef);
mySignal.setAttrValue(pAttr, myValue);
}
/**
* parse the node2signal attribute.
* @param pSystem the system
* @param pAttr the attribute
* @param pAttrDef the attribute definition
* @throws ArduinoParserException on error
*/
private static void parseNode2SignalAttribute(final ArduinoSystem pSystem,
final ArduinoAttribute pAttr,
final String pAttrDef) throws ArduinoParserException {
/* Marker is first token */
String myMarker = ArduinoParser.nextToken(pAttrDef);
String myDef = ArduinoParser.stripToken(pAttrDef, myMarker);
if (!myMarker.equals(ArduinoAttribute.NODE2SIG_MARKER)) {
throw new ArduinoParserException("Invalid Node2Signal marker", pAttrDef);
}
/* Access node */
String myName = ArduinoParser.nextToken(myDef);
myDef = ArduinoParser.stripToken(myDef, myName);
final ArduinoNode myNode = pSystem.findNodeByName(myName);
/* Access signal marker */
myMarker = ArduinoParser.nextToken(myDef);
myDef = ArduinoParser.stripToken(myDef, myMarker);
if (!myMarker.equals(ArduinoSignal.MARKER)) {
throw new ArduinoParserException("Invalid Node2Signal signal marker", pAttrDef);
}
/* Access msgId */
final String myMsgId = ArduinoParser.nextToken(myDef);
myDef = ArduinoParser.stripToken(myDef, myMsgId);
/* Access signal name */
myName = ArduinoParser.nextToken(myDef);
myDef = ArduinoParser.stripToken(myDef, myName);
/* Locate the signal */
final ArduinoSignal mySignal = pSystem.findSignalByIdAndName(myMsgId, myName);
final Object myValue = parseAttributeValue(pAttr, myDef);
if (!myNode.receivesSignal(mySignal)) {
throw new ArduinoParserException("Impossible Node2Signal relationship", pAttrDef);
}
myNode.setRelationValue(pAttr, mySignal, myValue);
}
}