Logger done for now, start on analysis tool

This commit is contained in:
2017-06-15 22:50:35 +02:00
parent 93cbf6d19a
commit 90a534f2a7
23 changed files with 1388 additions and 68 deletions
+2 -2
View File
@@ -105,8 +105,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
// MARK: - Logging Functions
func logFreqs(_: Timer) {
// let frequencyOperation: FrequencyOperation = FrequencyOperation()
// OperationQueue.main.addOperation(frequencyOperation)
let frequencyOperation: FrequencyOperation = FrequencyOperation()
OperationQueue.main.addOperation(frequencyOperation)
}
func logFails(_: Timer) {
+30 -3
View File
@@ -1,6 +1,6 @@
//
// ConnectionLossOperation.swift
// Docsis Toolkit
// Logger4
//
// Created by Kilian Hofmann on 15.06.17.
// Copyright © 2017 Kilian Hofmann. All rights reserved.
@@ -44,7 +44,21 @@ class ConnectionLossOperation: Operation {
data, response, error in
if error != nil && data == nil && self.result2 {
log(entry, path: file)
let fileH: FileHandle? = FileHandle(forUpdatingAtPath: file)
if fileH == nil {
do{
try entry.write(toFile: file, atomically: true, encoding: .utf8)
}
catch {
NSLog("WRITE TO FILE FAILED\n");
}
} else {
fileH?.seekToEndOfFile();
let data = (entry as NSString).data(using: String.Encoding.utf8.rawValue)
fileH?.write(data!)
fileH?.closeFile()
}
self.result2 = false;
NSLog("Internet loss logged!")
}
@@ -56,7 +70,20 @@ class ConnectionLossOperation: Operation {
data, response, error in
if error != nil && data == nil && self.result1 {
log(entry, path: file)
let fileH: FileHandle? = FileHandle(forUpdatingAtPath: file)
if fileH == nil {
do{
try entry.write(toFile: file, atomically: true, encoding: .utf8)
}
catch {
NSLog("WRITE TO FILE \(file) FAILED\n");
}
} else {
fileH?.seekToEndOfFile();
let data = (entry as NSString).data(using: String.Encoding.utf8.rawValue)
fileH?.write(data!)
fileH?.closeFile()
}
self.result1 = false;
NSLog("Internet loss logged!")
}
+235 -2
View File
@@ -1,6 +1,6 @@
//
// FrequencyOperation.swift
// Docsis Toolkit
// Logger4
//
// Created by Kilian Hofmann on 15.06.17.
// Copyright © 2017 Kilian Hofmann. All rights reserved.
@@ -17,6 +17,12 @@ class FrequencyOperation: Operation {
let modulationAdjust: NSDictionary = ["qpsk": "-1.18", "8qam": "-0.21", "16qam": "-0.21", "32qam" : "0", "64qam": "0", "128qam": "0.05"]
// XML Parser
let parser: XMLDictionaryParser = XMLDictionaryParser()
// Data handling
var downstream: Data?
var upstream: Data?
var upstreamBool: Bool = false
var downstreamBool: Bool = false
var dir: NSString = ""
override init() {
@@ -30,7 +36,7 @@ class FrequencyOperation: Operation {
let fileManager: FileManager = FileManager.default
// Make all relevant directories if not present
pthread_mutex_lock(&((NSApp.delegate as! AppDelegate).lock))
let dir: NSString = NSString(format: "~/KDLog/%@.docsisplist2/%@/%@", start[2], start[1], start[0])
dir = NSString(format: "~/KDLog/%@.docsisplist2/%@/%@/", start[2], start[1], start[0])
do {
try fileManager.createDirectory(atPath: dir.expandingTildeInPath, withIntermediateDirectories: true, attributes: nil)
}
@@ -38,5 +44,232 @@ class FrequencyOperation: Operation {
NSLog("ERROR ON SUBDIRECTORY CREATION: \(error.localizedDescription)")
}
pthread_mutex_unlock(&((NSApp.delegate as! AppDelegate).lock))
// URL Requests
let requestDownstream: URLRequest = URLRequest.init(url: URL.init(string: UserDefaults.standard.string(forKey: "downstream")!)!, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 4)
let requestUpstream: URLRequest = URLRequest.init(url: URL.init(string: UserDefaults.standard.string(forKey: "upstream")!)!, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 4)
// Tasks
let taskDownstream: URLSessionDataTask = (NSApp.delegate as! AppDelegate).urlSession.dataTask(with: requestDownstream){data, response, error in
pthread_mutex_lock(&(NSApp.delegate as! AppDelegate).lock)
self.downstream = data
self.downstreamBool = true
self.handleData(date: time)
pthread_mutex_unlock(&(NSApp.delegate as! AppDelegate).lock)
}
let taskUpstream: URLSessionDataTask = (NSApp.delegate as! AppDelegate).urlSession.dataTask(with: requestUpstream){data, response, error in
pthread_mutex_lock(&(NSApp.delegate as! AppDelegate).lock)
self.upstream = data
self.upstreamBool = true
self.handleData(date: time)
pthread_mutex_unlock(&(NSApp.delegate as! AppDelegate).lock)
}
// Start
taskUpstream.resume()
taskDownstream.resume()
}
func handleData(date: Date) {
// Both returned successfully
if upstream != nil && downstream != nil {
// Parse data
upstreamBool = false
downstreamBool = false
let dictionaryUpstream: Dictionary? = parser.dictionary(with: upstream)
let dictionaryDownstream: Dictionary? = parser.dictionary(with: downstream)
upstream = nil
downstream = nil
// Tracking padding adding
var filesModified: [String] = []
// Upstream is valid
if dictionaryUpstream != nil{
var entry: String = ""
var file: NSString = ""
// Grab channels
let channels: AnyObject = (dictionaryUpstream!["upstream_channel"]) as AnyObject
// We have multiple channels
if channels is [Dictionary<String,AnyObject>] {
// Simplify
let channels: [Dictionary<String,AnyObject>] = (channels as! [Dictionary<String,AnyObject>])
// Step through channels
for index in 0..<channels.count {
// Grab channel
let channel: Dictionary<String,AnyObject> = channels[index]
// We do not have an error
if channel["upstream_modulation"] != nil && (channel["frequency"] as! String) != "status_error" {
// Grab power rating in dBmV
let powerInt: Double? = Double(channel["power_level"] as! String)
// We have valid power rating
if powerInt != nil {
// Grab modulation data
let modulation: NSObject = ((channel["upstream_modulation"]!)["modulation"])! as! NSObject
// Grab power adjustment see DOCSIS 3.0 Specification for more Information)
var adjust: String = ""
if modulation.isKind(of: NSArray.self) {
adjust = modulationAdjust[(((modulation as! NSArray)[0] as! NSDictionary)["mod_type"]!) as! String]! as! String
} else{
adjust = modulationAdjust[((modulation as! NSDictionary)["mod_type"]!) as! String]! as! String
}
let adjustInt: Double = Double(adjust)!
// Add timestamp and power to file entry
entry = "\((NSApp.delegate as! AppDelegate).justTime.string(from: date));\(String(powerInt! + adjustInt));"
// Add threshold to file entry
switch channels.count {
case 2:
entry += "\(upstreamTwo[modulationAdjust.allKeys(for: adjust)[0] as! String]!)"
case 3:
entry += "\(upstreamThreeOrFour[modulationAdjust.allKeys(for: adjust)[0] as! String]!)"
case 4:
entry += "\(upstreamThreeOrFour[modulationAdjust.allKeys(for: adjust)[0] as! String]!)"
default:
entry += "NaN"
}
// Add ranging status
entry += ";\(channel["ranging_status"] as! String)\n"
// Add file name to path
file = dir.appending("Upstream \(channel["frequency"]!).csv") as NSString
// Track modification
filesModified.append("Upstream \(channel["frequency"]!).csv")
// Write to file
logFreq(header: ";Power Level;Threshold;Ranging Status\n", file: file, entry: entry)
}
}
}
}
// We have a single channel
else if channels is Dictionary<String,AnyObject> {
// Grab channel
let channel: Dictionary<String,AnyObject> = channels as! Dictionary<String,AnyObject>
// We do not have an error
if channel["upstream_modulation"] != nil && channel["frequency"] as! String != "status_error" {
// Grab power rating in dBmV
let powerInt: Double? = Double(channel["power_level"] as! String)
// We have valid power rating
if powerInt != nil {
// Grab modulation data
let modulation: NSObject = ((channel["upstream_modulation"]!)["modulation"])! as! NSObject
// Grab power adjustment see DOCSIS 3.0 Specification for more Information)
var adjust: String = ""
if modulation.isKind(of: NSArray.self) {
adjust = modulationAdjust[(((modulation as! NSArray)[0] as! NSDictionary)["mod_type"]!) as! String]! as! String
} else{
adjust = modulationAdjust[((modulation as! NSDictionary)["mod_type"]!) as! String]! as! String
}
let adjustInt: Double = Double(adjust)!
// Add data to entry
entry = "\((NSApp.delegate as! AppDelegate).justTime.string(from: date));\(String(powerInt! + adjustInt));\(upstreamOne[modulationAdjust.allKeys(for: adjust)[0] as! String]!);\(channel["ranging_status"] as! String)\n"
// Add file name to path
file = dir.appending("Upstream \(channel["frequency"]!).csv") as NSString
// Track modifiction
filesModified.append("Upstream \(channel["frequency"]!).csv")
// Write to file
logFreq(header: ";Power Level;Threshold;Ranging Status\n", file: file, entry: entry)
}
}
}
}
// Downstream is valid
if dictionaryDownstream != nil {
var entry: String = ""
var file: NSString = ""
// Grab channels
let channels: AnyObject = (dictionaryDownstream!["downstream_channel"]) as AnyObject
// We have multiple channels
if channels is [Dictionary<String,AnyObject>] {
// Simplify
let channels: [Dictionary<String, AnyObject>] = channels as! [Dictionary<String, AnyObject>]
// Step through channels
for index in 0..<channels.count {
// Grab channel
let channel: Dictionary<String,AnyObject> = channels[index]
// We do not have an error
if channel["frequency"] as! String != "status_error" {
// Add data to entry
entry = "\((NSApp.delegate as! AppDelegate).justTime.string(from: date));\(channel["power_level"]!);\(channel["snr"]!)\n"
// Add file name to path
file = dir.appending("Downstream \(channel["frequency"]!).csv") as NSString
// Tracking modification
filesModified.append("Downstream \(channel["frequency"]!).csv")
// Write to file
logFreq(header: ";Power;SNR\n", file: file, entry: entry)
}
}
}
// We have a single channel
else if channels is Dictionary<String,AnyObject> {
// Grab channel
let channel: Dictionary<String,AnyObject> = channels as! Dictionary<String,AnyObject>
// We do not have an error
if channel["frequency"] as! String != "status_error" {
// Add data to entry
entry = "\((NSApp.delegate as! AppDelegate).justTime.string(from: date));\(channel["power_level"]!);\(channel["snr"]!)\n"
// Add file name to path
file = dir.appending("Downstream \(channel["frequency"]!).csv") as NSString
// Tracking modification
filesModified.append("Downstream \(channel["frequency"]!).csv")
// Write to file
logFreq(header: ";Power;SNR\n", file: file, entry: entry)
}
}
// Update if not modified
let fileManager = FileManager.default
// Get all filenames in directory
let enumerator:FileManager.DirectoryEnumerator = fileManager.enumerator(atPath: dir.expandingTildeInPath)!
// Step through files
for element in enumerator {
// Check if modified
if !filesModified.contains(element as! String) && (element as! String).contains(".csv") {
logDummie(dir: dir, element: element as! String, date: date)
}
}
}
// Update if both nil (no connection to modem possible)
else if (upstreamBool && downstreamBool) {
upstreamBool = false
downstreamBool = false
let fileManager = FileManager.default
// Get all filenames in directory
let enumerator:FileManager.DirectoryEnumerator = fileManager.enumerator(atPath: dir.expandingTildeInPath)!
// Step through files
for element in enumerator {
logDummie(dir: dir, element: element as! String, date: date)
}
}
}
}
func logFreq(header: String, file: NSString, entry: String) {
let fileManager = FileManager.default
if !fileManager.fileExists(atPath: file.expandingTildeInPath) {
do {
try header.write(toFile: file.expandingTildeInPath, atomically: true, encoding: .utf8)
}
catch {
NSLog("WRITE TO FILE \(file.expandingTildeInPath) FAILED\n");
return
}
}
let fileH: FileHandle? = FileHandle(forUpdatingAtPath: file.expandingTildeInPath)
fileH?.seekToEndOfFile()
let data: Data = (entry as NSString).data(using: String.Encoding.utf8.rawValue)!
fileH?.write(data)
fileH?.closeFile()
}
func logDummie(dir: NSString, element: String, date: Date) {
// Get file
let file: FileHandle? = FileHandle(forUpdatingAtPath: dir.expandingTildeInPath.appending(element))
if file != nil {
// Write appropriate null entry to file
file?.seekToEndOfFile();
var data: Data?;
if element.contains("Upstream") {
data = ("\((NSApp.delegate as! AppDelegate).justTime.string(from: date));-;-;-\n" as NSString).data(using: String.Encoding.utf8.rawValue)
} else {
data = ("\((NSApp.delegate as! AppDelegate).justTime.string(from: date));-;-\n" as NSString).data(using: String.Encoding.utf8.rawValue)
}
file?.write(data!)
file?.closeFile()
}
}
}
+3 -1
View File
@@ -17,7 +17,9 @@
<key>CFBundleShortVersionString</key>
<string>4.0</string>
<key>CFBundleVersion</key>
<string>13</string>
<string>26</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.utilities</string>
<key>LSMinimumSystemVersion</key>
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
<key>LSUIElement</key>
+6
View File
@@ -0,0 +1,6 @@
//
// Use this file to import your target's public headers that you would like to
// expose to Swift.
//
#import "XMLDictionary.h"
+1 -1
View File
@@ -1,6 +1,6 @@
//
// SettingsWindowController.swift
// Logger3
// Logger4
//
// Created by Kilian Hofmann on 15.08.16.
// Copyright © 2016 Kilian Hofmann. All rights reserved.
-26
View File
@@ -1,26 +0,0 @@
//
// Shared.swift
// Docsis Toolkit
//
// Created by Kilian Hofmann on 15.06.17.
// Copyright © 2017 Kilian Hofmann. All rights reserved.
//
import Cocoa
func log(_ entry: String, path: String) {
let file: FileHandle? = FileHandle(forUpdatingAtPath: path)
if file == nil {
do{
try (entry).write(toFile: path, atomically: true, encoding: String.Encoding.utf8)
}
catch {
NSLog("WRITE TO FILE FAILED\n");
}
} else {
file?.seekToEndOfFile();
let data = (entry as NSString).data(using: String.Encoding.utf8.rawValue)
file?.write(data!)
file?.closeFile()
}
}
+103
View File
@@ -0,0 +1,103 @@
//
// XMLDictionary.h
//
// Version 1.4
//
// Created by Nick Lockwood on 15/11/2010.
// Copyright 2010 Charcoal Design. All rights reserved.
//
// Get the latest version of XMLDictionary from here:
//
// https://github.com/nicklockwood/XMLDictionary
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
#import <Foundation/Foundation.h>
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wobjc-missing-property-synthesis"
typedef NS_ENUM(NSInteger, XMLDictionaryAttributesMode) {
XMLDictionaryAttributesModePrefixed = 0, // default
XMLDictionaryAttributesModeDictionary,
XMLDictionaryAttributesModeUnprefixed,
XMLDictionaryAttributesModeDiscard
};
typedef NS_ENUM(NSInteger, XMLDictionaryNodeNameMode) {
XMLDictionaryNodeNameModeRootOnly = 0, // default
XMLDictionaryNodeNameModeAlways,
XMLDictionaryNodeNameModeNever
};
static NSString *const XMLDictionaryAttributesKey = @"__attributes";
static NSString *const XMLDictionaryCommentsKey = @"__comments";
static NSString *const XMLDictionaryTextKey = @"__text";
static NSString *const XMLDictionaryNodeNameKey = @"__name";
static NSString *const XMLDictionaryAttributePrefix = @"_";
@interface XMLDictionaryParser : NSObject <NSCopying>
+ (XMLDictionaryParser *)sharedInstance;
@property(nonatomic, assign) BOOL collapseTextNodes; // defaults to YES
@property(nonatomic, assign) BOOL stripEmptyNodes; // defaults to YES
@property(nonatomic, assign) BOOL trimWhiteSpace; // defaults to YES
@property(nonatomic, assign) BOOL alwaysUseArrays; // defaults to NO
@property(nonatomic, assign) BOOL preserveComments; // defaults to NO
@property(nonatomic, assign) BOOL wrapRootNode; // defaults to NO
@property(nonatomic, assign) XMLDictionaryAttributesMode attributesMode;
@property(nonatomic, assign) XMLDictionaryNodeNameMode nodeNameMode;
- (NSDictionary *)dictionaryWithParser:(NSXMLParser *)parser;
- (NSDictionary *)dictionaryWithData:(NSData *)data;
- (NSDictionary *)dictionaryWithString:(NSString *)string;
- (NSDictionary *)dictionaryWithFile:(NSString *)path;
@end
@interface NSDictionary (XMLDictionary)
+ (NSDictionary *)dictionaryWithXMLParser:(NSXMLParser *)parser;
+ (NSDictionary *)dictionaryWithXMLData:(NSData *)data;
+ (NSDictionary *)dictionaryWithXMLString:(NSString *)string;
+ (NSDictionary *)dictionaryWithXMLFile:(NSString *)path;
- (NSDictionary *)attributes;
- (NSDictionary *)childNodes;
- (NSArray *)comments;
- (NSString *)nodeName;
- (NSString *)innerText;
- (NSString *)innerXML;
- (NSString *)XMLString;
- (NSArray *)arrayValueForKeyPath:(NSString *)keyPath;
- (NSString *)stringValueForKeyPath:(NSString *)keyPath;
- (NSDictionary *)dictionaryValueForKeyPath:(NSString *)keyPath;
@end
@interface NSString (XMLDictionary)
- (NSString *)XMLEncodedString;
@end
#pragma GCC diagnostic pop
+485
View File
@@ -0,0 +1,485 @@
//
// XMLDictionary.m
//
// Version 1.4
//
// Created by Nick Lockwood on 15/11/2010.
// Copyright 2010 Charcoal Design. All rights reserved.
//
// Get the latest version of XMLDictionary from here:
//
// https://github.com/nicklockwood/XMLDictionary
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
#import "XMLDictionary.h"
#pragma GCC diagnostic ignored "-Wobjc-missing-property-synthesis"
#pragma GCC diagnostic ignored "-Wdirect-ivar-access"
#pragma GCC diagnostic ignored "-Wformat-non-iso"
#pragma GCC diagnostic ignored "-Wgnu"
#import <Availability.h>
#if !__has_feature(objc_arc)
#error This class requires automatic reference counting
#endif
@interface XMLDictionaryParser () <NSXMLParserDelegate>
@property(nonatomic, strong) NSMutableDictionary *root;
@property(nonatomic, strong) NSMutableArray *stack;
@property(nonatomic, strong) NSMutableString *text;
@end
@implementation XMLDictionaryParser
+ (XMLDictionaryParser *)sharedInstance {
static dispatch_once_t once;
static XMLDictionaryParser *sharedInstance;
dispatch_once(&once, ^{
sharedInstance = [[XMLDictionaryParser alloc] init];
});
return sharedInstance;
}
- (id)init {
if ((self = [super init])) {
_collapseTextNodes = YES;
_stripEmptyNodes = YES;
_trimWhiteSpace = YES;
_alwaysUseArrays = NO;
_preserveComments = NO;
_wrapRootNode = NO;
}
return self;
}
- (id)copyWithZone:(NSZone *)zone {
XMLDictionaryParser *copy = [[[self class] allocWithZone:zone] init];
copy.collapseTextNodes = _collapseTextNodes;
copy.stripEmptyNodes = _stripEmptyNodes;
copy.trimWhiteSpace = _trimWhiteSpace;
copy.alwaysUseArrays = _alwaysUseArrays;
copy.preserveComments = _preserveComments;
copy.attributesMode = _attributesMode;
copy.nodeNameMode = _nodeNameMode;
copy.wrapRootNode = _wrapRootNode;
return copy;
}
- (NSDictionary *)dictionaryWithParser:(NSXMLParser *)parser {
[parser setDelegate:self];
[parser parse];
id result = _root;
_root = nil;
_stack = nil;
_text = nil;
return result;
}
- (NSDictionary *)dictionaryWithData:(NSData *)data {
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data];
return [self dictionaryWithParser:parser];
}
- (NSDictionary *)dictionaryWithString:(NSString *)string {
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
return [self dictionaryWithData:data];
}
- (NSDictionary *)dictionaryWithFile:(NSString *)path {
NSData *data = [NSData dataWithContentsOfFile:path];
return [self dictionaryWithData:data];
}
+ (NSString *)XMLStringForNode:(id)node withNodeName:(NSString *)nodeName {
if ([node isKindOfClass:[NSArray class]]) {
NSMutableArray *nodes = [NSMutableArray arrayWithCapacity:[node count]];
for (id individualNode in node) {
[nodes addObject:[self XMLStringForNode:individualNode
withNodeName:nodeName]];
}
return [nodes componentsJoinedByString:@"\n"];
} else if ([node isKindOfClass:[NSDictionary class]]) {
NSDictionary *attributes = [(NSDictionary *)node attributes];
NSMutableString *attributeString = [NSMutableString string];
for (NSString *key in [attributes allKeys]) {
[attributeString
appendFormat:@" %@=\"%@\"",
[[key description] XMLEncodedString],
[[attributes[key] description] XMLEncodedString]];
}
NSString *innerXML = [node innerXML];
if ([innerXML length]) {
return
[NSString stringWithFormat:@"<%1$@%2$@>%3$@</%1$@>", nodeName,
attributeString, innerXML];
} else {
return [NSString
stringWithFormat:@"<%@%@/>", nodeName, attributeString];
}
} else {
return
[NSString stringWithFormat:@"<%1$@>%2$@</%1$@>", nodeName,
[[node description] XMLEncodedString]];
}
}
- (void)endText {
if (_trimWhiteSpace) {
_text = [[_text stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]]
mutableCopy];
}
if ([_text length]) {
NSMutableDictionary *top = [_stack lastObject];
id existing = top[XMLDictionaryTextKey];
if ([existing isKindOfClass:[NSArray class]]) {
[existing addObject:_text];
} else if (existing) {
top[XMLDictionaryTextKey] = [@[ existing, _text ] mutableCopy];
} else {
top[XMLDictionaryTextKey] = _text;
}
}
_text = nil;
}
- (void)addText:(NSString *)text {
if (!_text) {
_text = [NSMutableString stringWithString:text];
} else {
[_text appendString:text];
}
}
- (void)parser:(__unused NSXMLParser *)parser
didStartElement:(NSString *)elementName
namespaceURI:(__unused NSString *)namespaceURI
qualifiedName:(__unused NSString *)qName
attributes:(NSDictionary *)attributeDict {
[self endText];
NSMutableDictionary *node = [NSMutableDictionary dictionary];
switch (_nodeNameMode) {
case XMLDictionaryNodeNameModeRootOnly: {
if (!_root) {
node[XMLDictionaryNodeNameKey] = elementName;
}
break;
}
case XMLDictionaryNodeNameModeAlways: {
node[XMLDictionaryNodeNameKey] = elementName;
break;
}
case XMLDictionaryNodeNameModeNever: {
break;
}
}
if ([attributeDict count]) {
switch (_attributesMode) {
case XMLDictionaryAttributesModePrefixed: {
for (NSString *key in [attributeDict allKeys]) {
node[[XMLDictionaryAttributePrefix
stringByAppendingString:key]] = attributeDict[key];
}
break;
}
case XMLDictionaryAttributesModeDictionary: {
node[XMLDictionaryAttributesKey] = attributeDict;
break;
}
case XMLDictionaryAttributesModeUnprefixed: {
[node addEntriesFromDictionary:attributeDict];
break;
}
case XMLDictionaryAttributesModeDiscard: {
break;
}
}
}
if (!_root) {
_root = node;
_stack = [NSMutableArray arrayWithObject:node];
if (_wrapRootNode) {
_root = [NSMutableDictionary dictionaryWithObject:_root
forKey:elementName];
[_stack insertObject:_root atIndex:0];
}
} else {
NSMutableDictionary *top = [_stack lastObject];
id existing = top[elementName];
if ([existing isKindOfClass:[NSArray class]]) {
[existing addObject:node];
} else if (existing) {
top[elementName] = [@[ existing, node ] mutableCopy];
} else if (_alwaysUseArrays) {
top[elementName] = [NSMutableArray arrayWithObject:node];
} else {
top[elementName] = node;
}
[_stack addObject:node];
}
}
- (NSString *)nameForNode:(NSDictionary *)node
inDictionary:(NSDictionary *)dict {
if (node.nodeName) {
return node.nodeName;
} else {
for (NSString *name in dict) {
id object = dict[name];
if (object == node) {
return name;
} else if ([object isKindOfClass:[NSArray class]] &&
[object containsObject:node]) {
return name;
}
}
}
return nil;
}
- (void)parser:(__unused NSXMLParser *)parser
didEndElement:(__unused NSString *)elementName
namespaceURI:(__unused NSString *)namespaceURI
qualifiedName:(__unused NSString *)qName {
[self endText];
NSMutableDictionary *top = [_stack lastObject];
[_stack removeLastObject];
if (!top.attributes && !top.childNodes && !top.comments) {
NSMutableDictionary *newTop = [_stack lastObject];
NSString *nodeName = [self nameForNode:top inDictionary:newTop];
if (nodeName) {
id parentNode = newTop[nodeName];
if (top.innerText && _collapseTextNodes) {
if ([parentNode isKindOfClass:[NSArray class]]) {
parentNode[[parentNode count] - 1] = top.innerText;
} else {
newTop[nodeName] = top.innerText;
}
} else if (!top.innerText && _stripEmptyNodes) {
if ([parentNode isKindOfClass:[NSArray class]]) {
[parentNode removeLastObject];
} else {
[newTop removeObjectForKey:nodeName];
}
} else if (!top.innerText && !_collapseTextNodes &&
!_stripEmptyNodes) {
top[XMLDictionaryTextKey] = @"";
}
}
}
}
- (void)parser:(__unused NSXMLParser *)parser
foundCharacters:(NSString *)string {
[self addText:string];
}
- (void)parser:(__unused NSXMLParser *)parser foundCDATA:(NSData *)CDATABlock {
[self addText:[[NSString alloc] initWithData:CDATABlock
encoding:NSUTF8StringEncoding]];
}
- (void)parser:(__unused NSXMLParser *)parser foundComment:(NSString *)comment {
if (_preserveComments) {
NSMutableDictionary *top = [_stack lastObject];
NSMutableArray *comments = top[XMLDictionaryCommentsKey];
if (!comments) {
comments = [@[ comment ] mutableCopy];
top[XMLDictionaryCommentsKey] = comments;
} else {
[comments addObject:comment];
}
}
}
@end
@implementation NSDictionary (XMLDictionary)
+ (NSDictionary *)dictionaryWithXMLParser:(NSXMLParser *)parser {
return [[[XMLDictionaryParser sharedInstance] copy]
dictionaryWithParser:parser];
}
+ (NSDictionary *)dictionaryWithXMLData:(NSData *)data {
return
[[[XMLDictionaryParser sharedInstance] copy] dictionaryWithData:data];
}
+ (NSDictionary *)dictionaryWithXMLString:(NSString *)string {
return [[[XMLDictionaryParser sharedInstance] copy]
dictionaryWithString:string];
}
+ (NSDictionary *)dictionaryWithXMLFile:(NSString *)path {
return
[[[XMLDictionaryParser sharedInstance] copy] dictionaryWithFile:path];
}
- (NSDictionary *)attributes {
NSDictionary *attributes = self[XMLDictionaryAttributesKey];
if (attributes) {
return [attributes count] ? attributes : nil;
} else {
NSMutableDictionary *filteredDict =
[NSMutableDictionary dictionaryWithDictionary:self];
[filteredDict removeObjectsForKeys:@[
XMLDictionaryCommentsKey,
XMLDictionaryTextKey,
XMLDictionaryNodeNameKey
]];
for (NSString *key in [filteredDict allKeys]) {
[filteredDict removeObjectForKey:key];
if ([key hasPrefix:XMLDictionaryAttributePrefix]) {
filteredDict[[key
substringFromIndex:[XMLDictionaryAttributePrefix length]]] =
self[key];
}
}
return [filteredDict count] ? filteredDict : nil;
}
return nil;
}
- (NSDictionary *)childNodes {
NSMutableDictionary *filteredDict = [self mutableCopy];
[filteredDict removeObjectsForKeys:@[
XMLDictionaryAttributesKey,
XMLDictionaryCommentsKey,
XMLDictionaryTextKey,
XMLDictionaryNodeNameKey
]];
for (NSString *key in [filteredDict allKeys]) {
if ([key hasPrefix:XMLDictionaryAttributePrefix]) {
[filteredDict removeObjectForKey:key];
}
}
return [filteredDict count] ? filteredDict : nil;
}
- (NSArray *)comments {
return self[XMLDictionaryCommentsKey];
}
- (NSString *)nodeName {
return self[XMLDictionaryNodeNameKey];
}
- (id)innerText {
id text = self[XMLDictionaryTextKey];
if ([text isKindOfClass:[NSArray class]]) {
return [text componentsJoinedByString:@"\n"];
} else {
return text;
}
}
- (NSString *)innerXML {
NSMutableArray *nodes = [NSMutableArray array];
for (NSString *comment in [self comments]) {
[nodes
addObject:[NSString stringWithFormat:@"<!--%@-->",
[comment XMLEncodedString]]];
}
NSDictionary *childNodes = [self childNodes];
for (NSString *key in childNodes) {
[nodes addObject:[XMLDictionaryParser XMLStringForNode:childNodes[key]
withNodeName:key]];
}
NSString *text = [self innerText];
if (text) {
[nodes addObject:[text XMLEncodedString]];
}
return [nodes componentsJoinedByString:@"\n"];
}
- (NSString *)XMLString {
if ([self count] == 1 && ![self nodeName]) {
// ignore outermost dictionary
return [self innerXML];
} else {
return
[XMLDictionaryParser XMLStringForNode:self
withNodeName:[self nodeName] ?: @"root"];
}
}
- (NSArray *)arrayValueForKeyPath:(NSString *)keyPath {
id value = [self valueForKeyPath:keyPath];
if (value && ![value isKindOfClass:[NSArray class]]) {
return @[ value ];
}
return value;
}
- (NSString *)stringValueForKeyPath:(NSString *)keyPath {
id value = [self valueForKeyPath:keyPath];
if ([value isKindOfClass:[NSArray class]]) {
value = [value count] ? value[0] : nil;
}
if ([value isKindOfClass:[NSDictionary class]]) {
return [(NSDictionary *)value innerText];
}
return value;
}
- (NSDictionary *)dictionaryValueForKeyPath:(NSString *)keyPath {
id value = [self valueForKeyPath:keyPath];
if ([value isKindOfClass:[NSArray class]]) {
value = [value count] ? value[0] : nil;
}
if ([value isKindOfClass:[NSString class]]) {
return @{XMLDictionaryTextKey : value};
}
return value;
}
@end
@implementation NSString (XMLDictionary)
- (NSString *)XMLEncodedString {
return
[[[[[self stringByReplacingOccurrencesOfString:@"&" withString:@"&amp;"]
stringByReplacingOccurrencesOfString:@"<"
withString:@"&lt;"]
stringByReplacingOccurrencesOfString:@">"
withString:@"&gt;"]
stringByReplacingOccurrencesOfString:@"\""
withString:@"&quot;"]
stringByReplacingOccurrencesOfString:@"\'"
withString:@"&apos;"];
}
@end
+1 -1
View File
@@ -1,2 +1,2 @@
version 4.0
build 13
build 26