View Javadoc
1   /*******************************************************************************
2    * jArduino: Arduino C++ Code Generation From Java
3    * Copyright 2020 Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * 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 net.sourceforge.jarduino.message;
18  
19  import java.util.ArrayList;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  
24  import net.sourceforge.jarduino.ArduinoException;
25  import net.sourceforge.jarduino.message.ArduinoAttribute.ArduinoAttrClass;
26  import net.sourceforge.jarduino.message.ArduinoAttribute.ArduinoAttrType;
27  import net.sourceforge.jarduino.message.ArduinoParser.ArduinoParserException;
28  
29  /**
30   * Attributes.
31   */
32  public class ArduinoAttributes {
33      /**
34       * The Attribute Marker.
35       */
36      static final String MARKER = "BA_";
37  
38      /**
39       * The Definition Marker.
40       */
41      static final String MARKER_DEF = "BA_DEF_";
42  
43      /**
44       * The Default Marker.
45       */
46      static final String MARKER_DEFAULT = "BA_DEF_DEF_";
47  
48      /**
49       * The Attribute Marker.
50       */
51      static final String MARKER_REL = MARKER + ArduinoAttribute.REL_MARKER;
52  
53      /**
54       * The Definition Marker.
55       */
56      static final String MARKER_REL_DEF = MARKER_DEF + ArduinoAttribute.REL_MARKER;
57  
58      /**
59       * The Default Marker.
60       */
61      static final String MARKER_REL_DEFAULT = MARKER_DEFAULT + ArduinoAttribute.REL_MARKER;
62  
63      /**
64       * Attributes Map.
65       */
66      private final Map<String, ArduinoAttribute> theAttributes;
67  
68      /**
69       * Attributes.
70       */
71      ArduinoAttributes() {
72          /* Create the map */
73          theAttributes = new HashMap<>();
74      }
75  
76      /**
77       * Obtain a list of all attributes in the system.
78       * @return the attributes
79       */
80      public List<ArduinoAttribute> getAttributes() {
81          return new ArrayList<>(theAttributes.values());
82      }
83  
84      /**
85        * Obtain attribute for name.
86        * @param pName the name
87        * @return the attribute
88        */
89      public ArduinoAttribute getAttributeForName(final String pName) {
90          return theAttributes.get(pName);
91      }
92  
93      /**
94       * Store the attribute.
95       * @param pAttr the attribute
96       * @throws ArduinoException on error
97       */
98      void storeAttribute(final ArduinoAttribute pAttr) throws ArduinoException {
99          /* Check that this is not a duplicate */
100         if (theAttributes.containsKey(pAttr.getName())) {
101             throw new ArduinoException("Duplicate Attribute " + pAttr.getName());
102         }
103 
104         /* Store the attribute */
105         theAttributes.put(pAttr.getName(), pAttr);
106     }
107 
108     /**
109      * parse the attribute definition.
110      * @param pSystem the system
111      * @param pMarker the marker
112      * @param pAttrDef the attribute definition
113      * @throws ArduinoException on error
114      */
115     public static void parseAttributeDef(final ArduinoSystem pSystem,
116                                          final String pMarker,
117                                          final String pAttrDef) throws ArduinoException {
118         /* Marker is first token */
119         final String myMarker = ArduinoParser.nextToken(pAttrDef);
120         String myDef = ArduinoParser.stripToken(pAttrDef, myMarker);
121         if (!myMarker.equals(pMarker)) {
122             throw new ArduinoException("Invalid AttrDef marker", pAttrDef);
123         }
124 
125         /* Attribute class is next token (if present) */
126         final boolean isRelation = MARKER_REL_DEF.equals(pMarker);
127         final String myClassDef = ArduinoParser.nextToken(myDef);
128         final ArduinoAttrClass myClass = ArduinoAttrClass.parseAttrClass(myClassDef);
129         if (myClass.isRelation() != isRelation) {
130             throw new ArduinoException("Relation mismatch for AttrDef", pAttrDef);
131         }
132         if (myClass != ArduinoAttrClass.SYSTEM) {
133             myDef = ArduinoParser.stripToken(myDef, myClassDef);
134         }
135 
136         /* Protect against exceptions */
137         try {
138             /* Access name */
139             final String myName = ArduinoParser.nextQuotedToken(myDef);
140             myDef = ArduinoParser.stripQuotedToken(myDef, myName);
141 
142             /* Check that the end character is semicolon */
143             final int myLen = myDef.length();
144             if (myDef.length() == 0 || myDef.charAt(myLen - 1) != ArduinoChar.SEMICOLON) {
145                 throw new ArduinoParserException("Missing AttrDef terminator", myDef);
146             }
147             myDef = myDef.substring(0, myLen - 1);
148 
149             /* Attribute type is next token */
150             final String myTypeDef = ArduinoParser.nextToken(myDef);
151             final ArduinoAttrType myType = ArduinoAttrType.parseAttrType(myTypeDef);
152             myDef = ArduinoParser.stripToken(myDef, myTypeDef);
153 
154             /* Create the new attribute and add to the map */
155             final ArduinoAttributenoAttribute.html#ArduinoAttribute">ArduinoAttribute myAttr = new ArduinoAttribute(myName, myClass, myType);
156             myAttr.setConstraints(ArduinoAttrConstraints.parseConstraints(myAttr, myDef));
157             pSystem.storeAttribute(myAttr);
158 
159             /* Handle parser exceptions */
160         } catch (ArduinoParserException e) {
161             throw new ArduinoException(e.getMessage() + ArduinoChar.COLON + e.getDetail(), pAttrDef);
162         }
163     }
164 
165     /**
166      * parse the attribute default.
167      * @param pSystem the system
168      * @param pMarker the marker
169      * @param pAttrDef the attribute default definition
170      * @throws ArduinoException on error
171      */
172     public static void parseAttributeDefault(final ArduinoSystem pSystem,
173                                              final String pMarker,
174                                              final String pAttrDef) throws ArduinoException {
175         /* Marker is first token */
176         final String myMarker = ArduinoParser.nextToken(pAttrDef);
177         String myDef = ArduinoParser.stripToken(pAttrDef, myMarker);
178         if (!myMarker.equals(pMarker)) {
179             throw new ArduinoException("Invalid AttrDefault marker", pAttrDef);
180         }
181 
182         /* Protect against exceptions */
183         try {
184             /* Access name */
185             final boolean isRelation = MARKER_REL_DEFAULT.equals(pMarker);
186             final String myName = ArduinoParser.nextQuotedToken(myDef);
187             myDef = ArduinoParser.stripQuotedToken(myDef, myName);
188 
189             /* Check that the end character is semicolon */
190             final int myLen = myDef.length();
191             if (myDef.length() == 0 || myDef.charAt(myLen - 1) != ArduinoChar.SEMICOLON) {
192                 throw new ArduinoException("Missing AttrDefault terminator", pAttrDef);
193             }
194             myDef = myDef.substring(0, myLen - 1);
195 
196             /* Access the attribute */
197             final ArduinoAttribute myAttr = pSystem.findAttributeByName(myName);
198             if (myAttr.getAttrClass().isRelation() != isRelation) {
199                 throw new ArduinoException("Relation mismatch for AttrDefault", pAttrDef);
200             }
201             final Object myDefault = parseAttributeValue(myAttr, myDef);
202 
203             /* Set the default */
204             myAttr.setDefault(myDefault);
205 
206             /* Handle parser exceptions */
207         } catch (ArduinoParserException e) {
208             throw new ArduinoException(e.getMessage() + ArduinoChar.COLON + e.getDetail(), pAttrDef);
209         }
210     }
211 
212     /**
213      * parse the attribute value.
214      * @param pAttr the attribute
215      * @param pValueDef the attribute definition
216      * @throws ArduinoParserException on error
217      * @return the parsed value
218      */
219     private static Object parseAttributeValue(final ArduinoAttribute pAttr,
220                                               final String pValueDef) throws ArduinoParserException {
221         /* Switch on attribute type */
222         Object myValue;
223         switch (pAttr.getAttrType()) {
224             case FLOAT:
225             case INT:
226             case HEX:
227                 myValue = ArduinoParser.parseNumber(pValueDef);
228                 break;
229             case STRING:
230                 myValue = ArduinoParser.nextQuotedToken(pValueDef);
231                 break;
232             case ENUM:
233             default:
234                 myValue = pValueDef.charAt(0) == ArduinoChar.QUOTE
235                         ? ArduinoParser.nextQuotedToken(pValueDef)
236                         : ArduinoParser.parseNumber(ArduinoParser.nextToken(pValueDef));
237                 break;
238         }
239 
240         /* If there are constraints */
241         final ArduinoAttrConstraints myConstraints = pAttr.getConstraints();
242         if (myConstraints != null) {
243             /* Check against the constraints */
244             myValue = myConstraints.checkValue(myValue);
245         }
246 
247         /* Return the value */
248         return myValue;
249     }
250 
251     /**
252      * parse the attribute.
253      * @param pSystem the system
254      * @param pMarker the marker
255      * @param pAttrDef the attribute definition
256      * @throws ArduinoException on error
257      */
258     public static void parseAttribute(final ArduinoSystem pSystem,
259                                       final String pMarker,
260                                       final String pAttrDef) throws ArduinoException {
261         /* Marker is first token */
262         final String myMarker = ArduinoParser.nextToken(pAttrDef);
263         String myDef = ArduinoParser.stripToken(pAttrDef, myMarker);
264         if (!myMarker.equals(pMarker)) {
265             throw new ArduinoException("Invalid Attr marker", pAttrDef);
266         }
267 
268         /* Protect against exceptions */
269         try {
270             /* Access name */
271             final boolean isRelation = MARKER_REL.equals(pMarker);
272             final String myName = ArduinoParser.nextQuotedToken(myDef);
273             myDef = ArduinoParser.stripQuotedToken(myDef, myName);
274 
275             /* Check that the end character is semicolon */
276             final int myLen = myDef.length();
277             if (myDef.length() == 0 || myDef.charAt(myLen - 1) != ArduinoChar.SEMICOLON) {
278                 throw new ArduinoException("Missing Attr terminator", pAttrDef);
279             }
280             myDef = myDef.substring(0, myLen - 1);
281 
282             /* Access the attribute */
283             final ArduinoAttribute myAttr = pSystem.findAttributeByName(myName);
284             if (myAttr.getAttrClass().isRelation() != isRelation) {
285                 throw new ArduinoException("Relation mismatch for Attr", pAttrDef);
286             }
287 
288             /* split processing based on class */
289             switch (myAttr.getAttrClass()) {
290                 case NODE:
291                     parseNodeAttribute(pSystem, myAttr, myDef);
292                     break;
293                 case MESSAGE:
294                     parseMessageAttribute(pSystem, myAttr, myDef);
295                     break;
296                 case SIGNAL:
297                     parseSignalAttribute(pSystem, myAttr, myDef);
298                     break;
299                 case NODE2MSG:
300                     parseNode2MsgAttribute(pSystem, myAttr, myDef);
301                     break;
302                 case NODE2SIGNAL:
303                     parseNode2SignalAttribute(pSystem, myAttr, myDef);
304                     break;
305                 case SYSTEM:
306                 default:
307                     parseSystemAttribute(pSystem, myAttr, myDef);
308                     break;
309             }
310 
311             /* Handle parser exceptions */
312         } catch (ArduinoParserException e) {
313             throw new ArduinoException(e.getMessage() + ArduinoChar.COLON + e.getDetail(), pAttrDef);
314         }
315     }
316 
317     /**
318      * parse the system attribute.
319      * @param pSystem the system
320      * @param pAttr the attribute
321      * @param pAttrDef the attribute definition
322      * @throws ArduinoParserException on error
323      */
324     private static void parseSystemAttribute(final ArduinoSystem pSystem,
325                                              final ArduinoAttribute pAttr,
326                                              final String pAttrDef) throws ArduinoParserException {
327         /* Parse and set value */
328         final Object myValue = parseAttributeValue(pAttr, pAttrDef);
329         pSystem.setAttrValue(pAttr, myValue);
330     }
331 
332     /**
333      * parse the node attribute.
334      * @param pSystem the system
335      * @param pAttr the attribute
336      * @param pAttrDef the attribute definition
337      * @throws ArduinoParserException on error
338      */
339     private static void parseNodeAttribute(final ArduinoSystem pSystem,
340                                            final ArduinoAttribute pAttr,
341                                            final String pAttrDef) throws ArduinoParserException {
342         /* Marker is first token */
343         final String myMarker = ArduinoParser.nextToken(pAttrDef);
344         String myDef = ArduinoParser.stripToken(pAttrDef, myMarker);
345         if (!myMarker.equals(ArduinoNode.MARKER)) {
346             throw new ArduinoParserException("Invalid Node marker", pAttrDef);
347         }
348 
349         /* Access name */
350         final String myName = ArduinoParser.nextToken(myDef);
351         myDef = ArduinoParser.stripToken(myDef, myName);
352 
353         /* Locate the node and set the value */
354         final ArduinoNode myNode = pSystem.findNodeByName(myName);
355         final Object myValue = parseAttributeValue(pAttr, myDef);
356         myNode.setAttrValue(pAttr, myValue);
357     }
358 
359     /**
360      * parse the message attribute.
361      * @param pSystem the system
362      * @param pAttr the attribute
363      * @param pAttrDef the attribute definition
364      * @throws ArduinoParserException on error
365      */
366     private static void parseMessageAttribute(final ArduinoSystem pSystem,
367                                               final ArduinoAttribute pAttr,
368                                               final String pAttrDef) throws ArduinoParserException {
369         /* Marker is first token */
370         final String myMarker = ArduinoParser.nextToken(pAttrDef);
371         String myDef = ArduinoParser.stripToken(pAttrDef, myMarker);
372         if (!myMarker.equals(ArduinoMessage.MARKER)) {
373             throw new ArduinoParserException("Invalid Message marker", pAttrDef);
374         }
375 
376         /* Access msgId */
377         final String myMsgId = ArduinoParser.nextToken(myDef);
378         myDef = ArduinoParser.stripToken(myDef, myMsgId);
379 
380         /* Locate the message and set the value */
381         final ArduinoMessage myMessage = pSystem.findMessageById(myMsgId);
382         final Object myValue = parseAttributeValue(pAttr, myDef);
383         myMessage.setAttrValue(pAttr, myValue);
384     }
385 
386     /**
387      * parse the node2message attribute.
388      * @param pSystem the system
389      * @param pAttr the attribute
390      * @param pAttrDef the attribute definition
391      * @throws ArduinoParserException on error
392      */
393     private static void parseNode2MsgAttribute(final ArduinoSystem pSystem,
394                                                final ArduinoAttribute pAttr,
395                                                final String pAttrDef) throws ArduinoParserException {
396         /* Marker is first token */
397         String myMarker = ArduinoParser.nextToken(pAttrDef);
398         String myDef = ArduinoParser.stripToken(pAttrDef, myMarker);
399         if (!myMarker.equals(ArduinoAttribute.NODE2MSG_MARKER)) {
400             throw new ArduinoParserException("Invalid Node2Message marker", pAttrDef);
401         }
402 
403         /* Access node */
404         final String myName = ArduinoParser.nextToken(myDef);
405         myDef = ArduinoParser.stripToken(myDef, myName);
406         final ArduinoNode myNode = pSystem.findNodeByName(myName);
407 
408         /* Access messagel marker */
409         myMarker = ArduinoParser.nextToken(myDef);
410         myDef = ArduinoParser.stripToken(myDef, myMarker);
411         if (!myMarker.equals(ArduinoMessage.MARKER)) {
412             throw new ArduinoParserException("Invalid Node2Msg message marker", pAttrDef);
413         }
414 
415         /* Access msgId */
416         final String myMsgId = ArduinoParser.nextToken(myDef);
417         myDef = ArduinoParser.stripToken(myDef, myMsgId);
418 
419         /* Locate the message and set the value */
420         final ArduinoMessage myMessage = pSystem.findMessageById(myMsgId);
421         final Object myValue = parseAttributeValue(pAttr, myDef);
422         if (!myNode.receivesMessage(myMessage)) {
423             throw new ArduinoParserException("Impossible Node2Msg relationship", pAttrDef);
424         }
425         myNode.setRelationValue(pAttr, myMessage, myValue);
426     }
427 
428     /**
429      * parse the signal attribute.
430      * @param pSystem the system
431      * @param pAttr the attribute
432      * @param pAttrDef the attribute definition
433      * @throws ArduinoParserException on error
434      */
435     private static void parseSignalAttribute(final ArduinoSystem pSystem,
436                                              final ArduinoAttribute pAttr,
437                                              final String pAttrDef) throws ArduinoParserException {
438         /* Marker is first token */
439         final String myMarker = ArduinoParser.nextToken(pAttrDef);
440         String myDef = ArduinoParser.stripToken(pAttrDef, myMarker);
441         if (!myMarker.equals(ArduinoSignal.MARKER)) {
442             throw new ArduinoParserException("Invalid Signal marker", pAttrDef);
443         }
444 
445         /* Access msgId */
446         final String myMsgId = ArduinoParser.nextToken(myDef);
447         myDef = ArduinoParser.stripToken(myDef, myMsgId);
448 
449         /* Access signal name */
450         final String myName = ArduinoParser.nextToken(myDef);
451         myDef = ArduinoParser.stripToken(myDef, myName);
452 
453         /* Locate the signal */
454         final ArduinoSignal mySignal = pSystem.findSignalByIdAndName(myMsgId, myName);
455         final Object myValue = parseAttributeValue(pAttr, myDef);
456         mySignal.setAttrValue(pAttr, myValue);
457     }
458 
459     /**
460      * parse the node2signal attribute.
461      * @param pSystem the system
462      * @param pAttr the attribute
463      * @param pAttrDef the attribute definition
464      * @throws ArduinoParserException on error
465      */
466     private static void parseNode2SignalAttribute(final ArduinoSystem pSystem,
467                                                   final ArduinoAttribute pAttr,
468                                                   final String pAttrDef) throws ArduinoParserException {
469         /* Marker is first token */
470         String myMarker = ArduinoParser.nextToken(pAttrDef);
471         String myDef = ArduinoParser.stripToken(pAttrDef, myMarker);
472         if (!myMarker.equals(ArduinoAttribute.NODE2SIG_MARKER)) {
473             throw new ArduinoParserException("Invalid Node2Signal marker", pAttrDef);
474         }
475 
476         /* Access node */
477         String myName = ArduinoParser.nextToken(myDef);
478         myDef = ArduinoParser.stripToken(myDef, myName);
479         final ArduinoNode myNode = pSystem.findNodeByName(myName);
480 
481         /* Access signal marker */
482         myMarker = ArduinoParser.nextToken(myDef);
483         myDef = ArduinoParser.stripToken(myDef, myMarker);
484         if (!myMarker.equals(ArduinoSignal.MARKER)) {
485             throw new ArduinoParserException("Invalid Node2Signal signal marker", pAttrDef);
486         }
487 
488         /* Access msgId */
489         final String myMsgId = ArduinoParser.nextToken(myDef);
490         myDef = ArduinoParser.stripToken(myDef, myMsgId);
491 
492         /* Access signal name */
493         myName = ArduinoParser.nextToken(myDef);
494         myDef = ArduinoParser.stripToken(myDef, myName);
495 
496         /* Locate the signal */
497         final ArduinoSignal mySignal = pSystem.findSignalByIdAndName(myMsgId, myName);
498         final Object myValue = parseAttributeValue(pAttr, myDef);
499         if (!myNode.receivesSignal(mySignal)) {
500             throw new ArduinoParserException("Impossible Node2Signal relationship", pAttrDef);
501         }
502         myNode.setRelationValue(pAttr, mySignal, myValue);
503     }
504 }