Use instanceof patterns in DefinitionGenerator
[netconf.git] / restconf / sal-rest-docgen / src / main / java / org / opendaylight / netconf / sal / rest / doc / impl / DefinitionGenerator.java
1 /*
2  * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netconf.sal.rest.doc.impl;
9
10 import static org.opendaylight.netconf.sal.rest.doc.impl.BaseYangSwaggerGenerator.MODULE_NAME_SUFFIX;
11 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.CONFIG;
12 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.NAME_KEY;
13 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.POST_SUFFIX;
14 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.TOP;
15 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.XML_KEY;
16 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.XML_SUFFIX;
17 import static org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.getAppropriateModelPrefix;
18
19 import com.fasterxml.jackson.databind.JsonNode;
20 import com.fasterxml.jackson.databind.node.ArrayNode;
21 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
22 import com.fasterxml.jackson.databind.node.ObjectNode;
23 import com.fasterxml.jackson.databind.node.TextNode;
24 import com.google.common.collect.Range;
25 import com.google.common.collect.RangeSet;
26 import com.mifmif.common.regex.Generex;
27 import java.io.IOException;
28 import java.math.BigDecimal;
29 import java.util.Collection;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Optional;
34 import java.util.Set;
35 import java.util.regex.Pattern;
36 import java.util.stream.Collectors;
37 import org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.OAversion;
38 import org.opendaylight.yangtools.yang.common.Decimal64;
39 import org.opendaylight.yangtools.yang.common.QName;
40 import org.opendaylight.yangtools.yang.model.api.ActionDefinition;
41 import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer;
42 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.ContainerLike;
47 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
48 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
49 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
50 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
51 import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
52 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
53 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
54 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
55 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
56 import org.opendaylight.yangtools.yang.model.api.MandatoryAware;
57 import org.opendaylight.yangtools.yang.model.api.Module;
58 import org.opendaylight.yangtools.yang.model.api.OperationDefinition;
59 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
60 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
61 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
62 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
63 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
64 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
65 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
66 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
67 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
68 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
69 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
70 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
71 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
72 import org.opendaylight.yangtools.yang.model.api.type.Int16TypeDefinition;
73 import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
74 import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
75 import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
76 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
77 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
78 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
79 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
80 import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
81 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
82 import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
83 import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
84 import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
85 import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
86 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
87 import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack;
88 import org.slf4j.Logger;
89 import org.slf4j.LoggerFactory;
90
91 /**
92  * Generates JSON Schema for data defined in YANG. This class is not thread-safe.
93  */
94 public class DefinitionGenerator {
95
96     private static final Logger LOG = LoggerFactory.getLogger(DefinitionGenerator.class);
97
98     private static final String UNIQUE_ITEMS_KEY = "uniqueItems";
99     private static final String MAX_ITEMS = "maxItems";
100     private static final String MIN_ITEMS = "minItems";
101     private static final String MAX_LENGTH_KEY = "maxLength";
102     private static final String MIN_LENGTH_KEY = "minLength";
103     private static final String REQUIRED_KEY = "required";
104     private static final String REF_KEY = "$ref";
105     private static final String ITEMS_KEY = "items";
106     private static final String TYPE_KEY = "type";
107     private static final String PROPERTIES_KEY = "properties";
108     private static final String DESCRIPTION_KEY = "description";
109     private static final String ARRAY_TYPE = "array";
110     private static final String ENUM_KEY = "enum";
111     private static final String TITLE_KEY = "title";
112     private static final String DEFAULT_KEY = "default";
113     private static final String FORMAT_KEY = "format";
114     private static final String NAMESPACE_KEY = "namespace";
115     public static final String INPUT = "input";
116     public static final String INPUT_SUFFIX = "_input";
117     public static final String OUTPUT = "output";
118     public static final String OUTPUT_SUFFIX = "_output";
119     private static final String STRING_TYPE = "string";
120     private static final String OBJECT_TYPE = "object";
121     private static final String NUMBER_TYPE = "number";
122     private static final String INTEGER_TYPE = "integer";
123     private static final String INT32_FORMAT = "int32";
124     private static final String INT64_FORMAT = "int64";
125     private static final String BOOLEAN_TYPE = "boolean";
126     // Special characters used in automaton inside Generex.
127     // See https://www.brics.dk/automaton/doc/dk/brics/automaton/RegExp.html
128     private static final Pattern AUTOMATON_SPECIAL_CHARACTERS = Pattern.compile("[@&\"<>#~]");
129
130     private Module topLevelModule;
131
132     public DefinitionGenerator() {
133     }
134
135     /**
136      * Creates Json definitions from provided module according to swagger spec.
137      *
138      * @param module          - Yang module to be converted
139      * @param schemaContext   - SchemaContext of all Yang files used by Api Doc
140      * @param definitionNames - Store for definition names
141      * @return ObjectNode containing data used for creating examples and definitions in Api Doc
142      * @throws IOException if I/O operation fails
143      */
144
145
146     public ObjectNode convertToJsonSchema(final Module module, final EffectiveModelContext schemaContext,
147                                           final ObjectNode definitions, final DefinitionNames definitionNames,
148                                           final OAversion oaversion, final boolean isForSingleModule)
149             throws IOException {
150         topLevelModule = module;
151
152         processIdentities(module, definitions, definitionNames, schemaContext);
153         processContainersAndLists(module, definitions, definitionNames, schemaContext, oaversion);
154         processRPCs(module, definitions, definitionNames, schemaContext, oaversion);
155
156         if (isForSingleModule) {
157             processModule(module, definitions, definitionNames, schemaContext, oaversion);
158         }
159
160         return definitions;
161     }
162
163     public ObjectNode convertToJsonSchema(final Module module, final EffectiveModelContext schemaContext,
164                                           final DefinitionNames definitionNames, final OAversion oaversion,
165                                           final boolean isForSingleModule)
166             throws IOException {
167         final ObjectNode definitions = JsonNodeFactory.instance.objectNode();
168         if (isForSingleModule) {
169             definitionNames.addUnlinkedName(module.getName() + MODULE_NAME_SUFFIX);
170         }
171         return convertToJsonSchema(module, schemaContext, definitions, definitionNames, oaversion, isForSingleModule);
172     }
173
174     private void processModule(final Module module, final ObjectNode definitions, final DefinitionNames definitionNames,
175                                final EffectiveModelContext schemaContext, final OAversion oaversion) {
176         final ObjectNode definition = JsonNodeFactory.instance.objectNode();
177         final ObjectNode properties = JsonNodeFactory.instance.objectNode();
178         final ArrayNode required = JsonNodeFactory.instance.arrayNode();
179         final String moduleName = module.getName();
180         final String definitionName = moduleName + MODULE_NAME_SUFFIX;
181         final SchemaInferenceStack stack = SchemaInferenceStack.of(schemaContext);
182         for (final DataSchemaNode node : module.getChildNodes()) {
183             stack.enterSchemaTree(node.getQName());
184             final String localName = node.getQName().getLocalName();
185             if (node.isConfiguration()) {
186                 if (node instanceof ContainerSchemaNode || node instanceof ListSchemaNode) {
187                     for (final DataSchemaNode childNode : ((DataNodeContainer) node).getChildNodes()) {
188                         final ObjectNode childNodeProperties = JsonNodeFactory.instance.objectNode();
189
190                         final String ref = getAppropriateModelPrefix(oaversion)
191                                 + moduleName + CONFIG
192                                 + "_" + localName
193                                 + definitionNames.getDiscriminator(node);
194
195                         if (node instanceof ListSchemaNode) {
196                             childNodeProperties.put(TYPE_KEY, ARRAY_TYPE);
197                             final ObjectNode items = JsonNodeFactory.instance.objectNode();
198                             items.put(REF_KEY, ref);
199                             childNodeProperties.set(ITEMS_KEY, items);
200                             childNodeProperties.put(DESCRIPTION_KEY, childNode.getDescription().orElse(""));
201                             childNodeProperties.put(TITLE_KEY, localName + CONFIG);
202                         } else {
203                          /*
204                             Description can't be added, because nothing allowed alongside $ref.
205                             allOf is not an option, because ServiceNow can't parse it.
206                           */
207                             childNodeProperties.put(REF_KEY, ref);
208                         }
209                         //add module name prefix to property name, when ServiceNow can process colons
210                         properties.set(localName, childNodeProperties);
211                     }
212                 } else if (node instanceof LeafSchemaNode) {
213                     /*
214                         Add module name prefix to property name, when ServiceNow can process colons(second parameter
215                         of processLeafNode).
216                      */
217                     processLeafNode((LeafSchemaNode) node, localName, properties, required, stack,
218                             definitions, definitionNames, oaversion);
219                 }
220             }
221             stack.exit();
222         }
223         definition.put(TITLE_KEY, definitionName);
224         definition.put(TYPE_KEY, OBJECT_TYPE);
225         definition.set(PROPERTIES_KEY, properties);
226         definition.put(DESCRIPTION_KEY, module.getDescription().orElse(""));
227         setRequiredIfNotEmpty(definition, required);
228
229         definitions.set(definitionName, definition);
230     }
231
232     private void processContainersAndLists(final Module module, final ObjectNode definitions,
233             final DefinitionNames definitionNames, final EffectiveModelContext schemaContext, final OAversion oaversion)
234                 throws IOException {
235         final String moduleName = module.getName();
236         final SchemaInferenceStack stack = SchemaInferenceStack.of(schemaContext);
237         for (final DataSchemaNode childNode : module.getChildNodes()) {
238             stack.enterSchemaTree(childNode.getQName());
239             // For every container and list in the module
240             if (childNode instanceof ContainerSchemaNode || childNode instanceof ListSchemaNode) {
241                 if (childNode.isConfiguration()) {
242                     processDataNodeContainer((DataNodeContainer) childNode, moduleName, definitions, definitionNames,
243                             true, stack, oaversion);
244                 }
245                 processDataNodeContainer((DataNodeContainer) childNode, moduleName, definitions, definitionNames,
246                         false, stack, oaversion);
247                 processActionNodeContainer(childNode, moduleName, definitions, definitionNames, stack, oaversion);
248             }
249             stack.exit();
250         }
251     }
252
253     private void processActionNodeContainer(final DataSchemaNode childNode, final String moduleName,
254                                             final ObjectNode definitions, final DefinitionNames definitionNames,
255                                             final SchemaInferenceStack stack, final OAversion oaversion)
256             throws IOException {
257         for (final ActionDefinition actionDef : ((ActionNodeContainer) childNode).getActions()) {
258             stack.enterSchemaTree(actionDef.getQName());
259             processOperations(actionDef, moduleName, definitions, definitionNames, stack, oaversion);
260             stack.exit();
261         }
262     }
263
264     private void processRPCs(final Module module, final ObjectNode definitions, final DefinitionNames definitionNames,
265                              final EffectiveModelContext schemaContext, final OAversion oaversion) throws IOException {
266         final String moduleName = module.getName();
267         final SchemaInferenceStack stack = SchemaInferenceStack.of(schemaContext);
268         for (final RpcDefinition rpcDefinition : module.getRpcs()) {
269             stack.enterSchemaTree(rpcDefinition.getQName());
270             processOperations(rpcDefinition, moduleName, definitions, definitionNames, stack, oaversion);
271             stack.exit();
272         }
273     }
274
275     private void processOperations(final OperationDefinition operationDef, final String parentName,
276             final ObjectNode definitions, final DefinitionNames definitionNames,
277             final SchemaInferenceStack stack, final OAversion oaversion)
278                 throws IOException {
279         final String operationName = operationDef.getQName().getLocalName();
280         processOperationInputOutput(operationDef.getInput(), operationName, parentName, true, definitions,
281                 definitionNames, stack, oaversion);
282         processOperationInputOutput(operationDef.getOutput(), operationName, parentName, false, definitions,
283                 definitionNames, stack, oaversion);
284     }
285
286     private void processOperationInputOutput(final ContainerLike container, final String operationName,
287                                              final String parentName, final boolean isInput,
288                                              final ObjectNode definitions, final DefinitionNames definitionNames,
289                                              final SchemaInferenceStack stack, final OAversion oaversion)
290             throws IOException {
291         stack.enterSchemaTree(container.getQName());
292         if (!container.getChildNodes().isEmpty()) {
293             final String filename = parentName + "_" + operationName + (isInput ? INPUT_SUFFIX : OUTPUT_SUFFIX);
294             final ObjectNode childSchema = JsonNodeFactory.instance.objectNode();
295             processChildren(childSchema, container.getChildNodes(), parentName, definitions, definitionNames,
296                     false, stack, oaversion);
297
298             childSchema.put(TYPE_KEY, OBJECT_TYPE);
299             final ObjectNode xml = JsonNodeFactory.instance.objectNode();
300             xml.put(NAME_KEY, isInput ? INPUT : OUTPUT);
301             childSchema.set(XML_KEY, xml);
302             childSchema.put(TITLE_KEY, filename);
303             final String discriminator =
304                     definitionNames.pickDiscriminator(container, List.of(filename, filename + TOP));
305             definitions.set(filename + discriminator, childSchema);
306
307             processTopData(filename, discriminator, definitions, container, oaversion);
308         }
309         stack.exit();
310     }
311
312     private static ObjectNode processTopData(final String filename, final String discriminator,
313             final ObjectNode definitions, final SchemaNode schemaNode, final OAversion oaversion) {
314         final ObjectNode dataNodeProperties = JsonNodeFactory.instance.objectNode();
315         final String name = filename + discriminator;
316         final String ref = getAppropriateModelPrefix(oaversion) + name;
317         final String topName = filename + TOP;
318
319         if (schemaNode instanceof ListSchemaNode) {
320             dataNodeProperties.put(TYPE_KEY, ARRAY_TYPE);
321             final ObjectNode items = JsonNodeFactory.instance.objectNode();
322             items.put(REF_KEY, ref);
323             dataNodeProperties.set(ITEMS_KEY, items);
324             dataNodeProperties.put(DESCRIPTION_KEY, schemaNode.getDescription().orElse(""));
325         } else {
326              /*
327                 Description can't be added, because nothing allowed alongside $ref.
328                 allOf is not an option, because ServiceNow can't parse it.
329               */
330             dataNodeProperties.put(REF_KEY, ref);
331         }
332
333         final ObjectNode properties = JsonNodeFactory.instance.objectNode();
334         /*
335             Add module name prefix to property name, when needed, when ServiceNow can process colons,
336             use RestDocGenUtil#resolveNodesName for creating property name
337          */
338         properties.set(schemaNode.getQName().getLocalName(), dataNodeProperties);
339         final ObjectNode finalChildSchema = JsonNodeFactory.instance.objectNode();
340         finalChildSchema.put(TYPE_KEY, OBJECT_TYPE);
341         finalChildSchema.set(PROPERTIES_KEY, properties);
342         finalChildSchema.put(TITLE_KEY, topName);
343
344
345         definitions.set(topName + discriminator, finalChildSchema);
346
347         return dataNodeProperties;
348     }
349
350     /**
351      * Processes the 'identity' statement in a yang model and maps it to a 'model' in the Swagger JSON spec.
352      * @param module          The module from which the identity stmt will be processed
353      * @param definitions     The ObjectNode in which the parsed identity will be put as a 'model' obj
354      * @param definitionNames Store for definition names
355      */
356     private static void processIdentities(final Module module, final ObjectNode definitions,
357                                           final DefinitionNames definitionNames, final EffectiveModelContext context) {
358         final String moduleName = module.getName();
359         final Collection<? extends IdentitySchemaNode> idNodes = module.getIdentities();
360         LOG.debug("Processing Identities for module {} . Found {} identity statements", moduleName, idNodes.size());
361
362         for (final IdentitySchemaNode idNode : idNodes) {
363             final ObjectNode identityObj = buildIdentityObject(idNode, context);
364             final String idName = idNode.getQName().getLocalName();
365             final String discriminator = definitionNames.pickDiscriminator(idNode, List.of(idName));
366             final String name = idName + discriminator;
367             definitions.set(name, identityObj);
368         }
369     }
370
371     private static void populateEnumWithDerived(final Collection<? extends IdentitySchemaNode> derivedIds,
372                                                 final ArrayNode enumPayload, final EffectiveModelContext context) {
373         for (final IdentitySchemaNode derivedId : derivedIds) {
374             enumPayload.add(derivedId.getQName().getLocalName());
375             populateEnumWithDerived(context.getDerivedIdentities(derivedId), enumPayload, context);
376         }
377     }
378
379     private ObjectNode processDataNodeContainer(final DataNodeContainer dataNode, final String parentName,
380                                                 final ObjectNode definitions, final DefinitionNames definitionNames,
381                                                 final boolean isConfig, final SchemaInferenceStack stack,
382                                                 final OAversion oaversion) throws IOException {
383         if (dataNode instanceof ListSchemaNode || dataNode instanceof ContainerSchemaNode) {
384             final Collection<? extends DataSchemaNode> containerChildren = dataNode.getChildNodes();
385             final SchemaNode schemaNode = (SchemaNode) dataNode;
386             final String localName = schemaNode.getQName().getLocalName();
387             final ObjectNode childSchema = JsonNodeFactory.instance.objectNode();
388             final String nameAsParent = parentName + "_" + localName;
389             final ObjectNode properties =
390                     processChildren(childSchema, containerChildren, parentName + "_" + localName, definitions,
391                             definitionNames, isConfig, stack, oaversion);
392
393             final String nodeName = parentName + (isConfig ? CONFIG : "") + "_" + localName;
394             final String postNodeName = parentName + CONFIG + "_" + localName + POST_SUFFIX;
395             final String postXmlNodeName = postNodeName + XML_SUFFIX;
396             final String parentNameConfigLocalName = parentName + CONFIG + "_" + localName;
397
398             final String description = schemaNode.getDescription().orElse("");
399             final String discriminator;
400
401             if (!definitionNames.isListedNode(schemaNode)) {
402                 final List<String> names = List.of(parentNameConfigLocalName,
403                         parentNameConfigLocalName + TOP,
404                         nameAsParent,
405                         nameAsParent + TOP,
406                         postNodeName,
407                         postXmlNodeName);
408                 discriminator = definitionNames.pickDiscriminator(schemaNode, names);
409             } else {
410                 discriminator = definitionNames.getDiscriminator(schemaNode);
411             }
412
413             if (isConfig) {
414                 final ObjectNode postSchema = createPostJsonSchema(schemaNode, properties, postNodeName, description);
415                 String truePostNodeName = postNodeName + discriminator;
416                 definitions.set(truePostNodeName, postSchema);
417
418                 final ObjectNode postXmlSchema = JsonNodeFactory.instance.objectNode();
419                 postXmlSchema.put(REF_KEY, getAppropriateModelPrefix(oaversion) + truePostNodeName);
420                 definitions.set(postXmlNodeName + discriminator, postXmlSchema);
421             }
422
423             childSchema.put(TYPE_KEY, OBJECT_TYPE);
424             childSchema.set(PROPERTIES_KEY, properties);
425             childSchema.put(TITLE_KEY, nodeName);
426             childSchema.put(DESCRIPTION_KEY, description);
427
428             final String defName = nodeName + discriminator;
429             childSchema.set(XML_KEY, buildXmlParameter(schemaNode));
430             definitions.set(defName, childSchema);
431
432             return processTopData(nodeName, discriminator, definitions, schemaNode, oaversion);
433         }
434         return null;
435     }
436
437     private static ObjectNode createPostJsonSchema(final SchemaNode dataNode, final ObjectNode properties,
438             final String postNodeName, final String description) {
439         final ObjectNode postSchema = JsonNodeFactory.instance.objectNode();
440         final ObjectNode postItemProperties;
441         if (dataNode instanceof ListSchemaNode) {
442             postItemProperties = createListItemProperties(properties, (ListSchemaNode) dataNode);
443         } else {
444             postItemProperties = properties.deepCopy();
445         }
446         postSchema.put(TYPE_KEY, OBJECT_TYPE);
447         postSchema.set(PROPERTIES_KEY, postItemProperties);
448         postSchema.put(TITLE_KEY, postNodeName);
449         postSchema.put(DESCRIPTION_KEY, description);
450         postSchema.set(XML_KEY, buildXmlParameter(dataNode));
451         return postSchema;
452     }
453
454     private static ObjectNode createListItemProperties(final ObjectNode properties, final ListSchemaNode listNode) {
455         final ObjectNode postListItemProperties = JsonNodeFactory.instance.objectNode();
456         final List<QName> keyDefinition = listNode.getKeyDefinition();
457         final Set<String> keys = listNode.getChildNodes().stream()
458                 .filter(node -> keyDefinition.contains(node.getQName()))
459                 .map(node -> node.getQName().getLocalName())
460                 .collect(Collectors.toSet());
461
462         Iterator<Map.Entry<String, JsonNode>> it = properties.fields();
463         while (it.hasNext()) {
464             Map.Entry<String, JsonNode> property = it.next();
465             if (!keys.contains(property.getKey())) {
466                 postListItemProperties.set(property.getKey(), property.getValue());
467             }
468         }
469
470         return postListItemProperties;
471     }
472
473     /**
474      * Processes the nodes.
475      */
476     private ObjectNode processChildren(
477             final ObjectNode parentNode, final Collection<? extends DataSchemaNode> nodes, final String parentName,
478             final ObjectNode definitions, final DefinitionNames definitionNames, final boolean isConfig,
479             final SchemaInferenceStack stack, final OAversion oaversion) throws IOException {
480         final ObjectNode properties = JsonNodeFactory.instance.objectNode();
481         final ArrayNode required = JsonNodeFactory.instance.arrayNode();
482         for (final DataSchemaNode node : nodes) {
483             stack.enterSchemaTree(node.getQName());
484             if (!isConfig || node.isConfiguration()) {
485                 /*
486                     Add module name prefix to property name, when needed, when ServiceNow can process colons,
487                     use RestDocGenUtil#resolveNodesName for creating property name
488                  */
489                 final String propertyName = node.getQName().getLocalName();
490                 final ObjectNode property;
491                 if (node instanceof LeafSchemaNode leaf) {
492                     processLeafNode(leaf, propertyName, properties, required, stack, definitions, definitionNames,
493                             oaversion);
494                 } else if (node instanceof AnyxmlSchemaNode anyxml) {
495                     processAnyXMLNode(anyxml, propertyName, properties, required);
496                 } else if (node instanceof AnydataSchemaNode anydata) {
497                     processAnydataNode(anydata, propertyName, properties, required);
498                 } else {
499                     if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) {
500                         property = processDataNodeContainer((DataNodeContainer) node, parentName, definitions,
501                                 definitionNames, isConfig, stack, oaversion);
502                         if (!isConfig) {
503                             processActionNodeContainer(node, parentName, definitions, definitionNames, stack,
504                                     oaversion);
505                         }
506                     } else if (node instanceof LeafListSchemaNode leafList) {
507                         property = processLeafListNode(leafList, stack, definitions, definitionNames, oaversion);
508
509                     } else if (node instanceof ChoiceSchemaNode choice) {
510                         for (final CaseSchemaNode variant : choice.getCases()) {
511                             stack.enterSchemaTree(variant.getQName());
512                             processChoiceNode(variant.getChildNodes(), parentName, definitions, definitionNames,
513                                     isConfig, stack, properties, oaversion);
514                             stack.exit();
515                         }
516                         stack.exit();
517                         // FIXME dangerous statement here! Try to rework without continue.
518                         continue;
519                     } else {
520                         throw new IllegalArgumentException("Unknown DataSchemaNode type: " + node.getClass());
521                     }
522                     properties.set(propertyName, property);
523                 }
524             }
525             stack.exit();
526         }
527         parentNode.set(PROPERTIES_KEY, properties);
528         setRequiredIfNotEmpty(parentNode, required);
529         return properties;
530     }
531
532     private ObjectNode processLeafListNode(final LeafListSchemaNode listNode, final SchemaInferenceStack stack,
533                                            final ObjectNode definitions, final DefinitionNames definitionNames,
534                                            final OAversion oaversion) {
535         final ObjectNode props = JsonNodeFactory.instance.objectNode();
536         props.put(TYPE_KEY, ARRAY_TYPE);
537
538         final ObjectNode itemsVal = JsonNodeFactory.instance.objectNode();
539         final Optional<ElementCountConstraint> optConstraint = listNode.getElementCountConstraint();
540         processElementCount(optConstraint, props);
541
542         processTypeDef(listNode.getType(), listNode, itemsVal, stack, definitions, definitionNames, oaversion);
543         props.set(ITEMS_KEY, itemsVal);
544
545         props.put(DESCRIPTION_KEY, listNode.getDescription().orElse(""));
546
547         return props;
548     }
549
550     private void processChoiceNode(
551             final Iterable<? extends DataSchemaNode> nodes, final String parentName, final ObjectNode definitions,
552             final DefinitionNames definitionNames, final boolean isConfig,
553             final SchemaInferenceStack stack, final ObjectNode properties, final OAversion oaversion)
554             throws IOException {
555         for (final DataSchemaNode node : nodes) {
556             stack.enterSchemaTree(node.getQName());
557             /*
558                 Add module name prefix to property name, when needed, when ServiceNow can process colons,
559                 use RestDocGenUtil#resolveNodesName for creating property name
560              */
561             final String name = node.getQName().getLocalName();
562             final ObjectNode property;
563
564             /*
565                 Ignore mandatoriness(passing unreferenced arrayNode to process...Node), because choice produces multiple
566                 properties
567              */
568             if (node instanceof LeafSchemaNode leaf) {
569                 processLeafNode(leaf, name, properties, JsonNodeFactory.instance.arrayNode(), stack, definitions,
570                         definitionNames, oaversion);
571             } else if (node instanceof AnyxmlSchemaNode anyxml) {
572                 processAnyXMLNode(anyxml, name, properties, JsonNodeFactory.instance.arrayNode());
573             } else if (node instanceof AnydataSchemaNode anydata) {
574                 processAnydataNode(anydata, name, properties, JsonNodeFactory.instance.arrayNode());
575             } else {
576                 if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) {
577                     property = processDataNodeContainer((DataNodeContainer) node, parentName, definitions,
578                             definitionNames, isConfig, stack, oaversion);
579                     if (!isConfig) {
580                         processActionNodeContainer(node, parentName, definitions, definitionNames, stack,
581                                 oaversion);
582                     }
583                 } else if (node instanceof LeafListSchemaNode leafList) {
584                     property = processLeafListNode(leafList, stack, definitions, definitionNames, oaversion);
585
586                 } else if (node instanceof ChoiceSchemaNode choice) {
587                     for (final CaseSchemaNode variant : choice.getCases()) {
588                         processChoiceNode(variant.getChildNodes(), parentName, definitions, definitionNames, isConfig,
589                                 stack, properties, oaversion);
590                     }
591                     continue;
592                 } else {
593                     throw new IllegalArgumentException("Unknown DataSchemaNode type: " + node.getClass());
594                 }
595                 properties.set(name, property);
596             }
597             stack.exit();
598         }
599     }
600
601     private static void processElementCount(final Optional<ElementCountConstraint> constraint, final ObjectNode props) {
602         if (constraint.isPresent()) {
603             final ElementCountConstraint constr = constraint.get();
604             final Integer minElements = constr.getMinElements();
605             if (minElements != null) {
606                 props.put(MIN_ITEMS, minElements);
607             }
608             final Integer maxElements = constr.getMaxElements();
609             if (maxElements != null) {
610                 props.put(MAX_ITEMS, maxElements);
611             }
612         }
613     }
614
615     private static void processMandatory(final MandatoryAware node, final String nodeName, final ArrayNode required) {
616         if (node.isMandatory()) {
617             required.add(nodeName);
618         }
619     }
620
621     private ObjectNode processLeafNode(final LeafSchemaNode leafNode, final String jsonLeafName,
622                                        final ObjectNode properties, final ArrayNode required,
623                                        final SchemaInferenceStack stack, final ObjectNode definitions,
624                                        final DefinitionNames definitionNames, final OAversion oaversion) {
625         final ObjectNode property = JsonNodeFactory.instance.objectNode();
626
627         final String leafDescription = leafNode.getDescription().orElse("");
628         /*
629             Description can't be added, because nothing allowed alongside $ref.
630             allOf is not an option, because ServiceNow can't parse it.
631         */
632         if (!(leafNode.getType() instanceof IdentityrefTypeDefinition)) {
633             property.put(DESCRIPTION_KEY, leafDescription);
634         }
635
636         processTypeDef(leafNode.getType(), leafNode, property, stack, definitions, definitionNames, oaversion);
637         properties.set(jsonLeafName, property);
638         property.set(XML_KEY, buildXmlParameter(leafNode));
639         processMandatory(leafNode, jsonLeafName, required);
640
641         return property;
642     }
643
644     private static ObjectNode processAnydataNode(final AnydataSchemaNode leafNode, final String name,
645                                                  final ObjectNode properties, final ArrayNode required) {
646         final ObjectNode property = JsonNodeFactory.instance.objectNode();
647
648         final String leafDescription = leafNode.getDescription().orElse("");
649         property.put(DESCRIPTION_KEY, leafDescription);
650
651         final String localName = leafNode.getQName().getLocalName();
652         setDefaultValue(property, String.format("<%s> ... </%s>", localName, localName));
653         property.put(TYPE_KEY, STRING_TYPE);
654         property.set(XML_KEY, buildXmlParameter(leafNode));
655         processMandatory(leafNode, name, required);
656         properties.set(name, property);
657
658         return property;
659     }
660
661     private static ObjectNode processAnyXMLNode(final AnyxmlSchemaNode leafNode, final String name,
662                                                 final ObjectNode properties, final ArrayNode required) {
663         final ObjectNode property = JsonNodeFactory.instance.objectNode();
664
665         final String leafDescription = leafNode.getDescription().orElse("");
666         property.put(DESCRIPTION_KEY, leafDescription);
667
668         final String localName = leafNode.getQName().getLocalName();
669         setDefaultValue(property, String.format("<%s> ... </%s>", localName, localName));
670         property.put(TYPE_KEY, STRING_TYPE);
671         property.set(XML_KEY, buildXmlParameter(leafNode));
672         processMandatory(leafNode, name, required);
673         properties.set(name, property);
674
675         return property;
676     }
677
678     private String processTypeDef(final TypeDefinition<?> leafTypeDef, final DataSchemaNode node,
679                                   final ObjectNode property, final SchemaInferenceStack stack,
680                                   final ObjectNode definitions, final DefinitionNames definitionNames,
681                                   final OAversion oaversion) {
682         final String jsonType;
683         if (leafTypeDef instanceof BinaryTypeDefinition) {
684             jsonType = processBinaryType(property);
685
686         } else if (leafTypeDef instanceof BitsTypeDefinition) {
687             jsonType = processBitsType((BitsTypeDefinition) leafTypeDef, property);
688
689         } else if (leafTypeDef instanceof EnumTypeDefinition) {
690             jsonType = processEnumType((EnumTypeDefinition) leafTypeDef, property);
691
692         } else if (leafTypeDef instanceof IdentityrefTypeDefinition) {
693             jsonType = processIdentityRefType((IdentityrefTypeDefinition) leafTypeDef, property, definitions,
694                     definitionNames, oaversion, stack.getEffectiveModelContext());
695
696         } else if (leafTypeDef instanceof StringTypeDefinition) {
697             jsonType = processStringType(leafTypeDef, property, node.getQName().getLocalName());
698
699         } else if (leafTypeDef instanceof UnionTypeDefinition) {
700             jsonType = processUnionType((UnionTypeDefinition) leafTypeDef);
701
702         } else if (leafTypeDef instanceof EmptyTypeDefinition) {
703             jsonType = OBJECT_TYPE;
704         } else if (leafTypeDef instanceof LeafrefTypeDefinition) {
705             return processTypeDef(stack.resolveLeafref((LeafrefTypeDefinition) leafTypeDef), node, property,
706                 stack, definitions, definitionNames, oaversion);
707         } else if (leafTypeDef instanceof BooleanTypeDefinition) {
708             jsonType = BOOLEAN_TYPE;
709             setDefaultValue(property, true);
710         } else if (leafTypeDef instanceof RangeRestrictedTypeDefinition) {
711             jsonType = processNumberType((RangeRestrictedTypeDefinition<?, ?>) leafTypeDef, property);
712         } else if (leafTypeDef instanceof InstanceIdentifierTypeDefinition) {
713             jsonType = processInstanceIdentifierType(node, property, stack.getEffectiveModelContext());
714         } else {
715             jsonType = STRING_TYPE;
716         }
717         if (!(leafTypeDef instanceof IdentityrefTypeDefinition)) {
718             putIfNonNull(property, TYPE_KEY, jsonType);
719             if (leafTypeDef.getDefaultValue().isPresent()) {
720                 final Object defaultValue = leafTypeDef.getDefaultValue().get();
721                 if (defaultValue instanceof String stringDefaultValue) {
722                     if (leafTypeDef instanceof BooleanTypeDefinition) {
723                         setDefaultValue(property, Boolean.valueOf(stringDefaultValue));
724                     } else if (leafTypeDef instanceof DecimalTypeDefinition
725                             || leafTypeDef instanceof Uint64TypeDefinition) {
726                         setDefaultValue(property, new BigDecimal(stringDefaultValue));
727                     } else if (leafTypeDef instanceof RangeRestrictedTypeDefinition) {
728                         //uint8,16,32 int8,16,32,64
729                         if (isHexadecimalOrOctal((RangeRestrictedTypeDefinition<?, ?>)leafTypeDef)) {
730                             setDefaultValue(property, stringDefaultValue);
731                         } else {
732                             setDefaultValue(property, Long.valueOf(stringDefaultValue));
733                         }
734                     } else {
735                         setDefaultValue(property, stringDefaultValue);
736                     }
737                 } else {
738                     //we should never get here. getDefaultValue always gives us string
739                     setDefaultValue(property, defaultValue.toString());
740                 }
741             }
742         }
743         return jsonType;
744     }
745
746     private static String processBinaryType(final ObjectNode property) {
747         property.put(FORMAT_KEY, "byte");
748         return STRING_TYPE;
749     }
750
751     private static String processEnumType(final EnumTypeDefinition enumLeafType,
752                                           final ObjectNode property) {
753         final List<EnumPair> enumPairs = enumLeafType.getValues();
754         final ArrayNode enumNames = new ArrayNode(JsonNodeFactory.instance);
755         for (final EnumPair enumPair : enumPairs) {
756             enumNames.add(new TextNode(enumPair.getName()));
757         }
758
759         property.set(ENUM_KEY, enumNames);
760         setDefaultValue(property, enumLeafType.getValues().iterator().next().getName());
761         return STRING_TYPE;
762     }
763
764     private String processIdentityRefType(final IdentityrefTypeDefinition leafTypeDef, final ObjectNode property,
765                                           final ObjectNode definitions, final DefinitionNames definitionNames,
766                                           final OAversion oaversion, final EffectiveModelContext schemaContext) {
767         final String definitionName;
768         if (isImported(leafTypeDef)) {
769             definitionName = addImportedIdentity(leafTypeDef, definitions, definitionNames, schemaContext);
770         } else {
771             final SchemaNode node = leafTypeDef.getIdentities().iterator().next();
772             definitionName = node.getQName().getLocalName() + definitionNames.getDiscriminator(node);
773         }
774         property.put(REF_KEY, getAppropriateModelPrefix(oaversion) + definitionName);
775         return STRING_TYPE;
776     }
777
778     private static String addImportedIdentity(final IdentityrefTypeDefinition leafTypeDef,
779                                               final ObjectNode definitions, final DefinitionNames definitionNames,
780                                               final EffectiveModelContext context) {
781         final IdentitySchemaNode idNode = leafTypeDef.getIdentities().iterator().next();
782         final String identityName = idNode.getQName().getLocalName();
783         if (!definitionNames.isListedNode(idNode)) {
784             final ObjectNode identityObj = buildIdentityObject(idNode, context);
785             final String discriminator = definitionNames.pickDiscriminator(idNode, List.of(identityName));
786             final String name = identityName + discriminator;
787             definitions.set(name, identityObj);
788             return name;
789         } else {
790             return identityName + definitionNames.getDiscriminator(idNode);
791         }
792     }
793
794     private static ObjectNode buildIdentityObject(final IdentitySchemaNode idNode,
795             final EffectiveModelContext context) {
796         final ObjectNode identityObj = JsonNodeFactory.instance.objectNode();
797         final String identityName = idNode.getQName().getLocalName();
798         LOG.debug("Processing Identity: {}", identityName);
799
800         identityObj.put(TITLE_KEY, identityName);
801         identityObj.put(DESCRIPTION_KEY, idNode.getDescription().orElse(""));
802
803         final Collection<? extends IdentitySchemaNode> derivedIds = context.getDerivedIdentities(idNode);
804
805         final ArrayNode enumPayload = JsonNodeFactory.instance.arrayNode();
806         enumPayload.add(identityName);
807         populateEnumWithDerived(derivedIds, enumPayload, context);
808         identityObj.set(ENUM_KEY, enumPayload);
809         identityObj.put(TYPE_KEY, STRING_TYPE);
810         return identityObj;
811     }
812
813     private boolean isImported(final IdentityrefTypeDefinition leafTypeDef) {
814         return !leafTypeDef.getQName().getModule().equals(topLevelModule.getQNameModule());
815     }
816
817     private static String processBitsType(final BitsTypeDefinition bitsType,
818                                           final ObjectNode property) {
819         property.put(MIN_ITEMS, 0);
820         property.put(UNIQUE_ITEMS_KEY, true);
821         final ArrayNode enumNames = new ArrayNode(JsonNodeFactory.instance);
822         final Collection<? extends Bit> bits = bitsType.getBits();
823         for (final Bit bit : bits) {
824             enumNames.add(new TextNode(bit.getName()));
825         }
826         property.set(ENUM_KEY, enumNames);
827         property.put(DEFAULT_KEY, enumNames.iterator().next() + " " + enumNames.get(enumNames.size() - 1));
828         return STRING_TYPE;
829     }
830
831     private static String processStringType(final TypeDefinition<?> stringType, final ObjectNode property,
832                                             final String nodeName) {
833         StringTypeDefinition type = (StringTypeDefinition) stringType;
834         Optional<LengthConstraint> lengthConstraints = ((StringTypeDefinition) stringType).getLengthConstraint();
835         while (lengthConstraints.isEmpty() && type.getBaseType() != null) {
836             type = type.getBaseType();
837             lengthConstraints = type.getLengthConstraint();
838         }
839
840         if (lengthConstraints.isPresent()) {
841             final Range<Integer> range = lengthConstraints.get().getAllowedRanges().span();
842             putIfNonNull(property, MIN_LENGTH_KEY, range.lowerEndpoint());
843             putIfNonNull(property, MAX_LENGTH_KEY, range.upperEndpoint());
844         }
845
846         if (type.getPatternConstraints().iterator().hasNext()) {
847             final PatternConstraint pattern = type.getPatternConstraints().iterator().next();
848             String regex = pattern.getJavaPatternString();
849             regex = regex.substring(1, regex.length() - 1);
850             // Escape special characters to prevent issues inside Generex.
851             regex = AUTOMATON_SPECIAL_CHARACTERS.matcher(regex).replaceAll("\\\\$0");
852             String defaultValue = "";
853             try {
854                 final Generex generex = new Generex(regex);
855                 defaultValue = generex.random();
856             } catch (IllegalArgumentException ex) {
857                 LOG.warn("Cannot create example string for type: {} with regex: {}.", stringType.getQName(), regex);
858             }
859             setDefaultValue(property, defaultValue);
860         } else {
861             setDefaultValue(property, "Some " + nodeName);
862         }
863         return STRING_TYPE;
864     }
865
866     private static String processNumberType(final RangeRestrictedTypeDefinition<?, ?> leafTypeDef,
867             final ObjectNode property) {
868         final Optional<Number> maybeLower = leafTypeDef.getRangeConstraint()
869                 .map(RangeConstraint::getAllowedRanges).map(RangeSet::span).map(Range::lowerEndpoint);
870
871         if (isHexadecimalOrOctal(leafTypeDef)) {
872             return STRING_TYPE;
873         }
874
875         if (leafTypeDef instanceof DecimalTypeDefinition) {
876             maybeLower.ifPresent(number -> setDefaultValue(property, ((Decimal64) number).decimalValue()));
877             return NUMBER_TYPE;
878         }
879         if (leafTypeDef instanceof Uint8TypeDefinition
880                 || leafTypeDef instanceof Uint16TypeDefinition
881                 || leafTypeDef instanceof Int8TypeDefinition
882                 || leafTypeDef instanceof Int16TypeDefinition
883                 || leafTypeDef instanceof Int32TypeDefinition) {
884
885             property.put(FORMAT_KEY, INT32_FORMAT);
886             maybeLower.ifPresent(number -> setDefaultValue(property, Integer.valueOf(number.toString())));
887         } else if (leafTypeDef instanceof Uint32TypeDefinition
888                 || leafTypeDef instanceof Int64TypeDefinition) {
889
890             property.put(FORMAT_KEY, INT64_FORMAT);
891             maybeLower.ifPresent(number -> setDefaultValue(property, Long.valueOf(number.toString())));
892         } else {
893             //uint64
894             setDefaultValue(property, 0);
895         }
896         return INTEGER_TYPE;
897     }
898
899     private static boolean isHexadecimalOrOctal(final RangeRestrictedTypeDefinition<?, ?> typeDef) {
900         final Optional<?> optDefaultValue = typeDef.getDefaultValue();
901         if (optDefaultValue.isPresent()) {
902             final String defaultValue = (String)optDefaultValue.get();
903             return defaultValue.startsWith("0") || defaultValue.startsWith("-0");
904         }
905         return false;
906     }
907
908     private static String processInstanceIdentifierType(final DataSchemaNode node, final ObjectNode property,
909                                                         final EffectiveModelContext schemaContext) {
910         // create example instance-identifier to the first container of node's module if exists or leave it empty
911         final var module = schemaContext.findModule(node.getQName().getModule());
912         if (module.isPresent()) {
913             final var container = module.get().getChildNodes().stream()
914                     .filter(n -> n instanceof ContainerSchemaNode)
915                     .findFirst();
916             container.ifPresent(c -> setDefaultValue(property, String.format("/%s:%s", module.get().getPrefix(),
917                     c.getQName().getLocalName())));
918         }
919
920         return STRING_TYPE;
921     }
922
923     private static String processUnionType(final UnionTypeDefinition unionType) {
924         boolean isStringTakePlace = false;
925         boolean isNumberTakePlace = false;
926         boolean isBooleanTakePlace = false;
927         for (final TypeDefinition<?> typeDef : unionType.getTypes()) {
928             if (!isStringTakePlace) {
929                 if (typeDef instanceof StringTypeDefinition
930                         || typeDef instanceof BitsTypeDefinition
931                         || typeDef instanceof BinaryTypeDefinition
932                         || typeDef instanceof IdentityrefTypeDefinition
933                         || typeDef instanceof EnumTypeDefinition
934                         || typeDef instanceof LeafrefTypeDefinition
935                         || typeDef instanceof UnionTypeDefinition) {
936                     isStringTakePlace = true;
937                 } else if (!isNumberTakePlace && typeDef instanceof RangeRestrictedTypeDefinition) {
938                     isNumberTakePlace = true;
939                 } else if (!isBooleanTakePlace && typeDef instanceof BooleanTypeDefinition) {
940                     isBooleanTakePlace = true;
941                 }
942             }
943         }
944         if (isStringTakePlace) {
945             return STRING_TYPE;
946         }
947         if (isBooleanTakePlace) {
948             if (isNumberTakePlace) {
949                 return STRING_TYPE;
950             }
951             return BOOLEAN_TYPE;
952         }
953         return NUMBER_TYPE;
954     }
955
956     private static ObjectNode buildXmlParameter(final SchemaNode node) {
957         final ObjectNode xml = JsonNodeFactory.instance.objectNode();
958         final QName qName = node.getQName();
959         xml.put(NAME_KEY, qName.getLocalName());
960         xml.put(NAMESPACE_KEY, qName.getNamespace().toString());
961         return xml;
962     }
963
964     private static void putIfNonNull(final ObjectNode property, final String key, final Number number) {
965         if (key != null && number != null) {
966             if (number instanceof Double) {
967                 property.put(key, (Double) number);
968             } else if (number instanceof Float) {
969                 property.put(key, (Float) number);
970             } else if (number instanceof Integer) {
971                 property.put(key, (Integer) number);
972             } else if (number instanceof Short) {
973                 property.put(key, (Short) number);
974             } else if (number instanceof Long) {
975                 property.put(key, (Long) number);
976             }
977         }
978     }
979
980     private static void putIfNonNull(final ObjectNode property, final String key, final String value) {
981         if (key != null && value != null) {
982             property.put(key, value);
983         }
984     }
985
986     private static void setRequiredIfNotEmpty(final ObjectNode node, final ArrayNode required) {
987         if (required.size() > 0) {
988             node.set(REQUIRED_KEY, required);
989         }
990     }
991
992     private static void setDefaultValue(final ObjectNode property, final String value) {
993         property.put(DEFAULT_KEY, value);
994     }
995
996     private static void setDefaultValue(final ObjectNode property, final Integer value) {
997         property.put(DEFAULT_KEY, value);
998     }
999
1000     private static void setDefaultValue(final ObjectNode property, final Long value) {
1001         property.put(DEFAULT_KEY, value);
1002     }
1003
1004     private static void setDefaultValue(final ObjectNode property, final BigDecimal value) {
1005         property.put(DEFAULT_KEY, value);
1006     }
1007
1008     private static void setDefaultValue(final ObjectNode property, final Boolean value) {
1009         property.put(DEFAULT_KEY, value);
1010     }
1011
1012 }