react / react-0.13.3 / examples / basic-commonjs / node_modules / reactify / node_modules / react-tools / src / vendor / core / mergeDeepInto.js
81158 views/**1* Copyright 2013-2014, Facebook, Inc.2* All rights reserved.3*4* This source code is licensed under the BSD-style license found in the5* LICENSE file in the root directory of this source tree. An additional grant6* of patent rights can be found in the PATENTS file in the same directory.7*8* @providesModule mergeDeepInto9*/1011// Empty blocks improve readability so disable that warning12// jshint -W0351314"use strict";1516var invariant = require('invariant');17var mergeHelpers = require('mergeHelpers');1819var ArrayStrategies = mergeHelpers.ArrayStrategies;20var checkArrayStrategy = mergeHelpers.checkArrayStrategy;21var checkMergeArrayArgs = mergeHelpers.checkMergeArrayArgs;22var checkMergeLevel = mergeHelpers.checkMergeLevel;23var checkMergeObjectArgs = mergeHelpers.checkMergeObjectArgs;24var isTerminal = mergeHelpers.isTerminal;25var normalizeMergeArg = mergeHelpers.normalizeMergeArg;2627/**28* Every deep merge function must handle merging in each of the following cases29* at every level. We may refer to letters below in implementations. For each30* case listed, the "Result" listed describes the *value* of the result, but31* does not specify anything about the memory graph of the result. In other32* words the Results listed below will be the same for `merge` and `mergeInto`33* but `merge` may have different guarantees about mutation/cloning. In the34* table, "Object" refers to a non- Array, non-terminal object. One result is35* undefined and requires that the caller specify the resolution policy (only36* when trying to merge two arrays).37*38* Scenario Result39* --------------------------------------------------------------40* [A]: (terminal, terminal) right terminal41* [B]: (terminal, Array) right Array42* [C]: (terminal, Object) right Object43* [D]: (terminal, not present) left terminal44* [E]: (Array, terminal) right terminal45* [F]: (Array, Array) UNDEFINED - MUST SPECIFY POLICY46* [G]: (Array, Object) right Object47* [H]: (Array, not present) left Array48* [I]: (Object, terminal) right terminal49* [J]: (Object, Array) right Array50* [K]: (Object, Object) merge of left and right Objects51* [L]: (Object, not present) left Object52* [M]: (not present, terminal) right terminal53* [N]: (not present, Array) right Array54* [O]: (not present, Object) right Object55*56* All merge functions are only expected to reason about "own" properties and,57* any prototypical properties should have no effect on the result. At the first58* level of recursion in deep merges, we may choose to normalize arguments59* (convert undefined to empty objects etc.) The above chart does not describe60* the result of those top level operations which behave specially due to the61* normalization.62*/6364/**65* Deep merge implementation for non-Array, non-terminal Objects.66*67* @param {!Object} one non-Array, non-terminal Object to be mutated deeply.68* @param {!Object} two non-Array, non-terminal taking precedence over `one`.69* @param {?Enum=} arrayStrategy one of `arrayStrategies`.70* @param {!number} level The level of recursion.71*/72var mergeDeepIntoObjects = function(one, two, arrayStrategy, level) {73checkMergeObjectArgs(one, two);74checkMergeLevel(level);75var twoKeys = two ? Object.keys(two) : [];76for (var i = 0; i < twoKeys.length; i++) {77var twoKey = twoKeys[i];78mergeSingleFieldDeep(one, two, twoKey, arrayStrategy, level);79}80};8182/**83* Deep merge implementation for Arrays.84*85* @param {!Array} one Array to deep merge.86* @param {!Array} two Array to deep merge "into" `one`.87* @param {?Enum=} arrayStrategy one of `arrayStrategies`.88* @param {!number} level Level of recursion.89*/90var mergeDeepIntoArrays = function(one, two, arrayStrategy, level) {91checkMergeArrayArgs(one, two);92checkMergeLevel(level);9394var maxLen = Math.max(one.length, two.length);95for (var i = 0; i < maxLen; i++) {96mergeSingleFieldDeep(one, two, i, arrayStrategy, level);97}98};99100/**101* Given two truthy containers `one` and `two`, and a `key`, performs a deep102* merge on a logical field.103*104* @param {!Array|Object} one Container to merge into.105* @param {!Array|Object} two Container to merge from.106* @param {!string} key Key of field that should be merged.107* @param {lEnum=} arrayStrategy One of `arrayStrategies`.108* @param {!number} level Current level of recursion.109*/110var mergeSingleFieldDeep = function(one, two, key, arrayStrategy, level) {111var twoVal = two[key];112var twoValIsPresent = two.hasOwnProperty(key);113var twoValIsTerminal = twoValIsPresent && isTerminal(twoVal);114var twoValIsArray = twoValIsPresent && Array.isArray(twoVal);115var twoValIsProperObject =116twoValIsPresent && !twoValIsArray && !twoValIsArray;117118var oneVal = one[key];119var oneValIsPresent = one.hasOwnProperty(key);120var oneValIsTerminal = oneValIsPresent && isTerminal(oneVal);121var oneValIsArray = oneValIsPresent && Array.isArray(oneVal);122var oneValIsProperObject =123oneValIsPresent && !oneValIsArray && !oneValIsArray;124125if (oneValIsTerminal) {126if (twoValIsTerminal) { // [A]127one[key] = twoVal;128} else if (twoValIsArray) { // [B]129one[key] = [];130mergeDeepIntoArrays(one[key], twoVal, arrayStrategy, level + 1);131} else if (twoValIsProperObject) { // [C]132one[key] = {};133mergeDeepIntoObjects(one[key], twoVal, arrayStrategy, level + 1);134} else if (!twoValIsPresent) { // [D]135one[key] = oneVal;136}137} else if (oneValIsArray) {138if (twoValIsTerminal) { // [E]139one[key] = twoVal;140} else if (twoValIsArray) { // [F]141invariant(142ArrayStrategies[arrayStrategy],143'mergeDeepInto(...): Attempted to merge two arrays, but a valid ' +144'ArrayStrategy was not specified.'145);146// Else: At this point, the only other valid option is `IndexByIndex`147if (arrayStrategy === ArrayStrategies.Clobber) {148oneVal.length = 0;149}150mergeDeepIntoArrays(oneVal, twoVal, arrayStrategy, level + 1);151} else if (twoValIsProperObject) { // [G]152one[key] = {};153mergeDeepIntoObjects(one[key], twoVal, arrayStrategy, level + 1);154} else if (!twoValIsPresent) { // [H]155// Leave the left Array alone156}157} else if (oneValIsProperObject) {158if (twoValIsTerminal) { // [I]159one[key] = twoVal;160} else if (twoValIsArray) { // [J]161one[key] = [];162mergeDeepIntoArrays(one[key], twoVal, arrayStrategy, level + 1);163} else if (twoValIsProperObject) { // [K]164mergeDeepIntoObjects(oneVal, twoVal, arrayStrategy, level + 1);165} else if (!twoValIsPresent) { // [L]166// Leave the left Object alone167}168} else if (!oneValIsPresent) {169if (twoValIsTerminal) { // [M]170one[key] = twoVal;171} else if (twoValIsArray) { // [N]172one[key] = [];173mergeDeepIntoArrays(one[key], twoVal, arrayStrategy, level + 1);174} else if (twoValIsProperObject) { // [O]175one[key] = {};176mergeDeepIntoObjects(one[key], twoVal, arrayStrategy, level + 1);177} else if (!twoValIsPresent) {178// Could/should never happen179}180}181};182183184185/**186* Provides same functionality as mergeInto, but merges by mutating the first187* argument. Will never mutate the second argument.188*189* mergeDeepInto provides two guarantees:190* 1. In the process of mutating one, will not mutate two.191* 2. Will not cause any nonTerminal memory to be shared between one and two.192* This means that no further mutations to one will effect two (unless some193* other part of your application forms shared memory between one and two).194*195* mergeDeepInto does not guarantee that it will *preserve* any shared memory196* between two and one that existed before invocation. After calling197* mergeDeepInto, there will be less (or equal) amount of shared memory between198* one and two.199*200* Will tolerate a circular structure as the first parameter, but not the201* second.202*203* Requires that first parameter be a non-Array, non-terminal `Object`, and the204* second argument either be an `Object` or `null/undefined`. Arrays may exist205* in the depths of either `Object` as long as an `arrayStrategy` is supplied.206*207* The third parameter indicates how merging two `Array`s should be performed.208*209* @param {!Object} one Object to be mutated deeply.210* @param {?Object} two Values from two take precedence over values in one.211* @param {?Enum=} arrayStrategy One of arrayStrategy212*/213var mergeDeepInto = function(one, twoParam, arrayStrategy) {214var two = normalizeMergeArg(twoParam);215checkArrayStrategy(arrayStrategy); // Will be checked twice, for now.216mergeDeepIntoObjects(one, two, arrayStrategy, 0);217};218219module.exports = mergeDeepInto;220221222