Author: marvin
Date: Fri May 11 20:44:44 2012
New Revision: 1337367
URL: http://svn.apache.org/viewvc?rev=1337367&view=rev
Log:
Add CFCParcel_new_from_json().
Modified:
lucy/trunk/clownfish/perl/lib/Clownfish/CFC.pm
lucy/trunk/clownfish/perl/lib/Clownfish/CFC.xs
lucy/trunk/clownfish/perl/t/403-parcel.t
lucy/trunk/clownfish/src/CFCParcel.c
lucy/trunk/clownfish/src/CFCParcel.h
Modified: lucy/trunk/clownfish/perl/lib/Clownfish/CFC.pm
URL:
http://svn.apache.org/viewvc/lucy/trunk/clownfish/perl/lib/Clownfish/CFC.pm?rev=1337367&r1=1337366&r2=1337367&view=diff
==============================================================================
--- lucy/trunk/clownfish/perl/lib/Clownfish/CFC.pm (original)
+++ lucy/trunk/clownfish/perl/lib/Clownfish/CFC.pm Fri May 11 20:44:44 2012
@@ -334,6 +334,29 @@ BEGIN { XSLoader::load( 'Clownfish::CFC'
return _singleton( @args{qw( name cnick )} );
}
+ our %new_PARAMS = (
+ name => undef,
+ cnick => undef,
+ );
+
+ sub new {
+ my ( $either, %args ) = @_;
+ verify_args( \%new_PARAMS, %args ) or confess $@;
+ confess "no subclassing allowed" unless $either eq __PACKAGE__;
+ return _new( @args{qw( name cnick )} );
+ }
+
+ our %new_from_json_PARAMS = (
+ json => undef,
+ );
+
+ sub new_from_json {
+ my ( $either, %args ) = @_;
+ verify_args( \%new_from_json_PARAMS, %args ) or confess $@;
+ confess "no subclassing allowed" unless $either eq __PACKAGE__;
+ return _new_from_json( $args{json} );
+ }
+
# $parcel =
Clownfish::CFC::Model::Parcel->aquire($parcel_name_or_parcel_object);
#
# Aquire a parcel one way or another. If the supplied argument is a
Modified: lucy/trunk/clownfish/perl/lib/Clownfish/CFC.xs
URL:
http://svn.apache.org/viewvc/lucy/trunk/clownfish/perl/lib/Clownfish/CFC.xs?rev=1337367&r1=1337366&r2=1337367&view=diff
==============================================================================
--- lucy/trunk/clownfish/perl/lib/Clownfish/CFC.xs (original)
+++ lucy/trunk/clownfish/perl/lib/Clownfish/CFC.xs Fri May 11 20:44:44 2012
@@ -1061,6 +1061,27 @@ PPCODE:
MODULE = Clownfish::CFC PACKAGE = Clownfish::CFC::Model::Parcel
SV*
+_new(name_sv, cnick_sv)
+ SV *name_sv;
+ SV *cnick_sv;
+CODE:
+ const char *name = SvOK(name_sv) ? SvPV_nolen(name_sv) : NULL;
+ const char *cnick = SvOK(cnick_sv) ? SvPV_nolen(cnick_sv) : NULL;
+ CFCParcel *self = CFCParcel_new(name, cnick);
+ RETVAL = S_cfcbase_to_perlref(self);
+ CFCBase_decref((CFCBase*)self);
+OUTPUT: RETVAL
+
+SV*
+_new_from_json(json)
+ const char *json;
+CODE:
+ CFCParcel *self = CFCParcel_new_from_json(json);
+ RETVAL = S_cfcbase_to_perlref(self);
+ CFCBase_decref((CFCBase*)self);
+OUTPUT: RETVAL
+
+SV*
_singleton(name_sv, cnick_sv)
SV *name_sv;
SV *cnick_sv;
Modified: lucy/trunk/clownfish/perl/t/403-parcel.t
URL:
http://svn.apache.org/viewvc/lucy/trunk/clownfish/perl/t/403-parcel.t?rev=1337367&r1=1337366&r2=1337367&view=diff
==============================================================================
--- lucy/trunk/clownfish/perl/t/403-parcel.t (original)
+++ lucy/trunk/clownfish/perl/t/403-parcel.t Fri May 11 20:44:44 2012
@@ -16,10 +16,24 @@
use strict;
use warnings;
-use Test::More tests => 7;
+use Test::More tests => 9;
BEGIN { use_ok('Clownfish::CFC::Model::Parcel') }
+isa_ok(
+ Clownfish::CFC::Model::Parcel->new( name => "Foo" ),
+ "Clownfish::CFC::Model::Parcel",
+ "new"
+);
+
+isa_ok(
+ Clownfish::CFC::Model::Parcel->new_from_json(
+ json => ' { "name": "Crustacean", "nickname": "Crust" } ',
+ ),
+ "Clownfish::CFC::Model::Parcel",
+ "new_from_json"
+);
+
# Register singleton.
Clownfish::CFC::Model::Parcel->singleton(
name => 'Crustacean',
Modified: lucy/trunk/clownfish/src/CFCParcel.c
URL:
http://svn.apache.org/viewvc/lucy/trunk/clownfish/src/CFCParcel.c?rev=1337367&r1=1337366&r2=1337367&view=diff
==============================================================================
--- lucy/trunk/clownfish/src/CFCParcel.c (original)
+++ lucy/trunk/clownfish/src/CFCParcel.c Fri May 11 20:44:44 2012
@@ -16,6 +16,7 @@
#include <ctype.h>
#include <string.h>
+#include <stdlib.h>
#ifndef true
#define true 1
@@ -36,6 +37,31 @@ struct CFCParcel {
char *PREFIX;
};
+#define JSON_STRING 1
+#define JSON_HASH 2
+
+typedef struct JSONNode {
+ int type;
+ char *string;
+ struct JSONNode **kids;
+ size_t num_kids;
+} JSONNode;
+
+static JSONNode*
+S_parse_json_for_parcel(const char *json);
+
+static JSONNode*
+S_parse_json_hash(const char **json);
+
+static JSONNode*
+S_parse_json_string(const char **json);
+
+static void
+S_skip_whitespace(const char **json);
+
+static void
+S_destroy_json(JSONNode *node);
+
#define MAX_PARCELS 100
static CFCParcel *registry[MAX_PARCELS + 1];
static int first_time = true;
@@ -155,6 +181,56 @@ CFCParcel_init(CFCParcel *self, const ch
return self;
}
+CFCParcel*
+CFCParcel_new_from_json(const char *json) {
+ JSONNode *parsed = S_parse_json_for_parcel(json);
+ if (!parsed) {
+ CFCUtil_die("Invalid JSON parcel definition");
+ }
+ const char *name = NULL;
+ const char *nickname = NULL;
+ for (size_t i = 0, max = parsed->num_kids; i < max; i += 2) {
+ JSONNode *key = parsed->kids[i];
+ JSONNode *value = parsed->kids[i + 1];
+ if (key->type != JSON_STRING) {
+ CFCUtil_die("JSON parsing error");
+ }
+ if (strcmp(key->string, "name") == 0) {
+ if (value->type != JSON_STRING) {
+ CFCUtil_die("'name' must be a string");
+ }
+ name = value->string;
+ }
+ else if (strcmp(key->string, "nickname") == 0) {
+ if (value->type != JSON_STRING) {
+ CFCUtil_die("'name' must be a string");
+ }
+ nickname = value->string;
+ }
+ }
+ if (!name) {
+ CFCUtil_die("Missing required key 'name'");
+ }
+ CFCParcel *self = CFCParcel_new(name, nickname);
+
+ for (size_t i = 0, max = parsed->num_kids; i < max; i += 2) {
+ JSONNode *key = parsed->kids[i];
+ JSONNode *value = parsed->kids[i + 1];
+ if (strcmp(key->string, "name") == 0
+ || strcmp(key->string, "nickname") == 0
+ ) {
+ ;
+ }
+ else {
+ CFCUtil_die("Unrecognized key in parcel definition: '%s'",
+ key->string);
+ }
+ }
+
+ S_destroy_json(parsed);
+ return self;
+}
+
void
CFCParcel_destroy(CFCParcel *self) {
FREEMEM(self->name);
@@ -213,3 +289,149 @@ CFCParcel_get_PREFIX(CFCParcel *self) {
return self->PREFIX;
}
+/*****************************************************************************
+ * The hack JSON parser coded up below is only meant to parse Clownfish parcel
+ * file content. It is limited in its capabilities because so little is legal
+ * in a .cfp file.
+ */
+
+static JSONNode*
+S_parse_json_for_parcel(const char *json) {
+ if (!json) {
+ return NULL;
+ }
+ S_skip_whitespace(&json);
+ if (*json != '{') {
+ return NULL;
+ }
+ JSONNode *parsed = S_parse_json_hash(&json);
+ S_skip_whitespace(&json);
+ if (*json != '\0') {
+ S_destroy_json(parsed);
+ parsed = NULL;
+ }
+ return parsed;
+}
+
+static void
+S_append_kid(JSONNode *node, JSONNode *child) {
+ size_t size = (node->num_kids + 2) * sizeof(JSONNode*);
+ node->kids = (JSONNode**)realloc(node->kids, size);
+ node->kids[node->num_kids++] = child;
+ node->kids[node->num_kids] = NULL;
+}
+
+static JSONNode*
+S_parse_json_hash(const char **json) {
+ const char *text = *json;
+ S_skip_whitespace(&text);
+ if (*text != '{') {
+ return NULL;
+ }
+ text++;
+ JSONNode *node = (JSONNode*)calloc(1, sizeof(JSONNode));
+ node->type = JSON_HASH;
+ while (1) {
+ // Parse key.
+ S_skip_whitespace(&text);
+ if (*text == '}') {
+ text++;
+ break;
+ }
+ else if (*text == '"') {
+ JSONNode *key = S_parse_json_string(&text);
+ S_skip_whitespace(&text);
+ if (!key || *text != ':') {
+ S_destroy_json(node);
+ return NULL;
+ }
+ text++;
+ S_append_kid(node, key);
+ }
+ else {
+ S_destroy_json(node);
+ return NULL;
+ }
+
+ // Parse value.
+ S_skip_whitespace(&text);
+ JSONNode *value = NULL;
+ if (*text == '"') {
+ value = S_parse_json_string(&text);
+ }
+ else if (*text == '{') {
+ value = S_parse_json_hash(&text);
+ }
+ if (!value) {
+ S_destroy_json(node);
+ return NULL;
+ }
+ S_append_kid(node, value);
+
+ // Parse comma.
+ S_skip_whitespace(&text);
+ if (*text == ',') {
+ text++;
+ }
+ else if (*text == '}') {
+ text++;
+ break;
+ }
+ else {
+ S_destroy_json(node);
+ return NULL;
+ }
+ }
+
+ // Move pointer.
+ *json = text;
+
+ return node;
+}
+
+// Parse a double quoted string. Don't allow escapes.
+static JSONNode*
+S_parse_json_string(const char **json) {
+ const char *text = *json;
+ if (*text != '\"') {
+ return NULL;
+ }
+ text++;
+ const char *start = text;
+ while (*text != '"') {
+ if (*text == '\\' || *text == '\0') {
+ return NULL;
+ }
+ text++;
+ }
+ JSONNode *node = (JSONNode*)calloc(1, sizeof(JSONNode));
+ node->type = JSON_STRING;
+ node->string = CFCUtil_strndup(start, text - start);
+
+ // Move pointer.
+ text++;
+ *json = text;
+
+ return node;
+}
+
+static void
+S_skip_whitespace(const char **json) {
+ while (isspace(json[0][0])) { *json = *json + 1; }
+}
+
+static void
+S_destroy_json(JSONNode *node) {
+ if (!node) {
+ return;
+ }
+ if (node->kids) {
+ for (size_t i = 0; node->kids[i] != NULL; i++) {
+ S_destroy_json(node->kids[i]);
+ }
+ }
+ free(node->string);
+ free(node->kids);
+ free(node);
+}
+
Modified: lucy/trunk/clownfish/src/CFCParcel.h
URL:
http://svn.apache.org/viewvc/lucy/trunk/clownfish/src/CFCParcel.h?rev=1337367&r1=1337366&r2=1337367&view=diff
==============================================================================
--- lucy/trunk/clownfish/src/CFCParcel.h (original)
+++ lucy/trunk/clownfish/src/CFCParcel.h Fri May 11 20:44:44 2012
@@ -58,6 +58,9 @@ CFCParcel*
CFCParcel_new(const char *name, const char *cnick);
CFCParcel*
+CFCParcel_new_from_json(const char *json);
+
+CFCParcel*
CFCParcel_init(CFCParcel *self, const char *name, const char *cnick);
void