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 {boolean }
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,71 @@ const isNullable = e => hasTypeAttribute(e, 'nullable');
5559 */
5660const isOptional = e => hasTypeAttribute ( e , 'optional' ) ;
5761
62+ const baseTypes = new Set ( [ 'boolean' , 'string' , 'number' , 'array' , 'object' , 'enum' ] ) ;
63+ /**
64+ * Check if element is one of the base types
65+ * @param {element } e - element
66+ * @return {boolean }
67+ */
68+ const isBaseType = e => baseTypes . has ( e && e . element ) ;
69+
70+ /**
71+ * Get the element type - prefer a base type, if found
72+ * @param {element } e - element
73+ * @param {object= } elements - object map of elements to look for inherited type
74+ * @return {string }
75+ */
76+ const getType = ( e , elements ) => {
77+ if ( e === undefined ) {
78+ return undefined ;
79+ }
80+ if ( isBaseType ( e ) ) {
81+ return e . element ;
82+ }
83+
84+ const inheritedType = e . element && elements && elements [ e . element ] ;
85+ if ( inheritedType !== undefined ) {
86+ return getType ( inheritedType , elements ) ;
87+ }
88+
89+ return e && e . element ;
90+ } ;
91+
92+ const primitives = new Set ( [ 'string' , 'number' , 'boolean' , 'null' ] ) ;
5893/**
5994 * Check if the element is of a primitive type
6095 * @param {element } e - element
96+ * @param {object= } elements - object map of elements to look for inherited type
6197 * @return {boolean }
6298 */
63- const isPrimitive = e => ( e instanceof StringElement ) || ( e instanceof NumberElement ) || ( e instanceof BooleanElement ) ;
99+ const isPrimitive = ( e , elements ) => {
100+ const type = getType ( e , elements ) ;
101+ return primitives . has ( String ( type ) ) ;
102+ } ;
64103
65104/**
66105 * Check if the element type is Enum
67106 * @param {element } e - element
107+ * @param {object= } elements - object map of elements to look for inherited type
68108 * @return {boolean }
69109 */
70- const isEnum = e => e instanceof EnumElement ;
110+ const isEnum = ( e , elements ) => getType ( e , elements ) === 'enum' ;
71111
72112/**
73113 * Check if the element type is Array
74114 * @param {element } e - element
115+ * @param {object= } elements - object map of elements to look for inherited type
75116 * @return {boolean }
76117 */
77- const isArray = e => e instanceof ArrayElement ;
118+ const isArray = ( e , elements ) => getType ( e , elements ) === 'array' ;
78119
79120/**
80121 * Check if the element type is Object
81122 * @param {element } e - element
123+ * @param {object= } elements - object map of elements to look for inherited type
82124 * @return {boolean }
83125 */
84- const isObject = e => e instanceof ObjectElement ;
126+ const isObject = ( e , elements ) => getType ( e , elements ) === 'object' ;
85127
86128/**
87129 * Get the element default
@@ -149,23 +191,27 @@ const hasNoValue = R.complement(hasValue);
149191/**
150192 * Check if the element is of a primitive type and has no value (content/sample/default)
151193 * @param {element } e - element
194+ * @param {object= } elements - object map of elements to look for inherited type
152195 * @return {boolean }
153196 */
154- const isNoValuePrimitive = R . both ( isPrimitive , hasNoValue ) ;
197+ const isNoValuePrimitive = ( e , elements ) => isPrimitive ( e , elements ) && hasNoValue ( e ) ;
155198
156199/**
157200 * Check if the element type is array and is not empty
158201 * @param {element } e - element
202+ * @param {object= } elements - object map of elements to look for inherited type
159203 * @return {boolean }
160204 */
161- const isNonEmptyArray = e => isArray ( e ) && e . content && ! e . isEmpty ;
205+ const isNonEmptyArray = ( e , elements ) => isArray ( e , elements ) && e . content !== undefined && ! e . isEmpty ;
162206
163207/**
164208 * Check if the element type is array and has only primitive elements with no value
165209 * @param {element } e - element
210+ * @param {object= } elements - object map of elements to look for inherited type
166211 * @return {boolean }
167212 */
168- const isEmptyArray = e => isArray ( e ) && e . content . every ( isNoValuePrimitive ) ;
213+ const isEmptyArray = ( e , elements ) => isArray ( e , elements )
214+ && ( e . content === undefined || e . content . every ( member => isNoValuePrimitive ( member , elements ) ) ) ;
169215
170216/**
171217 * Check if the element type is 'ref'
@@ -177,10 +223,12 @@ const isRef = e => e && e.element === 'ref';
177223/**
178224 * Check if the element type is object and has all property values undefined
179225 * @param {element } e - element
226+ * @param {object= } elements - object map of elements to look for inherited type
180227 * @return {boolean }
181228 */
182- const isObjectWithUndefinedValues = e => isObject ( e )
183- && e . content . every ( prop => prop . value === undefined || prop . value . content === undefined ) ;
229+ const isObjectWithUndefinedValues = ( e , elements ) => isObject ( e , elements ) && e . content . every (
230+ prop => prop . value === undefined || prop . value . content === undefined
231+ ) ;
184232
185233/**
186234 * Get a trivial value, to fill the unset, according to the element type
@@ -207,6 +255,93 @@ function trivialValue(e) {
207255 return undefined ;
208256}
209257
258+ /**
259+ * Get a key for the element member
260+ * This is used to identify each member on the Map, allowing overrides in Objects
261+ * @param {element } e - element
262+ * @param {object= } elements - object map of elements to look for inherited type
263+ * @return {(string|number|object|null) } - Map key
264+ */
265+ function getMemberKey ( e , elements ) {
266+ if ( isPrimitive ( e , elements ) ) {
267+ // return unique identifier
268+ return Math . random ( ) ;
269+ }
270+
271+ const key = e && e . content && e . content . key && e . content . key . toValue ( ) ;
272+ const content = e && e . content ;
273+ const type = e . element ;
274+
275+ return key || content || type ;
276+ }
277+
278+ /**
279+ * Get an Array with the element members
280+ * @param {element } e - element
281+ * @param {object= } elements - object map of elements to look for inherited type
282+ * @return {element[] } - element members
283+ */
284+ function getMembers ( e , elements ) {
285+ if ( e === undefined ) {
286+ return [ ] ;
287+ }
288+
289+ if ( isEnum ( e , elements ) ) {
290+ const enumerations = getAttribute ( e , 'enumerations' ) ;
291+ if ( enumerations && enumerations . content !== undefined ) {
292+ return enumerations . content ;
293+ }
294+ }
295+
296+ if ( Array . isArray ( e . content ) ) {
297+ return e . content ;
298+ }
299+
300+ return [ e ] ;
301+ }
302+
303+ /**
304+ * Get a Map with all the element members, including references
305+ * @param {element } e - element
306+ * @param {object= } elements - object map of elements to look for inherited type
307+ * @return {Map<element> } - element members
308+ */
309+ function getAllMembersMap ( e , elements ) {
310+ if ( e === undefined ) {
311+ return new Map ( ) ;
312+ }
313+
314+ const typeElement = elements && elements [ e . element ] ;
315+ const typeMembersMap = getAllMembersMap ( typeElement , elements ) ;
316+ const ownMembers = getMembers ( e , elements ) ;
317+ const ownMembersMap = new Map ( ) ;
318+
319+ ownMembers . forEach ( ( member ) => {
320+ if ( isRef ( member ) ) {
321+ const refElement = elements && elements [ member . content ] ;
322+ const refMembersMap = getAllMembersMap ( refElement , elements ) ;
323+ refMembersMap . forEach ( ( refMember ) => {
324+ ownMembersMap . set ( getMemberKey ( refMember , elements ) , refMember ) ;
325+ } ) ;
326+ } else {
327+ ownMembersMap . set ( getMemberKey ( member , elements ) , member ) ;
328+ }
329+ } ) ;
330+ return new Map ( [ ...typeMembersMap , ...ownMembersMap ] ) ;
331+ }
332+
333+ /**
334+ * Get an Array with all the element members, including references
335+ * @param {element } e - element
336+ * @param {object= } elements - object map of elements to look for inherited type
337+ * @return {element[] } - element members
338+ */
339+ function getStructureMembers ( e , elements ) {
340+ const membersMap = getAllMembersMap ( e , elements ) ;
341+
342+ return Array . from ( membersMap . values ( ) ) ;
343+ }
344+
210345module . exports = {
211346 isFixed,
212347 isRequired,
@@ -223,4 +358,5 @@ module.exports = {
223358 isRef,
224359 isObjectWithUndefinedValues,
225360 trivialValue,
361+ getStructureMembers,
226362} ;
0 commit comments