Skip to content
Permalink
Browse files

* Add a JSON formatter

* MySQL JSON type columns are now automatically formatted when opening them in the Field Editor
  • Loading branch information
dmoagx committed Feb 12, 2017
1 parent 2c3a594 commit 618e84a46786b90f731ad963c5e14ea78dcfe58e
@@ -188,6 +188,7 @@

NSInteger editSheetReturnCode;
BOOL _isGeometry;
BOOL _isJSON;
NSUndoManager *esUndoManager;

NSDictionary *editedFieldInfo;
@@ -37,6 +37,7 @@
#include <objc/objc-runtime.h>
#import "SPCustomQuery.h"
#import "SPTableContent.h"
#import "SPJSONFormatter.h"

#import <SPMySQL/SPMySQL.h>

@@ -237,6 +238,7 @@ - (void)editWithObject:(id)data
callerInstance = sender;

_isGeometry = ([[fieldType uppercaseString] isEqualToString:@"GEOMETRY"]) ? YES : NO;
_isJSON = ([[fieldType uppercaseString] isEqualToString:SPMySQLJsonType]);

// Set field label
NSMutableString *label = [NSMutableString string];
@@ -250,7 +252,7 @@ - (void)editWithObject:(id)data
[label appendString:fieldType];

//skip length for JSON type since it's a constant and MySQL doesn't display it either
if (maxTextLength > 0 && ![[fieldType uppercaseString] isEqualToString:SPMySQLJsonType])
if (maxTextLength > 0 && !_isJSON)
[label appendFormat:@"(%lld) ", maxTextLength];

if (!_allowNULL)
@@ -353,7 +355,8 @@ - (void)editWithObject:(id)data

encoding = anEncoding;

_isBlob = isFieldBlob;
// we don't want the hex/image controls for JSON
_isBlob = (!_isJSON && isFieldBlob);

BOOL isBinary = ([[fieldType uppercaseString] isEqualToString:@"BINARY"] || [[fieldType uppercaseString] isEqualToString:@"VARBINARY"]);

@@ -443,7 +446,18 @@ - (void)editWithObject:(id)data
[editTextScrollView setHidden:NO];
}
else {
stringValue = [sheetEditData retain];
// If the input is a JSON type column we can format it.
// Since MySQL internally stores JSON in binary, it does not retain any formatting
do {
if(_isJSON) {
NSString *formatted = [SPJSONFormatter stringByFormattingString:sheetEditData];
if(formatted) {
stringValue = [formatted retain];
break;
}
}
stringValue = [sheetEditData retain];
} while(0);

[hexTextView setString:@""];

@@ -652,6 +666,12 @@ - (IBAction)closeEditSheet:(id)sender
if(callerInstance) {
id returnData = ( editSheetReturnCode && _isEditable ) ? (_isGeometry) ? [editTextView string] : sheetEditData : nil;

//for MySQLs JSON type remove the formatting again, since it won't be stored anyway
if(_isJSON) {
NSString *unformatted = [SPJSONFormatter stringByUnformattingString:returnData];
if(unformatted) returnData = unformatted;
}

#ifdef SP_CODA /* patch */
if ( [callerInstance isKindOfClass:[SPCustomQuery class]] )
[(SPCustomQuery*)callerInstance processFieldEditorResult:returnData contextInfo:contextInfo];
@@ -0,0 +1,123 @@
//
// SPJSONFormatter.h
// sequel-pro
//
// Created by Max Lohrmann on 10.02.17.
// Copyright (c) 2017 Max Lohrmann. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
//
// More info at <https://github.com/sequelpro/sequelpro>

#import <Foundation/Foundation.h>

typedef NS_ENUM(UInt8, SPJSONToken) {
JSON_TOK_EOF,
JSON_TOK_CURLY_BRACE_OPEN,
JSON_TOK_CURLY_BRACE_CLOSE,
JSON_TOK_SQUARE_BRACE_OPEN,
JSON_TOK_SQUARE_BRACE_CLOSE,
JSON_TOK_DOUBLE_QUOTE,
JSON_TOK_COLON,
JSON_TOK_COMMA,
JSON_TOK_OTHER,
JSON_TOK_STRINGDATA
};

typedef NS_ENUM(UInt8, SPJSONContext) {
JSON_ROOT_CONTEXT,
JSON_STRING_CONTEXT
};

typedef struct {
const char *str;
size_t len;
size_t pos;
SPJSONContext ctxt;
} SPJSONTokenizerState;

typedef struct {
SPJSONToken tok;
size_t pos;
size_t len;
} SPJSONTokenInfo;

/**
* Initializes a caller defined SPJSONTokenizerState structure to the string that is passed.
* The string is not retained. The caller is responsible for making sure it stays around as long
* as the tokenizer is used!
*
* @return 0 on success, -1 if an argument was NULL.
*/
int SPJSONTokenizerInit(NSString *input, SPJSONTokenizerState *stateInfo);

/**
* This function returns the token that is at the current position of the input string or following
* it most closely and forward the input string accordingly.
*
* The JSON_TOK_EOF token is a zero length token that is returned after the last character in the input
* string has been read and tokenized. Any call to this function after JSON_TOK_EOF has been returned
* will return the same.
*
* JSON_TOK_OTHER and JSON_TOK_STRINGDATA are variable length tokens (but never 0) that represent whitespace,
* numbers, true/false/null and the contents of strings (without the double quotes).
*
* The remaining tokens correspond to the respective control characters in JSON and are always a single
* character long.
*
* The token/position/length information will be assigned to the tokenMatch argument given by the caller.
*
* @return 1 If a token was successfully matched
* 0 If the matched token was JSON_TOK_EOF (tokenMatch will still be set, like for 1)
* -1 If the passed arguments were invalid (tokenMatch will not be updated)
*
* DO NOT try to build a parser/syntax validator based on this code! It is much too lenient for those purposes!
*/
int SPJSONTokenizerGetNextToken(SPJSONTokenizerState *stateInfo, SPJSONTokenInfo *tokenMatch);


@interface SPJSONFormatter : NSObject

/**
* This method will return a formatted copy of the input string.
*
* - A line break is inserted after every ",".
* - There will be a line break after every "{" and "[" (except if they are empty) and the indent
* of the following lines is increased by 1.
* - There will be a line break before "]" and "}" (except if they are empty) and the indent of this line
* and the following lines will be decreased by 1.
* - A line break will be inserted after "]" and "}", except if a "," follows.
* - Indenting is done using a single "\t" character per level.
*
* @return The formatted string or nil if formatting failed.
*/
+ (NSString *)stringByFormattingString:(NSString *)input;

/**
* This method will return a compact copy of the input string.
* All whitespace (outside of strings) will be removed (except for a single space after ":" and ",")
*
* @return The unformatted string or nil if unformatting failed.
*/
+ (NSString *)stringByUnformattingString:(NSString *)input;

@end

0 comments on commit 618e84a

Please sign in to comment.
You can’t perform that action at this time.