X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=restconf%2Fsal-rest-docgen%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Fsal%2Frest%2Fdoc%2Fimpl%2FDefinitionGenerator.java;h=c6ae4a2697008707967d9646791c4ab8d4880c3c;hb=c5b2232060e8e14ae1213a8c017c50a5eff8681f;hp=56c2638edeada5a227ab80486dea29c6e3c1baea;hpb=b1b4d4276706e1ec4564af5fb9075301ffd7de17;p=netconf.git diff --git a/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/DefinitionGenerator.java b/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/DefinitionGenerator.java index 56c2638ede..c6ae4a2697 100644 --- a/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/DefinitionGenerator.java +++ b/restconf/sal-rest-docgen/src/main/java/org/opendaylight/netconf/sal/rest/doc/impl/DefinitionGenerator.java @@ -32,8 +32,10 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.regex.Pattern; import java.util.stream.Collectors; import org.opendaylight.netconf.sal.rest.doc.impl.ApiDocServiceImpl.OAversion; +import org.opendaylight.yangtools.yang.common.Decimal64; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.model.api.ActionDefinition; import org.opendaylight.yangtools.yang.model.api.ActionNodeContainer; @@ -121,6 +123,9 @@ public class DefinitionGenerator { private static final String INT32_FORMAT = "int32"; private static final String INT64_FORMAT = "int64"; private static final String BOOLEAN_TYPE = "boolean"; + // Special characters used in automaton inside Generex. + // See https://www.brics.dk/automaton/doc/dk/brics/automaton/RegExp.html + private static final Pattern AUTOMATON_SPECIAL_CHARACTERS = Pattern.compile("[@&\"<>#~]"); private Module topLevelModule; @@ -139,9 +144,8 @@ public class DefinitionGenerator { public ObjectNode convertToJsonSchema(final Module module, final EffectiveModelContext schemaContext, - final ObjectNode definitions, final DefinitionNames definitionNames, - final OAversion oaversion, final boolean isForSingleModule) - throws IOException { + final ObjectNode definitions, final DefinitionNames definitionNames, final OAversion oaversion, + final boolean isForSingleModule) throws IOException { topLevelModule = module; processIdentities(module, definitions, definitionNames, schemaContext); @@ -156,8 +160,7 @@ public class DefinitionGenerator { } public ObjectNode convertToJsonSchema(final Module module, final EffectiveModelContext schemaContext, - final DefinitionNames definitionNames, final OAversion oaversion, - final boolean isForSingleModule) + final DefinitionNames definitionNames, final OAversion oaversion, final boolean isForSingleModule) throws IOException { final ObjectNode definitions = JsonNodeFactory.instance.objectNode(); if (isForSingleModule) { @@ -167,7 +170,7 @@ public class DefinitionGenerator { } private void processModule(final Module module, final ObjectNode definitions, final DefinitionNames definitionNames, - final EffectiveModelContext schemaContext, final OAversion oaversion) { + final EffectiveModelContext schemaContext, final OAversion oaversion) { final ObjectNode definition = JsonNodeFactory.instance.objectNode(); final ObjectNode properties = JsonNodeFactory.instance.objectNode(); final ArrayNode required = JsonNodeFactory.instance.arrayNode(); @@ -204,15 +207,13 @@ public class DefinitionGenerator { //add module name prefix to property name, when ServiceNow can process colons properties.set(localName, childNodeProperties); } - } else { - if (node instanceof LeafSchemaNode) { - /* - Add module name prefix to property name, when ServiceNow can process colons(second parameter - of processLeafNode). - */ - processLeafNode((LeafSchemaNode) node, localName, properties, required, stack, - definitions, definitionNames, oaversion); - } + } else if (node instanceof LeafSchemaNode) { + /* + Add module name prefix to property name, when ServiceNow can process colons(second parameter + of processLeafNode). + */ + processLeafNode((LeafSchemaNode) node, localName, properties, required, stack, + definitions, definitionNames, oaversion); } } stack.exit(); @@ -227,8 +228,8 @@ public class DefinitionGenerator { } private void processContainersAndLists(final Module module, final ObjectNode definitions, - final DefinitionNames definitionNames, final EffectiveModelContext schemaContext, final OAversion oaversion) - throws IOException { + final DefinitionNames definitionNames, final EffectiveModelContext schemaContext, + final OAversion oaversion) throws IOException { final String moduleName = module.getName(); final SchemaInferenceStack stack = SchemaInferenceStack.of(schemaContext); for (final DataSchemaNode childNode : module.getChildNodes()) { @@ -248,16 +249,17 @@ public class DefinitionGenerator { } private void processActionNodeContainer(final DataSchemaNode childNode, final String moduleName, - final ObjectNode definitions, final DefinitionNames definitionNames, - final SchemaInferenceStack stack, final OAversion oaversion) - throws IOException { + final ObjectNode definitions, final DefinitionNames definitionNames, final SchemaInferenceStack stack, + final OAversion oaversion) throws IOException { for (final ActionDefinition actionDef : ((ActionNodeContainer) childNode).getActions()) { + stack.enterSchemaTree(actionDef.getQName()); processOperations(actionDef, moduleName, definitions, definitionNames, stack, oaversion); + stack.exit(); } } private void processRPCs(final Module module, final ObjectNode definitions, final DefinitionNames definitionNames, - final EffectiveModelContext schemaContext, final OAversion oaversion) throws IOException { + final EffectiveModelContext schemaContext, final OAversion oaversion) throws IOException { final String moduleName = module.getName(); final SchemaInferenceStack stack = SchemaInferenceStack.of(schemaContext); for (final RpcDefinition rpcDefinition : module.getRpcs()) { @@ -269,8 +271,7 @@ public class DefinitionGenerator { private void processOperations(final OperationDefinition operationDef, final String parentName, final ObjectNode definitions, final DefinitionNames definitionNames, - final SchemaInferenceStack stack, final OAversion oaversion) - throws IOException { + final SchemaInferenceStack stack, final OAversion oaversion) throws IOException { final String operationName = operationDef.getQName().getLocalName(); processOperationInputOutput(operationDef.getInput(), operationName, parentName, true, definitions, definitionNames, stack, oaversion); @@ -279,9 +280,8 @@ public class DefinitionGenerator { } private void processOperationInputOutput(final ContainerLike container, final String operationName, - final String parentName, final boolean isInput, - final ObjectNode definitions, final DefinitionNames definitionNames, - final SchemaInferenceStack stack, final OAversion oaversion) + final String parentName, final boolean isInput, final ObjectNode definitions, + final DefinitionNames definitionNames, final SchemaInferenceStack stack, final OAversion oaversion) throws IOException { stack.enterSchemaTree(container.getQName()); if (!container.getChildNodes().isEmpty()) { @@ -349,7 +349,7 @@ public class DefinitionGenerator { * @param definitionNames Store for definition names */ private static void processIdentities(final Module module, final ObjectNode definitions, - final DefinitionNames definitionNames, final EffectiveModelContext context) { + final DefinitionNames definitionNames, final EffectiveModelContext context) { final String moduleName = module.getName(); final Collection idNodes = module.getIdentities(); LOG.debug("Processing Identities for module {} . Found {} identity statements", moduleName, idNodes.size()); @@ -364,7 +364,7 @@ public class DefinitionGenerator { } private static void populateEnumWithDerived(final Collection derivedIds, - final ArrayNode enumPayload, final EffectiveModelContext context) { + final ArrayNode enumPayload, final EffectiveModelContext context) { for (final IdentitySchemaNode derivedId : derivedIds) { enumPayload.add(derivedId.getQName().getLocalName()); populateEnumWithDerived(context.getDerivedIdentities(derivedId), enumPayload, context); @@ -372,9 +372,8 @@ public class DefinitionGenerator { } private ObjectNode processDataNodeContainer(final DataNodeContainer dataNode, final String parentName, - final ObjectNode definitions, final DefinitionNames definitionNames, - final boolean isConfig, final SchemaInferenceStack stack, - final OAversion oaversion) throws IOException { + final ObjectNode definitions, final DefinitionNames definitionNames, final boolean isConfig, + final SchemaInferenceStack stack, final OAversion oaversion) throws IOException { if (dataNode instanceof ListSchemaNode || dataNode instanceof ContainerSchemaNode) { final Collection containerChildren = dataNode.getChildNodes(); final SchemaNode schemaNode = (SchemaNode) dataNode; @@ -468,67 +467,80 @@ public class DefinitionGenerator { /** * Processes the nodes. */ - private ObjectNode processChildren( - final ObjectNode parentNode, final Collection nodes, final String parentName, - final ObjectNode definitions, final DefinitionNames definitionNames, final boolean isConfig, - final SchemaInferenceStack stack, final OAversion oaversion) throws IOException { + private ObjectNode processChildren(final ObjectNode parentNode, final Collection nodes, + final String parentName, final ObjectNode definitions, final DefinitionNames definitionNames, + final boolean isConfig, final SchemaInferenceStack stack, final OAversion oaversion) throws IOException { final ObjectNode properties = JsonNodeFactory.instance.objectNode(); final ArrayNode required = JsonNodeFactory.instance.arrayNode(); for (final DataSchemaNode node : nodes) { - stack.enterSchemaTree(node.getQName()); if (!isConfig || node.isConfiguration()) { - /* - Add module name prefix to property name, when needed, when ServiceNow can process colons, - use RestDocGenUtil#resolveNodesName for creating property name - */ - final String propertyName = node.getQName().getLocalName(); - final ObjectNode property; - if (node instanceof LeafSchemaNode) { - processLeafNode((LeafSchemaNode) node, propertyName, properties, - required, stack, definitions, definitionNames, oaversion); - } else if (node instanceof AnyxmlSchemaNode) { - processAnyXMLNode((AnyxmlSchemaNode) node, propertyName, properties, - required); - } else if (node instanceof AnydataSchemaNode) { - processAnydataNode((AnydataSchemaNode) node, propertyName, properties, required); - } else { - if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) { - property = processDataNodeContainer((DataNodeContainer) node, parentName, definitions, - definitionNames, isConfig, stack, oaversion); - if (!isConfig) { - processActionNodeContainer(node, parentName, definitions, definitionNames, stack, - oaversion); - } - } else if (node instanceof LeafListSchemaNode) { - property = processLeafListNode((LeafListSchemaNode) node, stack, definitions, - definitionNames, oaversion); - - } else if (node instanceof ChoiceSchemaNode) { - for (final CaseSchemaNode variant : ((ChoiceSchemaNode) node).getCases()) { - stack.enterSchemaTree(variant.getQName()); - processChoiceNode(variant.getChildNodes(), parentName, definitions, definitionNames, - isConfig, stack, properties, oaversion); - stack.exit(); - } - stack.exit(); - // FIXME dangerous statement here! Try to rework without continue. - continue; - } else { - throw new IllegalArgumentException("Unknown DataSchemaNode type: " + node.getClass()); - } - properties.set(propertyName, property); - } + processChildNode(node, parentName, definitions, definitionNames, isConfig, stack, properties, + oaversion); } - stack.exit(); } parentNode.set(PROPERTIES_KEY, properties); setRequiredIfNotEmpty(parentNode, required); return properties; } + private void processChildNode(final DataSchemaNode node, final String parentName, final ObjectNode definitions, + final DefinitionNames definitionNames, final boolean isConfig, final SchemaInferenceStack stack, + final ObjectNode properties, final OAversion oaversion) throws IOException { + + stack.enterSchemaTree(node.getQName()); + + /* + Add module name prefix to property name, when needed, when ServiceNow can process colons, + use RestDocGenUtil#resolveNodesName for creating property name + */ + final String name = node.getQName().getLocalName(); + + if (node instanceof LeafSchemaNode leaf) { + processLeafNode(leaf, name, properties, JsonNodeFactory.instance.arrayNode(), stack, definitions, + definitionNames, oaversion); + + } else if (node instanceof AnyxmlSchemaNode anyxml) { + processAnyXMLNode(anyxml, name, properties, JsonNodeFactory.instance.arrayNode()); + + } else if (node instanceof AnydataSchemaNode anydata) { + processAnydataNode(anydata, name, properties, JsonNodeFactory.instance.arrayNode()); + + } else { + + final ObjectNode property; + if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) { + property = processDataNodeContainer((DataNodeContainer) node, parentName, definitions, + definitionNames, isConfig, stack, oaversion); + if (!isConfig) { + processActionNodeContainer(node, parentName, definitions, definitionNames, stack, oaversion); + } + } else if (node instanceof LeafListSchemaNode leafList) { + property = processLeafListNode(leafList, stack, definitions, definitionNames, oaversion); + + } else if (node instanceof ChoiceSchemaNode choice) { + for (final CaseSchemaNode variant : choice.getCases()) { + stack.enterSchemaTree(variant.getQName()); + for (final DataSchemaNode childNode : variant.getChildNodes()) { + processChildNode(childNode, parentName, definitions, definitionNames, isConfig, stack, + properties, oaversion); + } + stack.exit(); + } + property = null; + + } else { + throw new IllegalArgumentException("Unknown DataSchemaNode type: " + node.getClass()); + } + if (property != null) { + properties.set(name, property); + } + } + + stack.exit(); + } + private ObjectNode processLeafListNode(final LeafListSchemaNode listNode, final SchemaInferenceStack stack, - final ObjectNode definitions, final DefinitionNames definitionNames, - final OAversion oaversion) { + final ObjectNode definitions, final DefinitionNames definitionNames, final OAversion oaversion) { final ObjectNode props = JsonNodeFactory.instance.objectNode(); props.put(TYPE_KEY, ARRAY_TYPE); @@ -544,60 +556,6 @@ public class DefinitionGenerator { return props; } - private void processChoiceNode( - final Iterable nodes, final String parentName, final ObjectNode definitions, - final DefinitionNames definitionNames, final boolean isConfig, - final SchemaInferenceStack stack, final ObjectNode properties, final OAversion oaversion) - throws IOException { - for (final DataSchemaNode node : nodes) { - stack.enterSchemaTree(node.getQName()); - /* - Add module name prefix to property name, when needed, when ServiceNow can process colons, - use RestDocGenUtil#resolveNodesName for creating property name - */ - final String name = node.getQName().getLocalName(); - final ObjectNode property; - - /* - Ignore mandatoriness(passing unreferenced arrayNode to process...Node), because choice produces multiple - properties - */ - if (node instanceof LeafSchemaNode) { - processLeafNode((LeafSchemaNode) node, name, properties, - JsonNodeFactory.instance.arrayNode(), stack, definitions, definitionNames, oaversion); - } else if (node instanceof AnyxmlSchemaNode) { - processAnyXMLNode((AnyxmlSchemaNode) node, name, properties, - JsonNodeFactory.instance.arrayNode()); - } else if (node instanceof AnydataSchemaNode) { - processAnydataNode((AnydataSchemaNode) node, name, properties, - JsonNodeFactory.instance.arrayNode()); - } else { - if (node instanceof ListSchemaNode || node instanceof ContainerSchemaNode) { - property = processDataNodeContainer((DataNodeContainer) node, parentName, definitions, - definitionNames, isConfig, stack, oaversion); - if (!isConfig) { - processActionNodeContainer(node, parentName, definitions, definitionNames, stack, - oaversion); - } - } else if (node instanceof LeafListSchemaNode) { - property = processLeafListNode((LeafListSchemaNode) node, stack, definitions, - definitionNames, oaversion); - - } else if (node instanceof ChoiceSchemaNode) { - for (final CaseSchemaNode variant : ((ChoiceSchemaNode) node).getCases()) { - processChoiceNode(variant.getChildNodes(), parentName, definitions, definitionNames, isConfig, - stack, properties, oaversion); - } - continue; - } else { - throw new IllegalArgumentException("Unknown DataSchemaNode type: " + node.getClass()); - } - properties.set(name, property); - } - stack.exit(); - } - } - private static void processElementCount(final Optional constraint, final ObjectNode props) { if (constraint.isPresent()) { final ElementCountConstraint constr = constraint.get(); @@ -619,9 +577,8 @@ public class DefinitionGenerator { } private ObjectNode processLeafNode(final LeafSchemaNode leafNode, final String jsonLeafName, - final ObjectNode properties, final ArrayNode required, - final SchemaInferenceStack stack, final ObjectNode definitions, - final DefinitionNames definitionNames, final OAversion oaversion) { + final ObjectNode properties, final ArrayNode required, final SchemaInferenceStack stack, + final ObjectNode definitions, final DefinitionNames definitionNames, final OAversion oaversion) { final ObjectNode property = JsonNodeFactory.instance.objectNode(); final String leafDescription = leafNode.getDescription().orElse(""); @@ -642,7 +599,7 @@ public class DefinitionGenerator { } private static ObjectNode processAnydataNode(final AnydataSchemaNode leafNode, final String name, - final ObjectNode properties, final ArrayNode required) { + final ObjectNode properties, final ArrayNode required) { final ObjectNode property = JsonNodeFactory.instance.objectNode(); final String leafDescription = leafNode.getDescription().orElse(""); @@ -659,7 +616,7 @@ public class DefinitionGenerator { } private static ObjectNode processAnyXMLNode(final AnyxmlSchemaNode leafNode, final String name, - final ObjectNode properties, final ArrayNode required) { + final ObjectNode properties, final ArrayNode required) { final ObjectNode property = JsonNodeFactory.instance.objectNode(); final String leafDescription = leafNode.getDescription().orElse(""); @@ -676,9 +633,8 @@ public class DefinitionGenerator { } private String processTypeDef(final TypeDefinition leafTypeDef, final DataSchemaNode node, - final ObjectNode property, final SchemaInferenceStack stack, - final ObjectNode definitions, final DefinitionNames definitionNames, - final OAversion oaversion) { + final ObjectNode property, final SchemaInferenceStack stack, final ObjectNode definitions, + final DefinitionNames definitionNames, final OAversion oaversion) { final String jsonType; if (leafTypeDef instanceof BinaryTypeDefinition) { jsonType = processBinaryType(property); @@ -718,8 +674,7 @@ public class DefinitionGenerator { putIfNonNull(property, TYPE_KEY, jsonType); if (leafTypeDef.getDefaultValue().isPresent()) { final Object defaultValue = leafTypeDef.getDefaultValue().get(); - if (defaultValue instanceof String) { - final String stringDefaultValue = (String) defaultValue; + if (defaultValue instanceof String stringDefaultValue) { if (leafTypeDef instanceof BooleanTypeDefinition) { setDefaultValue(property, Boolean.valueOf(stringDefaultValue)); } else if (leafTypeDef instanceof DecimalTypeDefinition @@ -750,7 +705,7 @@ public class DefinitionGenerator { } private static String processEnumType(final EnumTypeDefinition enumLeafType, - final ObjectNode property) { + final ObjectNode property) { final List enumPairs = enumLeafType.getValues(); final ArrayNode enumNames = new ArrayNode(JsonNodeFactory.instance); for (final EnumPair enumPair : enumPairs) { @@ -763,8 +718,8 @@ public class DefinitionGenerator { } private String processIdentityRefType(final IdentityrefTypeDefinition leafTypeDef, final ObjectNode property, - final ObjectNode definitions, final DefinitionNames definitionNames, - final OAversion oaversion, final EffectiveModelContext schemaContext) { + final ObjectNode definitions, final DefinitionNames definitionNames, final OAversion oaversion, + final EffectiveModelContext schemaContext) { final String definitionName; if (isImported(leafTypeDef)) { definitionName = addImportedIdentity(leafTypeDef, definitions, definitionNames, schemaContext); @@ -777,8 +732,7 @@ public class DefinitionGenerator { } private static String addImportedIdentity(final IdentityrefTypeDefinition leafTypeDef, - final ObjectNode definitions, final DefinitionNames definitionNames, - final EffectiveModelContext context) { + final ObjectNode definitions, final DefinitionNames definitionNames, final EffectiveModelContext context) { final IdentitySchemaNode idNode = leafTypeDef.getIdentities().iterator().next(); final String identityName = idNode.getQName().getLocalName(); if (!definitionNames.isListedNode(idNode)) { @@ -815,8 +769,7 @@ public class DefinitionGenerator { return !leafTypeDef.getQName().getModule().equals(topLevelModule.getQNameModule()); } - private static String processBitsType(final BitsTypeDefinition bitsType, - final ObjectNode property) { + private static String processBitsType(final BitsTypeDefinition bitsType, final ObjectNode property) { property.put(MIN_ITEMS, 0); property.put(UNIQUE_ITEMS_KEY, true); final ArrayNode enumNames = new ArrayNode(JsonNodeFactory.instance); @@ -830,7 +783,7 @@ public class DefinitionGenerator { } private static String processStringType(final TypeDefinition stringType, final ObjectNode property, - final String nodeName) { + final String nodeName) { StringTypeDefinition type = (StringTypeDefinition) stringType; Optional lengthConstraints = ((StringTypeDefinition) stringType).getLengthConstraint(); while (lengthConstraints.isEmpty() && type.getBaseType() != null) { @@ -848,6 +801,8 @@ public class DefinitionGenerator { final PatternConstraint pattern = type.getPatternConstraints().iterator().next(); String regex = pattern.getJavaPatternString(); regex = regex.substring(1, regex.length() - 1); + // Escape special characters to prevent issues inside Generex. + regex = AUTOMATON_SPECIAL_CHARACTERS.matcher(regex).replaceAll("\\\\$0"); String defaultValue = ""; try { final Generex generex = new Generex(regex); @@ -864,7 +819,7 @@ public class DefinitionGenerator { private static String processNumberType(final RangeRestrictedTypeDefinition leafTypeDef, final ObjectNode property) { - final Optional maybeLower = ((RangeRestrictedTypeDefinition) leafTypeDef).getRangeConstraint() + final Optional maybeLower = leafTypeDef.getRangeConstraint() .map(RangeConstraint::getAllowedRanges).map(RangeSet::span).map(Range::lowerEndpoint); if (isHexadecimalOrOctal(leafTypeDef)) { @@ -872,7 +827,7 @@ public class DefinitionGenerator { } if (leafTypeDef instanceof DecimalTypeDefinition) { - maybeLower.ifPresent(number -> setDefaultValue(property, (BigDecimal) number)); + maybeLower.ifPresent(number -> setDefaultValue(property, ((Decimal64) number).decimalValue())); return NUMBER_TYPE; } if (leafTypeDef instanceof Uint8TypeDefinition @@ -898,14 +853,14 @@ public class DefinitionGenerator { private static boolean isHexadecimalOrOctal(final RangeRestrictedTypeDefinition typeDef) { final Optional optDefaultValue = typeDef.getDefaultValue(); if (optDefaultValue.isPresent()) { - final String defaultValue = (String)optDefaultValue.get(); + final String defaultValue = (String) optDefaultValue.get(); return defaultValue.startsWith("0") || defaultValue.startsWith("-0"); } return false; } private static String processInstanceIdentifierType(final DataSchemaNode node, final ObjectNode property, - final EffectiveModelContext schemaContext) { + final EffectiveModelContext schemaContext) { // create example instance-identifier to the first container of node's module if exists or leave it empty final var module = schemaContext.findModule(node.getQName().getModule()); if (module.isPresent()) {