Skip to content

Commit

Permalink
Fix semantics of Message details: not always a map
Browse files Browse the repository at this point in the history
  • Loading branch information
brunchboy committed Oct 15, 2023
1 parent 4956ad8 commit e12ee71
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 20 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ This change log follows the conventions of

### Changed:

- The `details` field in a `Message` is now an `Object` rather than a `Map`, because the
`version` response sends a `String` and the `unsupported` response sends a `Symbol`.
- Compile for compatibility back to Java 8, to work with Afterglow (in particular, the
user guide and API documentation build on Netlify which still uses such an ancient version.)

Expand Down
67 changes: 47 additions & 20 deletions src/main/java/org/deepsymmetry/libcarabiner/Message.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package org.deepsymmetry.libcarabiner;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import us.bpsm.edn.Keyword;
import us.bpsm.edn.Symbol;
import us.bpsm.edn.parser.Parseable;
Expand Down Expand Up @@ -35,20 +33,25 @@
* <p>Created by James Elliott on 2020-01-19.</p>
*/
public class Message {

private static final Logger logger = LoggerFactory.getLogger(Message.class);

/**
* Identifies the type of the message that was received, which was the symbol that the message started with.
* This value is interned, so message types can be compared for equality by object identity.
*/
public final String messageType;

/**
* Holds the details sent after the message type, if any. Will be {@code null} if there were no details.
* Keys in the map are interned strings, so they can be compared for equality by object identity.
* Holds the details sent after the message type.
*
* <p>For messages {@code status}, {@code beat-at-time}, and {@code phase-at-time}, the details will be a
* {@link Map} of keys to values.
* Keys in the map are interned strings, so they can be compared for equality by object identity.</p>
*
* <p>For {@code version} messages, the details will be a {@link String}.</p>
*
* <p>For {@code unsupported} messages, the details will be the {@link Symbol} corresponding to the
* command that was not supported by Carabiner.</p>
*/
public final Map<String, Object> details;
public final Object details;

/**
* Construct an instance given an <a href="https://github.com/edn-format/edn#edn">edn</a>
Expand All @@ -70,22 +73,46 @@ public Message(String response) {

// Now see if there is a payload.
read = parser.nextValue(parseable);
if (read == Parser.END_OF_INPUT) {
details = null;
} else if (read instanceof Map) {
details = Collections.unmodifiableMap(((Map<Keyword, Object>) read).entrySet().stream().collect(
HashMap::new,
(map, e) -> map.put(e.getKey().getName().intern(), e.getValue()),
Map::putAll
));
} else {
System.out.println(read);
throw new IllegalArgumentException("Carabiner message details, if present, must be a map. Received: " + response);
switch (messageType) {
case "status":
case "beat-at-time":
case "phase-at-time":
if (read instanceof Map) {
//noinspection unchecked
details = Collections.unmodifiableMap(((Map<Keyword, Object>) read).entrySet().stream().collect(
HashMap::new,
(map, e) -> map.put(e.getKey().getName().intern(), e.getValue()),
Map::putAll
));
} else {
throw new IllegalArgumentException("Carabiner " + messageType +
" response details must be a map. Received: " + response);
}
break;

case "version":
if (read instanceof String) {
details = read;
} else {
throw new IllegalArgumentException("Carabiner version response details must be a string. Received: " + response);
}
break;

case "unsupported":
if (read instanceof Symbol) {
details = read;
} else {
throw new IllegalArgumentException("Carabiner unsupported response details must be a symbol. Received: " + response);
}
break;

default:
throw new IllegalArgumentException("Unrecognized Carabiner response message :" + response);
}

read = parser.nextValue(parseable);
if (read != Parser.END_OF_INPUT) {
throw new IllegalArgumentException("Carabiner messages must consist of a symbol optionally followed by a map. Received: " +
throw new IllegalArgumentException("Carabiner messages must consist of a symbol followed by a map, string, or symbol. Received: " +
response);
}
}
Expand Down

0 comments on commit e12ee71

Please sign in to comment.