hammerspoon/Pods/MIKMIDI/Source/MIKMIDIMappingItem.m

319 lines
11 KiB
Objective-C

//
// MIKMIDIMappingItem.m
// MIKMIDI
//
// Created by Andrew Madsen on 5/20/15.
// Copyright (c) 2015 Mixed In Key. All rights reserved.
//
#import "MIKMIDIMappingItem.h"
#import "MIKMIDIPrivateUtilities.h"
#import "MIKMIDIUtilities.h"
#if TARGET_OS_IPHONE
#import <libxml/xmlwriter.h>
#endif
@interface MIKMIDIMappingItem ()
@property (nonatomic, weak, readwrite) MIKMIDIMapping *mapping;
@end
@implementation MIKMIDIMappingItem
- (instancetype)initWithMIDIResponderIdentifier:(NSString *)MIDIResponderIdentifier andCommandIdentifier:(NSString *)commandIdentifier;
{
self = [super init];
if (self) {
_MIDIResponderIdentifier = [MIDIResponderIdentifier copy];
_commandIdentifier = [commandIdentifier copy];
}
return self;
}
- (id)init
{
[NSException raise:NSInternalInconsistencyException format:@"-[MIKMIDIMappingItem init] is deprecated and should be replaced with a call to -initWithMIDIResponderIdentifier:andCommandIdentifier:."];
return [self initWithMIDIResponderIdentifier:@"Unknown" andCommandIdentifier:@"Unknown"];
}
#if !TARGET_OS_IPHONE
- (instancetype)initWithXMLElement:(NSXMLElement *)element;
{
if (!element) { self = nil; return self; }
NSError *error = nil;
NSXMLElement *responderIdentifier = [[element nodesForXPath:@"ResponderIdentifier" error:&error] lastObject];
if (!responderIdentifier) {
NSLog(@"Unable to read responder identifier from %@: %@", element, error);
self = nil;
return nil;
}
NSXMLElement *commandIdentifier = [[element nodesForXPath:@"CommandIdentifier" error:&error] lastObject];
if (!commandIdentifier) {
NSLog(@"Unable to read command identifier from %@: %@", element, error);
self = nil;
return nil;
}
NSXMLElement *channel = [[element nodesForXPath:@"Channel" error:&error] lastObject];
if (!channel) {
NSLog(@"Unable to read channel from %@: %@", element, error);
self = nil;
return nil;
}
NSXMLElement *commandType = [[element nodesForXPath:@"CommandType" error:&error] lastObject];
if (!commandType) {
NSLog(@"Unable to read command type from %@: %@", element, error);
}
NSXMLElement *controlNumber = [[element nodesForXPath:@"ControlNumber" error:&error] lastObject];
if (!controlNumber) {
NSLog(@"Unable to read control number from %@: %@", element, error);
self = nil;
return nil;
}
NSXMLElement *interactionType = [[element nodesForXPath:@"@InteractionType" error:&error] lastObject];
if (!interactionType) {
NSLog(@"Unable to read interaction type from %@: %@", element, error);
self = nil;
return nil;
}
NSXMLElement *flippedStatus = [[element nodesForXPath:@"@Flipped" error:&error] lastObject];
NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
for (NSXMLNode *attribute in [element attributes]) {
if (![[attribute stringValue] length]) continue;
if ([[attribute name] isEqualToString:@"InteractionType"]) continue;
if ([[attribute name] isEqualToString:@"Flipped"]) continue;
[attributes setObject:[attribute stringValue] forKey:[attribute name]];
}
self = [self initWithMIDIResponderIdentifier:[responderIdentifier stringValue] andCommandIdentifier:[commandIdentifier stringValue]];
if (self) {
_channel = [[channel stringValue] integerValue];
_commandType = [[commandType stringValue] integerValue];
_controlNumber = [[controlNumber stringValue] integerValue];
_interactionType = MIKMIDIMappingInteractionTypeForAttributeString([interactionType stringValue]);
_flipped = [[flippedStatus stringValue] boolValue];
_additionalAttributes = [attributes copy];
}
return self;
}
- (NSXMLDocument *)XMLRepresentation
{
return [self privateXMLRepresentation];
}
- (NSXMLDocument *)privateXMLRepresentation
{
NSXMLElement *responderIdentifier = [NSXMLElement elementWithName:@"ResponderIdentifier" stringValue:self.MIDIResponderIdentifier];
NSXMLElement *commandIdentifier = [NSXMLElement elementWithName:@"CommandIdentifier" stringValue:self.commandIdentifier];
NSXMLElement *channel = [NSXMLElement elementWithName:@"Channel"];
[channel setStringValue:[@(self.channel) stringValue]];
NSXMLElement *commandType = [NSXMLElement elementWithName:@"CommandType"];
[commandType setStringValue:[@(self.commandType) stringValue]];
NSXMLElement *controlNumber = [NSXMLElement elementWithName:@"ControlNumber"];
[controlNumber setStringValue:[@(self.controlNumber) stringValue]];
NSXMLElement *interactionType = [[NSXMLElement alloc] initWithKind:NSXMLAttributeKind];
[interactionType setName:@"InteractionType"];
NSString *interactionTypeString = MIKMIDIMappingAttributeStringForInteractionType(self.interactionType);
[interactionType setStringValue:interactionTypeString];
NSXMLElement *flippedStatus = [[NSXMLElement alloc] initWithKind:NSXMLAttributeKind];
[flippedStatus setName:@"Flipped"];
NSString *flippedStatusString = self.flipped ? @"true" : @"false";
[flippedStatus setStringValue:flippedStatusString];
NSMutableArray *attributes = [NSMutableArray arrayWithArray:@[interactionType, flippedStatus]];
for (NSString *key in self.additionalAttributes) {
NSXMLElement *attributeElement = [[NSXMLElement alloc] initWithKind:NSXMLAttributeKind];
NSString *stringValue = self.additionalAttributes[key];
if (![stringValue isKindOfClass:[NSString class]]) {
NSLog(@"Ignoring additional attribute %@ : %@ because it is not a string.", key, stringValue);
continue;
}
[attributeElement setName:key];
[attributeElement setStringValue:stringValue];
[attributes addObject:attributeElement];
}
return [NSXMLElement elementWithName:@"MappingItem"
children:@[responderIdentifier, commandIdentifier, channel, commandType, controlNumber]
attributes:attributes];
}
#endif
- (NSString *)XMLStringRepresentation
{
#if !TARGET_OS_IPHONE
return [[self XMLRepresentation] XMLStringWithOptions:NSXMLNodePrettyPrint];
#else
int err = 0;
xmlTextWriterPtr writer = NULL;
xmlBufferPtr buffer = xmlBufferCreate();
if (!buffer) {
NSLog(@"Unable to create XML buffer.");
goto CLEANUP_AND_EXIT;
}
{
writer = xmlNewTextWriterMemory(buffer, 0);
if (!writer) {
xmlBufferFree(buffer);
NSLog(@"Unable to create XML writer.");
goto CLEANUP_AND_EXIT;
}
xmlTextWriterSetIndent(writer, 1);
err = xmlTextWriterStartElement(writer, BAD_CAST "MappingItem"); // <MappingItem>
if (err < 0) {
NSLog(@"Unable to start XML MappingItem element: %i", err);
goto CLEANUP_AND_EXIT;
}
NSString *interactionTypeString = MIKMIDIMappingAttributeStringForInteractionType(self.interactionType);
err = xmlTextWriterWriteAttribute(writer, BAD_CAST "InteractionType", BAD_CAST [interactionTypeString UTF8String]);
if (err < 0) {
NSLog(@"Unable to write InteractionType attribute for MappingItem element: %i", err);
goto CLEANUP_AND_EXIT;
}
NSString *flippedStatusString = self.flipped ? @"true" : @"false";
err = xmlTextWriterWriteAttribute(writer, BAD_CAST "Flipped", BAD_CAST [flippedStatusString UTF8String]);
if (err < 0) {
NSLog(@"Unable to write InteractionType attribute for MappingItem element: %i", err);
goto CLEANUP_AND_EXIT;
}
for (NSString *key in self.additionalAttributes) {
NSString *stringValue = self.additionalAttributes[key];
if (![stringValue isKindOfClass:[NSString class]]) {
NSLog(@"Ignoring additional attribute %@ : %@ because it is not a string.", key, stringValue);
continue;
}
err = xmlTextWriterWriteAttribute(writer, BAD_CAST [key UTF8String], BAD_CAST [stringValue UTF8String]);
if (err < 0) {
NSLog(@"Unable to write MappingName attribute for Mapping element: %i", err);
goto CLEANUP_AND_EXIT;
}
}
err = xmlTextWriterWriteElement(writer, BAD_CAST "ResponderIdentifier", BAD_CAST [self.MIDIResponderIdentifier UTF8String]);
if (err < 0) {
NSLog(@"Unable to write ResponderIdentifier element for mapping %@: %i", self, err);
goto CLEANUP_AND_EXIT;
}
err = xmlTextWriterWriteElement(writer, BAD_CAST "CommandIdentifier", BAD_CAST [self.commandIdentifier UTF8String]);
if (err < 0) {
NSLog(@"Unable to write CommandIdentifier element for mapping %@: %i", self, err);
goto CLEANUP_AND_EXIT;
}
err = xmlTextWriterWriteElement(writer, BAD_CAST "Channel", BAD_CAST [[@(self.channel) stringValue] UTF8String]);
if (err < 0) {
NSLog(@"Unable to write Channel element for mapping %@: %i", self, err);
goto CLEANUP_AND_EXIT;
}
err = xmlTextWriterWriteElement(writer, BAD_CAST "CommandType", BAD_CAST [[@(self.commandType) stringValue] UTF8String]);
if (err < 0) {
NSLog(@"Unable to write CommandType element for mapping %@: %i", self, err);
goto CLEANUP_AND_EXIT;
}
err = xmlTextWriterWriteElement(writer, BAD_CAST "ControlNumber", BAD_CAST [[@(self.controlNumber) stringValue] UTF8String]);
if (err < 0) {
NSLog(@"Unable to write ControlNumber element for mapping %@: %i", self, err);
goto CLEANUP_AND_EXIT;
}
err = xmlTextWriterEndElement(writer); // </MappingItem>
if (err < 0) {
NSLog(@"Unable to end XML MappingItem element: %i", err);
goto CLEANUP_AND_EXIT;
}
}
CLEANUP_AND_EXIT:
if (writer) xmlFreeTextWriter(writer);
NSString *result = nil;
if (buffer && err >= 0) {
result = [[NSString alloc] initWithCString:(const char *)buffer->content encoding:NSUTF8StringEncoding];
xmlBufferFree(buffer);
}
return result;
#endif
}
- (id)copyWithZone:(NSZone *)zone
{
MIKMIDIMappingItem *result = [[MIKMIDIMappingItem alloc] initWithMIDIResponderIdentifier:self.MIDIResponderIdentifier andCommandIdentifier:self.commandIdentifier];
result.interactionType = self.interactionType;
result.flipped = self.flipped;
result.channel = self.channel;
result.commandType = self.commandType;
result.controlNumber = self.controlNumber;
result.additionalAttributes = self.additionalAttributes;
return result;
}
- (BOOL)isEqual:(MIKMIDIMappingItem *)otherMappingItem
{
if (self == otherMappingItem) return YES;
if (self.controlNumber != otherMappingItem.controlNumber) return NO;
if (self.channel != otherMappingItem.channel) return NO;
if (self.commandType != otherMappingItem.commandType) return NO;
if (self.interactionType != otherMappingItem.interactionType) return NO;
if (self.flipped != otherMappingItem.flipped) return NO;
if (![self.MIDIResponderIdentifier isEqualToString:otherMappingItem.MIDIResponderIdentifier]) return NO;
if (![self.commandIdentifier isEqualToString:otherMappingItem.commandIdentifier]) return NO;
if (![self.additionalAttributes isEqualToDictionary:otherMappingItem.additionalAttributes]) return NO;
return YES;
}
- (NSUInteger)hash
{
// Only depend on non-mutable properties
NSUInteger result = [_MIDIResponderIdentifier hash];
result += [_commandIdentifier hash];
return result;
}
- (NSString *)description
{
NSMutableString *result = [NSMutableString stringWithFormat:@"%@ %@ %@ CommandID: %@ Channel %li MIDI Command %li Control Number %lu flipped %i", [super description], MIKMIDIMappingAttributeStringForInteractionType(self.interactionType), self.MIDIResponderIdentifier, self.commandIdentifier, (long)self.channel, (long)self.commandType, (unsigned long)self.controlNumber, (int)self.flipped];
if ([self.additionalAttributes count]) {
for (NSString *key in self.additionalAttributes) {
NSString *value = self.additionalAttributes[key];
[result appendFormat:@" %@: %@", key, value];
}
}
return result;
}
@end