Modified: olingo/site/trunk/content/doc/javascript/apidoc/json.js.html URL: http://svn.apache.org/viewvc/olingo/site/trunk/content/doc/javascript/apidoc/json.js.html?rev=1672271&r1=1672270&r2=1672271&view=diff ============================================================================== --- olingo/site/trunk/content/doc/javascript/apidoc/json.js.html (original) +++ olingo/site/trunk/content/doc/javascript/apidoc/json.js.html Thu Apr 9 09:03:39 2015 @@ -25,923 +25,1013 @@ <section> <article> - <pre class="prettyprint source"><code>/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/** @module odata/json */ - - - -var utils = require('./../odatajs.js').utils; -var oDataUtils = require('./utils.js'); -var oDataHandler = require('./handler.js'); - -var odataNs = "odata"; -var odataAnnotationPrefix = odataNs + "."; -var contextUrlAnnotation = "@" + odataAnnotationPrefix + "context"; - -var assigned = utils.assigned; -var defined = utils.defined; -var isArray = utils.isArray; -//var isDate = utils.isDate; -var isObject = utils.isObject; -//var normalizeURI = utils.normalizeURI; -var parseInt10 = utils.parseInt10; -var getFormatKind = utils.getFormatKind; - -var formatDateTimeOffset = oDataUtils.formatDateTimeOffset; -var formatDuration = oDataUtils.formatDuration; -var formatNumberWidth = oDataUtils.formatNumberWidth; -var getCanonicalTimezone = oDataUtils.getCanonicalTimezone; -var handler = oDataUtils.handler; -var isComplex = oDataUtils.isComplex; -var isPrimitive = oDataUtils.isPrimitive; -var isCollectionType = oDataUtils.isCollectionType; -var lookupComplexType = oDataUtils.lookupComplexType; -var lookupEntityType = oDataUtils.lookupEntityType; -var lookupSingleton = oDataUtils.lookupSingleton; -var lookupEntitySet = oDataUtils.lookupEntitySet; -var lookupDefaultEntityContainer = oDataUtils.lookupDefaultEntityContainer; -var lookupProperty = oDataUtils.lookupProperty; -var MAX_DATA_SERVICE_VERSION = oDataUtils.MAX_DATA_SERVICE_VERSION; -var maxVersion = oDataUtils.maxVersion; -var XXXparseDateTime = oDataUtils.XXXparseDateTime; - -var isPrimitiveEdmType = oDataUtils.isPrimitiveEdmType; -var isGeographyEdmType = oDataUtils.isGeographyEdmType; -var isGeometryEdmType = oDataUtils.isGeometryEdmType; - -var PAYLOADTYPE_FEED = "f"; -var PAYLOADTYPE_ENTRY = "e"; -var PAYLOADTYPE_PROPERTY = "p"; -var PAYLOADTYPE_COLLECTION = "c"; -var PAYLOADTYPE_ENUMERATION_PROPERTY = "enum"; -var PAYLOADTYPE_SVCDOC = "s"; -var PAYLOADTYPE_ENTITY_REF_LINK = "erl"; -var PAYLOADTYPE_ENTITY_REF_LINKS = "erls"; - -var PAYLOADTYPE_VALUE = "v"; - -var PAYLOADTYPE_DELTA = "d"; -var DELTATYPE_FEED = "f"; -var DELTATYPE_DELETED_ENTRY = "de"; -var DELTATYPE_LINK = "l"; -var DELTATYPE_DELETED_LINK = "dl"; - -var jsonMediaType = "application/json"; -var jsonContentType = oDataHandler.contentType(jsonMediaType); - - -// The regular expression corresponds to something like this: -// /Date(123+60)/ -// -// This first number is date ticks, the + may be a - and is optional, -// with the second number indicating a timezone offset in minutes. -// -// On the wire, the leading and trailing forward slashes are -// escaped without being required to so the chance of collisions is reduced; -// however, by the time we see the objects, the characters already -// look like regular forward slashes. -var jsonDateRE = /^\/Date\((-?\d+)(\+|-)?(\d+)?\)\/$/; - -/** Formats the given minutes into (+/-)hh:mm format. - * @param {Number} minutes - Number of minutes to format. - * @returns {String} The minutes in (+/-)hh:mm format. - */ -function minutesToOffset(minutes) { - - var sign; - if (minutes < 0) { - sign = "-"; - minutes = -minutes; - } else { - sign = "+"; - } - - var hours = Math.floor(minutes / 60); - minutes = minutes - (60 * hours); - - return sign + formatNumberWidth(hours, 2) + ":" + formatNumberWidth(minutes, 2); -} - -/** Parses the JSON Date representation into a Date object. - * @param {String} value - String value. - * @returns {Date} A Date object if the value matches one; falsy otherwise. - */ -function parseJsonDateString(value) { - - var arr = value && jsonDateRE.exec(value); - if (arr) { - // 0 - complete results; 1 - ticks; 2 - sign; 3 - minutes - var result = new Date(parseInt10(arr[1])); - if (arr[2]) { - var mins = parseInt10(arr[3]); - if (arr[2] === "-") { - mins = -mins; - } - - // The offset is reversed to get back the UTC date, which is - // what the API will eventually have. - var current = result.getUTCMinutes(); - result.setUTCMinutes(current - mins); - result.__edmType = "Edm.DateTimeOffset"; - result.__offset = minutesToOffset(mins); - } - if (!isNaN(result.valueOf())) { - return result; - } - } - - // Allow undefined to be returned. -} - -// Some JSON implementations cannot produce the character sequence \/ -// which is needed to format DateTime and DateTimeOffset into the -// JSON string representation defined by the OData protocol. -// See the history of this file for a candidate implementation of -// a 'formatJsonDateString' function. - -/** Parses a JSON OData payload. - * @param handler - This handler. - * @param text - Payload text (this parser also handles pre-parsed objects). - * @param {Object} context - Object with parsing context. - * @return An object representation of the OData payload.</returns> - */ -function jsonParser(handler, text, context) { - - var recognizeDates = defined(context.recognizeDates, handler.recognizeDates); - var model = context.metadata; - var json = (typeof text === "string") ? JSON.parse(text) : text; - var metadataContentType; - if (assigned(context.contentType) && assigned(context.contentType.properties)) { - metadataContentType = context.contentType.properties["odata.metadata"]; //TODO convert to lower before comparism - } - - var payloadFormat = getFormatKind(metadataContentType, 1); // none: 0, minimal: 1, full: 2 - - // No errors should be throw out if we could not parse the json payload, instead we should just return the original json object. - if (payloadFormat === 0) { - return json; - } - else if (payloadFormat === 1) { - return readPayloadMinimal(json, model, recognizeDates); - } - else if (payloadFormat === 2) { - // to do: using the EDM Model to get the type of each property instead of just guessing. - return readPayloadFull(json, model, recognizeDates); - } - else { - return json; - } -} - - -function addType(data, name, value ) { - var fullName = name + '@odata.type'; - - if ( data[fullName] === undefined) { - data[fullName] = value; - } -} - -function addTypeNoEdm(data, name, value ) { - var fullName = name + '@odata.type'; - - if ( data[fullName] === undefined) { - if ( value.substring(0,4)==='Edm.') { - data[fullName] = value.substring(4); - } else { - data[fullName] = value; - } - - } -} - -function addTypeColNoEdm(data, name, value ) { - var fullName = name + '@odata.type'; - - if ( data[fullName] === undefined) { - if ( value.substring(0,4)==='Edm.') { - data[fullName] = 'Collection('+value.substring(4)+ ')'; - } else { - data[fullName] = 'Collection('+value+ ')'; - } - } -} - - -/* Adds typeinformation for String, Boolean and numerical EDM-types. - * The type is determined from the odata-json-format-v4.0.doc specification - * @param data - Date which will be extendet - * @param {Boolean} recognizeDates - True if strings formatted as datetime values should be treated as datetime values. False otherwise. - * @returns An object representation of the OData payload. - */ -function readPayloadFull(data, model, recognizeDates) { - var type; - if (utils.isObject(data)) { - for (var key in data) { - if (data.hasOwnProperty(key)) { - if (key.indexOf('@') === -1) { - if (utils.isArray(data[key])) { - for (var i = 0; i < data[key].length; ++i) { - readPayloadFull(data[key][i], model, recognizeDates); - } - } else if (utils.isObject(data[key])) { - if (data[key] !== null) { - //don't step into geo.. objects - var isGeo = false; - type = data[key+'@odata.type']; - if (type && (isGeographyEdmType(type) || isGeometryEdmType(type))) { - // is gemometry type - } else { - readPayloadFull(data[key], model, recognizeDates); - } - } - } else { - type = data[key + '@odata.type']; - - // On .Net OData library, some basic EDM type is omitted, e.g. Edm.String, Edm.Int, and etc. - // For the full metadata payload, we need to full fill the @data.type for each property if it is missing. - // We do this is to help the OlingoJS consumers to easily get the type of each property. - if (!assigned(type)) { - // Guessing the "type" from the type of the value is not the right way here. - // To do: we need to get the type from metadata instead of guessing. - var typeFromObject = typeof data[key]; - if (typeFromObject === 'string') { - addType(data, key, '#String'); - } else if (typeFromObject === 'boolean') { - addType(data, key, '#Boolean'); - } else if (typeFromObject === 'number') { - if (data[key] % 1 === 0) { // has fraction - addType(data, key, '#Int32'); // the biggst integer - } else { - addType(data, key, '#Decimal'); // the biggst float single,doulbe,decimal - } - } - } - else { - if (recognizeDates) { - convertDatesNoEdm(data, key, type.substring(1)); - } - } - } - } - } - } - } - - return data; -} - -/** Serializes the data by returning its string representation. - * @param handler - This handler. - * @param data - Data to serialize. - * @param {Object} context - Object with serialization context. - * @returns {String} The string representation of data. - */ -function jsonSerializer(handler, data, context) { - - var dataServiceVersion = context.dataServiceVersion || "4.0"; - var cType = context.contentType = context.contentType || jsonContentType; - - if (cType && cType.mediaType === jsonContentType.mediaType) { - context.dataServiceVersion = maxVersion(dataServiceVersion, "4.0"); - var newdata = formatJsonRequestPayload(data); - if (newdata) { - return JSON.stringify(newdata); - } - } - - return undefined; -} - -function formatJsonRequestPayload(data) { - if (!data) { - return data; - } - - if (isPrimitive(data)) { - return data; - } - - if (isArray(data)) { - var newArrayData = []; - var i, len; - for (i = 0, len = data.length; i < len; i++) { - newArrayData[i] = formatJsonRequestPayload(data[i]); - } - - return newArrayData; - } - - var newdata = {}; - for (var property in data) { - if (isJsonSerializableProperty(property)) { - newdata[property] = formatJsonRequestPayload(data[property]); - } - } - - return newdata; -} - -/** JSON replacer function for converting a value to its JSON representation. - * @param {Object} value - Value to convert.</param> - * @returns {String} JSON representation of the input value. - * This method is used during JSON serialization and invoked only by the JSON.stringify function. - * It should never be called directly. - */ -function jsonReplacer(_, value) { - - - if (value && value.__edmType === "Edm.Time") { - return formatDuration(value); - } else { - return value; - } -} - - -/** Creates an object containing information for the json payload. - * @param {String} kind - JSON payload kind, one of the PAYLOADTYPE_XXX constant values. - * @param {String} typeName - Type name of the JSON payload. - * @returns {Object} Object with kind and type fields. - */ -function jsonMakePayloadInfo(kind, type) { - - /// TODO docu - /// <field name="kind" type="String">Kind of the JSON payload. One of the PAYLOADTYPE_XXX constant values.</field> - /// <field name="type" type="String">Data type of the JSON payload.</field> - - return { kind: kind, type: type || null }; -} - -/** Creates an object containing information for the context - * TODO check dou layout - * @returns {Object} Object with type information - * @returns {Object.detectedPayloadKind(optional)} see constants starting with PAYLOADTYPE_ - * @returns {Object.deltaKind(optional)} deltainformation, one of the following valus DELTATYPE_FEED | DELTATYPE_DELETED_ENTRY | DELTATYPE_LINK | DELTATYPE_DELETED_LINK - * @returns {Object.typeName(optional)} name of the type - * @returns {Object.type(optional)} object containing type information for entity- and complex-types ( null if a typeName is a primitive) -*/ -function parseContextUriFragment( fragments, model ) { - var ret = {}; - - if (fragments.indexOf('/') === -1 ) { - if (fragments.length === 0) { - // Capter 10.1 - ret.detectedPayloadKind = PAYLOADTYPE_SVCDOC; - return ret; - } else if (fragments === 'Edm.Null') { - // Capter 10.15 - ret.detectedPayloadKind = PAYLOADTYPE_VALUE; - ret.isNullProperty = true; - return ret; - } else if (fragments === 'Collection($ref)') { - // Capter 10.11 - ret.detectedPayloadKind = PAYLOADTYPE_ENTITY_REF_LINKS; - return ret; - } else if (fragments === '$ref') { - // Capter 10.12 - ret.detectedPayloadKind = PAYLOADTYPE_ENTITY_REF_LINK; - return ret; - } else { - //TODO check for navigation resource - } - } - - ret.type = undefined; - ret.typeName = undefined; - - var fragmentParts = fragments.split("/"); - var type; - - for(var i = 0; i < fragmentParts.length; ++i) { - var fragment = fragmentParts[i]; - if (ret.typeName === undefined) { - //preparation - if ( fragment.indexOf('(') !== -1 ) { - //remove the query function, cut fragment to matching '(' - var index = fragment.length - 2 ; - for ( var rCount = 1; rCount > 0 && index > 0; --index) { - if ( fragment.charAt(index)=='(') { - rCount --; - } else if ( fragment.charAt(index)==')') { - rCount ++; - } - } - - if (index === 0) { - //TODO throw error - } - - //remove the projected entity from the fragment; TODO decide if we want to store the projected entity - var inPharenthesis = fragment.substring(index+2,fragment.length - 1); - fragment = fragment.substring(0,index+1); - - if (utils.startsWith(fragment, 'Collection')) { - ret.detectedPayloadKind = PAYLOADTYPE_COLLECTION; - // Capter 10.14 - ret.typeName = inPharenthesis; - - type = lookupEntityType(ret.typeName, model); - if ( type !== null) { - ret.type = type; - continue; - } - type = lookupComplexType(ret.typeName, model); - if ( type !== null) { - ret.type = type; - continue; - } - - ret.type = null;//in case of #Collection(Edm.String) only lastTypeName is filled - continue; - } else { - // projection: Capter 10.7, 10.8 and 10.9 - ret.projection = inPharenthesis; - } - } - - - if (jsonIsPrimitiveType(fragment)) { - ret.typeName = fragment; - ret.type = null; - ret.detectedPayloadKind = PAYLOADTYPE_VALUE; - continue; - } - - var container = lookupDefaultEntityContainer(model); - - //check for entity - var entitySet = lookupEntitySet(container.entitySet, fragment); - if ( entitySet !== null) { - ret.typeName = entitySet.entityType; - ret.type = lookupEntityType( ret.typeName, model); - ret.name = fragment; - ret.detectedPayloadKind = PAYLOADTYPE_FEED; - // Capter 10.2 - continue; - } - - //check for singleton - var singleton = lookupSingleton(container.singleton, fragment); - if ( singleton !== null) { - ret.typeName = singleton.entityType; - ret.type = lookupEntityType( ret.typeName, model); - ret.name = fragment; - ret.detectedPayloadKind = PAYLOADTYPE_ENTRY; - // Capter 10.4 - continue; - } - - - - //TODO throw ERROR - } else { - //check for $entity - if (utils.endsWith(fragment, '$entity') && (ret.detectedPayloadKind === PAYLOADTYPE_FEED)) { - //TODO ret.name = fragment; - ret.detectedPayloadKind = PAYLOADTYPE_ENTRY; - // Capter 10.3 and 10.6 - continue; - } - - //check for derived types - if (fragment.indexOf('.') !== -1) { - // Capter 10.6 - ret.typeName = fragment; - type = lookupEntityType(ret.typeName, model); - if ( type !== null) { - ret.type = type; - continue; - } - type = lookupComplexType(ret.typeName, model); - if ( type !== null) { - ret.type = type; - continue; - } - - //TODO throw ERROR invalid type - } - - //check for property value - if ( ret.detectedPayloadKind === PAYLOADTYPE_FEED || ret.detectedPayloadKind === PAYLOADTYPE_ENTRY) { - var property = lookupProperty(ret.type.property, fragment); - if (property !== null) { - //PAYLOADTYPE_COLLECTION - ret.typeName = property.type; - - - if (utils.startsWith(property.type, 'Collection')) { - ret.detectedPayloadKind = PAYLOADTYPE_COLLECTION; - var tmp12 = property.type.substring(10+1,property.type.length - 1); - ret.typeName = tmp12; - ret.type = lookupComplexType(tmp12, model); - ret.detectedPayloadKind = PAYLOADTYPE_COLLECTION; - } else { - ret.type = lookupComplexType(property.type, model); - ret.detectedPayloadKind = PAYLOADTYPE_PROPERTY; - } - - ret.name = fragment; - // Capter 10.15 - } - continue; - } - - if (fragment === '$delta') { - ret.deltaKind = DELTATYPE_FEED; - continue; - } else if (utils.endsWith(fragment, '/$deletedEntity')) { - ret.deltaKind = DELTATYPE_DELETED_ENTRY; - continue; - } else if (utils.endsWith(fragment, '/$link')) { - ret.deltaKind = DELTATYPE_LINK; - continue; - } else if (utils.endsWith(fragment, '/$deletedLink')) { - ret.deltaKind = DELTATYPE_DELETED_LINK; - continue; - } - //TODO throw ERROr - } - } - - return ret; -} - -/** Infers the information describing the JSON payload from its metadata annotation, structure, and data model. - * @param {Object} data - Json response payload object. - * @param {Object} model - Object describing an OData conceptual schema. - * If the arguments passed to the function don't convey enough information about the payload to determine without doubt that the payload is a feed then it - * will try to use the payload object structure instead. If the payload looks like a feed (has value property that is an array or non-primitive values) then - * the function will report its kind as PAYLOADTYPE_FEED unless the inferFeedAsComplexType flag is set to true. This flag comes from the user request - * and allows the user to control how the library behaves with an ambigous JSON payload. - * @return Object with kind and type fields. Null if there is no metadata annotation or the payload info cannot be obtained.. -*/ -function createPayloadInfo(data, model) { - - - var metadataUri = data[contextUrlAnnotation]; - if (!metadataUri || typeof metadataUri !== "string") { - return null; - } - - var fragmentStart = metadataUri.lastIndexOf("#"); - if (fragmentStart === -1) { - return jsonMakePayloadInfo(PAYLOADTYPE_SVCDOC); - } - - var fragment = metadataUri.substring(fragmentStart + 1); - return parseContextUriFragment(fragment,model); -} - -/** Processe a JSON response payload with metadata-minimal - * @param {Object} data - Json response payload object - * @param {Object} model - Object describing an OData conceptual schema - * @param {Boolean} recognizeDates - Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. - * @returns {Object} Object in the library's representation. - */ -function readPayloadMinimal(data, model, recognizeDates) { - - if (!assigned(model) || isArray(model)) { - return data; - } - - var baseURI = data[contextUrlAnnotation]; - var payloadInfo = createPayloadInfo(data, model); - - switch (payloadInfo.detectedPayloadKind) { - case PAYLOADTYPE_VALUE: - return readPayloadMinimalProperty(data, model, payloadInfo, baseURI, recognizeDates); - case PAYLOADTYPE_FEED: - return readPayloadMinimalFeed(data, model, payloadInfo, baseURI, recognizeDates); - case PAYLOADTYPE_ENTRY: - return readPayloadMinimalEntry(data, model, payloadInfo, baseURI, recognizeDates); - case PAYLOADTYPE_COLLECTION: - return readPayloadMinimalCollection(data, model, payloadInfo, baseURI, recognizeDates); - case PAYLOADTYPE_PROPERTY: - return readPayloadMinimalProperty(data, model, payloadInfo, baseURI, recognizeDates); - case PAYLOADTYPE_SVCDOC: - return data; - case PAYLOADTYPE_LINKS: - return data; - } - - return data; -} - -/** Gets the key of an entry. - * @param {Object} data - JSON entry. - * - * @returns {string} Entry instance key. - */ -function jsonGetEntryKey(data, entityModel) { - - var entityInstanceKey; - var entityKeys = entityModel.key[0].propertyRef; - var type; - entityInstanceKey = "("; - if (entityKeys.length == 1) { - type = lookupProperty(entityModel.property, entityKeys[0].name).type; - entityInstanceKey += formatLiteral(data[entityKeys[0].name], type); - } else { - var first = true; - for (var i = 0; i < entityKeys.length; i++) { - if (!first) { - entityInstanceKey += ","; - } else { - first = false; - } - type = lookupProperty(entityModel.property, entityKeys[i].name).type; - entityInstanceKey += entityKeys[i].name + "=" + formatLiteral(data[entityKeys[i].name], type); - } - } - entityInstanceKey += ")"; - return entityInstanceKey; -} - -function readPayloadMinimalProperty(data, model, collectionInfo, baseURI, recognizeDates) { - if (collectionInfo.type !== null) { - readPayloadMinimalObject(data, collectionInfo, baseURI, model, recognizeDates); - } else { - addTypeNoEdm(data,'value', collectionInfo.typeName); - //data['[email protected]'] = '#'+collectionInfo.typeName; - } - return data; -} - -function readPayloadMinimalCollection(data, model, collectionInfo, baseURI, recognizeDates) { - //data['@odata.type'] = '#Collection('+collectionInfo.typeName + ')'; - addTypeColNoEdm(data,'', collectionInfo.typeName); - - if (collectionInfo.type !== null) { - var entries = []; - - var items = data.value; - for (i = 0, len = items.length; i < len; i++) { - var item = items[i]; - if ( defined(item['@odata.type'])) { // in case of mixed collections - var typeName = item['@odata.type'].substring(1); - var type = lookupEntityType( typeName, model); - var entryInfo = { - contentTypeOdata : collectionInfo.contentTypeOdata, - detectedPayloadKind : collectionInfo.detectedPayloadKind, - name : collectionInfo.name, - type : type, - typeName : typeName - }; - - entry = readPayloadMinimalObject(item, entryInfo, baseURI, model, recognizeDates); - } else { - entry = readPayloadMinimalObject(item, collectionInfo, baseURI, model, recognizeDates); - } - - entries.push(entry); - } - data.value = entries; - } - return data; -} - -function readPayloadMinimalFeed(data, model, feedInfo, baseURI, recognizeDates) { - var entries = []; - var items = data.value; - for (i = 0, len = items.length; i < len; i++) { - var item = items[i]; - if ( defined(item['@odata.type'])) { // in case of mixed feeds - var typeName = item['@odata.type'].substring(1); - var type = lookupEntityType( typeName, model); - var entryInfo = { - contentTypeOdata : feedInfo.contentTypeOdata, - detectedPayloadKind : feedInfo.detectedPayloadKind, - name : feedInfo.name, - type : type, - typeName : typeName - }; - - entry = readPayloadMinimalObject(item, entryInfo, baseURI, model, recognizeDates); - } else { - entry = readPayloadMinimalObject(item, feedInfo, baseURI, model, recognizeDates); - } - - entries.push(entry); - } - data.value = entries; - return data; -} - -function readPayloadMinimalEntry(data, model, entryInfo, baseURI, recognizeDates) { - return readPayloadMinimalObject(data, entryInfo, baseURI, model, recognizeDates); -} - -/** Formats a value according to Uri literal format - * @param value - Value to be formatted. - * @param type - Edm type of the value - * @returns {string} Value after formatting - */ -function formatLiteral(value, type) { - - value = "" + formatRowLiteral(value, type); - value = encodeURIComponent(value.replace("'", "''")); - switch ((type)) { - case "Edm.Binary": - return "X'" + value + "'"; - case "Edm.DateTime": - return "datetime" + "'" + value + "'"; - case "Edm.DateTimeOffset": - return "datetimeoffset" + "'" + value + "'"; - case "Edm.Decimal": - return value + "M"; - case "Edm.Guid": - return "guid" + "'" + value + "'"; - case "Edm.Int64": - return value + "L"; - case "Edm.Float": - return value + "f"; - case "Edm.Double": - return value + "D"; - case "Edm.Geography": - return "geography" + "'" + value + "'"; - case "Edm.Geometry": - return "geometry" + "'" + value + "'"; - case "Edm.Time": - return "time" + "'" + value + "'"; - case "Edm.String": - return "'" + value + "'"; - default: - return value; - } -} - -function formatRowLiteral(value, type) { - switch (type) { - case "Edm.Binary": - return convertByteArrayToHexString(value); - default: - return value; - } -} - -function convertDates(data, propertyName,type) { - if (type === 'Edm.Date') { - data[propertyName] = oDataUtils.parseDate(data[propertyName], true); - } else if (type === 'Edm.DateTimeOffset') { - data[propertyName] = oDataUtils.parseDateTimeOffset(data[propertyName], true); - } else if (type === 'Edm.Duration') { - data[propertyName] = oDataUtils.parseDuration(data[propertyName], true); - } else if (type === 'Edm.Time') { - data[propertyName] = oDataUtils.parseTime(data[propertyName], true); - } -} - -function convertDatesNoEdm(data, propertyName,type) { - if (type === 'Date') { - data[propertyName] = oDataUtils.parseDate(data[propertyName], true); - } else if (type === 'DateTimeOffset') { - data[propertyName] = oDataUtils.parseDateTimeOffset(data[propertyName], true); - } else if (type === 'Duration') { - data[propertyName] = oDataUtils.parseDuration(data[propertyName], true); - } else if (type === 'Time') { - data[propertyName] = oDataUtils.parseTime(data[propertyName], true); - } -} - -function checkProperties(data, objectInfoType, baseURI, model, recognizeDates) { - for (var name in data) { - if (name.indexOf("@") === -1) { - var curType = objectInfoType; - var propertyValue = data[name]; - var property = lookupProperty(curType.property,name); //TODO SK add check for parent type - - while (( property === null) && (curType.baseType !== undefined)) { - curType = lookupEntityType(curType.baseType, model); - property = lookupProperty(curType.property,name); - } - - if ( isArray(propertyValue)) { - //data[name+'@odata.type'] = '#' + property.type; - if (isCollectionType(property.type)) { - addTypeColNoEdm(data,name,property.type.substring(11,property.type.length-1)); - } else { - addTypeNoEdm(data,name,property.type); - } - - - for ( var i = 0; i < propertyValue.length; i++) { - readPayloadMinimalComplexObject(propertyValue[i], property, baseURI, model, recognizeDates); - } - } else if (isObject(propertyValue) && (propertyValue !== null)) { - readPayloadMinimalComplexObject(propertyValue, property, baseURI, model, recognizeDates); - } else { - //data[name+'@odata.type'] = '#' + property.type; - addTypeNoEdm(data,name,property.type); - if (recognizeDates) { - convertDates(data, name, property.type); - } - } - } - } -} - -function readPayloadMinimalComplexObject(data, property, baseURI, model, recognizeDates) { - var type = property.type; - if (isCollectionType(property.type)) { - type =property.type.substring(11,property.type.length-1); - } - - //data['@odata.type'] = '#'+type; - addType(data,'',property.type); - - - var propertyType = lookupComplexType(type, model); - if (propertyType === null) { - return; //TODO check what to do if the type is not known e.g. type #GeometryCollection - } - - checkProperties(data, propertyType, baseURI, model, recognizeDates); -} - -function readPayloadMinimalObject(data, objectInfo, baseURI, model, recognizeDates) { - //data['@odata.type'] = '#'+objectInfo.typeName; - addType(data,'',objectInfo.typeName); - - var keyType = objectInfo.type; - while ((defined(keyType)) && ( keyType.key === undefined) && (keyType.baseType !== undefined)) { - keyType = lookupEntityType(keyType.baseType, model); - } - - //if ((keyType !== undefined) && (keyType.key !== undefined)) { - if (keyType.key !== undefined) { - var lastIdSegment = objectInfo.name + jsonGetEntryKey(data, keyType); - data['@odata.id'] = baseURI.substring(0, baseURI.lastIndexOf("$metadata")) + lastIdSegment; - data['@odata.editLink'] = lastIdSegment; - } - - var serviceURI = baseURI.substring(0, baseURI.lastIndexOf("$metadata")); - //json ComputeUrisIfMissing(data, entryInfo, actualType, serviceURI, dataModel, baseTypeModel); - - checkProperties(data, objectInfo.type, baseURI, model, recognizeDates); - - return data; -} - -var jsonSerializableMetadata = ["@odata.id", "@odata.type"]; - -function isJsonSerializableProperty(property) { - if (!property) { - return false; - } - - if (property.indexOf("@odata.") == -1) { - return true; - } - - var i, len; - for (i = 0, len = jsonSerializableMetadata.length; i < len; i++) { - var name = jsonSerializableMetadata[i]; - if (property.indexOf(name) != -1) { - return true; - } - } - - return false; -} - -/** Determines whether a type name is a primitive type in a JSON payload. - * @param {String} typeName - Type name to test. - * @returns {Boolean} True if the type name an EDM primitive type or an OData spatial type; false otherwise. - */ -function jsonIsPrimitiveType(typeName) { - - return isPrimitiveEdmType(typeName) || isGeographyEdmType(typeName) || isGeometryEdmType(typeName); -} - - -var jsonHandler = oDataHandler.handler(jsonParser, jsonSerializer, jsonMediaType, MAX_DATA_SERVICE_VERSION); -jsonHandler.recognizeDates = false; - - - -exports.createPayloadInfo = createPayloadInfo; -exports.jsonHandler = jsonHandler; -exports.jsonParser = jsonParser; -exports.jsonSerializer = jsonSerializer; + <pre class="prettyprint source"><code>/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** @module odata/json */ + + + +var utils = require('./../utils.js'); +var oDataUtils = require('./odatautils.js'); +var oDataHandler = require('./handler.js'); + +var odataNs = "odata"; +var odataAnnotationPrefix = odataNs + "."; +var contextUrlAnnotation = "@" + odataAnnotationPrefix + "context"; + +var assigned = utils.assigned; +var defined = utils.defined; +var isArray = utils.isArray; +//var isDate = utils.isDate; +var isObject = utils.isObject; +//var normalizeURI = utils.normalizeURI; +var parseInt10 = utils.parseInt10; +var getFormatKind = utils.getFormatKind; +var convertByteArrayToHexString = utils.convertByteArrayToHexString; + + +var formatDateTimeOffset = oDataUtils.formatDateTimeOffset; +var formatDuration = oDataUtils.formatDuration; +var formatNumberWidth = oDataUtils.formatNumberWidth; +var getCanonicalTimezone = oDataUtils.getCanonicalTimezone; +var handler = oDataUtils.handler; +var isComplex = oDataUtils.isComplex; +var isPrimitive = oDataUtils.isPrimitive; +var isCollectionType = oDataUtils.isCollectionType; +var lookupComplexType = oDataUtils.lookupComplexType; +var lookupEntityType = oDataUtils.lookupEntityType; +var lookupSingleton = oDataUtils.lookupSingleton; +var lookupEntitySet = oDataUtils.lookupEntitySet; +var lookupDefaultEntityContainer = oDataUtils.lookupDefaultEntityContainer; +var lookupProperty = oDataUtils.lookupProperty; +var MAX_DATA_SERVICE_VERSION = oDataUtils.MAX_DATA_SERVICE_VERSION; +var maxVersion = oDataUtils.maxVersion; + +var isPrimitiveEdmType = oDataUtils.isPrimitiveEdmType; +var isGeographyEdmType = oDataUtils.isGeographyEdmType; +var isGeometryEdmType = oDataUtils.isGeometryEdmType; + +var PAYLOADTYPE_FEED = "f"; +var PAYLOADTYPE_ENTRY = "e"; +var PAYLOADTYPE_PROPERTY = "p"; +var PAYLOADTYPE_COLLECTION = "c"; +var PAYLOADTYPE_ENUMERATION_PROPERTY = "enum"; +var PAYLOADTYPE_SVCDOC = "s"; +var PAYLOADTYPE_ENTITY_REF_LINK = "erl"; +var PAYLOADTYPE_ENTITY_REF_LINKS = "erls"; + +var PAYLOADTYPE_VALUE = "v"; + +var PAYLOADTYPE_DELTA = "d"; +var DELTATYPE_FEED = "f"; +var DELTATYPE_DELETED_ENTRY = "de"; +var DELTATYPE_LINK = "l"; +var DELTATYPE_DELETED_LINK = "dl"; + +var jsonMediaType = "application/json"; +var jsonContentType = oDataHandler.contentType(jsonMediaType); + +var jsonSerializableMetadata = ["@odata.id", "@odata.type"]; + + + + + +/** Extend JSON OData payload with metadata + * @param handler - This handler. + * @param text - Payload text (this parser also handles pre-parsed objects). + * @param {Object} context - Object with parsing context. + * @return An object representation of the OData payload. + */ +function jsonParser(handler, text, context) { + var recognizeDates = defined(context.recognizeDates, handler.recognizeDates); + var model = context.metadata; + var json = (typeof text === "string") ? JSON.parse(text) : text; + var metadataContentType; + if (assigned(context.contentType) && assigned(context.contentType.properties)) { + metadataContentType = context.contentType.properties["odata.metadata"]; //TODO convert to lower before comparism + } + + var payloadFormat = getFormatKind(metadataContentType, 1); // none: 0, minimal: 1, full: 2 + + // No errors should be throw out if we could not parse the json payload, instead we should just return the original json object. + if (payloadFormat === 0) { + return json; + } + else if (payloadFormat === 1) { + return addMinimalMetadataToJsonPayload(json, model, recognizeDates); + } + else if (payloadFormat === 2) { + // to do: using the EDM Model to get the type of each property instead of just guessing. + return addFullMetadataToJsonPayload(json, model, recognizeDates); + } + else { + return json; + } +} + + + + + + + + + + + + + + + + + + +// The regular expression corresponds to something like this: +// /Date(123+60)/ +// +// This first number is date ticks, the + may be a - and is optional, +// with the second number indicating a timezone offset in minutes. +// +// On the wire, the leading and trailing forward slashes are +// escaped without being required to so the chance of collisions is reduced; +// however, by the time we see the objects, the characters already +// look like regular forward slashes. +var jsonDateRE = /^\/Date\((-?\d+)(\+|-)?(\d+)?\)\/$/; + + +// Some JSON implementations cannot produce the character sequence \/ +// which is needed to format DateTime and DateTimeOffset into the +// JSON string representation defined by the OData protocol. +// See the history of this file for a candidate implementation of +// a 'formatJsonDateString' function. + + +var jsonReplacer = function (_, value) { + /// <summary>JSON replacer function for converting a value to its JSON representation.</summary> + /// <param value type="Object">Value to convert.</param> + /// <returns type="String">JSON representation of the input value.</returns> + /// <remarks> + /// This method is used during JSON serialization and invoked only by the JSON.stringify function. + /// It should never be called directly. + /// </remarks> + + if (value && value.__edmType === "Edm.Time") { + return formatDuration(value); + } else { + return value; + } +}; + +/** Serializes a ODataJs payload structure to the wire format which can be send to the server + * @param handler - This handler. + * @param data - Data to serialize. + * @param {Object} context - Object with serialization context. + * @returns {String} The string representation of data. + */ +function jsonSerializer(handler, data, context) { + + var dataServiceVersion = context.dataServiceVersion || "4.0"; + var cType = context.contentType = context.contentType || jsonContentType; + + if (cType && cType.mediaType === jsonContentType.mediaType) { + context.dataServiceVersion = maxVersion(dataServiceVersion, "4.0"); + var newdata = formatJsonRequestPayload(data); + if (newdata) { + return JSON.stringify(newdata,jsonReplacer); + } + } + return undefined; +} + + + + +/** Convert OData objects for serialisation in to a new data structure + * @param data - Data to serialize. + * @returns {String} The string representation of data. + */ +function formatJsonRequestPayload(data) { + if (!data) { + return data; + } + + if (isPrimitive(data)) { + return data; + } + + if (isArray(data)) { + var newArrayData = []; + var i, len; + for (i = 0, len = data.length; i < len; i++) { + newArrayData[i] = formatJsonRequestPayload(data[i]); + } + + return newArrayData; + } + + var newdata = {}; + for (var property in data) { + if (isJsonSerializableProperty(property)) { + newdata[property] = formatJsonRequestPayload(data[property]); + } + } + + return newdata; +} + +/** Determine form the attribute name if the attribute is a serializable property + * @param attribute + * @returns {boolean} + */ +function isJsonSerializableProperty(attribute) { + if (!attribute) { + return false; + } + + if (attribute.indexOf("@odata.") == -1) { + return true; + } + + var i, len; + for (i = 0, len = jsonSerializableMetadata.length; i < len; i++) { + var name = jsonSerializableMetadata[i]; + if (attribute.indexOf(name) != -1) { + return true; + } + } + + return false; +} + +/** Creates an object containing information for the json payload. + * @param {String} kind - JSON payload kind + * @param {String} type - Type name of the JSON payload. + * @returns {Object} Object with kind and type fields. + */ +function jsonMakePayloadInfo(kind, type) { + return { kind: kind, type: type || null }; +} + + + +/** Add metadata to an JSON payload complex object containing full metadata + * @param {Object} data - Data structure to be extended + * @param {Object} model - Metadata model + * @param {Boolean} recognizeDates - Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. + */ +function addFullMetadataToJsonPayload(data, model, recognizeDates) { + var type; + if (utils.isObject(data)) { + for (var key in data) { + if (data.hasOwnProperty(key)) { + if (key.indexOf('@') === -1) { + if (utils.isArray(data[key])) { + for (var i = 0; i < data[key].length; ++i) { + addFullMetadataToJsonPayload(data[key][i], model, recognizeDates); + } + } else if (utils.isObject(data[key])) { + if (data[key] !== null) { + //don't step into geo.. objects + type = data[key+'@odata.type']; + if (!type) { + //type unknown + addFullMetadataToJsonPayload(data[key], model, recognizeDates); + } else { + type = type.substring(1); + if (isGeographyEdmType(type) || isGeometryEdmType(type)) { + // don't add type info for geo* types + } else { + addFullMetadataToJsonPayload(data[key], model, recognizeDates); + } + } + } + } else { + type = data[key + '@odata.type']; + + // On .Net OData library, some basic EDM type is omitted, e.g. Edm.String, Edm.Int, and etc. + // For the full metadata payload, we need to full fill the @data.type for each property if it is missing. + // We do this is to help the OlingoJS consumers to easily get the type of each property. + if (!assigned(type)) { + // Guessing the "type" from the type of the value is not the right way here. + // To do: we need to get the type from metadata instead of guessing. + var typeFromObject = typeof data[key]; + if (typeFromObject === 'string') { + addType(data, key, 'String'); + } else if (typeFromObject === 'boolean') { + addType(data, key, 'Boolean'); + } else if (typeFromObject === 'number') { + if (data[key] % 1 === 0) { // has fraction + addType(data, key, 'Int32'); // the biggst integer + } else { + addType(data, key, 'Decimal'); // the biggst float single,doulbe,decimal + } + } + } + else { + if (recognizeDates) { + convertDatesNoEdm(data, key, type.substring(1)); + } + } + } + } + } + } + } + + return data; +} + +/** Loop through the properties of an JSON payload object, look up the type info of the property and call + * the appropriate add*MetadataToJsonPayloadObject function + * @param {Object} data - Data structure to be extended + * @param {String} objectInfoType - Information about the data (name,type,typename,...) + * @param {String} baseURI - Base Url + * @param {Object} model - Metadata model + * @param {Boolean} recognizeDates - Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. + */ +function checkProperties(data, objectInfoType, baseURI, model, recognizeDates) { + for (var name in data) { + if (name.indexOf("@") === -1) { + var curType = objectInfoType; + var propertyValue = data[name]; + var property = lookupProperty(curType.property,name); //TODO SK add check for parent type + + while (( property === null) && (curType.baseType !== undefined)) { + curType = lookupEntityType(curType.baseType, model); + property = lookupProperty(curType.property,name); + } + + if ( isArray(propertyValue)) { + //data[name+'@odata.type'] = '#' + property.type; + if (isCollectionType(property.type)) { + addTypeColNoEdm(data,name,property.type.substring(11,property.type.length-1)); + } else { + addTypeNoEdm(data,name,property.type); + } + + + for ( var i = 0; i < propertyValue.length; i++) { + addMetadataToJsonMinimalPayloadComplex(propertyValue[i], property, baseURI, model, recognizeDates); + } + } else if (isObject(propertyValue) && (propertyValue !== null)) { + addMetadataToJsonMinimalPayloadComplex(propertyValue, property, baseURI, model, recognizeDates); + } else { + //data[name+'@odata.type'] = '#' + property.type; + addTypeNoEdm(data,name,property.type); + if (recognizeDates) { + convertDates(data, name, property.type); + } + } + } + } +} + + + +/** Add metadata to an JSON payload object containing minimal metadata + * @param {Object} data - Json response payload object + * @param {Object} model - Object describing an OData conceptual schema + * @param {Boolean} recognizeDates - Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. + * @returns {Object} Object in the library's representation. + */ +function addMinimalMetadataToJsonPayload(data, model, recognizeDates) { + + if (!assigned(model) || isArray(model)) { + return data; + } + + var baseURI = data[contextUrlAnnotation]; + var payloadInfo = createPayloadInfo(data, model); + + switch (payloadInfo.detectedPayloadKind) { + + case PAYLOADTYPE_VALUE: + if (payloadInfo.type !== null) { + return addMetadataToJsonMinimalPayloadEntity(data, payloadInfo, baseURI, model, recognizeDates); + } else { + return addTypeNoEdm(data,'value', payloadInfo.typeName); + } + + case PAYLOADTYPE_FEED: + return addMetadataToJsonMinimalPayloadFeed(data, model, payloadInfo, baseURI, recognizeDates); + + case PAYLOADTYPE_ENTRY: + return addMetadataToJsonMinimalPayloadEntity(data, payloadInfo, baseURI, model, recognizeDates); + + case PAYLOADTYPE_COLLECTION: + return addMetadataToJsonMinimalPayloadCollection(data, model, payloadInfo, baseURI, recognizeDates); + + case PAYLOADTYPE_PROPERTY: + if (payloadInfo.type !== null) { + return addMetadataToJsonMinimalPayloadEntity(data, payloadInfo, baseURI, model, recognizeDates); + } else { + return addTypeNoEdm(data,'value', payloadInfo.typeName); + } + + case PAYLOADTYPE_SVCDOC: + return data; + + case PAYLOADTYPE_LINKS: + return data; + } + + return data; +} + +/** Add metadata to an JSON payload feed object containing minimal metadata + * @param {Object} data - Data structure to be extended + * @param {Object} model - Metadata model + * @param {String} feedInfo - Information about the data (name,type,typename,...) + * @param {String} baseURI - Base Url + * @param {Boolean} recognizeDates - Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. + */ +function addMetadataToJsonMinimalPayloadFeed(data, model, feedInfo, baseURI, recognizeDates) { + var entries = []; + var items = data.value; + var i,len; + var entry; + for (i = 0, len = items.length; i < len; i++) { + var item = items[i]; + if ( defined(item['@odata.type'])) { // in case of mixed feeds + var typeName = item['@odata.type'].substring(1); + var type = lookupEntityType( typeName, model); + var entryInfo = { + contentTypeOdata : feedInfo.contentTypeOdata, + detectedPayloadKind : feedInfo.detectedPayloadKind, + name : feedInfo.name, + type : type, + typeName : typeName + }; + + entry = addMetadataToJsonMinimalPayloadEntity(item, entryInfo, baseURI, model, recognizeDates); + } else { + entry = addMetadataToJsonMinimalPayloadEntity(item, feedInfo, baseURI, model, recognizeDates); + } + + entries.push(entry); + } + data.value = entries; + return data; +} + + +/** Add metadata to an JSON payload entity object containing minimal metadata + * @param {Object} data - Data structure to be extended + * @param {String} objectInfo - Information about the data (name,type,typename,...) + * @param {String} baseURI - Base Url + * @param {Object} model - Metadata model + * @param {Boolean} recognizeDates - Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. + */ +function addMetadataToJsonMinimalPayloadEntity(data, objectInfo, baseURI, model, recognizeDates) { + addType(data,'',objectInfo.typeName); + + var keyType = objectInfo.type; + while ((defined(keyType)) && ( keyType.key === undefined) && (keyType.baseType !== undefined)) { + keyType = lookupEntityType(keyType.baseType, model); + } + + if (keyType.key !== undefined) { + var lastIdSegment = objectInfo.name + jsonGetEntryKey(data, keyType); + data['@odata.id'] = baseURI.substring(0, baseURI.lastIndexOf("$metadata")) + lastIdSegment; + data['@odata.editLink'] = lastIdSegment; + } + + //var serviceURI = baseURI.substring(0, baseURI.lastIndexOf("$metadata")); + + checkProperties(data, objectInfo.type, baseURI, model, recognizeDates); + + return data; +} + +/** Add metadata to an JSON payload complex object containing minimal metadata + * @param {Object} data - Data structure to be extended + * @param {String} property - Information about the data (name,type,typename,...) + * @param {String} baseURI - Base Url + * @param {Object} model - Metadata model + * @param {Boolean} recognizeDates - Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. + */ +function addMetadataToJsonMinimalPayloadComplex(data, property, baseURI, model, recognizeDates) { + var type = property.type; + if (isCollectionType(property.type)) { + type =property.type.substring(11,property.type.length-1); + } + + addType(data,'',property.type); + + var propertyType = lookupComplexType(type, model); + if (propertyType === null) { + return; //TODO check what to do if the type is not known e.g. type #GeometryCollection + } + + checkProperties(data, propertyType, baseURI, model, recognizeDates); +} + +/** Add metadata to an JSON payload collection object containing minimal metadata + * @param {Object} data - Data structure to be extended + * @param {Object} model - Metadata model + * @param {String} collectionInfo - Information about the data (name,type,typename,...) + * @param {String} baseURI - Base Url + * @param {Boolean} recognizeDates - Flag indicating whether datetime literal strings should be converted to JavaScript Date objects. + */ +function addMetadataToJsonMinimalPayloadCollection(data, model, collectionInfo, baseURI, recognizeDates) { + + addTypeColNoEdm(data,'', collectionInfo.typeName); + + if (collectionInfo.type !== null) { + var entries = []; + + var items = data.value; + var i,len; + var entry; + for (i = 0, len = items.length; i < len; i++) { + var item = items[i]; + if ( defined(item['@odata.type'])) { // in case of mixed collections + var typeName = item['@odata.type'].substring(1); + var type = lookupEntityType( typeName, model); + var entryInfo = { + contentTypeOdata : collectionInfo.contentTypeOdata, + detectedPayloadKind : collectionInfo.detectedPayloadKind, + name : collectionInfo.name, + type : type, + typeName : typeName + }; + + entry = addMetadataToJsonMinimalPayloadEntity(item, entryInfo, baseURI, model, recognizeDates); + } else { + entry = addMetadataToJsonMinimalPayloadEntity(item, collectionInfo, baseURI, model, recognizeDates); + } + + entries.push(entry); + } + data.value = entries; + } + return data; +} + +/** Add an OData type tag to an JSON payload object + * @param {Object} data - Data structure to be extended + * @param {String} name - Name of the property whose type is set + * @param {String} value - Type name + */ +function addType(data, name, value ) { + var fullName = name + '@odata.type'; + + if ( data[fullName] === undefined) { + data[fullName] = '#' + value; + } +} + +/** Add an OData type tag to an JSON payload object collection (without "Edm." namespace) + * @param {Object} data - Data structure to be extended + * @param {String} name - Name of the property whose type is set + * @param {String} typeName - Type name + */ +function addTypeColNoEdm(data, name, typeName ) { + var fullName = name + '@odata.type'; + + if ( data[fullName] === undefined) { + if ( typeName.substring(0,4)==='Edm.') { + data[fullName] = '#Collection('+typeName.substring(4)+ ')'; + } else { + data[fullName] = '#Collection('+typeName+ ')'; + } + } +} + + +/** Add an OData type tag to an JSON payload object (without "Edm." namespace) + * @param {Object} data - Data structure to be extended + * @param {String} name - Name of the property whose type is set + * @param {String} value - Type name + */ +function addTypeNoEdm(data, name, value ) { + var fullName = name + '@odata.type'; + + if ( data[fullName] === undefined) { + if ( value.substring(0,4)==='Edm.') { + data[fullName] = '#' + value.substring(4); + } else { + data[fullName] = '#' + value; + } + } + return data; +} +/** Convert the date/time format of an property from the JSON payload object (without "Edm." namespace) + * @param {Object} data - Data structure to be extended + * @param propertyName - Name of the property to be changed + * @param type - Type + */ +function convertDates(data, propertyName,type) { + if (type === 'Edm.Date') { + data[propertyName] = oDataUtils.parseDate(data[propertyName], true); + } else if (type === 'Edm.DateTimeOffset') { + data[propertyName] = oDataUtils.parseDateTimeOffset(data[propertyName], true); + } else if (type === 'Edm.Duration') { + data[propertyName] = oDataUtils.parseDuration(data[propertyName], true); + } else if (type === 'Edm.Time') { + data[propertyName] = oDataUtils.parseTime(data[propertyName], true); + } +} + +/** Convert the date/time format of an property from the JSON payload object + * @param {Object} data - Data structure to be extended + * @param propertyName - Name of the property to be changed + * @param type - Type + */ +function convertDatesNoEdm(data, propertyName,type) { + if (type === 'Date') { + data[propertyName] = oDataUtils.parseDate(data[propertyName], true); + } else if (type === 'DateTimeOffset') { + data[propertyName] = oDataUtils.parseDateTimeOffset(data[propertyName], true); + } else if (type === 'Duration') { + data[propertyName] = oDataUtils.parseDuration(data[propertyName], true); + } else if (type === 'Time') { + data[propertyName] = oDataUtils.parseTime(data[propertyName], true); + } +} + +/** Formats a value according to Uri literal format + * @param value - Value to be formatted. + * @param type - Edm type of the value + * @returns {string} Value after formatting + */ +function formatLiteral(value, type) { + + value = "" + formatRawLiteral(value, type); + value = encodeURIComponent(value.replace("'", "''")); + switch ((type)) { + case "Edm.Binary": + return "X'" + value + "'"; + case "Edm.DateTime": + return "datetime" + "'" + value + "'"; + case "Edm.DateTimeOffset": + return "datetimeoffset" + "'" + value + "'"; + case "Edm.Decimal": + return value + "M"; + case "Edm.Guid": + return "guid" + "'" + value + "'"; + case "Edm.Int64": + return value + "L"; + case "Edm.Float": + return value + "f"; + case "Edm.Double": + return value + "D"; + case "Edm.Geography": + return "geography" + "'" + value + "'"; + case "Edm.Geometry": + return "geometry" + "'" + value + "'"; + case "Edm.Time": + return "time" + "'" + value + "'"; + case "Edm.String": + return "'" + value + "'"; + default: + return value; + } +} + +/** convert raw byteArray to hexString if the property is an binary property + * @param value - Value to be formatted. + * @param type - Edm type of the value + * @returns {string} Value after formatting + */ +function formatRawLiteral(value, type) { + switch (type) { + case "Edm.Binary": + return convertByteArrayToHexString(value); + default: + return value; + } +} + +/** Formats the given minutes into (+/-)hh:mm format. + * @param {Number} minutes - Number of minutes to format. + * @returns {String} The minutes in (+/-)hh:mm format. + */ +function minutesToOffset(minutes) { + + var sign; + if (minutes < 0) { + sign = "-"; + minutes = -minutes; + } else { + sign = "+"; + } + + var hours = Math.floor(minutes / 60); + minutes = minutes - (60 * hours); + + return sign + formatNumberWidth(hours, 2) + ":" + formatNumberWidth(minutes, 2); +} + +/** Parses the JSON Date representation into a Date object. + * @param {String} value - String value. + * @returns {Date} A Date object if the value matches one; falsy otherwise. + */ +function parseJsonDateString(value) { + + var arr = value && jsonDateRE.exec(value); + if (arr) { + // 0 - complete results; 1 - ticks; 2 - sign; 3 - minutes + var result = new Date(parseInt10(arr[1])); + if (arr[2]) { + var mins = parseInt10(arr[3]); + if (arr[2] === "-") { + mins = -mins; + } + + // The offset is reversed to get back the UTC date, which is + // what the API will eventually have. + var current = result.getUTCMinutes(); + result.setUTCMinutes(current - mins); + result.__edmType = "Edm.DateTimeOffset"; + result.__offset = minutesToOffset(mins); + } + if (!isNaN(result.valueOf())) { + return result; + } + } + + // Allow undefined to be returned. +} + +/** Creates an object containing information for the context + * @param {String} fragments - Uri fragment + * @param {Object} model - Object describing an OData conceptual schema + * @returns {Object} type(optional) object containing type information for entity- and complex-types ( null if a typeName is a primitive) + */ +function parseContextUriFragment( fragments, model ) { + var ret = {}; + + if (fragments.indexOf('/') === -1 ) { + if (fragments.length === 0) { + // Capter 10.1 + ret.detectedPayloadKind = PAYLOADTYPE_SVCDOC; + return ret; + } else if (fragments === 'Edm.Null') { + // Capter 10.15 + ret.detectedPayloadKind = PAYLOADTYPE_VALUE; + ret.isNullProperty = true; + return ret; + } else if (fragments === 'Collection($ref)') { + // Capter 10.11 + ret.detectedPayloadKind = PAYLOADTYPE_ENTITY_REF_LINKS; + return ret; + } else if (fragments === '$ref') { + // Capter 10.12 + ret.detectedPayloadKind = PAYLOADTYPE_ENTITY_REF_LINK; + return ret; + } else { + //TODO check for navigation resource + } + } + + ret.type = undefined; + ret.typeName = undefined; + + var fragmentParts = fragments.split("/"); + var type; + + for(var i = 0; i < fragmentParts.length; ++i) { + var fragment = fragmentParts[i]; + if (ret.typeName === undefined) { + //preparation + if ( fragment.indexOf('(') !== -1 ) { + //remove the query function, cut fragment to matching '(' + var index = fragment.length - 2 ; + for ( var rCount = 1; rCount > 0 && index > 0; --index) { + if ( fragment.charAt(index)=='(') { + rCount --; + } else if ( fragment.charAt(index)==')') { + rCount ++; + } + } + + if (index === 0) { + //TODO throw error + } + + //remove the projected entity from the fragment; TODO decide if we want to store the projected entity + var inPharenthesis = fragment.substring(index+2,fragment.length - 1); + fragment = fragment.substring(0,index+1); + + if (utils.startsWith(fragment, 'Collection')) { + ret.detectedPayloadKind = PAYLOADTYPE_COLLECTION; + // Capter 10.14 + ret.typeName = inPharenthesis; + + type = lookupEntityType(ret.typeName, model); + if ( type !== null) { + ret.type = type; + continue; + } + type = lookupComplexType(ret.typeName, model); + if ( type !== null) { + ret.type = type; + continue; + } + + ret.type = null;//in case of #Collection(Edm.String) only lastTypeName is filled + continue; + } else { + // projection: Capter 10.7, 10.8 and 10.9 + ret.projection = inPharenthesis; + } + } + + + if (jsonIsPrimitiveType(fragment)) { + ret.typeName = fragment; + ret.type = null; + ret.detectedPayloadKind = PAYLOADTYPE_VALUE; + continue; + } + + var container = lookupDefaultEntityContainer(model); + + //check for entity + var entitySet = lookupEntitySet(container.entitySet, fragment); + if ( entitySet !== null) { + ret.typeName = entitySet.entityType; + ret.type = lookupEntityType( ret.typeName, model); + ret.name = fragment; + ret.detectedPayloadKind = PAYLOADTYPE_FEED; + // Capter 10.2 + continue; + } + + //check for singleton + var singleton = lookupSingleton(container.singleton, fragment); + if ( singleton !== null) { + ret.typeName = singleton.entityType; + ret.type = lookupEntityType( ret.typeName, model); + ret.name = fragment; + ret.detectedPayloadKind = PAYLOADTYPE_ENTRY; + // Capter 10.4 + continue; + } + + + + //TODO throw ERROR + } else { + //check for $entity + if (utils.endsWith(fragment, '$entity') && (ret.detectedPayloadKind === PAYLOADTYPE_FEED)) { + //TODO ret.name = fragment; + ret.detectedPayloadKind = PAYLOADTYPE_ENTRY; + // Capter 10.3 and 10.6 + continue; + } + + //check for derived types + if (fragment.indexOf('.') !== -1) { + // Capter 10.6 + ret.typeName = fragment; + type = lookupEntityType(ret.typeName, model); + if ( type !== null) { + ret.type = type; + continue; + } + type = lookupComplexType(ret.typeName, model); + if ( type !== null) { + ret.type = type; + continue; + } + + //TODO throw ERROR invalid type + } + + //check for property value + if ( ret.detectedPayloadKind === PAYLOADTYPE_FEED || ret.detectedPayloadKind === PAYLOADTYPE_ENTRY) { + var property = lookupProperty(ret.type.property, fragment); + if (property !== null) { + //PAYLOADTYPE_COLLECTION + ret.typeName = property.type; + + + if (utils.startsWith(property.type, 'Collection')) { + ret.detectedPayloadKind = PAYLOADTYPE_COLLECTION; + var tmp12 = property.type.substring(10+1,property.type.length - 1); + ret.typeName = tmp12; + ret.type = lookupComplexType(tmp12, model); + ret.detectedPayloadKind = PAYLOADTYPE_COLLECTION; + } else { + ret.type = lookupComplexType(property.type, model); + ret.detectedPayloadKind = PAYLOADTYPE_PROPERTY; + } + + ret.name = fragment; + // Capter 10.15 + } + continue; + } + + if (fragment === '$delta') { + ret.deltaKind = DELTATYPE_FEED; + continue; + } else if (utils.endsWith(fragment, '/$deletedEntity')) { + ret.deltaKind = DELTATYPE_DELETED_ENTRY; + continue; + } else if (utils.endsWith(fragment, '/$link')) { + ret.deltaKind = DELTATYPE_LINK; + continue; + } else if (utils.endsWith(fragment, '/$deletedLink')) { + ret.deltaKind = DELTATYPE_DELETED_LINK; + continue; + } + //TODO throw ERROr + } + } + + return ret; +} + + +/** Infers the information describing the JSON payload from its metadata annotation, structure, and data model. + * @param {Object} data - Json response payload object. + * @param {Object} model - Object describing an OData conceptual schema. + * If the arguments passed to the function don't convey enough information about the payload to determine without doubt that the payload is a feed then it + * will try to use the payload object structure instead. If the payload looks like a feed (has value property that is an array or non-primitive values) then + * the function will report its kind as PAYLOADTYPE_FEED unless the inferFeedAsComplexType flag is set to true. This flag comes from the user request + * and allows the user to control how the library behaves with an ambigous JSON payload. + * @return Object with kind and type fields. Null if there is no metadata annotation or the payload info cannot be obtained.. + */ +function createPayloadInfo(data, model) { + var metadataUri = data[contextUrlAnnotation]; + if (!metadataUri || typeof metadataUri !== "string") { + return null; + } + + var fragmentStart = metadataUri.lastIndexOf("#"); + if (fragmentStart === -1) { + return jsonMakePayloadInfo(PAYLOADTYPE_SVCDOC); + } + + var fragment = metadataUri.substring(fragmentStart + 1); + return parseContextUriFragment(fragment,model); +} +/** Gets the key of an entry. + * @param {Object} data - JSON entry. + * @param {Object} data - EDM entity model for key loockup. + * @returns {string} Entry instance key. + */ +function jsonGetEntryKey(data, entityModel) { + + var entityInstanceKey; + var entityKeys = entityModel.key[0].propertyRef; + var type; + entityInstanceKey = "("; + if (entityKeys.length == 1) { + type = lookupProperty(entityModel.property, entityKeys[0].name).type; + entityInstanceKey += formatLiteral(data[entityKeys[0].name], type); + } else { + var first = true; + for (var i = 0; i < entityKeys.length; i++) { + if (!first) { + entityInstanceKey += ","; + } else { + first = false; + } + type = lookupProperty(entityModel.property, entityKeys[i].name).type; + entityInstanceKey += entityKeys[i].name + "=" + formatLiteral(data[entityKeys[i].name], type); + } + } + entityInstanceKey += ")"; + return entityInstanceKey; +} +/** Determines whether a type name is a primitive type in a JSON payload. + * @param {String} typeName - Type name to test. + * @returns {Boolean} True if the type name an EDM primitive type or an OData spatial type; false otherwise. + */ +function jsonIsPrimitiveType(typeName) { + return isPrimitiveEdmType(typeName) || isGeographyEdmType(typeName) || isGeometryEdmType(typeName); +} + + +var jsonHandler = oDataHandler.handler(jsonParser, jsonSerializer, jsonMediaType, MAX_DATA_SERVICE_VERSION); +jsonHandler.recognizeDates = false; + +exports.createPayloadInfo = createPayloadInfo; +exports.jsonHandler = jsonHandler; +exports.jsonParser = jsonParser; +exports.jsonSerializer = jsonSerializer; exports.parseJsonDateString = parseJsonDateString;</code></pre> </article> </section> @@ -952,13 +1042,13 @@ exports.parseJsonDateString = parseJsonD </div> <nav> - <h2><a href="index.html">Index</a></h2><h3>Modules</h3><ul><li><a href="module-cache.html">cache</a></li><li><a href="source.html">cache/source</a></li><li><a href="module-datajs.html">datajs</a></li><li><a href="deferred.html">datajs/deferred</a></li><li><a href="utils.html">datajs/utils</a></li><li><a href="xml.html">datajs/xml</a></li><li><a href="module-odata.html">odata</a></li><li><a href="batch.html">odata/batch</a></li><li><a href="handler.html">odata/handler</a></li><li><a href="json.html">odata/json</a></li><li><a href="metadata.html">odata/metadata</a></li><li><a href="net.html">odata/net</a></li><li><a href="utils_.html">odata/utils</a></li><li><a href="module-store.html">store</a></li><li><a href="dom.html">store/dom</a></li><li><a href="indexeddb.html">store/indexeddb</a></li><li><a href="memory.html">store/memory</a></li></ul><h3>Classes</h3><ul><li><a href="DataCache.html">DataCache</a></li><li><a href="DataCacheOperation.html">DataCacheOperation</a></li><li><a h ref="DjsDeferred.html">DjsDeferred</a></li><li><a href="dom-DomStore.html">DomStore</a></li><li><a href="indexeddb-IndexedDBStore.html">IndexedDBStore</a></li><li><a href="memory-MemoryStore.html">MemoryStore</a></li><li><a href="ODataCacheSource.html">ODataCacheSource</a></li></ul><h3><a href="global.html">Global</a></h3> + <h2><a href="index.html">Index</a></h2><h3>Modules</h3><ul><li><a href="module-cache.html">cache</a></li><li><a href="source.html">cache/source</a></li><li><a href="module-odata.html">odata</a></li><li><a href="batch.html">odata/batch</a></li><li><a href="handler.html">odata/handler</a></li><li><a href="json.html">odata/json</a></li><li><a href="metadata.html">odata/metadata</a></li><li><a href="net.html">odata/net</a></li><li><a href="utils.html">odata/utils</a></li><li><a href="deferred.html">odatajs/deferred</a></li><li><a href="utils_.html">odatajs/utils</a></li><li><a href="xml.html">odatajs/xml</a></li><li><a href="module-store.html">store</a></li><li><a href="dom.html">store/dom</a></li><li><a href="indexeddb.html">store/indexeddb</a></li><li><a href="memory.html">store/memory</a></li></ul><h3>Classes</h3><ul><li><a href="DataCache.html">DataCache</a></li><li><a href="DataCacheOperation.html">DataCacheOperation</a></li><li><a href="DjsDeferred.html">DjsDeferred</a></li><l i><a href="dom-DomStore.html">DomStore</a></li><li><a href="indexeddb-IndexedDBStore.html">IndexedDBStore</a></li><li><a href="memory-MemoryStore.html">MemoryStore</a></li><li><a href="ODataCacheSource.html">ODataCacheSource</a></li></ul><h3><a href="global.html">Global</a></h3> </nav> <br clear="both"> <footer> - Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.2.2</a> on Mon Sep 15 2014 13:07:59 GMT+0200 (MESZ) + Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.2.2</a> on Thu Apr 09 2015 08:31:26 GMT+0200 (MESZ) </footer> <script> prettyPrint(); </script>
