You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@olingo.apache.org by ch...@apache.org on 2014/08/28 05:48:25 UTC
[18/51] [partial] rename folder /datajs into /odatajs. no file
modification.
http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/d5ec5557/odatajs/src/lib/odata/json.js
----------------------------------------------------------------------
diff --git a/odatajs/src/lib/odata/json.js b/odatajs/src/lib/odata/json.js
new file mode 100644
index 0000000..bed98ff
--- /dev/null
+++ b/odatajs/src/lib/odata/json.js
@@ -0,0 +1,918 @@
+/*
+ * 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('./../datajs.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['value@odata.type'] = '#'+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;
+exports.parseJsonDateString = parseJsonDateString;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/d5ec5557/odatajs/src/lib/odata/metadata.js
----------------------------------------------------------------------
diff --git a/odatajs/src/lib/odata/metadata.js b/odatajs/src/lib/odata/metadata.js
new file mode 100644
index 0000000..5ad70b6
--- /dev/null
+++ b/odatajs/src/lib/odata/metadata.js
@@ -0,0 +1,523 @@
+/*
+ * 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/metadata */
+
+var utils = require('./../datajs.js').utils;
+var oDSxml = require('./../datajs.js').xml;
+var odataHandler = require('./handler.js');
+
+
+
+// imports
+var contains = utils.contains;
+var normalizeURI = utils.normalizeURI;
+var xmlAttributes = oDSxml.xmlAttributes;
+var xmlChildElements = oDSxml.xmlChildElements;
+var xmlFirstChildElement = oDSxml.xmlFirstChildElement;
+var xmlInnerText = oDSxml.xmlInnerText;
+var xmlLocalName = oDSxml.xmlLocalName;
+var xmlNamespaceURI = oDSxml.xmlNamespaceURI;
+var xmlNS = oDSxml.xmlNS;
+var xmlnsNS = oDSxml.xmlnsNS;
+var xmlParse = oDSxml.xmlParse;
+
+var ado = oDSxml.http + "docs.oasis-open.org/odata/"; // http://docs.oasis-open.org/odata/
+var adoDs = ado + "ns"; // http://docs.oasis-open.org/odata/ns
+var edmxNs = adoDs + "/edmx"; // http://docs.oasis-open.org/odata/ns/edmx
+var edmNs1 = adoDs + "/edm"; // http://docs.oasis-open.org/odata/ns/edm
+var odataMetaXmlNs = adoDs + "/metadata"; // http://docs.oasis-open.org/odata/ns/metadata
+var MAX_DATA_SERVICE_VERSION = odataHandler.MAX_DATA_SERVICE_VERSION;
+
+var xmlMediaType = "application/xml";
+
+/** Creates an object that describes an element in an schema.
+ * @param {Array} attributes - List containing the names of the attributes allowed for this element.
+ * @param {Array} elements - List containing the names of the child elements allowed for this element.
+ * @param {Boolean} text - Flag indicating if the element's text value is of interest or not.
+ * @param {String} ns - Namespace to which the element belongs to.
+ * If a child element name ends with * then it is understood by the schema that that child element can appear 0 or more times.
+ * @returns {Object} Object with attributes, elements, text, and ns fields.
+ */
+function schemaElement(attributes, elements, text, ns) {
+
+ return {
+ attributes: attributes,
+ elements: elements,
+ text: text || false,
+ ns: ns
+ };
+}
+
+// It's assumed that all elements may have Documentation children and Annotation elements.
+// See http://docs.oasis-open.org/odata/odata/v4.0/cs01/part3-csdl/odata-v4.0-cs01-part3-csdl.html for a CSDL reference.
+var schema = {
+ elements: {
+ Action: schemaElement(
+ /*attributes*/["Name", "IsBound", "EntitySetPath"],
+ /*elements*/["ReturnType", "Parameter*", "Annotation*"]
+ ),
+ ActionImport: schemaElement(
+ /*attributes*/["Name", "Action", "EntitySet", "Annotation*"]
+ ),
+ Annotation: schemaElement(
+ /*attributes*/["Term", "Qualifier", "Binary", "Bool", "Date", "DateTimeOffset", "Decimal", "Duration", "EnumMember", "Float", "Guid", "Int", "String", "TimeOfDay", "AnnotationPath", "NavigationPropertyPath", "Path", "PropertyPath", "UrlRef"],
+ /*elements*/["Binary*", "Bool*", "Date*", "DateTimeOffset*", "Decimal*", "Duration*", "EnumMember*", "Float*", "Guid*", "Int*", "String*", "TimeOfDay*", "And*", "Or*", "Not*", "Eq*", "Ne*", "Gt*", "Ge*", "Lt*", "Le*", "AnnotationPath*", "Apply*", "Cast*", "Collection*", "If*", "IsOf*", "LabeledElement*", "LabeledElementReference*", "Null*", "NavigationPropertyPath*", "Path*", "PropertyPath*", "Record*", "UrlRef*", "Annotation*"]
+ ),
+ AnnotationPath: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Annotations: schemaElement(
+ /*attributes*/["Target", "Qualifier"],
+ /*elements*/["Annotation*"]
+ ),
+ Apply: schemaElement(
+ /*attributes*/["Function"],
+ /*elements*/["String*", "Path*", "LabeledElement*", "Annotation*"]
+ ),
+ And: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Or: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Not: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Eq: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Ne: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Gt: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Ge: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Lt: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Le: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Binary: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Bool: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Cast: schemaElement(
+ /*attributes*/["Type"],
+ /*elements*/["Path*", "Annotation*"]
+ ),
+ Collection: schemaElement(
+ /*attributes*/null,
+ /*elements*/["Binary*", "Bool*", "Date*", "DateTimeOffset*", "Decimal*", "Duration*", "EnumMember*", "Float*", "Guid*", "Int*", "String*", "TimeOfDay*", "And*", "Or*", "Not*", "Eq*", "Ne*", "Gt*", "Ge*", "Lt*", "Le*", "AnnotationPath*", "Apply*", "Cast*", "Collection*", "If*", "IsOf*", "LabeledElement*", "LabeledElementReference*", "Null*", "NavigationPropertyPath*", "Path*", "PropertyPath*", "Record*", "UrlRef*"]
+ ),
+ ComplexType: schemaElement(
+ /*attributes*/["Name", "BaseType", "Abstract", "OpenType"],
+ /*elements*/["Property*", "NavigationProperty*", "Annotation*"]
+ ),
+ Date: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ DateTimeOffset: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Decimal: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Duration: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ EntityContainer: schemaElement(
+ /*attributes*/["Name", "Extends"],
+ /*elements*/["EntitySet*", "Singleton*", "ActionImport*", "FunctionImport*", "Annotation*"]
+ ),
+ EntitySet: schemaElement(
+ /*attributes*/["Name", "EntityType", "IncludeInServiceDocument"],
+ /*elements*/["NavigationPropertyBinding*", "Annotation*"]
+ ),
+ EntityType: schemaElement(
+ /*attributes*/["Name", "BaseType", "Abstract", "OpenType", "HasStream"],
+ /*elements*/["Key*", "Property*", "NavigationProperty*", "Annotation*"]
+ ),
+ EnumMember: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ EnumType: schemaElement(
+ /*attributes*/["Name", "UnderlyingType", "IsFlags"],
+ /*elements*/["Member*"]
+ ),
+ Float: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Function: schemaElement(
+ /*attributes*/["Name", "IsBound", "IsComposable", "EntitySetPath"],
+ /*elements*/["ReturnType", "Parameter*", "Annotation*"]
+ ),
+ FunctionImport: schemaElement(
+ /*attributes*/["Name", "Function", "EntitySet", "IncludeInServiceDocument", "Annotation*"]
+ ),
+ Guid: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ If: schemaElement(
+ /*attributes*/null,
+ /*elements*/["Path*", "String*", "Annotation*"]
+ ),
+ Int: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ IsOf: schemaElement(
+ /*attributes*/["Type", "MaxLength", "Precision", "Scale", "Unicode", "SRID", "DefaultValue", "Annotation*"],
+ /*elements*/["Path*"]
+ ),
+ Key: schemaElement(
+ /*attributes*/null,
+ /*elements*/["PropertyRef*"]
+ ),
+ LabeledElement: schemaElement(
+ /*attributes*/["Name"],
+ /*elements*/["Binary*", "Bool*", "Date*", "DateTimeOffset*", "Decimal*", "Duration*", "EnumMember*", "Float*", "Guid*", "Int*", "String*", "TimeOfDay*", "And*", "Or*", "Not*", "Eq*", "Ne*", "Gt*", "Ge*", "Lt*", "Le*", "AnnotationPath*", "Apply*", "Cast*", "Collection*", "If*", "IsOf*", "LabeledElement*", "LabeledElementReference*", "Null*", "NavigationPropertyPath*", "Path*", "PropertyPath*", "Record*", "UrlRef*", "Annotation*"]
+ ),
+ LabeledElementReference: schemaElement(
+ /*attributes*/["Term"],
+ /*elements*/["Binary*", "Bool*", "Date*", "DateTimeOffset*", "Decimal*", "Duration*", "EnumMember*", "Float*", "Guid*", "Int*", "String*", "TimeOfDay*", "And*", "Or*", "Not*", "Eq*", "Ne*", "Gt*", "Ge*", "Lt*", "Le*", "AnnotationPath*", "Apply*", "Cast*", "Collection*", "If*", "IsOf*", "LabeledElement*", "LabeledElementReference*", "Null*", "NavigationPropertyPath*", "Path*", "PropertyPath*", "Record*", "UrlRef*"]
+ ),
+ Member: schemaElement(
+ /*attributes*/["Name", "Value"],
+ /*element*/["Annotation*"]
+ ),
+ NavigationProperty: schemaElement(
+ /*attributes*/["Name", "Type", "Nullable", "Partner", "ContainsTarget"],
+ /*elements*/["ReferentialConstraint*", "OnDelete*", "Annotation*"]
+ ),
+ NavigationPropertyBinding: schemaElement(
+ /*attributes*/["Path", "Target"]
+ ),
+ NavigationPropertyPath: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Null: schemaElement(
+ /*attributes*/null,
+ /*elements*/["Annotation*"]
+ ),
+ OnDelete: schemaElement(
+ /*attributes*/["Action"],
+ /*elements*/["Annotation*"]
+ ),
+ Path: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Parameter: schemaElement(
+ /*attributes*/["Name", "Type", "Nullable", "MaxLength", "Precision", "Scale", "SRID"],
+ /*elements*/["Annotation*"]
+ ),
+ Property: schemaElement(
+ /*attributes*/["Name", "Type", "Nullable", "MaxLength", "Precision", "Scale", "Unicode", "SRID", "DefaultValue"],
+ /*elements*/["Annotation*"]
+ ),
+ PropertyPath: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ PropertyRef: schemaElement(
+ /*attributes*/["Name", "Alias"]
+ ),
+ PropertyValue: schemaElement(
+ /*attributes*/["Property", "Path"],
+ /*elements*/["Binary*", "Bool*", "Date*", "DateTimeOffset*", "Decimal*", "Duration*", "EnumMember*", "Float*", "Guid*", "Int*", "String*", "TimeOfDay*", "And*", "Or*", "Not*", "Eq*", "Ne*", "Gt*", "Ge*", "Lt*", "Le*", "AnnotationPath*", "Apply*", "Cast*", "Collection*", "If*", "IsOf*", "LabeledElement*", "LabeledElementReference*", "Null*", "NavigationPropertyPath*", "Path*", "PropertyPath*", "Record*", "UrlRef*", "Annotation*"]
+ ),
+ Record: schemaElement(
+ /*attributes*/null,
+ /*Elements*/["PropertyValue*", "Property*", "Annotation*"]
+ ),
+ ReferentialConstraint: schemaElement(
+ /*attributes*/["Property", "ReferencedProperty", "Annotation*"]
+ ),
+ ReturnType: schemaElement(
+ /*attributes*/["Type", "Nullable", "MaxLength", "Precision", "Scale", "SRID"]
+ ),
+ String: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ Schema: schemaElement(
+ /*attributes*/["Namespace", "Alias"],
+ /*elements*/["Action*", "Annotations*", "Annotation*", "ComplexType*", "EntityContainer", "EntityType*", "EnumType*", "Function*", "Term*", "TypeDefinition*", "Annotation*"]
+ ),
+ Singleton: schemaElement(
+ /*attributes*/["Name", "Type"],
+ /*elements*/["NavigationPropertyBinding*", "Annotation*"]
+ ),
+ Term: schemaElement(
+ /*attributes*/["Name", "Type", "BaseTerm", "DefaultValue ", "AppliesTo", "Nullable", "MaxLength", "Precision", "Scale", "SRID"],
+ /*elements*/["Annotation*"]
+ ),
+ TimeOfDay: schemaElement(
+ /*attributes*/null,
+ /*elements*/null,
+ /*text*/true
+ ),
+ TypeDefinition: schemaElement(
+ /*attributes*/["Name", "UnderlyingType", "MaxLength", "Unicode", "Precision", "Scale", "SRID"],
+ /*elements*/["Annotation*"]
+ ),
+ UrlRef: schemaElement(
+ /*attributes*/null,
+ /*elements*/["Binary*", "Bool*", "Date*", "DateTimeOffset*", "Decimal*", "Duration*", "EnumMember*", "Float*", "Guid*", "Int*", "String*", "TimeOfDay*", "And*", "Or*", "Not*", "Eq*", "Ne*", "Gt*", "Ge*", "Lt*", "Le*", "AnnotationPath*", "Apply*", "Cast*", "Collection*", "If*", "IsOf*", "LabeledElement*", "LabeledElementReference*", "Null*", "NavigationPropertyPath*", "Path*", "PropertyPath*", "Record*", "UrlRef*", "Annotation*"]
+ ),
+
+ // See http://msdn.microsoft.com/en-us/library/dd541238(v=prot.10) for an EDMX reference.
+ Edmx: schemaElement(
+ /*attributes*/["Version"],
+ /*elements*/["DataServices", "Reference*"],
+ /*text*/false,
+ /*ns*/edmxNs
+ ),
+ DataServices: schemaElement(
+ /*attributes*/["m:MaxDataServiceVersion", "m:DataServiceVersion"],
+ /*elements*/["Schema*"],
+ /*text*/false,
+ /*ns*/edmxNs
+ ),
+ Reference: schemaElement(
+ /*attributes*/["Uri"],
+ /*elements*/["Include*", "IncludeAnnotations*", "Annotation*"]
+ ),
+ Include: schemaElement(
+ /*attributes*/["Namespace", "Alias"]
+ ),
+ IncludeAnnotations: schemaElement(
+ /*attributes*/["TermNamespace", "Qualifier", "TargetNamespace"]
+ )
+ }
+};
+
+
+/** Converts a Pascal-case identifier into a camel-case identifier.
+ * @param {String} text - Text to convert.
+ * @returns {String} Converted text.
+ * If the text starts with multiple uppercase characters, it is left as-is.</remarks>
+ */
+function scriptCase(text) {
+
+ if (!text) {
+ return text;
+ }
+
+ if (text.length > 1) {
+ var firstTwo = text.substr(0, 2);
+ if (firstTwo === firstTwo.toUpperCase()) {
+ return text;
+ }
+
+ return text.charAt(0).toLowerCase() + text.substr(1);
+ }
+
+ return text.charAt(0).toLowerCase();
+}
+
+/** Gets the schema node for the specified element.
+ * @param {Object} parentSchema - Schema of the parent XML node of 'element'.
+ * @param candidateName - XML element name to consider.
+ * @returns {Object} The schema that describes the specified element; null if not found.
+ */
+function getChildSchema(parentSchema, candidateName) {
+
+ var elements = parentSchema.elements;
+ if (!elements) {
+ return null;
+ }
+
+ var i, len;
+ for (i = 0, len = elements.length; i < len; i++) {
+ var elementName = elements[i];
+ var multipleElements = false;
+ if (elementName.charAt(elementName.length - 1) === "*") {
+ multipleElements = true;
+ elementName = elementName.substr(0, elementName.length - 1);
+ }
+
+ if (candidateName === elementName) {
+ var propertyName = scriptCase(elementName);
+ return { isArray: multipleElements, propertyName: propertyName };
+ }
+ }
+
+ return null;
+}
+
+/** Checks whether the specifies namespace URI is one of the known CSDL namespace URIs.
+ * @param {String} nsURI - Namespace URI to check.
+ * @returns {Boolean} true if nsURI is a known CSDL namespace; false otherwise.
+ */
+function isEdmNamespace(nsURI) {
+
+ return nsURI === edmNs1;
+}
+
+/** Parses a CSDL document.
+ * @param element - DOM element to parse.
+ * @returns {Object} An object describing the parsed element.
+ */
+function parseConceptualModelElement(element) {
+
+ var localName = xmlLocalName(element);
+ var nsURI = xmlNamespaceURI(element);
+ var elementSchema = schema.elements[localName];
+ if (!elementSchema) {
+ return null;
+ }
+
+ if (elementSchema.ns) {
+ if (nsURI !== elementSchema.ns) {
+ return null;
+ }
+ } else if (!isEdmNamespace(nsURI)) {
+ return null;
+ }
+
+ var item = {};
+ var attributes = elementSchema.attributes || [];
+ xmlAttributes(element, function (attribute) {
+
+ var localName = xmlLocalName(attribute);
+ var nsURI = xmlNamespaceURI(attribute);
+ var value = attribute.value;
+
+ // Don't do anything with xmlns attributes.
+ if (nsURI === xmlnsNS) {
+ return;
+ }
+
+ // Currently, only m: for metadata is supported as a prefix in the internal schema table,
+ // un-prefixed element names imply one a CSDL element.
+ var schemaName = null;
+ var handled = false;
+ if (isEdmNamespace(nsURI) || nsURI === null) {
+ schemaName = "";
+ } else if (nsURI === odataMetaXmlNs) {
+ schemaName = "m:";
+ }
+
+ if (schemaName !== null) {
+ schemaName += localName;
+
+ if (contains(attributes, schemaName)) {
+ item[scriptCase(localName)] = value;
+ }
+ }
+
+ });
+
+ xmlChildElements(element, function (child) {
+ var localName = xmlLocalName(child);
+ var childSchema = getChildSchema(elementSchema, localName);
+ if (childSchema) {
+ if (childSchema.isArray) {
+ var arr = item[childSchema.propertyName];
+ if (!arr) {
+ arr = [];
+ item[childSchema.propertyName] = arr;
+ }
+ arr.push(parseConceptualModelElement(child));
+ } else {
+ item[childSchema.propertyName] = parseConceptualModelElement(child);
+ }
+ }
+ });
+
+ if (elementSchema.text) {
+ item.text = xmlInnerText(element);
+ }
+
+ return item;
+}
+
+/** Parses a metadata document.
+ * @param handler - This handler.
+ * @param {String} text - Metadata text.
+ * @returns An object representation of the conceptual model.</returns>
+ */
+function metadataParser(handler, text) {
+
+ var doc = xmlParse(text);
+ var root = xmlFirstChildElement(doc);
+ return parseConceptualModelElement(root) || undefined;
+}
+
+
+
+exports.metadataHandler = odataHandler.handler(metadataParser, null, xmlMediaType, MAX_DATA_SERVICE_VERSION);
+
+exports.schema = schema;
+exports.scriptCase = scriptCase;
+exports.getChildSchema = getChildSchema;
+exports.parseConceptualModelElement = parseConceptualModelElement;
+exports.metadataParser = metadataParser;
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/olingo-odata4-js/blob/d5ec5557/odatajs/src/lib/odata/net.js
----------------------------------------------------------------------
diff --git a/odatajs/src/lib/odata/net.js b/odatajs/src/lib/odata/net.js
new file mode 100644
index 0000000..9821892
--- /dev/null
+++ b/odatajs/src/lib/odata/net.js
@@ -0,0 +1,340 @@
+/*
+ * 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/net */
+
+
+
+var utils = require('./../datajs.js').utils;
+// Imports.
+
+var defined = utils.defined;
+var delay = utils.delay;
+
+var ticks = 0;
+
+/* Checks whether the specified request can be satisfied with a JSONP request.
+ * @param request - Request object to check.
+ * @returns {Boolean} true if the request can be satisfied; false otherwise.
+
+ * Requests that 'degrade' without changing their meaning by going through JSONP
+ * are considered usable.
+ *
+ * We allow data to come in a different format, as the servers SHOULD honor the Accept
+ * request but may in practice return content with a different MIME type.
+ */
+function canUseJSONP(request) {
+
+ if (request.method && request.method !== "GET") {
+ return false;
+ }
+
+ return true;
+}
+
+/** Creates an IFRAME tag for loading the JSONP script
+ * @param {String} url - The source URL of the script
+ * @returns {HTMLElement} The IFRAME tag
+ */
+function createIFrame(url) {
+ var iframe = window.document.createElement("IFRAME");
+ iframe.style.display = "none";
+
+ var attributeEncodedUrl = url.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
+ var html = "<html><head><script type=\"text/javascript\" src=\"" + attributeEncodedUrl + "\"><\/script><\/head><body><\/body><\/html>";
+
+ var body = window.document.getElementsByTagName("BODY")[0];
+ body.appendChild(iframe);
+
+ writeHtmlToIFrame(iframe, html);
+ return iframe;
+};
+
+/** Creates a XmlHttpRequest object.
+ * @returns {XmlHttpRequest} XmlHttpRequest object.
+ */
+function createXmlHttpRequest() {
+ if (window.XMLHttpRequest) {
+ return new window.XMLHttpRequest();
+ }
+ var exception;
+ if (window.ActiveXObject) {
+ try {
+ return new window.ActiveXObject("Msxml2.XMLHTTP.6.0");
+ } catch (_) {
+ try {
+ return new window.ActiveXObject("Msxml2.XMLHTTP.3.0");
+ } catch (e) {
+ exception = e;
+ }
+ }
+ } else {
+ exception = { message: "XMLHttpRequest not supported" };
+ }
+ throw exception;
+}
+
+/** Checks whether the specified URL is an absolute URL.
+ * @param {String} url - URL to check.
+ * @returns {Boolean} true if the url is an absolute URL; false otherwise.
+*/
+function isAbsoluteUrl(url) {
+ return url.indexOf("http://") === 0 ||
+ url.indexOf("https://") === 0 ||
+ url.indexOf("file://") === 0;
+}
+
+/** Checks whether the specified URL is local to the current context.
+ * @param {String} url - URL to check.
+ * @returns {Boolean} true if the url is a local URL; false otherwise.
+ */
+function isLocalUrl(url) {
+
+ if (!isAbsoluteUrl(url)) {
+ return true;
+ }
+
+ // URL-embedded username and password will not be recognized as same-origin URLs.
+ var location = window.location;
+ var locationDomain = location.protocol + "//" + location.host + "/";
+ return (url.indexOf(locationDomain) === 0);
+}
+
+/** Removes a callback used for a JSONP request.
+ * @param {String} name - Function name to remove.
+ * @param {Number} tick - Tick count used on the callback.
+ */
+function removeCallback(name, tick) {
+ try {
+ delete window[name];
+ } catch (err) {
+ window[name] = undefined;
+ if (tick === ticks - 1) {
+ ticks -= 1;
+ }
+ }
+};
+
+/** Removes an iframe.
+ * @param {Object} iframe - The iframe to remove.
+ * @returns {Object} Null value to be assigned to iframe reference.
+ */
+function removeIFrame(iframe) {
+ if (iframe) {
+ writeHtmlToIFrame(iframe, "");
+ iframe.parentNode.removeChild(iframe);
+ }
+
+ return null;
+};
+
+/** Reads response headers into array.
+ * @param {XMLHttpRequest} xhr - HTTP request with response available.
+ * @param {Array} headers - Target array to fill with name/value pairs.
+ */
+function readResponseHeaders(xhr, headers) {
+
+ var responseHeaders = xhr.getAllResponseHeaders().split(/\r?\n/);
+ var i, len;
+ for (i = 0, len = responseHeaders.length; i < len; i++) {
+ if (responseHeaders[i]) {
+ var header = responseHeaders[i].split(": ");
+ headers[header[0]] = header[1];
+ }
+ }
+}
+
+/** Writes HTML to an IFRAME document.
+ * @param {HTMLElement} iframe - The IFRAME element to write to.
+ * @param {String} html - The HTML to write.
+ */
+function writeHtmlToIFrame(iframe, html) {
+ var frameDocument = (iframe.contentWindow) ? iframe.contentWindow.document : iframe.contentDocument.document;
+ frameDocument.open();
+ frameDocument.write(html);
+ frameDocument.close();
+}
+
+exports.defaultHttpClient = {
+ callbackParameterName: "$callback",
+
+ formatQueryString: "$format=json",
+
+ enableJsonpCallback: false,
+
+ /** Performs a network request.
+ * @param {Object} request - Request description
+ * @param {Function} success - Success callback with the response object.
+ * @param {Function} error - Error callback with an error object.
+ * @returns {Object} Object with an 'abort' method for the operation.
+ */
+ request: function (request, success, error) {
+
+ var result = {};
+ var xhr = null;
+ var done = false;
+ var iframe;
+
+ result.abort = function () {
+ iframe = removeIFrame(iframe);
+ if (done) {
+ return;
+ }
+
+ done = true;
+ if (xhr) {
+ xhr.abort();
+ xhr = null;
+ }
+
+ error({ message: "Request aborted" });
+ };
+
+ var handleTimeout = function () {
+ iframe = removeIFrame(iframe);
+ if (!done) {
+ done = true;
+ xhr = null;
+ error({ message: "Request timed out" });
+ }
+ };
+
+ var name;
+ var url = request.requestUri;
+ var enableJsonpCallback = defined(request.enableJsonpCallback, this.enableJsonpCallback);
+ var callbackParameterName = defined(request.callbackParameterName, this.callbackParameterName);
+ var formatQueryString = defined(request.formatQueryString, this.formatQueryString);
+ if (!enableJsonpCallback || isLocalUrl(url)) {
+
+ xhr = createXmlHttpRequest();
+ xhr.onreadystatechange = function () {
+ if (done || xhr === null || xhr.readyState !== 4) {
+ return;
+ }
+
+ // Workaround for XHR behavior on IE.
+ var statusText = xhr.statusText;
+ var statusCode = xhr.status;
+ if (statusCode === 1223) {
+ statusCode = 204;
+ statusText = "No Content";
+ }
+
+ var headers = [];
+ readResponseHeaders(xhr, headers);
+
+ var response = { requestUri: url, statusCode: statusCode, statusText: statusText, headers: headers, body: xhr.responseText };
+
+ done = true;
+ xhr = null;
+ if (statusCode >= 200 && statusCode <= 299) {
+ success(response);
+ } else {
+ error({ message: "HTTP request failed", request: request, response: response });
+ }
+ };
+
+ xhr.open(request.method || "GET", url, true, request.user, request.password);
+
+ // Set the name/value pairs.
+ if (request.headers) {
+ for (name in request.headers) {
+ xhr.setRequestHeader(name, request.headers[name]);
+ }
+ }
+
+ // Set the timeout if available.
+ if (request.timeoutMS) {
+ xhr.timeout = request.timeoutMS;
+ xhr.ontimeout = handleTimeout;
+ }
+
+ xhr.send(request.body);
+ } else {
+ if (!canUseJSONP(request)) {
+ throw { message: "Request is not local and cannot be done through JSONP." };
+ }
+
+ var tick = ticks;
+ ticks += 1;
+ var tickText = tick.toString();
+ var succeeded = false;
+ var timeoutId;
+ name = "handleJSONP_" + tickText;
+ window[name] = function (data) {
+ iframe = removeIFrame(iframe);
+ if (!done) {
+ succeeded = true;
+ window.clearTimeout(timeoutId);
+ removeCallback(name, tick);
+
+ // Workaround for IE8 and IE10 below where trying to access data.constructor after the IFRAME has been removed
+ // throws an "unknown exception"
+ if (window.ActiveXObject) {
+ data = window.JSON.parse(window.JSON.stringify(data));
+ }
+
+
+ var headers;
+ if (!formatQueryString || formatQueryString == "$format=json") {
+ headers = { "Content-Type": "application/json;odata.metadata=minimal", "OData-Version": "4.0" };
+ } else {
+ // the formatQueryString should be in the format of "$format=xxx", xxx should be one of the application/json;odata.metadata=minimal(none or full)
+ // set the content-type with the string xxx which stars from index 8.
+ headers = { "Content-Type": formatQueryString.substring(8), "OData-Version": "4.0" };
+ }
+
+ // Call the success callback in the context of the parent window, instead of the IFRAME
+ delay(function () {
+ removeIFrame(iframe);
+ success({ body: data, statusCode: 200, headers: headers });
+ });
+ }
+ };
+
+ // Default to two minutes before timing out, 1000 ms * 60 * 2 = 120000.
+ var timeoutMS = (request.timeoutMS) ? request.timeoutMS : 120000;
+ timeoutId = window.setTimeout(handleTimeout, timeoutMS);
+
+ var queryStringParams = callbackParameterName + "=parent." + name;
+ if (formatQueryString) {
+ queryStringParams += "&" + formatQueryString;
+ }
+
+ var qIndex = url.indexOf("?");
+ if (qIndex === -1) {
+ url = url + "?" + queryStringParams;
+ } else if (qIndex === url.length - 1) {
+ url = url + queryStringParams;
+ } else {
+ url = url + "&" + queryStringParams;
+ }
+
+ iframe = createIFrame(url);
+ }
+
+ return result;
+ }
+};
+
+
+
+exports.canUseJSONP = canUseJSONP;
+exports.isAbsoluteUrl = isAbsoluteUrl;
+exports.isLocalUrl = isLocalUrl;
\ No newline at end of file