2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.netconf.sal.rest.doc.impl;
10 import static org.opendaylight.netconf.sal.rest.doc.util.RestDocgenUtil.resolveNodesName;
12 import com.fasterxml.jackson.databind.JsonNode;
13 import com.fasterxml.jackson.databind.node.ArrayNode;
14 import com.fasterxml.jackson.databind.node.JsonNodeFactory;
15 import com.fasterxml.jackson.databind.node.ObjectNode;
16 import com.fasterxml.jackson.databind.node.TextNode;
17 import com.google.common.collect.Range;
18 import com.google.common.collect.RangeSet;
19 import com.mifmif.common.regex.Generex;
20 import java.io.IOException;
21 import java.util.List;
22 import java.util.Optional;
24 import java.util.regex.Pattern;
25 import javax.annotation.concurrent.NotThreadSafe;
26 import org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder;
27 import org.opendaylight.netconf.sal.rest.doc.model.builder.OperationBuilder.Post;
28 import org.opendaylight.yangtools.yang.common.QName;
29 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
30 import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
31 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
33 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
34 import org.opendaylight.yangtools.yang.model.api.ElementCountConstraint;
35 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.MandatoryAware;
40 import org.opendaylight.yangtools.yang.model.api.Module;
41 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
42 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
43 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
44 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
46 import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
48 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
49 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
50 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
51 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
52 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
53 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
54 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
55 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
56 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
57 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
58 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
59 import org.opendaylight.yangtools.yang.model.api.type.RangeRestrictedTypeDefinition;
60 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
61 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
62 import org.opendaylight.yangtools.yang.model.util.RevisionAwareXPathImpl;
63 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
68 * Generates JSON Schema for data defined in YANG.
71 public class ModelGenerator {
73 private static final Logger LOG = LoggerFactory.getLogger(ModelGenerator.class);
75 private static final Pattern STRIP_PATTERN = Pattern.compile("\\[[^\\[\\]]*\\]");
76 private static final String BASE_64 = "base64";
77 private static final String BINARY_ENCODING_KEY = "binaryEncoding";
78 private static final String MEDIA_KEY = "media";
79 private static final String UNIQUE_ITEMS_KEY = "uniqueItems";
80 private static final String MAX_ITEMS = "maxItems";
81 private static final String MIN_ITEMS = "minItems";
82 private static final String SCHEMA_URL = "http://json-schema.org/draft-04/schema";
83 private static final String SCHEMA_KEY = "$schema";
84 private static final String MAX_LENGTH_KEY = "maxLength";
85 private static final String MIN_LENGTH_KEY = "minLength";
86 private static final String REQUIRED_KEY = "required";
87 private static final String REF_KEY = "$ref";
88 private static final String ITEMS_KEY = "items";
89 private static final String TYPE_KEY = "type";
90 private static final String PROPERTIES_KEY = "properties";
91 private static final String DESCRIPTION_KEY = "description";
92 private static final String OBJECT_TYPE = "object";
93 private static final String ARRAY_TYPE = "array";
94 private static final String ENUM = "enum";
95 private static final String ID_KEY = "id";
96 private static final String SUB_TYPES_KEY = "subTypes";
97 private static final String UNIQUE_EMPTY_IDENTIFIER = "unique_empty_identifier";
99 private Module topLevelModule;
101 public ModelGenerator() {
105 * Creates Json models from provided module according to swagger spec.
107 * @param module - Yang module to be converted
108 * @param schemaContext - SchemaContext of all Yang files used by Api Doc
109 * @return ObjectNode containing data used for creating examples and models in Api Doc
110 * @throws IOException if I/O operation fails
112 public ObjectNode convertToJsonSchema(final Module module,
113 final SchemaContext schemaContext) throws IOException {
114 final ObjectNode models = JsonNodeFactory.instance.objectNode();
115 final ObjectNode emptyIdentifier = JsonNodeFactory.instance.objectNode();
116 models.set(UNIQUE_EMPTY_IDENTIFIER, emptyIdentifier);
117 topLevelModule = module;
118 processModules(module, models, schemaContext);
119 processContainersAndLists(module, models, schemaContext);
120 processRPCs(module, models, schemaContext);
121 processIdentities(module, models);
125 private void processModules(final Module module, final ObjectNode models,
126 final SchemaContext schemaContext) {
127 createConcreteModelForPost(models, module.getName() + BaseYangSwaggerGenerator.MODULE_NAME_SUFFIX,
128 createPropertiesForPost(module, schemaContext, module.getName()));
131 private void processContainersAndLists(final Module module, final ObjectNode models,
132 final SchemaContext schemaContext) throws IOException {
133 final String moduleName = module.getName();
135 for (final DataSchemaNode childNode : module.getChildNodes()) {
136 // For every container and list in the module
137 if (childNode instanceof ContainerSchemaNode || childNode instanceof ListSchemaNode) {
138 processDataNodeContainer((DataNodeContainer) childNode, moduleName, models, true, schemaContext);
139 processDataNodeContainer((DataNodeContainer) childNode, moduleName, models, false, schemaContext);
145 * Process the RPCs for a Module Spits out a file each of the name
146 * {@code <rpcName>-input.json and <rpcName>-output.json}
147 * for each RPC that contains input & output elements.
149 * @param module module
150 * @throws IOException if I/O operation fails
152 private void processRPCs(final Module module, final ObjectNode models,
153 final SchemaContext schemaContext) throws IOException {
154 final Set<RpcDefinition> rpcs = module.getRpcs();
155 final String moduleName = module.getName();
156 for (final RpcDefinition rpc : rpcs) {
157 final ContainerSchemaNode input = rpc.getInput();
158 if (!input.getChildNodes().isEmpty()) {
159 final ObjectNode properties =
160 processChildren(input.getChildNodes(), moduleName, models, true, schemaContext);
162 final String filename = "(" + rpc.getQName().getLocalName() + ")input";
163 final ObjectNode childSchema = getSchemaTemplate();
164 childSchema.put(TYPE_KEY, OBJECT_TYPE);
165 childSchema.set(PROPERTIES_KEY, properties);
166 childSchema.put(ID_KEY, filename);
167 models.set(filename, childSchema);
169 processTopData(filename, models, input);
172 final ContainerSchemaNode output = rpc.getOutput();
173 if (!output.getChildNodes().isEmpty()) {
174 final ObjectNode properties =
175 processChildren(output.getChildNodes(), moduleName, models, true, schemaContext);
176 final String filename = "(" + rpc.getQName().getLocalName() + ")output";
177 final ObjectNode childSchema = getSchemaTemplate();
178 childSchema.put(TYPE_KEY, OBJECT_TYPE);
179 childSchema.set(PROPERTIES_KEY, properties);
180 childSchema.put(ID_KEY, filename);
181 models.set(filename, childSchema);
183 processTopData(filename, models, output);
188 private ObjectNode processTopData(final String filename, final ObjectNode models, final SchemaNode schemaNode) {
189 final ObjectNode items = JsonNodeFactory.instance.objectNode();
191 items.put(REF_KEY, filename);
192 final ObjectNode dataNodeProperties = JsonNodeFactory.instance.objectNode();
193 dataNodeProperties.put(TYPE_KEY, schemaNode instanceof ListSchemaNode ? ARRAY_TYPE : OBJECT_TYPE);
194 dataNodeProperties.set(ITEMS_KEY, items);
196 putIfNonNull(dataNodeProperties, DESCRIPTION_KEY, schemaNode.getDescription().orElse(null));
197 final ObjectNode properties = JsonNodeFactory.instance.objectNode();
198 properties.set(topLevelModule.getName() + ":" + schemaNode.getQName().getLocalName(), dataNodeProperties);
199 final ObjectNode finalChildSchema = getSchemaTemplate();
200 finalChildSchema.put(TYPE_KEY, OBJECT_TYPE);
201 finalChildSchema.set(PROPERTIES_KEY, properties);
202 finalChildSchema.put(ID_KEY, filename + OperationBuilder.TOP);
203 models.set(filename + OperationBuilder.TOP, finalChildSchema);
205 return dataNodeProperties;
209 * Processes the 'identity' statement in a yang model and maps it to a 'model' in the Swagger JSON spec.
211 * @param module The module from which the identity stmt will be processed
212 * @param models The ObjectNode in which the parsed identity will be put as a 'model' obj
214 private static void processIdentities(final Module module, final ObjectNode models) {
216 final String moduleName = module.getName();
217 final Set<IdentitySchemaNode> idNodes = module.getIdentities();
218 LOG.debug("Processing Identities for module {} . Found {} identity statements", moduleName, idNodes.size());
220 for (final IdentitySchemaNode idNode : idNodes) {
221 final ObjectNode identityObj = JsonNodeFactory.instance.objectNode();
222 final String identityName = idNode.getQName().getLocalName();
223 LOG.debug("Processing Identity: {}", identityName);
225 identityObj.put(ID_KEY, identityName);
226 putIfNonNull(identityObj, DESCRIPTION_KEY, idNode.getDescription().orElse(null));
228 final ObjectNode props = JsonNodeFactory.instance.objectNode();
230 if (idNode.getBaseIdentities().isEmpty()) {
232 * This is a base identity. So lets see if it has sub types. If it does, then add them to the model
235 final Set<IdentitySchemaNode> derivedIds = idNode.getDerivedIdentities();
237 if (derivedIds != null) {
238 final ArrayNode subTypes = new ArrayNode(JsonNodeFactory.instance);
239 for (final IdentitySchemaNode derivedId : derivedIds) {
240 subTypes.add(derivedId.getQName().getLocalName());
242 identityObj.set(SUB_TYPES_KEY, subTypes);
247 * This is a derived entity. Add it's base type & move on.
249 props.put(TYPE_KEY, idNode.getBaseIdentities().iterator().next().getQName().getLocalName());
252 // Add the properties. For a base type, this will be an empty object as required by the Swagger spec.
253 identityObj.set(PROPERTIES_KEY, props);
254 models.set(identityName, identityObj);
258 private ObjectNode processDataNodeContainer(
259 final DataNodeContainer dataNode, final String parentName, final ObjectNode models, final boolean isConfig,
260 final SchemaContext schemaContext) throws IOException {
261 if (dataNode instanceof ListSchemaNode || dataNode instanceof ContainerSchemaNode) {
262 final Iterable<DataSchemaNode> containerChildren = dataNode.getChildNodes();
263 final String localName = ((SchemaNode) dataNode).getQName().getLocalName();
264 final ObjectNode properties =
265 processChildren(containerChildren, parentName + "/" + localName, models, isConfig, schemaContext);
266 final String nodeName = parentName + (isConfig ? OperationBuilder.CONFIG : OperationBuilder.OPERATIONAL)
269 final ObjectNode childSchema = getSchemaTemplate();
270 childSchema.put(TYPE_KEY, OBJECT_TYPE);
271 childSchema.set(PROPERTIES_KEY, properties);
273 childSchema.put(ID_KEY, nodeName);
274 models.set(nodeName, childSchema);
277 createConcreteModelForPost(models, localName,
278 createPropertiesForPost(dataNode, schemaContext, parentName + "/" + localName));
281 return processTopData(nodeName, models, (SchemaNode) dataNode);
286 private static void createConcreteModelForPost(final ObjectNode models, final String localName,
287 final JsonNode properties) {
288 final String nodePostName = OperationBuilder.CONFIG + localName + Post.METHOD_NAME;
289 final ObjectNode postSchema = getSchemaTemplate();
290 postSchema.put(TYPE_KEY, OBJECT_TYPE);
291 postSchema.put(ID_KEY, nodePostName);
292 postSchema.set(PROPERTIES_KEY, properties);
293 models.set(nodePostName, postSchema);
296 private JsonNode createPropertiesForPost(final DataNodeContainer dataNodeContainer,
297 final SchemaContext schemaContext, final String parentName) {
298 final ObjectNode properties = JsonNodeFactory.instance.objectNode();
299 for (final DataSchemaNode childNode : dataNodeContainer.getChildNodes()) {
300 if (childNode instanceof ListSchemaNode || childNode instanceof ContainerSchemaNode) {
301 final ObjectNode items = JsonNodeFactory.instance.objectNode();
302 items.put(REF_KEY, parentName + "(config)" + childNode.getQName().getLocalName());
303 final ObjectNode property = JsonNodeFactory.instance.objectNode();
304 property.put(TYPE_KEY, childNode instanceof ListSchemaNode ? ARRAY_TYPE : OBJECT_TYPE);
305 property.set(ITEMS_KEY, items);
306 properties.set(childNode.getQName().getLocalName(), property);
307 } else if (childNode instanceof LeafSchemaNode) {
308 final ObjectNode property = processLeafNode((LeafSchemaNode) childNode, schemaContext);
309 properties.set(childNode.getQName().getLocalName(), property);
316 * Processes the nodes.
318 private ObjectNode processChildren(
319 final Iterable<DataSchemaNode> nodes, final String parentName, final ObjectNode models,
320 final boolean isConfig, final SchemaContext schemaContext) throws IOException {
321 final ObjectNode properties = JsonNodeFactory.instance.objectNode();
322 for (final DataSchemaNode node : nodes) {
323 if (node.isConfiguration() == isConfig) {
324 final String name = resolveNodesName(node, topLevelModule, schemaContext);
325 final ObjectNode property;
326 if (node instanceof LeafSchemaNode) {
327 property = processLeafNode((LeafSchemaNode) node, schemaContext);
329 } else if (node instanceof ListSchemaNode) {
330 property = processDataNodeContainer((ListSchemaNode) node, parentName, models, isConfig,
333 } else if (node instanceof LeafListSchemaNode) {
334 property = processLeafListNode((LeafListSchemaNode) node, schemaContext);
336 } else if (node instanceof ChoiceSchemaNode) {
337 if (((ChoiceSchemaNode) node).getCases().values().iterator().hasNext()) {
338 processChoiceNode(((ChoiceSchemaNode) node).getCases().values().iterator().next()
339 .getChildNodes(), parentName, models, schemaContext, isConfig, properties);
343 } else if (node instanceof AnyXmlSchemaNode) {
344 property = processAnyXMLNode((AnyXmlSchemaNode) node);
346 } else if (node instanceof ContainerSchemaNode) {
347 property = processDataNodeContainer((ContainerSchemaNode) node, parentName, models, isConfig,
351 throw new IllegalArgumentException("Unknown DataSchemaNode type: " + node.getClass());
353 putIfNonNull(property, DESCRIPTION_KEY, node.getDescription().orElse(null));
354 properties.set(topLevelModule.getName() + ":" + name, property);
360 private ObjectNode processLeafListNode(final LeafListSchemaNode listNode,
361 final SchemaContext schemaContext) {
362 final ObjectNode props = JsonNodeFactory.instance.objectNode();
363 props.put(TYPE_KEY, ARRAY_TYPE);
365 final ObjectNode itemsVal = JsonNodeFactory.instance.objectNode();
366 final Optional<ElementCountConstraint> optConstraint = listNode.getElementCountConstraint();
368 if (optConstraint.isPresent()) {
369 final Integer constraintMax = optConstraint.get().getMaxElements();
370 max = constraintMax == null ? 2 : constraintMax;
371 processElementCount(optConstraint.get(), props);
377 processTypeDef(listNode.getType(), listNode, itemsVal, schemaContext);
378 processTypeDef(listNode.getType(), listNode, itemsVal, schemaContext);
380 processTypeDef(listNode.getType(), listNode, itemsVal, schemaContext);
382 props.set(ITEMS_KEY, itemsVal);
387 private void processChoiceNode(
388 final Iterable<DataSchemaNode> nodes, final String moduleName, final ObjectNode models,
389 final SchemaContext schemaContext, final boolean isConfig, final ObjectNode properties)
391 for (final DataSchemaNode node : nodes) {
392 final String name = resolveNodesName(node, topLevelModule, schemaContext);
393 final ObjectNode property;
395 if (node instanceof LeafSchemaNode) {
396 property = processLeafNode((LeafSchemaNode) node, schemaContext);
398 } else if (node instanceof ListSchemaNode) {
399 property = processDataNodeContainer((ListSchemaNode) node, moduleName, models, isConfig,
402 } else if (node instanceof LeafListSchemaNode) {
403 property = processLeafListNode((LeafListSchemaNode) node, schemaContext);
405 } else if (node instanceof ChoiceSchemaNode) {
406 if (((ChoiceSchemaNode) node).getCases().values().iterator().hasNext()) {
407 processChoiceNode(((ChoiceSchemaNode) node).getCases().values().iterator().next().getChildNodes(),
408 moduleName, models, schemaContext, isConfig, properties);
412 } else if (node instanceof AnyXmlSchemaNode) {
413 property = processAnyXMLNode((AnyXmlSchemaNode) node);
415 } else if (node instanceof ContainerSchemaNode) {
416 property = processDataNodeContainer((ContainerSchemaNode) node, moduleName, models, isConfig,
420 throw new IllegalArgumentException("Unknown DataSchemaNode type: " + node.getClass());
423 putIfNonNull(property, DESCRIPTION_KEY, node.getDescription().orElse(null));
424 properties.set(name, property);
428 private static void processElementCount(final ElementCountConstraint constraint, final ObjectNode props) {
429 final Integer minElements = constraint.getMinElements();
430 if (minElements != null) {
431 props.put(MIN_ITEMS, minElements);
433 final Integer maxElements = constraint.getMaxElements();
434 if (maxElements != null) {
435 props.put(MAX_ITEMS, maxElements);
439 private static void processMandatory(final MandatoryAware node, final ObjectNode props) {
440 props.put(REQUIRED_KEY, node.isMandatory());
443 private ObjectNode processLeafNode(final LeafSchemaNode leafNode,
444 final SchemaContext schemaContext) {
445 final ObjectNode property = JsonNodeFactory.instance.objectNode();
447 final String leafDescription = leafNode.getDescription().orElse(null);
448 putIfNonNull(property, DESCRIPTION_KEY, leafDescription);
449 processMandatory(leafNode, property);
450 processTypeDef(leafNode.getType(), leafNode, property, schemaContext);
455 private static ObjectNode processAnyXMLNode(final AnyXmlSchemaNode leafNode) {
456 final ObjectNode property = JsonNodeFactory.instance.objectNode();
458 final String leafDescription = leafNode.getDescription().orElse(null);
459 putIfNonNull(property, DESCRIPTION_KEY, leafDescription);
461 processMandatory(leafNode, property);
462 final String localName = leafNode.getQName().getLocalName();
463 property.put(TYPE_KEY, "example of anyxml " + localName);
468 private String processTypeDef(final TypeDefinition<?> leafTypeDef, final DataSchemaNode node,
469 final ObjectNode property, final SchemaContext schemaContext) {
470 final String jsonType;
471 if (leafTypeDef.getDefaultValue() == null) {
472 if (leafTypeDef instanceof BinaryTypeDefinition) {
473 jsonType = processBinaryType(property);
475 } else if (leafTypeDef instanceof BitsTypeDefinition) {
476 jsonType = processBitsType((BitsTypeDefinition) leafTypeDef, property);
478 } else if (leafTypeDef instanceof EnumTypeDefinition) {
479 jsonType = processEnumType((EnumTypeDefinition) leafTypeDef, property);
481 } else if (leafTypeDef instanceof IdentityrefTypeDefinition) {
482 final String name = topLevelModule.getName();
483 jsonType = name + ":" + ((IdentityrefTypeDefinition) leafTypeDef).getIdentities().iterator().next()
484 .getQName().getLocalName();
486 } else if (leafTypeDef instanceof StringTypeDefinition) {
487 jsonType = processStringType(leafTypeDef, property, node.getQName().getLocalName());
489 } else if (leafTypeDef instanceof UnionTypeDefinition) {
490 jsonType = processUnionType((UnionTypeDefinition) leafTypeDef, property, schemaContext, node);
492 } else if (leafTypeDef instanceof EmptyTypeDefinition) {
493 jsonType = UNIQUE_EMPTY_IDENTIFIER;
495 } else if (leafTypeDef instanceof LeafrefTypeDefinition) {
496 return processLeafRef(node, property, schemaContext, leafTypeDef);
498 } else if (leafTypeDef instanceof BooleanTypeDefinition) {
501 } else if (leafTypeDef instanceof RangeRestrictedTypeDefinition) {
502 final Number maybeLower = ((RangeRestrictedTypeDefinition<?, ?>) leafTypeDef).getRangeConstraint()
503 .map(RangeConstraint::getAllowedRanges).map(RangeSet::span).map(Range::lowerEndpoint)
505 jsonType = String.valueOf(maybeLower);
508 jsonType = OBJECT_TYPE;
512 jsonType = String.valueOf(leafTypeDef.getDefaultValue());
514 putIfNonNull(property, TYPE_KEY, jsonType);
518 private String processLeafRef(final DataSchemaNode node, final ObjectNode property,
519 final SchemaContext schemaContext, final TypeDefinition<?> leafTypeDef) {
520 RevisionAwareXPath xpath = ((LeafrefTypeDefinition) leafTypeDef).getPathStatement();
521 final SchemaNode schemaNode;
523 final String xPathString = STRIP_PATTERN.matcher(xpath.toString()).replaceAll("");
524 xpath = new RevisionAwareXPathImpl(xPathString, xpath.isAbsolute());
527 if (xpath.isAbsolute()) {
528 module = findModule(schemaContext, leafTypeDef.getQName());
529 schemaNode = SchemaContextUtil.findDataSchemaNode(schemaContext, module, xpath);
531 module = findModule(schemaContext, node.getQName());
532 schemaNode = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(schemaContext, module, node, xpath);
535 return processTypeDef(((TypedDataSchemaNode) schemaNode).getType(), (DataSchemaNode) schemaNode,
536 property, schemaContext);
539 private static Module findModule(final SchemaContext schemaContext, final QName qualifiedName) {
540 return schemaContext.findModule(qualifiedName.getNamespace(), qualifiedName.getRevision()).orElse(null);
543 private static String processBinaryType(final ObjectNode property) {
544 final ObjectNode media = JsonNodeFactory.instance.objectNode();
545 media.put(BINARY_ENCODING_KEY, BASE_64);
546 property.set(MEDIA_KEY, media);
550 private static String processEnumType(final EnumTypeDefinition enumLeafType,
551 final ObjectNode property) {
552 final List<EnumPair> enumPairs = enumLeafType.getValues();
553 ArrayNode enumNames = new ArrayNode(JsonNodeFactory.instance);
554 for (final EnumPair enumPair : enumPairs) {
555 enumNames.add(new TextNode(enumPair.getName()));
558 property.set(ENUM, enumNames);
559 return enumLeafType.getValues().iterator().next().getName();
562 private static String processBitsType(final BitsTypeDefinition bitsType,
563 final ObjectNode property) {
564 property.put(MIN_ITEMS, 0);
565 property.put(UNIQUE_ITEMS_KEY, true);
566 ArrayNode enumNames = new ArrayNode(JsonNodeFactory.instance);
567 final List<Bit> bits = bitsType.getBits();
568 for (final Bit bit : bits) {
569 enumNames.add(new TextNode(bit.getName()));
571 property.set(ENUM, enumNames);
573 return enumNames.iterator().next() + " " + enumNames.get(enumNames.size() - 1);
576 private static String processStringType(final TypeDefinition<?> stringType,
577 final ObjectNode property, final String nodeName) {
578 StringTypeDefinition type = (StringTypeDefinition) stringType;
579 Optional<LengthConstraint> lengthConstraints = ((StringTypeDefinition) stringType).getLengthConstraint();
580 while (!lengthConstraints.isPresent() && type.getBaseType() != null) {
581 type = type.getBaseType();
582 lengthConstraints = type.getLengthConstraint();
585 if (lengthConstraints.isPresent()) {
586 final Range<Integer> range = lengthConstraints.get().getAllowedRanges().span();
587 putIfNonNull(property, MIN_LENGTH_KEY, range.lowerEndpoint());
588 putIfNonNull(property, MAX_LENGTH_KEY, range.upperEndpoint());
591 if (type.getPatternConstraints().iterator().hasNext()) {
592 final PatternConstraint pattern = type.getPatternConstraints().iterator().next();
593 String regex = pattern.getJavaPatternString();
594 regex = regex.substring(1, regex.length() - 1);
595 final Generex generex = new Generex(regex);
596 return generex.random();
598 return "Some " + nodeName;
602 private String processUnionType(final UnionTypeDefinition unionType, final ObjectNode property,
603 final SchemaContext schemaContext, final DataSchemaNode node) {
604 final ArrayNode unionNames = new ArrayNode(JsonNodeFactory.instance);
605 for (final TypeDefinition<?> typeDef : unionType.getTypes()) {
606 unionNames.add(processTypeDef(typeDef, node, property, schemaContext));
608 property.set(ENUM, unionNames);
609 return unionNames.iterator().next().asText();
613 * Helper method to generate a pre-filled JSON schema object.
615 private static ObjectNode getSchemaTemplate() {
616 final ObjectNode schemaJSON = JsonNodeFactory.instance.objectNode();
617 schemaJSON.put(SCHEMA_KEY, SCHEMA_URL);
622 private static void putIfNonNull(final ObjectNode property, final String key, final Number number) {
623 if (key != null && number != null) {
624 if (number instanceof Double) {
625 property.put(key, (Double) number);
626 } else if (number instanceof Float) {
627 property.put(key, (Float) number);
628 } else if (number instanceof Integer) {
629 property.put(key, (Integer) number);
630 } else if (number instanceof Short) {
631 property.put(key, (Short) number);
632 } else if (number instanceof Long) {
633 property.put(key, (Long) number);
638 private static void putIfNonNull(final ObjectNode property, final String key, final String value) {
639 if (key != null && value != null) {
640 property.put(key, value);