]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/dataschema/dataschema-json.js
Release 6.5.0
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / dataschema / dataschema-json.js
1 /*
2 Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 3.3.0
6 build: 3167
7 */
8 YUI.add('dataschema-json', function(Y) {
9
10 /**
11  * Provides a DataSchema implementation which can be used to work with JSON data.
12  *
13  * @module dataschema
14  * @submodule dataschema-json
15  */
16
17 /**
18  * JSON subclass for the DataSchema Utility.
19  * @class DataSchema.JSON
20  * @extends DataSchema.Base
21  * @static
22  */
23 var LANG = Y.Lang,
24
25     SchemaJSON = {
26
27         /////////////////////////////////////////////////////////////////////////////
28         //
29         // DataSchema.JSON static methods
30         //
31         /////////////////////////////////////////////////////////////////////////////
32         /**
33          * Utility function converts JSON locator strings into walkable paths
34          *
35          * @method DataSchema.JSON.getPath
36          * @param locator {String} JSON value locator.
37          * @return {String[]} Walkable path to data value.
38          * @static
39          */
40         getPath: function(locator) {
41             var path = null,
42                 keys = [],
43                 i = 0;
44
45             if (locator) {
46                 // Strip the ["string keys"] and [1] array indexes
47                 locator = locator.
48                     replace(/\[(['"])(.*?)\1\]/g,
49                     function (x,$1,$2) {keys[i]=$2;return '.@'+(i++);}).
50                     replace(/\[(\d+)\]/g,
51                     function (x,$1) {keys[i]=parseInt($1,10)|0;return '.@'+(i++);}).
52                     replace(/^\./,''); // remove leading dot
53
54                 // Validate against problematic characters.
55                 if (!/[^\w\.\$@]/.test(locator)) {
56                     path = locator.split('.');
57                     for (i=path.length-1; i >= 0; --i) {
58                         if (path[i].charAt(0) === '@') {
59                             path[i] = keys[parseInt(path[i].substr(1),10)];
60                         }
61                     }
62                 }
63                 else {
64                 }
65             }
66             return path;
67         },
68
69         /**
70          * Utility function to walk a path and return the value located there.
71          *
72          * @method DataSchema.JSON.getLocationValue
73          * @param path {String[]} Locator path.
74          * @param data {String} Data to traverse.
75          * @return {Object} Data value at location.
76          * @static
77          */
78         getLocationValue: function (path, data) {
79             var i = 0,
80                 len = path.length;
81             for (;i<len;i++) {
82                 if(
83                     LANG.isObject(data) &&
84                     (path[i] in data)
85                 ) {
86                     data = data[path[i]];
87                 }
88                 else {
89                     data = undefined;
90                     break;
91                 }
92             }
93             return data;
94         },
95
96         /**
97          * Applies a given schema to given JSON data.
98          *
99          * @method apply
100          * @param schema {Object} Schema to apply.
101          * @param data {Object} JSON data.
102          * @return {Object} Schema-parsed data.
103          * @static
104          */
105         apply: function(schema, data) {
106             var data_in = data,
107                 data_out = {results:[],meta:{}};
108
109             // Convert incoming JSON strings
110             if(!LANG.isObject(data)) {
111                 try {
112                     data_in = Y.JSON.parse(data);
113                 }
114                 catch(e) {
115                     data_out.error = e;
116                     return data_out;
117                 }
118             }
119
120             if(LANG.isObject(data_in) && schema) {
121                 // Parse results data
122                 if(!LANG.isUndefined(schema.resultListLocator)) {
123                     data_out = SchemaJSON._parseResults.call(this, schema, data_in, data_out);
124                 }
125
126                 // Parse meta data
127                 if(!LANG.isUndefined(schema.metaFields)) {
128                     data_out = SchemaJSON._parseMeta(schema.metaFields, data_in, data_out);
129                 }
130             }
131             else {
132                 data_out.error = new Error("JSON schema parse failure");
133             }
134
135             return data_out;
136         },
137
138         /**
139          * Schema-parsed list of results from full data
140          *
141          * @method _parseResults
142          * @param schema {Object} Schema to parse against.
143          * @param json_in {Object} JSON to parse.
144          * @param data_out {Object} In-progress parsed data to update.
145          * @return {Object} Parsed data object.
146          * @static
147          * @protected
148          */
149         _parseResults: function(schema, json_in, data_out) {
150             var results = [],
151                 path,
152                 error;
153
154             if(schema.resultListLocator) {
155                 path = SchemaJSON.getPath(schema.resultListLocator);
156                 if(path) {
157                     results = SchemaJSON.getLocationValue(path, json_in);
158                     if (results === undefined) {
159                         data_out.results = [];
160                         error = new Error("JSON results retrieval failure");
161                     }
162                     else {
163                         if(LANG.isArray(results)) {
164                             // if no result fields are passed in, then just take the results array whole-hog
165                             // Sometimes you're getting an array of strings, or want the whole object,
166                             // so resultFields don't make sense.
167                             if (LANG.isArray(schema.resultFields)) {
168                                 data_out = SchemaJSON._getFieldValues.call(this, schema.resultFields, results, data_out);
169                             }
170                             else {
171                                 data_out.results = results;
172                             }
173                         }
174                         else {
175                             data_out.results = [];
176                             error = new Error("JSON Schema fields retrieval failure");
177                         }
178                     }
179                 }
180                 else {
181                     error = new Error("JSON Schema results locator failure");
182                 }
183
184                 if (error) {
185                     data_out.error = error;
186                 }
187
188             }
189             return data_out;
190         },
191
192         /**
193          * Get field data values out of list of full results
194          *
195          * @method _getFieldValues
196          * @param fields {Array} Fields to find.
197          * @param array_in {Array} Results to parse.
198          * @param data_out {Object} In-progress parsed data to update.
199          * @return {Object} Parsed data object.
200          * @static
201          * @protected
202          */
203         _getFieldValues: function(fields, array_in, data_out) {
204             var results = [],
205                 len = fields.length,
206                 i, j,
207                 field, key, locator, path, parser,
208                 simplePaths = [], complexPaths = [], fieldParsers = [],
209                 result, record;
210
211             // First collect hashes of simple paths, complex paths, and parsers
212             for (i=0; i<len; i++) {
213                 field = fields[i]; // A field can be a simple string or a hash
214                 key = field.key || field; // Find the key
215                 locator = field.locator || key; // Find the locator
216
217                 // Validate and store locators for later
218                 path = SchemaJSON.getPath(locator);
219                 if (path) {
220                     if (path.length === 1) {
221                         simplePaths[simplePaths.length] = {key:key, path:path[0]};
222                     } else {
223                         complexPaths[complexPaths.length] = {key:key, path:path};
224                     }
225                 } else {
226                 }
227
228                 // Validate and store parsers for later
229                 //TODO: use Y.DataSchema.parse?
230                 parser = (LANG.isFunction(field.parser)) ? field.parser : Y.Parsers[field.parser+''];
231                 if (parser) {
232                     fieldParsers[fieldParsers.length] = {key:key, parser:parser};
233                 }
234             }
235
236             // Traverse list of array_in, creating records of simple fields,
237             // complex fields, and applying parsers as necessary
238             for (i=array_in.length-1; i>=0; --i) {
239                 record = {};
240                 result = array_in[i];
241                 if(result) {
242                     // Cycle through simpleLocators
243                     for (j=simplePaths.length-1; j>=0; --j) {
244                         // Bug 1777850: The result might be an array instead of object
245                         record[simplePaths[j].key] = Y.DataSchema.Base.parse.call(this,
246                                 (LANG.isUndefined(result[simplePaths[j].path]) ?
247                                 result[j] : result[simplePaths[j].path]), simplePaths[j]);
248                     }
249
250                     // Cycle through complexLocators
251                     for (j=complexPaths.length - 1; j>=0; --j) {
252                         record[complexPaths[j].key] = Y.DataSchema.Base.parse.call(this,
253                             (SchemaJSON.getLocationValue(complexPaths[j].path, result)), complexPaths[j] );
254                     }
255
256                     // Cycle through fieldParsers
257                     for (j=fieldParsers.length-1; j>=0; --j) {
258                         key = fieldParsers[j].key;
259                         record[key] = fieldParsers[j].parser.call(this, record[key]);
260                         // Safety net
261                         if (LANG.isUndefined(record[key])) {
262                             record[key] = null;
263                         }
264                     }
265                     results[i] = record;
266                 }
267             }
268             data_out.results = results;
269             return data_out;
270         },
271
272         /**
273          * Parses results data according to schema
274          *
275          * @method _parseMeta
276          * @param metaFields {Object} Metafields definitions.
277          * @param json_in {Object} JSON to parse.
278          * @param data_out {Object} In-progress parsed data to update.
279          * @return {Object} Schema-parsed meta data.
280          * @static
281          * @protected
282          */
283         _parseMeta: function(metaFields, json_in, data_out) {
284             if(LANG.isObject(metaFields)) {
285                 var key, path;
286                 for(key in metaFields) {
287                     if (metaFields.hasOwnProperty(key)) {
288                         path = SchemaJSON.getPath(metaFields[key]);
289                         if (path && json_in) {
290                             data_out.meta[key] = SchemaJSON.getLocationValue(path, json_in);
291                         }
292                     }
293                 }
294             }
295             else {
296                 data_out.error = new Error("JSON meta data retrieval failure");
297             }
298             return data_out;
299         }
300     };
301
302 Y.DataSchema.JSON = Y.mix(SchemaJSON, Y.DataSchema.Base);
303
304
305
306 }, '3.3.0' ,{requires:['dataschema-base','json']});