11/* eslint-disable no-underscore-dangle */
2-
32const R = require ( 'ramda' ) ;
3+
44const {
5- ArrayElement,
6- ObjectElement,
75 StringElement,
86 BooleanElement,
97 NumberElement,
108 NullElement,
119} = require ( 'minim' ) ;
1210
13- const EnumElement = require ( './elements/Enum' ) ;
11+ /**
12+ * Get element attribute
13+ * @param {element } e - element - element
14+ * @param {string } attribute
15+ * @return {element }
16+ */
17+ const getAttribute = ( e , attribute ) => e . _attributes && e . attributes . get ( attribute ) ;
1418
1519/**
1620 * Check if element has a typeAttribute
@@ -19,7 +23,7 @@ const EnumElement = require('./elements/Enum');
1923 * @return {boolean }
2024 */
2125function hasTypeAttribute ( e , attribute ) {
22- const typeAttributes = e . _attributes && e . attributes . get ( 'typeAttributes' ) ;
26+ const typeAttributes = getAttribute ( e , 'typeAttributes' ) ;
2327 if ( typeAttributes ) {
2428 return typeAttributes . includes ( attribute ) ;
2529 }
@@ -55,33 +59,85 @@ const isNullable = e => hasTypeAttribute(e, 'nullable');
5559 */
5660const isOptional = e => hasTypeAttribute ( e , 'optional' ) ;
5761
62+ const baseTypes = new Set ( [
63+ 'boolean' ,
64+ 'string' ,
65+ 'number' ,
66+ 'array' ,
67+ 'object' ,
68+ 'enum' ,
69+ 'null' ,
70+ 'member' ,
71+ 'select' ,
72+ 'option' ,
73+ 'extend' ,
74+ 'ref' ,
75+ 'link' ,
76+ ] ) ;
77+ /**
78+ * Check if element is one of the base types
79+ * @param {element } e - element
80+ * @return {boolean }
81+ */
82+ const isBaseType = e => baseTypes . has ( e && e . element ) ;
83+
84+ /**
85+ * Get the element type - prefer a base type, if found
86+ * @param {element } e - element
87+ * @param {object= } elements - object map of elements to look for inherited type
88+ * @return {string }
89+ */
90+ const getType = ( e , elements ) => {
91+ if ( e === undefined ) {
92+ return undefined ;
93+ }
94+ if ( isBaseType ( e ) ) {
95+ return e . element ;
96+ }
97+
98+ const inheritedType = e . element && elements && elements [ e . element ] ;
99+ if ( inheritedType !== undefined ) {
100+ return getType ( inheritedType , elements ) ;
101+ }
102+
103+ return e && e . element ;
104+ } ;
105+
106+ const primitives = new Set ( [ 'string' , 'number' , 'boolean' , 'null' ] ) ;
58107/**
59108 * Check if the element is of a primitive type
60109 * @param {element } e - element
110+ * @param {object= } elements - object map of elements to look for inherited type
61111 * @return {boolean }
62112 */
63- const isPrimitive = e => ( e instanceof StringElement ) || ( e instanceof NumberElement ) || ( e instanceof BooleanElement ) ;
113+ const isPrimitive = ( e , elements ) => {
114+ const type = getType ( e , elements ) ;
115+ return primitives . has ( String ( type ) ) ;
116+ } ;
64117
65118/**
66119 * Check if the element type is Enum
67120 * @param {element } e - element
121+ * @param {object= } elements - object map of elements to look for inherited type
68122 * @return {boolean }
69123 */
70- const isEnum = e => e instanceof EnumElement ;
124+ const isEnum = ( e , elements ) => getType ( e , elements ) === 'enum' ;
71125
72126/**
73127 * Check if the element type is Array
74128 * @param {element } e - element
129+ * @param {object= } elements - object map of elements to look for inherited type
75130 * @return {boolean }
76131 */
77- const isArray = e => e instanceof ArrayElement ;
132+ const isArray = ( e , elements ) => getType ( e , elements ) === 'array' ;
78133
79134/**
80135 * Check if the element type is Object
81136 * @param {element } e - element
137+ * @param {object= } elements - object map of elements to look for inherited type
82138 * @return {boolean }
83139 */
84- const isObject = e => e instanceof ObjectElement ;
140+ const isObject = ( e , elements ) => getType ( e , elements ) === 'object' ;
85141
86142/**
87143 * Get the element default
@@ -149,23 +205,27 @@ const hasNoValue = R.complement(hasValue);
149205/**
150206 * Check if the element is of a primitive type and has no value (content/sample/default)
151207 * @param {element } e - element
208+ * @param {object= } elements - object map of elements to look for inherited type
152209 * @return {boolean }
153210 */
154- const isNoValuePrimitive = R . both ( isPrimitive , hasNoValue ) ;
211+ const isNoValuePrimitive = ( e , elements ) => isPrimitive ( e , elements ) && hasNoValue ( e ) ;
155212
156213/**
157214 * Check if the element type is array and is not empty
158215 * @param {element } e - element
216+ * @param {object= } elements - object map of elements to look for inherited type
159217 * @return {boolean }
160218 */
161- const isNonEmptyArray = e => isArray ( e ) && e . content && ! e . isEmpty ;
219+ const isNonEmptyArray = ( e , elements ) => isArray ( e , elements ) && e . content !== undefined && ! e . isEmpty ;
162220
163221/**
164222 * Check if the element type is array and has only primitive elements with no value
165223 * @param {element } e - element
224+ * @param {object= } elements - object map of elements to look for inherited type
166225 * @return {boolean }
167226 */
168- const isEmptyArray = e => isArray ( e ) && e . content . every ( isNoValuePrimitive ) ;
227+ const isEmptyArray = ( e , elements ) => isArray ( e , elements )
228+ && ( e . content === undefined || e . content . every ( member => isNoValuePrimitive ( member , elements ) ) ) ;
169229
170230/**
171231 * Check if the element type is 'ref'
@@ -177,10 +237,12 @@ const isRef = e => e && e.element === 'ref';
177237/**
178238 * Check if the element type is object and has all property values undefined
179239 * @param {element } e - element
240+ * @param {object= } elements - object map of elements to look for inherited type
180241 * @return {boolean }
181242 */
182- const isObjectWithUndefinedValues = e => isObject ( e )
183- && e . content . every ( prop => prop . value === undefined || prop . value . content === undefined ) ;
243+ const isObjectWithUndefinedValues = ( e , elements ) => isObject ( e , elements ) && e . content . every (
244+ prop => prop . value === undefined || prop . value . content === undefined
245+ ) ;
184246
185247/**
186248 * Get a trivial value, to fill the unset, according to the element type
@@ -207,6 +269,93 @@ function trivialValue(e) {
207269 return undefined ;
208270}
209271
272+ /**
273+ * Get a key for the element member
274+ * This is used to identify each member on the Map, allowing overrides in Objects
275+ * @param {element } e - element
276+ * @param {object= } elements - object map of elements to look for inherited type
277+ * @return {(string|number|object|null) } - Map key
278+ */
279+ function getMemberKey ( e , elements ) {
280+ if ( isPrimitive ( e , elements ) ) {
281+ // return unique identifier
282+ return Math . random ( ) ;
283+ }
284+
285+ const key = e && e . content && e . content . key && e . content . key . toValue ( ) ;
286+ const content = e && e . content ;
287+ const type = e . element ;
288+
289+ return key || content || type ;
290+ }
291+
292+ /**
293+ * Get an Array with the element members
294+ * @param {element } e - element
295+ * @param {object= } elements - object map of elements to look for inherited type
296+ * @return {element[] } - element members
297+ */
298+ function getMembers ( e , elements ) {
299+ if ( e === undefined ) {
300+ return [ ] ;
301+ }
302+
303+ if ( isEnum ( e , elements ) ) {
304+ const enumerations = getAttribute ( e , 'enumerations' ) ;
305+ if ( enumerations && enumerations . content !== undefined ) {
306+ return enumerations . content ;
307+ }
308+ }
309+
310+ if ( Array . isArray ( e . content ) ) {
311+ return e . content ;
312+ }
313+
314+ return [ e ] ;
315+ }
316+
317+ /**
318+ * Get a Map with all the element members, including references
319+ * @param {element } e - element
320+ * @param {object= } elements - object map of elements to look for inherited type
321+ * @return {Map<element> } - element members
322+ */
323+ function getAllMembersMap ( e , elements ) {
324+ if ( e === undefined ) {
325+ return new Map ( ) ;
326+ }
327+
328+ const typeElement = elements && elements [ e . element ] ;
329+ const typeMembersMap = getAllMembersMap ( typeElement , elements ) ;
330+ const ownMembers = getMembers ( e , elements ) ;
331+ const ownMembersMap = new Map ( ) ;
332+
333+ ownMembers . forEach ( ( member ) => {
334+ if ( isRef ( member ) ) {
335+ const refElement = elements && elements [ member . content ] ;
336+ const refMembersMap = getAllMembersMap ( refElement , elements ) ;
337+ refMembersMap . forEach ( ( refMember ) => {
338+ ownMembersMap . set ( getMemberKey ( refMember , elements ) , refMember ) ;
339+ } ) ;
340+ } else {
341+ ownMembersMap . set ( getMemberKey ( member , elements ) , member ) ;
342+ }
343+ } ) ;
344+ return new Map ( [ ...typeMembersMap , ...ownMembersMap ] ) ;
345+ }
346+
347+ /**
348+ * Get an Array with all the element members, including references
349+ * @param {element } e - element
350+ * @param {object= } elements - object map of elements to look for inherited type
351+ * @return {element[] } - element members
352+ */
353+ function getStructureMembers ( e , elements ) {
354+ const membersMap = getAllMembersMap ( e , elements ) ;
355+
356+ return Array . from ( membersMap . values ( ) ) ;
357+ }
358+
210359module . exports = {
211360 isFixed,
212361 isRequired,
@@ -223,4 +372,5 @@ module.exports = {
223372 isRef,
224373 isObjectWithUndefinedValues,
225374 trivialValue,
375+ getStructureMembers,
226376} ;
0 commit comments