Logger done for now, start on analysis tool
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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!")
|
||||
}
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
//
|
||||
// SettingsWindowController.swift
|
||||
// Logger3
|
||||
// Logger4
|
||||
//
|
||||
// Created by Kilian Hofmann on 15.08.16.
|
||||
// Copyright © 2016 Kilian Hofmann. All rights reserved.
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
Executable
+103
@@ -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
|
||||
Executable
+485
@@ -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:@"&"]
|
||||
stringByReplacingOccurrencesOfString:@"<"
|
||||
withString:@"<"]
|
||||
stringByReplacingOccurrencesOfString:@">"
|
||||
withString:@">"]
|
||||
stringByReplacingOccurrencesOfString:@"\""
|
||||
withString:@"""]
|
||||
stringByReplacingOccurrencesOfString:@"\'"
|
||||
withString:@"'"];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,2 +1,2 @@
|
||||
version 4.0
|
||||
build 13
|
||||
build 26
|
||||
|
||||
Reference in New Issue
Block a user