@@ -8,6 +8,13 @@ import {JsonPrimitiveNode, JsonStringNode} from './primitiveNode';
88import { JsonValueFactory } from './factory' ;
99import { JsonIdentifierNode } from './identifierNode' ;
1010import { JsonError } from '../error' ;
11+ import { NodeMatcher } from '../manipulator' ;
12+ import { JsonTokenNode } from './tokenNode' ;
13+ import { JsonTokenType } from '../token' ;
14+ import INSIGNIFICANT = NodeMatcher . INSIGNIFICANT ;
15+ import NEWLINE = NodeMatcher . NEWLINE ;
16+ import SIGNIFICANT = NodeMatcher . SIGNIFICANT ;
17+ import SPACE = NodeMatcher . SPACE ;
1118
1219export interface JsonObjectDefinition extends JsonCompositeDefinition {
1320 readonly properties : readonly JsonPropertyNode [ ] ;
@@ -33,6 +40,159 @@ export class JsonObjectNode extends JsonStructureNode implements JsonCompositeDe
3340 } ) ;
3441 }
3542
43+ public merge ( source : JsonObjectNode ) : void {
44+ if ( source . propertyNodes . length === 0 ) {
45+ return ;
46+ }
47+
48+ if ( this . propertyNodes . length === 0 ) {
49+ this . propertyNodes . push ( ...source . propertyNodes . map ( property => property . clone ( ) ) ) ;
50+ this . children . splice ( 0 , this . children . length , ...source . children . map ( child => child . clone ( ) ) ) ;
51+
52+ return ;
53+ }
54+
55+ for ( const property of source . propertyNodes ) {
56+ const key = property . key . toJSON ( ) ;
57+ const sourceRange = source . findPropertyRange ( key ) ;
58+
59+ let sourceChildren : JsonNode [ ] = [ property ] ;
60+
61+ if ( sourceRange !== null ) {
62+ sourceChildren = source . children . slice ( sourceRange [ 0 ] , sourceRange [ 1 ] + 1 ) ;
63+ }
64+
65+ const newProperty = property . clone ( ) ;
66+
67+ sourceChildren = sourceChildren . map ( node => ( node === property ? newProperty : node . clone ( ) ) ) ;
68+
69+ const range = this . findPropertyRange ( key ) ;
70+
71+ if ( range === null ) {
72+ this . propertyNodes . push ( newProperty ) ;
73+ this . insert ( sourceChildren ) ;
74+
75+ continue ;
76+ }
77+
78+ const currentIndex = this . propertyNodes . findIndex ( candidate => candidate . key . toJSON ( ) === key ) ;
79+
80+ this . propertyNodes . splice ( currentIndex , 1 , newProperty ) ;
81+ this . children . splice ( range [ 0 ] , range [ 1 ] - range [ 0 ] + 1 , ...sourceChildren ) ;
82+ }
83+ }
84+
85+ private insert ( nodes : JsonNode [ ] ) : void {
86+ let insertionIndex = this . children . length ;
87+
88+ for ( let index = this . children . length - 1 ; index >= 0 ; index -- ) {
89+ const child = this . children [ index ] ;
90+
91+ if ( child instanceof JsonTokenNode ) {
92+ if ( child . isType ( JsonTokenType . BRACE_RIGHT ) ) {
93+ insertionIndex = index ;
94+
95+ continue ;
96+ }
97+
98+ if ( child . isType ( JsonTokenType . COMMA ) ) {
99+ insertionIndex = index + 1 ;
100+
101+ break ;
102+ }
103+ }
104+
105+ if ( SIGNIFICANT ( child ) ) {
106+ break ;
107+ }
108+
109+ if ( NEWLINE ( child ) ) {
110+ while ( index > 0 && SPACE ( this . children [ index - 1 ] ) ) {
111+ index -- ;
112+ }
113+
114+ insertionIndex = index ;
115+
116+ break ;
117+ }
118+
119+ insertionIndex = index ;
120+ }
121+
122+ let needsComma = false ;
123+
124+ for ( let index = insertionIndex - 1 ; index >= 0 ; index -- ) {
125+ const child = this . children [ index ] ;
126+
127+ if ( child instanceof JsonTokenNode ) {
128+ if ( child . isType ( JsonTokenType . COMMA ) ) {
129+ needsComma = false ;
130+
131+ break ;
132+ }
133+ }
134+
135+ if ( SIGNIFICANT ( child ) ) {
136+ needsComma = true ;
137+
138+ break ;
139+ }
140+ }
141+
142+ if ( needsComma ) {
143+ this . children . splice ( insertionIndex , 0 , new JsonTokenNode ( {
144+ type : JsonTokenType . COMMA ,
145+ value : ',' ,
146+ } ) ) ;
147+
148+ insertionIndex ++ ;
149+ }
150+
151+ this . children . splice ( insertionIndex , 0 , ...nodes ) ;
152+ }
153+
154+ private findPropertyRange ( name : string ) : [ number , number ] | null {
155+ let startIndex = this . children . findIndex (
156+ node => node instanceof JsonPropertyNode && node . key . toJSON ( ) === name ,
157+ ) ;
158+
159+ if ( startIndex === - 1 ) {
160+ return null ;
161+ }
162+
163+ let endIndex = startIndex ;
164+
165+ for ( let lookBehind = startIndex - 1 ; lookBehind >= 0 ; lookBehind -- ) {
166+ const child = this . children [ lookBehind ] ;
167+
168+ if ( ! INSIGNIFICANT ( child ) ) {
169+ break ;
170+ }
171+
172+ if ( NEWLINE ( child ) ) {
173+ startIndex = lookBehind ;
174+ }
175+ }
176+
177+ for ( let lookAhead = endIndex + 1 ; lookAhead < this . children . length ; lookAhead ++ ) {
178+ const child = this . children [ lookAhead ] ;
179+
180+ if ( ! ( child instanceof JsonTokenNode ) || ( SIGNIFICANT ( child ) && ! child . isType ( JsonTokenType . COMMA ) ) ) {
181+ break ;
182+ }
183+
184+ if ( NEWLINE ( child ) ) {
185+ endIndex = lookAhead - 1 ;
186+
187+ break ;
188+ }
189+
190+ endIndex = lookAhead ;
191+ }
192+
193+ return [ startIndex , endIndex ] ;
194+ }
195+
36196 public update ( other : JsonValueNode | JsonValue ) : JsonValueNode {
37197 if ( ! ( other instanceof JsonValueNode ) ) {
38198 if ( typeof other !== 'object' || other === null || Array . isArray ( other ) ) {
0 commit comments