3b503ebba35c016382d390ad695c4bda7c553ef2
[controller.git] / opendaylight / md-sal / sal-rest-docgen / src / main / java / org / opendaylight / controller / sal / rest / doc / impl / ModelGenerator.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. 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.controller.sal.rest.doc.impl;
9
10 import static org.opendaylight.controller.sal.rest.doc.impl.BaseYangSwaggerGenerator.MODULE_NAME_SUFFIX;
11 import static org.opendaylight.controller.sal.rest.doc.model.builder.OperationBuilder.Post.METHOD_NAME;
12 import static org.opendaylight.controller.sal.rest.doc.util.RestDocgenUtil.resolveNodesName;
13
14 import com.google.common.base.Preconditions;
15 import java.io.IOException;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.HashMap;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22 import org.apache.commons.lang3.BooleanUtils;
23 import org.json.JSONArray;
24 import org.json.JSONException;
25 import org.json.JSONObject;
26 import org.opendaylight.controller.sal.rest.doc.model.builder.OperationBuilder;
27 import org.opendaylight.yangtools.yang.common.QName;
28 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
30 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
31 import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
32 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
34 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
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.Module;
40 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
41 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
42 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
44 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
45 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
46 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition.Bit;
47 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
48 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
49 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
50 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
51 import org.opendaylight.yangtools.yang.model.util.BooleanType;
52 import org.opendaylight.yangtools.yang.model.util.Decimal64;
53 import org.opendaylight.yangtools.yang.model.util.EnumerationType;
54 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
55 import org.opendaylight.yangtools.yang.model.util.Int16;
56 import org.opendaylight.yangtools.yang.model.util.Int32;
57 import org.opendaylight.yangtools.yang.model.util.Int64;
58 import org.opendaylight.yangtools.yang.model.util.Int8;
59 import org.opendaylight.yangtools.yang.model.util.StringType;
60 import org.opendaylight.yangtools.yang.model.util.Uint16;
61 import org.opendaylight.yangtools.yang.model.util.Uint32;
62 import org.opendaylight.yangtools.yang.model.util.Uint64;
63 import org.opendaylight.yangtools.yang.model.util.Uint8;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
66
67 /**
68  * Generates JSON Schema for data defined in Yang
69  */
70 public class ModelGenerator {
71
72     private static Logger _logger = LoggerFactory.getLogger(ModelGenerator.class);
73
74     private static final String BASE_64 = "base64";
75     private static final String BINARY_ENCODING_KEY = "binaryEncoding";
76     private static final String MEDIA_KEY = "media";
77     private static final String ONE_OF_KEY = "oneOf";
78     private static final String UNIQUE_ITEMS_KEY = "uniqueItems";
79     private static final String MAX_ITEMS = "maxItems";
80     private static final String MIN_ITEMS = "minItems";
81     private static final String SCHEMA_URL = "http://json-schema.org/draft-04/schema";
82     private static final String SCHEMA_KEY = "$schema";
83     private static final String MAX_LENGTH_KEY = "maxLength";
84     private static final String MIN_LENGTH_KEY = "minLength";
85     private static final String REQUIRED_KEY = "required";
86     private static final String REF_KEY = "$ref";
87     private static final String ITEMS_KEY = "items";
88     private static final String TYPE_KEY = "type";
89     private static final String PROPERTIES_KEY = "properties";
90     private static final String DESCRIPTION_KEY = "description";
91     private static final String OBJECT_TYPE = "object";
92     private static final String ARRAY_TYPE = "array";
93     private static final String ENUM = "enum";
94     private static final String INTEGER = "integer";
95     private static final String NUMBER = "number";
96     private static final String BOOLEAN = "boolean";
97     private static final String STRING = "string";
98     private static final String ID_KEY = "id";
99     private static final String SUB_TYPES_KEY = "subTypes";
100
101     private static final Map<Class<? extends TypeDefinition<?>>, String> YANG_TYPE_TO_JSON_TYPE_MAPPING;
102
103     static {
104         Map<Class<? extends TypeDefinition<?>>, String> tempMap1 = new HashMap<Class<? extends TypeDefinition<?>>, String>(
105                 10);
106         tempMap1.put(StringType.class, STRING);
107         tempMap1.put(BooleanType.class, BOOLEAN);
108         tempMap1.put(Int8.class, INTEGER);
109         tempMap1.put(Int16.class, INTEGER);
110         tempMap1.put(Int32.class, INTEGER);
111         tempMap1.put(Int64.class, INTEGER);
112         tempMap1.put(Uint16.class, INTEGER);
113         tempMap1.put(Uint32.class, INTEGER);
114         tempMap1.put(Uint64.class, INTEGER);
115         tempMap1.put(Uint8.class, INTEGER);
116         tempMap1.put(Decimal64.class, NUMBER);
117         tempMap1.put(EnumerationType.class, ENUM);
118         // TODO: Binary type
119
120         YANG_TYPE_TO_JSON_TYPE_MAPPING = Collections.unmodifiableMap(tempMap1);
121     }
122
123     private Module topLevelModule;
124
125     public ModelGenerator() {
126     }
127
128     public JSONObject convertToJsonSchema(Module module, SchemaContext schemaContext) throws IOException, JSONException {
129         JSONObject models = new JSONObject();
130         topLevelModule = module;
131         processModules(module, models);
132         processContainersAndLists(module, models, schemaContext);
133         processRPCs(module, models, schemaContext);
134         processIdentities(module, models);
135         return models;
136     }
137
138     private void processModules(Module module, JSONObject models) throws JSONException {
139         createConcreteModelForPost(models, module.getName()+MODULE_NAME_SUFFIX, createPropertiesForPost(module));
140     }
141
142     private void processContainersAndLists(Module module, JSONObject models, SchemaContext schemaContext)
143             throws IOException, JSONException {
144
145         String moduleName = module.getName();
146
147         for (DataSchemaNode childNode : module.getChildNodes()) {
148             // For every container and list in the module
149             if (childNode instanceof ContainerSchemaNode || childNode instanceof ListSchemaNode) {
150                 processDataNodeContainer((DataNodeContainer) childNode, moduleName, models, true, schemaContext);
151                 processDataNodeContainer((DataNodeContainer) childNode, moduleName, models, false, schemaContext);
152             }
153         }
154
155     }
156
157     /**
158      * Process the RPCs for a Module Spits out a file each of the name <rpcName>-input.json and <rpcName>-output.json
159      * for each RPC that contains input & output elements
160      *
161      * @param module
162      * @throws JSONException
163      * @throws IOException
164      */
165     private void processRPCs(Module module, JSONObject models, SchemaContext schemaContext) throws JSONException,
166             IOException {
167
168         Set<RpcDefinition> rpcs = module.getRpcs();
169         String moduleName = module.getName();
170         for (RpcDefinition rpc : rpcs) {
171
172             ContainerSchemaNode input = rpc.getInput();
173             if (input != null) {
174                 JSONObject inputJSON = processDataNodeContainer(input, moduleName, models, schemaContext);
175                 String filename = "(" + rpc.getQName().getLocalName() + ")input";
176                 inputJSON.put("id", filename);
177                 // writeToFile(filename, inputJSON.toString(2), moduleName);
178                 models.put(filename, inputJSON);
179             }
180
181             ContainerSchemaNode output = rpc.getOutput();
182             if (output != null) {
183                 JSONObject outputJSON = processDataNodeContainer(output, moduleName, models, schemaContext);
184                 String filename = "(" + rpc.getQName().getLocalName() + ")output";
185                 outputJSON.put("id", filename);
186                 models.put(filename, outputJSON);
187             }
188         }
189     }
190
191     /**
192      * Processes the 'identity' statement in a yang model and maps it to a 'model' in the Swagger JSON spec.
193      *
194      * @param module
195      *            The module from which the identity stmt will be processed
196      * @param models
197      *            The JSONObject in which the parsed identity will be put as a 'model' obj
198      * @throws JSONException
199      */
200     private void processIdentities(Module module, JSONObject models) throws JSONException {
201
202         String moduleName = module.getName();
203         Set<IdentitySchemaNode> idNodes = module.getIdentities();
204         _logger.debug("Processing Identities for module {} . Found {} identity statements", moduleName, idNodes.size());
205
206         for (IdentitySchemaNode idNode : idNodes) {
207             JSONObject identityObj = new JSONObject();
208             String identityName = idNode.getQName().getLocalName();
209             _logger.debug("Processing Identity: {}", identityName);
210
211             identityObj.put(ID_KEY, identityName);
212             identityObj.put(DESCRIPTION_KEY, idNode.getDescription());
213
214             JSONObject props = new JSONObject();
215             IdentitySchemaNode baseId = idNode.getBaseIdentity();
216
217             if (baseId == null) {
218                 /**
219                  * This is a base identity. So lets see if it has sub types. If it does, then add them to the model
220                  * definition.
221                  */
222                 Set<IdentitySchemaNode> derivedIds = idNode.getDerivedIdentities();
223
224                 if (derivedIds != null) {
225                     JSONArray subTypes = new JSONArray();
226                     for (IdentitySchemaNode derivedId : derivedIds) {
227                         subTypes.put(derivedId.getQName().getLocalName());
228                     }
229                     identityObj.put(SUB_TYPES_KEY, subTypes);
230                 }
231             } else {
232                 /**
233                  * This is a derived entity. Add it's base type & move on.
234                  */
235                 props.put(TYPE_KEY, baseId.getQName().getLocalName());
236             }
237
238             // Add the properties. For a base type, this will be an empty object as required by the Swagger spec.
239             identityObj.put(PROPERTIES_KEY, props);
240             models.put(identityName, identityObj);
241         }
242     }
243
244     /**
245      * Processes the container and list nodes and populates the moduleJSON
246      *
247      * @param container
248      * @param moduleName
249      * @param isConfig
250      * @throws JSONException
251      * @throws IOException
252      */
253     private JSONObject processDataNodeContainer(DataNodeContainer dataNode, String moduleName, JSONObject models,
254             SchemaContext schemaContext) throws JSONException, IOException {
255         return processDataNodeContainer(dataNode, moduleName, models, (Boolean) null, schemaContext);
256     }
257
258     private JSONObject processDataNodeContainer(DataNodeContainer dataNode, String moduleName, JSONObject models,
259             Boolean isConfig, SchemaContext schemaContext) throws JSONException, IOException {
260         if (dataNode instanceof ListSchemaNode || dataNode instanceof ContainerSchemaNode) {
261             Preconditions.checkArgument(dataNode instanceof SchemaNode, "Data node should be also schema node");
262             Iterable<DataSchemaNode> containerChildren = dataNode.getChildNodes();
263             JSONObject properties = processChildren(containerChildren, ((SchemaNode) dataNode).getQName(), moduleName,
264                     models, isConfig, schemaContext);
265
266             String nodeName = (BooleanUtils.isNotFalse(isConfig) ? OperationBuilder.CONFIG
267                     : OperationBuilder.OPERATIONAL) + ((SchemaNode) dataNode).getQName().getLocalName();
268
269             JSONObject childSchema = getSchemaTemplate();
270             childSchema.put(TYPE_KEY, OBJECT_TYPE);
271             childSchema.put(PROPERTIES_KEY, properties);
272             childSchema.put("id", nodeName);
273             models.put(nodeName, childSchema);
274
275             if (BooleanUtils.isNotFalse(isConfig)) {
276                 createConcreteModelForPost(models, ((SchemaNode) dataNode).getQName().getLocalName(),
277                         createPropertiesForPost(dataNode));
278             }
279
280             JSONObject items = new JSONObject();
281             items.put(REF_KEY, nodeName);
282             JSONObject dataNodeProperties = new JSONObject();
283             dataNodeProperties.put(TYPE_KEY, dataNode instanceof ListSchemaNode ? ARRAY_TYPE : OBJECT_TYPE);
284             dataNodeProperties.put(ITEMS_KEY, items);
285
286             return dataNodeProperties;
287         }
288         return null;
289     }
290
291     private void createConcreteModelForPost(final JSONObject models, final String localName, final JSONObject properties)
292             throws JSONException {
293         String nodePostName = OperationBuilder.CONFIG + localName + METHOD_NAME;
294         JSONObject postSchema = getSchemaTemplate();
295         postSchema.put(TYPE_KEY, OBJECT_TYPE);
296         postSchema.put("id", nodePostName);
297         postSchema.put(PROPERTIES_KEY, properties);
298         models.put(nodePostName, postSchema);
299     }
300
301     private JSONObject createPropertiesForPost(final DataNodeContainer dataNodeContainer) throws JSONException {
302         JSONObject properties = new JSONObject();
303         for (DataSchemaNode childNode : dataNodeContainer.getChildNodes()) {
304             if (childNode instanceof ListSchemaNode || childNode instanceof ContainerSchemaNode) {
305                 JSONObject items = new JSONObject();
306                 items.put(REF_KEY, "(config)" + childNode.getQName().getLocalName());
307                 JSONObject property = new JSONObject();
308                 property.put(TYPE_KEY, childNode instanceof ListSchemaNode ? ARRAY_TYPE : OBJECT_TYPE);
309                 property.put(ITEMS_KEY, items);
310                 properties.put(childNode.getQName().getLocalName(), property);
311             } else if (childNode instanceof LeafSchemaNode){
312                 JSONObject property = processLeafNode((LeafSchemaNode)childNode);
313                 properties.put(childNode.getQName().getLocalName(), property);
314             }
315         }
316         return properties;
317     }
318
319     private JSONObject processChildren(Iterable<DataSchemaNode> nodes, QName parentQName, String moduleName,
320             JSONObject models, SchemaContext schemaContext) throws JSONException, IOException {
321         return processChildren(nodes, parentQName, moduleName, models, null, schemaContext);
322     }
323
324     /**
325      * Processes the nodes
326      *
327      * @param nodes
328      * @param parentQName
329      * @param moduleName
330      * @param isConfig
331      * @return
332      * @throws JSONException
333      * @throws IOException
334      */
335     private JSONObject processChildren(Iterable<DataSchemaNode> nodes, QName parentQName, String moduleName,
336             JSONObject models, Boolean isConfig, SchemaContext schemaContext) throws JSONException, IOException {
337
338         JSONObject properties = new JSONObject();
339
340         for (DataSchemaNode node : nodes) {
341             if (isConfig == null || node.isConfiguration() == isConfig) {
342
343                 String name = resolveNodesName(node, topLevelModule, schemaContext);
344                 JSONObject property = null;
345                 if (node instanceof LeafSchemaNode) {
346                     property = processLeafNode((LeafSchemaNode) node);
347                 } else if (node instanceof ListSchemaNode) {
348                     property = processDataNodeContainer((ListSchemaNode) node, moduleName, models, isConfig,
349                             schemaContext);
350
351                 } else if (node instanceof LeafListSchemaNode) {
352                     property = processLeafListNode((LeafListSchemaNode) node);
353
354                 } else if (node instanceof ChoiceNode) {
355                     property = processChoiceNode((ChoiceNode) node, moduleName, models, schemaContext);
356
357                 } else if (node instanceof AnyXmlSchemaNode) {
358                     property = processAnyXMLNode((AnyXmlSchemaNode) node);
359
360                 } else if (node instanceof ContainerSchemaNode) {
361                     property = processDataNodeContainer((ContainerSchemaNode) node, moduleName, models, isConfig,
362                             schemaContext);
363
364                 } else {
365                     throw new IllegalArgumentException("Unknown DataSchemaNode type: " + node.getClass());
366                 }
367
368                 property.putOpt(DESCRIPTION_KEY, node.getDescription());
369                 properties.put(name, property);
370             }
371         }
372         return properties;
373     }
374
375     /**
376      *
377      * @param listNode
378      * @throws JSONException
379      */
380     private JSONObject processLeafListNode(LeafListSchemaNode listNode) throws JSONException {
381         JSONObject props = new JSONObject();
382         props.put(TYPE_KEY, ARRAY_TYPE);
383
384         JSONObject itemsVal = new JSONObject();
385         processTypeDef(listNode.getType(), itemsVal);
386         props.put(ITEMS_KEY, itemsVal);
387
388         ConstraintDefinition constraints = listNode.getConstraints();
389         processConstraints(constraints, props);
390
391         return props;
392     }
393
394     /**
395      *
396      * @param choiceNode
397      * @param moduleName
398      * @throws JSONException
399      * @throws IOException
400      */
401     private JSONObject processChoiceNode(ChoiceNode choiceNode, String moduleName, JSONObject models,
402             SchemaContext schemaContext) throws JSONException, IOException {
403
404         Set<ChoiceCaseNode> cases = choiceNode.getCases();
405
406         JSONArray choiceProps = new JSONArray();
407         for (ChoiceCaseNode choiceCase : cases) {
408             String choiceName = choiceCase.getQName().getLocalName();
409             JSONObject choiceProp = processChildren(choiceCase.getChildNodes(), choiceCase.getQName(), moduleName,
410                     models, schemaContext);
411             JSONObject choiceObj = new JSONObject();
412             choiceObj.put(choiceName, choiceProp);
413             choiceObj.put(TYPE_KEY, OBJECT_TYPE);
414             choiceProps.put(choiceObj);
415         }
416
417         JSONObject oneOfProps = new JSONObject();
418         oneOfProps.put(ONE_OF_KEY, choiceProps);
419         oneOfProps.put(TYPE_KEY, OBJECT_TYPE);
420
421         return oneOfProps;
422     }
423
424     /**
425      *
426      * @param constraints
427      * @param props
428      * @throws JSONException
429      */
430     private void processConstraints(ConstraintDefinition constraints, JSONObject props) throws JSONException {
431         boolean isMandatory = constraints.isMandatory();
432         props.put(REQUIRED_KEY, isMandatory);
433
434         Integer minElements = constraints.getMinElements();
435         Integer maxElements = constraints.getMaxElements();
436         if (minElements != null) {
437             props.put(MIN_ITEMS, minElements);
438         }
439         if (maxElements != null) {
440             props.put(MAX_ITEMS, maxElements);
441         }
442     }
443
444     /**
445      *
446      * @param leafNode
447      * @return
448      * @throws JSONException
449      */
450     private JSONObject processLeafNode(LeafSchemaNode leafNode) throws JSONException {
451         JSONObject property = new JSONObject();
452
453         String leafDescription = leafNode.getDescription();
454         property.put(DESCRIPTION_KEY, leafDescription);
455
456         processConstraints(leafNode.getConstraints(), property);
457         processTypeDef(leafNode.getType(), property);
458
459         return property;
460     }
461
462     /**
463      *
464      * @param leafNode
465      * @return
466      * @throws JSONException
467      */
468     private JSONObject processAnyXMLNode(AnyXmlSchemaNode leafNode) throws JSONException {
469         JSONObject property = new JSONObject();
470
471         String leafDescription = leafNode.getDescription();
472         property.put(DESCRIPTION_KEY, leafDescription);
473
474         processConstraints(leafNode.getConstraints(), property);
475
476         return property;
477     }
478
479     /**
480      * @param property
481      * @throws JSONException
482      */
483     private void processTypeDef(TypeDefinition<?> leafTypeDef, JSONObject property) throws JSONException {
484
485         if (leafTypeDef instanceof ExtendedType) {
486             processExtendedType(leafTypeDef, property);
487         } else if (leafTypeDef instanceof EnumerationType) {
488             processEnumType((EnumerationType) leafTypeDef, property);
489
490         } else if (leafTypeDef instanceof BitsTypeDefinition) {
491             processBitsType((BitsTypeDefinition) leafTypeDef, property);
492
493         } else if (leafTypeDef instanceof UnionTypeDefinition) {
494             processUnionType((UnionTypeDefinition) leafTypeDef, property);
495
496         } else if (leafTypeDef instanceof IdentityrefTypeDefinition) {
497             property.putOpt(TYPE_KEY, ((IdentityrefTypeDefinition) leafTypeDef).getIdentity().getQName().getLocalName());
498         } else if (leafTypeDef instanceof BinaryTypeDefinition) {
499             processBinaryType((BinaryTypeDefinition) leafTypeDef, property);
500         } else {
501             // System.out.println("In else: " + leafTypeDef.getClass());
502             String jsonType = YANG_TYPE_TO_JSON_TYPE_MAPPING.get(leafTypeDef.getClass());
503             if (jsonType == null) {
504                 jsonType = "object";
505             }
506             property.putOpt(TYPE_KEY, jsonType);
507         }
508     }
509
510     /**
511      *
512      * @param leafTypeDef
513      * @param property
514      * @throws JSONException
515      */
516     private void processExtendedType(TypeDefinition<?> leafTypeDef, JSONObject property) throws JSONException {
517         Object leafBaseType = leafTypeDef.getBaseType();
518         if (leafBaseType instanceof ExtendedType) {
519             // recursively process an extended type until we hit a base type
520             processExtendedType((TypeDefinition<?>) leafBaseType, property);
521         } else {
522             List<LengthConstraint> lengthConstraints = ((ExtendedType) leafTypeDef).getLengthConstraints();
523             for (LengthConstraint lengthConstraint : lengthConstraints) {
524                 Number min = lengthConstraint.getMin();
525                 Number max = lengthConstraint.getMax();
526                 property.putOpt(MIN_LENGTH_KEY, min);
527                 property.putOpt(MAX_LENGTH_KEY, max);
528             }
529             String jsonType = YANG_TYPE_TO_JSON_TYPE_MAPPING.get(leafBaseType.getClass());
530             property.putOpt(TYPE_KEY, jsonType);
531         }
532
533     }
534
535     /*
536    *
537    */
538     private void processBinaryType(BinaryTypeDefinition binaryType, JSONObject property) throws JSONException {
539         property.put(TYPE_KEY, STRING);
540         JSONObject media = new JSONObject();
541         media.put(BINARY_ENCODING_KEY, BASE_64);
542         property.put(MEDIA_KEY, media);
543     }
544
545     /**
546      *
547      * @param enumLeafType
548      * @param property
549      * @throws JSONException
550      */
551     private void processEnumType(EnumerationType enumLeafType, JSONObject property) throws JSONException {
552         List<EnumPair> enumPairs = enumLeafType.getValues();
553         List<String> enumNames = new ArrayList<String>();
554         for (EnumPair enumPair : enumPairs) {
555             enumNames.add(enumPair.getName());
556         }
557         property.putOpt(ENUM, new JSONArray(enumNames));
558     }
559
560     /**
561      *
562      * @param bitsType
563      * @param property
564      * @throws JSONException
565      */
566     private void processBitsType(BitsTypeDefinition bitsType, JSONObject property) throws JSONException {
567         property.put(TYPE_KEY, ARRAY_TYPE);
568         property.put(MIN_ITEMS, 0);
569         property.put(UNIQUE_ITEMS_KEY, true);
570         JSONArray enumValues = new JSONArray();
571
572         List<Bit> bits = bitsType.getBits();
573         for (Bit bit : bits) {
574             enumValues.put(bit.getName());
575         }
576         JSONObject itemsValue = new JSONObject();
577         itemsValue.put(ENUM, enumValues);
578         property.put(ITEMS_KEY, itemsValue);
579     }
580
581     /**
582      *
583      * @param unionType
584      * @param property
585      * @throws JSONException
586      */
587     private void processUnionType(UnionTypeDefinition unionType, JSONObject property) throws JSONException {
588
589         StringBuilder type = new StringBuilder();
590         for (TypeDefinition<?> typeDef : unionType.getTypes()) {
591             if (type.length() > 0) {
592                 type.append(" or ");
593             }
594             type.append(YANG_TYPE_TO_JSON_TYPE_MAPPING.get(typeDef.getClass()));
595         }
596
597         property.put(TYPE_KEY, type);
598     }
599
600     /**
601      * Helper method to generate a pre-filled JSON schema object.
602      *
603      * @return
604      * @throws JSONException
605      */
606     private JSONObject getSchemaTemplate() throws JSONException {
607         JSONObject schemaJSON = new JSONObject();
608         schemaJSON.put(SCHEMA_KEY, SCHEMA_URL);
609
610         return schemaJSON;
611     }
612
613 }