import static org.opendaylight.mdsal.binding.model.util.Types.STRING;
import static org.opendaylight.mdsal.binding.model.util.Types.classType;
import static org.opendaylight.mdsal.binding.model.util.Types.listTypeFor;
+import static org.opendaylight.mdsal.binding.model.util.Types.listTypeWildcard;
import static org.opendaylight.mdsal.binding.model.util.Types.listenableFutureTypeFor;
import static org.opendaylight.mdsal.binding.model.util.Types.mapTypeFor;
+import static org.opendaylight.mdsal.binding.model.util.Types.objectType;
+import static org.opendaylight.mdsal.binding.model.util.Types.primitiveBooleanType;
+import static org.opendaylight.mdsal.binding.model.util.Types.primitiveIntType;
import static org.opendaylight.mdsal.binding.model.util.Types.primitiveVoidType;
import static org.opendaylight.mdsal.binding.model.util.Types.wildcardTypeFor;
import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNode;
import java.util.Optional;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
-import org.opendaylight.mdsal.binding.generator.util.BaseYangTypes;
import org.opendaylight.mdsal.binding.model.api.AccessModifier;
+import org.opendaylight.mdsal.binding.model.api.AnnotationType;
import org.opendaylight.mdsal.binding.model.api.Constant;
import org.opendaylight.mdsal.binding.model.api.DefaultType;
+import org.opendaylight.mdsal.binding.model.api.Enumeration;
import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject;
import org.opendaylight.mdsal.binding.model.api.GeneratedType;
import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
+import org.opendaylight.mdsal.binding.model.api.MethodSignature;
+import org.opendaylight.mdsal.binding.model.api.MethodSignature.ValueMechanics;
import org.opendaylight.mdsal.binding.model.api.ParameterizedType;
import org.opendaylight.mdsal.binding.model.api.Restrictions;
import org.opendaylight.mdsal.binding.model.api.Type;
import org.opendaylight.mdsal.binding.model.api.type.builder.GeneratedTypeBuilderBase;
import org.opendaylight.mdsal.binding.model.api.type.builder.MethodSignatureBuilder;
import org.opendaylight.mdsal.binding.model.api.type.builder.TypeMemberBuilder;
+import org.opendaylight.mdsal.binding.model.util.BaseYangTypes;
import org.opendaylight.mdsal.binding.model.util.BindingGeneratorUtil;
import org.opendaylight.mdsal.binding.model.util.TypeConstants;
import org.opendaylight.mdsal.binding.model.util.generated.type.builder.GeneratedPropertyBuilderImpl;
import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerLike;
import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DocumentedNode;
import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
+import org.opendaylight.yangtools.yang.model.api.InputSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ModuleImport;
import org.opendaylight.yangtools.yang.model.api.NotificationDefinition;
import org.opendaylight.yangtools.yang.model.api.NotificationNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.OutputSchemaNode;
import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
import org.opendaylight.yangtools.yang.model.api.UsesNode;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
import org.opendaylight.yangtools.yang.model.util.ModuleDependencySort;
import org.slf4j.LoggerFactory;
abstract class AbstractTypeGenerator {
+ private enum InheritedGetter {
+ /**
+ * There is no matching method present.
+ */
+ NOT_PRESENT,
+ /**
+ * There is a matching method and its return type is resolved.
+ */
+ RESOLVED,
+ /**
+ * There is a matching method and its return type is unresolved -- i.e. in case of a leafref pointing to outside
+ * of its parent grouping.
+ */
+ UNRESOLVED;
+
+ /**
+ * We are using {@code @Override} annotation to indicate specialization, hence we can differentiate between
+ * resolved and unresolved methods based on them.
+ *
+ * @param annotations Method annotations
+ * @return Either {@link #RESOLVED} or {@link #UNRESOLVED}.
+ */
+ static InheritedGetter fromAnnotations(final List<AnnotationType> annotations) {
+ for (AnnotationType annotation : annotations) {
+ if (OVERRIDE_ANNOTATION.equals(annotation.getIdentifier())) {
+ return InheritedGetter.RESOLVED;
+ }
+ }
+ return InheritedGetter.UNRESOLVED;
+ }
+ }
+
private static final Logger LOG = LoggerFactory.getLogger(AbstractTypeGenerator.class);
private static final Splitter COLON_SPLITTER = Splitter.on(':');
private static final JavaTypeName DEPRECATED_ANNOTATION = JavaTypeName.create(Deprecated.class);
private static final JavaTypeName OVERRIDE_ANNOTATION = JavaTypeName.create(Override.class);
+ private static final JavaTypeName CHECK_RETURN_VALUE_ANNOTATION =
+ // Do not refer to annotation class, as it may not be available at runtime
+ JavaTypeName.create("edu.umd.cs.findbugs.annotations", "CheckReturnValue");
private static final Type LIST_STRING_TYPE = listTypeFor(BaseYangTypes.STRING_TYPE);
/**
/**
* Holds reference to schema context to resolve data of augmented element when creating augmentation builder.
*/
- private final @NonNull SchemaContext schemaContext;
+ private final @NonNull EffectiveModelContext schemaContext;
/**
* Holds renamed elements.
*/
private final Map<SchemaNode, JavaTypeName> renames;
- AbstractTypeGenerator(final SchemaContext context, final AbstractTypeProvider typeProvider,
+ AbstractTypeGenerator(final EffectiveModelContext context, final AbstractTypeProvider typeProvider,
final Map<SchemaNode, JavaTypeName> renames) {
this.schemaContext = requireNonNull(context);
this.typeProvider = requireNonNull(typeProvider);
contexts.forEach(this::allAugmentsToGenTypes);
}
- final @NonNull SchemaContext schemaContext() {
+ final @NonNull EffectiveModelContext schemaContext() {
return schemaContext;
}
abstract void addComment(TypeMemberBuilder<?> genType, DocumentedNode node);
+ abstract void addRpcMethodComment(TypeMemberBuilder<?> genType, RpcDefinition node);
+
private ModuleContext moduleToGenTypes(final Module module) {
final ModuleContext context = new ModuleContext(module);
genCtx.put(module.getQNameModule(), context);
allTypeDefinitionsToGenTypes(context);
groupingsToGenTypes(context, module.getGroupings());
- rpcMethodsToGenType(context);
allIdentitiesToGenTypes(context);
- notificationsToGenType(context);
if (!module.getChildNodes().isEmpty()) {
final GeneratedTypeBuilder moduleType = moduleToDataType(context);
context.addModuleNode(moduleType);
resolveDataSchemaNodes(context, moduleType, moduleType, module.getChildNodes(), false);
}
+
+ // Resolve RPCs and notifications only after we have created instantiated tree
+ rpcMethodsToGenType(context);
+ notificationsToGenType(context);
return context;
}
return null;
}
final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(context, node, baseInterface);
- defaultImplementedInterace(genType);
+ addConcreteInterfaceMethods(genType);
annotateDeprecatedIfNecessary(node, genType);
final Module module = context.module();
return genType;
}
- private void containerToGenType(final ModuleContext context, final GeneratedTypeBuilder parent,
+ private Type containerToGenType(final ModuleContext context, final GeneratedTypeBuilder parent,
final Type baseInterface, final ContainerSchemaNode node, final boolean inGrouping) {
final GeneratedTypeBuilder genType = processDataSchemaNode(context, baseInterface, node, inGrouping);
if (genType != null) {
actionsToGenType(context, genType, node, null, inGrouping);
notificationsToGenType(context, genType, node, null, inGrouping);
}
+ return genType;
}
- private void listToGenType(final ModuleContext context, final GeneratedTypeBuilder parent,
+ private GeneratedTypeBuilder listToGenType(final ModuleContext context, final GeneratedTypeBuilder parent,
final Type baseInterface, final ListSchemaNode node, final boolean inGrouping) {
final GeneratedTypeBuilder genType = processDataSchemaNode(context, baseInterface, node, inGrouping);
if (genType != null) {
listType = listTypeFor(genType);
}
- constructGetter(parent, listType, node);
+ constructGetter(parent, listType, node).setMechanics(ValueMechanics.NULLIFY_EMPTY);
constructNonnull(parent, listType, node);
actionsToGenType(context, genType, node, keyTypeBuilder, inGrouping);
typeBuildersToGenTypes(context, genType, keyTypeBuilder);
}
+ return genType;
}
private void processUsesAugments(final DataNodeContainer node, final ModuleContext context,
final Module module = context.module();
addImplementedInterfaceFromUses(module, moduleDataTypeBuilder);
moduleDataTypeBuilder.addImplementsType(DATA_ROOT);
+ // if we have more than 2 top level uses statements we need to define getImplementedInterface() on the
+ // top level DataRoot object
+ if (module.getUses().size() > 1) {
+ narrowImplementedInterface(moduleDataTypeBuilder);
+ }
addCodegenInformation(moduleDataTypeBuilder, module);
return moduleDataTypeBuilder;
final GeneratedTypeBuilder builder = typeProvider.newGeneratedTypeBuilder(JavaTypeName.create(
packageNameForGeneratedType(context.modulePackageName(), action.getPath()),
BindingMapping.getClassName(qname)));
- qnameConstant(builder, JavaTypeName.create(context.modulePackageName(),
- BindingMapping.MODULE_INFO_CLASS_NAME), qname.getLocalName());
+ qnameConstant(builder, context.moduleInfoType(), qname.getLocalName());
annotateDeprecatedIfNecessary(action, builder);
builder.addImplementsType(keyType != null ? keyedListAction(parent, keyType, input, output)
}
private GeneratedType actionContainer(final ModuleContext context, final Type baseInterface,
- final ContainerSchemaNode schema, final boolean inGrouping) {
+ final ContainerLike schema, final boolean inGrouping) {
final GeneratedTypeBuilder genType = processDataSchemaNode(context, baseInterface, schema, inGrouping);
resolveDataSchemaNodes(context, genType, genType, schema.getChildNodes(), inGrouping);
return genType.build();
final MethodSignatureBuilder method = interfaceBuilder.addMethod(rpcMethodName);
// Do not refer to annotation class, as it may not be available at runtime
- method.addAnnotation("edu.umd.cs.findbugs.annotations", "CheckReturnValue");
- addComment(method, rpc);
+ method.addAnnotation(CHECK_RETURN_VALUE_ANNOTATION);
+ addRpcMethodComment(method, rpc);
method.addParameter(
createRpcContainer(context, rpcName, rpc, verifyNotNull(rpc.getInput()), RPC_INPUT), "input");
method.setReturnType(listenableFutureTypeFor(
}
private Type createRpcContainer(final ModuleContext context, final String rpcName, final RpcDefinition rpc,
- final ContainerSchemaNode schema, final Type type) {
+ final ContainerLike schema, final Type type) {
processUsesAugments(schema, context, false);
- final GeneratedTypeBuilder outType = addRawInterfaceDefinition(
+ final GeneratedTypeBuilder outType = addRawInterfaceDefinition(context,
JavaTypeName.create(context.modulePackageName(), rpcName + BindingMapping.getClassName(schema.getQName())),
schema);
addImplementedInterfaceFromUses(schema, outType);
outType.addImplementsType(type);
outType.addImplementsType(augmentable(outType));
- defaultImplementedInterace(outType);
+ addConcreteInterfaceMethods(outType);
annotateDeprecatedIfNecessary(rpc, outType);
resolveDataSchemaNodes(context, outType, outType, schema.getChildNodes(), false);
context.addChildNodeType(schema, outType);
final GeneratedTypeBuilder notificationInterface = addDefaultInterfaceDefinition(
context.modulePackageName(), notification, DATA_OBJECT, context);
- defaultImplementedInterace(notificationInterface);
+ addConcreteInterfaceMethods(notificationInterface);
annotateDeprecatedIfNecessary(notification, notificationInterface);
notificationInterface.addImplementsType(NOTIFICATION);
context.addChildNodeType(notification, notificationInterface);
resolveDataSchemaNodes(context, notificationInterface, notificationInterface,
notification.getChildNodes(), false);
- addComment(listenerInterface.addMethod("on" + notificationInterface.getName())
+ final MethodSignatureBuilder notificationMethod =
+ listenerInterface.addMethod("on" + notificationInterface.getName())
.setAccessModifier(AccessModifier.PUBLIC).addParameter(notificationInterface, "notification")
- .setReturnType(primitiveVoidType()), notification);
+ .setReturnType(primitiveVoidType());
+
+ annotateDeprecatedIfNecessary(notification, notificationMethod);
+ if (notification.getStatus().equals(Status.OBSOLETE)) {
+ notificationMethod.setDefault(true);
+ }
+ addComment(notificationMethod, notification);
}
}
final GeneratedTypeBuilder notifInterface = addDefaultInterfaceDefinition(
packageNameForGeneratedType(context.modulePackageName(), notif.getPath()), notif, DATA_OBJECT, context);
- defaultImplementedInterace(notifInterface);
+ addConcreteInterfaceMethods(notifInterface);
annotateDeprecatedIfNecessary(notif, notifInterface);
notifInterface.addImplementsType(keyType != null ? keyedListNotification(notifInterface, parent, keyType)
newType.setModuleName(module.getName());
newType.setSchemaPath(identity.getPath());
- qnameConstant(newType, JavaTypeName.create(context.modulePackageName(), BindingMapping.MODULE_INFO_CLASS_NAME),
- identity.getQName().getLocalName());
+ qnameConstant(newType, context.moduleInfoType(), identity.getQName().getLocalName());
context.addIdentityType(identity, newType);
}
* @param module Module in which type should be generated
* @return enumeration builder which contains data from <code>enumTypeDef</code>
*/
- private EnumBuilder resolveInnerEnumFromTypeDefinition(final EnumTypeDefinition enumTypeDef, final QName enumName,
+ private Enumeration resolveInnerEnumFromTypeDefinition(final EnumTypeDefinition enumTypeDef, final QName enumName,
final GeneratedTypeBuilder typeBuilder, final ModuleContext context) {
- if (enumTypeDef != null && typeBuilder != null && enumTypeDef.getQName().getLocalName() != null) {
- final EnumBuilder enumBuilder = typeBuilder.addEnumeration(BindingMapping.getClassName(enumName));
- typeProvider.addEnumDescription(enumBuilder, enumTypeDef);
- enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef);
- context.addInnerTypedefType(enumTypeDef.getPath(), enumBuilder);
- return enumBuilder;
- }
- return null;
+ final EnumBuilder enumBuilder = typeBuilder.addEnumeration(BindingMapping.getClassName(enumName));
+ typeProvider.addEnumDescription(enumBuilder, enumTypeDef);
+ enumBuilder.updateEnumPairsFromEnumTypeDef(enumTypeDef);
+ final Enumeration ret = enumBuilder.toInstance(typeBuilder);
+ context.addTypeToSchema(ret, enumTypeDef);
+ context.addInnerTypedefType(enumTypeDef.getPath(), ret);
+ return ret;
}
/**
final UsesNode parentUsesNode) {
SchemaNode result = parentUsesNode.getSourceGrouping();
for (final QName node : targetPath.getNodeIdentifiers()) {
+ // FIXME: this dispatch is rather ugly, we probably want to refactor it a bit
if (result instanceof DataNodeContainer) {
final QName resultNode = node.bindTo(result.getQName().getModule());
- result = ((DataNodeContainer) result).getDataChildByName(resultNode);
+
+ SchemaNode found = ((DataNodeContainer) result).getDataChildByName(resultNode);
+ if (found == null) {
+ if (result instanceof ActionNodeContainer) {
+ found = ((ActionNodeContainer) result).findAction(resultNode).orElse(null);
+ }
+ if (found == null && result instanceof NotificationNodeContainer) {
+ found = ((NotificationNodeContainer) result).findNotification(resultNode).orElse(null);
+ }
+ }
+ result = found;
} else if (result instanceof ChoiceSchemaNode) {
result = findNamedCase((ChoiceSchemaNode) result, node.getLocalName());
+ } else if (result instanceof ActionDefinition) {
+ final ActionDefinition action = (ActionDefinition) result;
+ final QName resultNode = node.bindTo(result.getQName().getModule());
+
+ final InputSchemaNode input = action.getInput();
+ final OutputSchemaNode output = action.getOutput();
+ if (resultNode.equals(input.getQName())) {
+ result = input;
+ } else if (resultNode.equals(output.getQName())) {
+ result = output;
+ } else {
+ result = null;
+ }
+ } else if (result != null) {
+ throw new IllegalStateException("Cannot handle " + result);
}
}
if (result == null) {
final GeneratedTypeBuilder augTypeBuilder = typeProvider.newGeneratedTypeBuilder(
JavaTypeName.create(augmentPackageName, augTypeName));
- augTypeBuilder.addImplementsType(DATA_OBJECT);
- defaultImplementedInterace(augTypeBuilder);
-
augTypeBuilder.addImplementsType(augmentation(targetTypeRef));
+ addConcreteInterfaceMethods(augTypeBuilder);
+
annotateDeprecatedIfNecessary(augSchema, augTypeBuilder);
addImplementedInterfaceFromUses(augSchema, augTypeBuilder);
if (schemaNodes != null && parent != null) {
final Type baseInterface = childOf == null ? DATA_OBJECT : childOf(childOf);
for (final DataSchemaNode schemaNode : schemaNodes) {
- if (!schemaNode.isAugmenting() && !schemaNode.isAddedByUses()) {
+ if (!schemaNode.isAugmenting()) {
addSchemaNodeToBuilderAsMethod(context, schemaNode, parent, baseInterface, inGrouping);
}
}
return parent;
}
+ private void addSchemaNodeToBuilderAsMethod(final ModuleContext context, final DataSchemaNode schemaNode,
+ final GeneratedTypeBuilder parent, final Type baseInterface, final boolean inGrouping) {
+ if (!schemaNode.isAddedByUses()) {
+ addUnambiguousNodeToBuilderAsMethod(context, schemaNode, parent, baseInterface, inGrouping);
+ } else if (needGroupingMethodOverride(schemaNode, parent)) {
+ addLeafrefNodeToBuilderAsMethod(context, (TypedDataSchemaNode) schemaNode, parent, inGrouping);
+ }
+ }
+
+ /**
+ * Determine whether a particular node, added from a grouping, needs to be reflected as a method. This method
+ * performs a check for {@link TypedDataSchemaNode} and defers to
+ * {@link #needGroupingMethodOverride(TypedDataSchemaNode, GeneratedTypeBuilder)}.
+ *
+ * @param parent {@code GeneratedType} where method should be defined
+ * @param child node from which method should be defined
+ * @return True if an override method is needed
+ */
+ private static boolean needGroupingMethodOverride(final DataSchemaNode child, final GeneratedTypeBuilder parent) {
+ return child instanceof TypedDataSchemaNode && needGroupingMethodOverride((TypedDataSchemaNode) child, parent);
+ }
+
+ /**
+ * Determine whether a particular {@link TypedDataSchemaNode}, added from a grouping, needs to be reflected as a
+ * method.
+ *
+ * <p>
+ * This check would be super easy were it not for relative leafrefs in groupings. These can legally point outside of
+ * the grouping -- which means we cannot inherently cannot determine their type, as they are polymorphic.
+ *
+ * @param parent {@code GeneratedType} where method should be defined
+ * @param child node from which method should be defined
+ * @return True if an override method is needed
+ */
+ private static boolean needGroupingMethodOverride(final TypedDataSchemaNode child,
+ final GeneratedTypeBuilder parent) {
+ // This is a child added through uses and it is is data-bearing, i.e. leaf or leaf-list. Under normal
+ // circumstances we would not bother, but if the target type is a leafref we have more checks to do.
+ return isRelativeLeafref(child.getType()) && needMethodDefinition(child.getQName().getLocalName(), parent);
+ }
+
+ private static boolean needMethodDefinition(final String localName, final GeneratedTypeBuilder parent) {
+ for (Type implementsType : parent.getImplementsTypes()) {
+ if (implementsType instanceof GeneratedType) {
+ final InheritedGetter precision = findInheritedGetter(localName, (GeneratedType) implementsType);
+ switch (precision) {
+ case RESOLVED:
+ return false;
+ case UNRESOLVED:
+ return true;
+ default:
+ // No-op
+ }
+ }
+ }
+ throw new IllegalStateException(localName + " should be present in " + parent
+ + " or in one of its ancestors as a getter");
+ }
+
+ private static InheritedGetter findInheritedGetter(final String localName, final GeneratedType impl) {
+ return findInheritedGetter(impl, BindingMapping.getGetterMethodName(localName));
+ }
+
+ private static InheritedGetter findInheritedGetter(final GeneratedType type, final String getter) {
+ for (MethodSignature method : type.getMethodDefinitions()) {
+ if (getter.equals(method.getName())) {
+ return InheritedGetter.fromAnnotations(method.getAnnotations());
+ }
+ }
+
+ // Try to find the method in other interfaces we implement
+ for (Type implementsType : type.getImplements()) {
+ if (implementsType instanceof GeneratedType) {
+ final InheritedGetter found = findInheritedGetter((GeneratedType) implementsType, getter);
+ if (found != InheritedGetter.NOT_PRESENT) {
+ return found;
+ }
+ }
+ }
+ return InheritedGetter.NOT_PRESENT;
+ }
+
+ private static boolean isRelativeLeafref(final TypeDefinition<? extends TypeDefinition<?>> type) {
+ return type instanceof LeafrefTypeDefinition && !((LeafrefTypeDefinition) type).getPathStatement().isAbsolute();
+ }
+
/**
* Adds the methods to <code>typeBuilder</code> what represents subnodes of node for which <code>typeBuilder</code>
* was created.
}
/**
- * Adds to <code>typeBuilder</code> a method which is derived from <code>schemaNode</code>.
+ * Adds to {@code typeBuilder} a method which is derived from {@code schemaNode}.
*
* @param node data schema node which is added to <code>typeBuilder</code> as a method
* @param typeBuilder generated type builder to which is <code>schemaNode</code> added as a method.
* @param childOf parent type
* @param module current module
*/
- private void addSchemaNodeToBuilderAsMethod(final ModuleContext context, final DataSchemaNode node,
- final GeneratedTypeBuilder typeBuilder, final Type baseInterface, final boolean inGrouping) {
+ private void addLeafrefNodeToBuilderAsMethod(final ModuleContext context, final TypedDataSchemaNode node,
+ final GeneratedTypeBuilder typeBuilder, final boolean inGrouping) {
if (node != null && typeBuilder != null) {
if (node instanceof LeafSchemaNode) {
- resolveLeafSchemaNodeAsMethod(typeBuilder, (LeafSchemaNode) node, context, inGrouping);
+ resolveLeafLeafrefNodeAsMethod(typeBuilder, (LeafSchemaNode) node, context, inGrouping);
} else if (node instanceof LeafListSchemaNode) {
- resolveLeafListSchemaNode(typeBuilder, (LeafListSchemaNode) node, context, inGrouping);
- } else if (node instanceof ContainerSchemaNode) {
- containerToGenType(context, typeBuilder, baseInterface, (ContainerSchemaNode) node, inGrouping);
- } else if (node instanceof ListSchemaNode) {
- listToGenType(context, typeBuilder, baseInterface, (ListSchemaNode) node, inGrouping);
- } else if (node instanceof ChoiceSchemaNode) {
- choiceToGeneratedType(context, typeBuilder, (ChoiceSchemaNode) node, inGrouping);
- } else if (node instanceof AnyxmlSchemaNode || node instanceof AnydataSchemaNode) {
- opaqueToGeneratedType(context, typeBuilder, node);
+ resolveLeafListLeafrefNode(typeBuilder, (LeafListSchemaNode) node, context, inGrouping);
} else {
- LOG.debug("Unable to add schema node {} as method in {}: unsupported type of node.", node.getClass(),
- typeBuilder.getFullyQualifiedName());
+ logUnableToAddNodeAsMethod(node, typeBuilder);
}
}
}
+ private void addUnambiguousNodeToBuilderAsMethod(final ModuleContext context, final DataSchemaNode node,
+ final GeneratedTypeBuilder typeBuilder, final Type baseInterface, final boolean inGrouping) {
+ if (node instanceof LeafSchemaNode) {
+ resolveUnambiguousLeafNodeAsMethod(typeBuilder, (LeafSchemaNode) node, context, inGrouping);
+ } else if (node instanceof LeafListSchemaNode) {
+ resolveUnambiguousLeafListNode(typeBuilder, (LeafListSchemaNode) node, context, inGrouping);
+ } else if (node instanceof ContainerSchemaNode) {
+ containerToGenType(context, typeBuilder, baseInterface, (ContainerSchemaNode) node, inGrouping);
+ } else if (node instanceof ListSchemaNode) {
+ listToGenType(context, typeBuilder, baseInterface, (ListSchemaNode) node, inGrouping);
+ } else if (node instanceof ChoiceSchemaNode) {
+ choiceToGeneratedType(context, typeBuilder, (ChoiceSchemaNode) node, inGrouping);
+ } else if (node instanceof AnyxmlSchemaNode || node instanceof AnydataSchemaNode) {
+ opaqueToGeneratedType(context, typeBuilder, node);
+ } else {
+ logUnableToAddNodeAsMethod(node, typeBuilder);
+ }
+ }
+
+ private static void logUnableToAddNodeAsMethod(final DataSchemaNode node, final GeneratedTypeBuilder typeBuilder) {
+ LOG.debug("Unable to add schema node {} as method in {}: unsupported type of node.", node.getClass(),
+ typeBuilder.getFullyQualifiedName());
+ }
+
/**
* Converts <code>choiceNode</code> to the list of generated types for choice and its cases. The package names
* for choice and for its cases are created as concatenation of the module package (<code>basePackageName</code>)
private void choiceToGeneratedType(final ModuleContext context, final GeneratedTypeBuilder parent,
final ChoiceSchemaNode choiceNode, final boolean inGrouping) {
if (!choiceNode.isAddedByUses()) {
- final GeneratedTypeBuilder choiceTypeBuilder = addRawInterfaceDefinition(
+ final GeneratedTypeBuilder choiceTypeBuilder = addRawInterfaceDefinition(context,
JavaTypeName.create(packageNameForGeneratedType(context.modulePackageName(), choiceNode.getPath()),
BindingMapping.getClassName(choiceNode.getQName())), choiceNode);
choiceTypeBuilder.addImplementsType(choiceIn(parent));
private void opaqueToGeneratedType(final ModuleContext context, final GeneratedTypeBuilder parent,
final DataSchemaNode anyNode) {
if (!anyNode.isAddedByUses()) {
- final GeneratedTypeBuilder anyxmlTypeBuilder = addRawInterfaceDefinition(
+ final GeneratedTypeBuilder anyxmlTypeBuilder = addRawInterfaceDefinition(context,
JavaTypeName.create(packageNameForGeneratedType(context.modulePackageName(), anyNode.getPath()),
BindingMapping.getClassName(anyNode.getQName())), anyNode);
anyxmlTypeBuilder.addImplementsType(opaqueObject(anyxmlTypeBuilder)).addImplementsType(childOf(parent));
if (caseNode != null && !caseNode.isAddedByUses() && !caseNode.isAugmenting()) {
final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(context, caseNode);
caseTypeBuilder.addImplementsType(refChoiceType);
- defaultImplementedInterace(caseTypeBuilder);
+ addConcreteInterfaceMethods(caseTypeBuilder);
annotateDeprecatedIfNecessary(caseNode, caseTypeBuilder);
context.addCaseType(caseNode.getPath(), caseTypeBuilder);
context.addChoiceToCaseMapping(refChoiceType, caseTypeBuilder, caseNode);
if (caseNode != null) {
final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(context, caseNode);
caseTypeBuilder.addImplementsType(targetType);
- defaultImplementedInterace(caseTypeBuilder);
+ addConcreteInterfaceMethods(caseTypeBuilder);
CaseSchemaNode node = null;
final String caseLocalName = caseNode.getQName().getLocalName();
}
}
+ private Type resolveLeafLeafrefNodeAsMethod(final GeneratedTypeBuilder typeBuilder, final LeafSchemaNode leaf,
+ final ModuleContext context, final boolean inGrouping) {
+ final Module parentModule = findParentModule(schemaContext, leaf);
+ final Type returnType = resolveReturnType(typeBuilder, leaf, context, parentModule, inGrouping);
+ if (returnType != null && isTypeSpecified(returnType)) {
+ processContextRefExtension(leaf, constructOverrideGetter(typeBuilder, returnType, leaf), parentModule);
+ }
+ return returnType;
+ }
+
/**
- * Converts <code>leaf</code> to the getter method which is added to <code>typeBuilder</code>.
+ * Converts {@code leafList} to the getter method which is added to {@code typeBuilder}.
*
- * @param typeBuilder generated type builder to which is added getter method as <code>leaf</code> mapping
- * @param leaf leaf schema node which is mapped as getter method which is added to <code>typeBuilder</code>
- * @param module Module in which type was defined
- * @return boolean value
- * <ul>
- * <li>false - if <code>leaf</code> or <code>typeBuilder</code> are
- * null</li>
- * <li>true - in other cases</li>
- * </ul>
+ * @param context module in which type was defined
+ * @param typeBuilder generated type builder to which is added getter method as {@code leafList} mapping
+ * @param leafList leaf-list schema node which is mapped as getter method which is added to {@code typeBuilder}
*/
- private Type resolveLeafSchemaNodeAsMethod(final GeneratedTypeBuilder typeBuilder, final LeafSchemaNode leaf,
+ private void resolveLeafListNodeAsMethod(final GeneratedTypeBuilder typeBuilder, final LeafListSchemaNode leafList,
final ModuleContext context, final boolean inGrouping) {
- if (leaf == null || typeBuilder == null || leaf.isAddedByUses()) {
- return null;
+ if (!leafList.isAddedByUses()) {
+ resolveUnambiguousLeafListNode(typeBuilder, leafList, context, inGrouping);
+ } else if (needGroupingMethodOverride(leafList, typeBuilder)) {
+ resolveLeafListLeafrefNode(typeBuilder, leafList, context, inGrouping);
}
+ }
+ private Type resolveUnambiguousLeafNodeAsMethod(final GeneratedTypeBuilder typeBuilder, final LeafSchemaNode leaf,
+ final ModuleContext context, final boolean inGrouping) {
final Module parentModule = findParentModule(schemaContext, leaf);
+ final Type returnType = resolveReturnType(typeBuilder, leaf, context, parentModule, inGrouping);
+ if (returnType != null) {
+ processContextRefExtension(leaf, constructGetter(typeBuilder, returnType, leaf), parentModule);
+ }
+ return returnType;
+ }
+
+ private Type resolveReturnType(final GeneratedTypeBuilder typeBuilder, final LeafSchemaNode leaf,
+ final ModuleContext context, final Module parentModule, final boolean inGrouping) {
Type returnType = null;
final TypeDefinition<?> typeDef = CompatUtils.compatType(leaf);
if (isInnerType(leaf, typeDef)) {
if (typeDef instanceof EnumTypeDefinition) {
- returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, leaf, inGrouping);
final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) typeDef;
- final EnumBuilder enumBuilder = resolveInnerEnumFromTypeDefinition(enumTypeDef, leaf.getQName(),
- typeBuilder, context);
- if (enumBuilder != null) {
- returnType = enumBuilder.toInstance(typeBuilder);
- }
+ returnType = resolveInnerEnumFromTypeDefinition(enumTypeDef, leaf.getQName(), typeBuilder, context);
typeProvider.putReferencedType(leaf.getPath(), returnType);
} else if (typeDef instanceof UnionTypeDefinition) {
final UnionTypeDefinition unionDef = (UnionTypeDefinition)typeDef;
returnType = genTOBuilder.build();
}
} else {
- // It is constrained version of already declared type (inner declared type exists,
- // onlyfor special cases (Enum, Union, Bits), which were already checked.
- // In order to get proper class we need to look up closest derived type
- // and apply restrictions from leaf type
+ // It is constrained version of already declared type (inner declared type exists, only for special
+ // cases (Enum, Union, Bits), which were already checked.
+ // In order to get proper class we need to look up closest derived type and apply restrictions from leaf
+ // type
final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
- returnType = typeProvider.javaTypeForSchemaDefinitionType(getBaseOrDeclaredType(typeDef), leaf,
- restrictions, inGrouping);
+ final TypeDefinition<?> baseOrDeclaredType = getBaseOrDeclaredType(typeDef);
+ // we need to try to lookup an already generated type in case the leafref is targetting a generated type
+ if (baseOrDeclaredType instanceof LeafrefTypeDefinition) {
+ final SchemaNode leafrefTarget =
+ typeProvider.getTargetForLeafref((LeafrefTypeDefinition) baseOrDeclaredType, leaf);
+ if (leafrefTarget instanceof TypedDataSchemaNode) {
+ returnType = context.getInnerType(((TypedDataSchemaNode) leafrefTarget).getType().getPath());
+ }
+ }
+ if (returnType == null) {
+ returnType = typeProvider.javaTypeForSchemaDefinitionType(baseOrDeclaredType, leaf,
+ restrictions, inGrouping);
+ }
+
addPatternConstant(typeBuilder, leaf.getQName().getLocalName(), restrictions.getPatternConstraints());
}
} else {
typeProvider.putReferencedType(leaf.getPath(), returnType);
}
- final MethodSignatureBuilder getter = constructGetter(typeBuilder, returnType, leaf);
- processContextRefExtension(leaf, getter, parentModule);
return returnType;
}
+ private static boolean isTypeSpecified(final Type type) {
+ return !type.equals(objectType());
+ }
+
private static TypeDefinition<?> getBaseOrDeclaredType(final TypeDefinition<?> typeDef) {
// Returns DerivedType in case of new parser.
final TypeDefinition<?> baseType = typeDef.getBaseType();
return true;
}
+ private void resolveLeafListLeafrefNode(final GeneratedTypeBuilder typeBuilder, final LeafListSchemaNode node,
+ final ModuleContext context, final boolean inGrouping) {
+ final Type returnType = resolveLeafListItemsType(typeBuilder, node, context, inGrouping,
+ findParentModule(schemaContext, node));
+ if (isTypeSpecified(returnType)) {
+ constructOverrideGetter(typeBuilder, listTypeFor(returnType), node);
+ }
+ }
+
/**
* Converts <code>node</code> leaf list schema node to getter method of <code>typeBuilder</code>.
*
+ * @param context module
* @param typeBuilder generated type builder to which is <code>node</code> added as getter method
* @param node leaf list schema node which is added to <code>typeBuilder</code> as getter method
- * @param module module
- * @return boolean value
- * <ul>
- * <li>true - if <code>node</code>, <code>typeBuilder</code>,
- * nodeName equal null or <code>node</code> is added by <i>uses</i></li>
- * <li>false - other cases</li>
- * </ul>
*/
- private boolean resolveLeafListSchemaNode(final GeneratedTypeBuilder typeBuilder, final LeafListSchemaNode node,
+ private Type resolveUnambiguousLeafListNode(final GeneratedTypeBuilder typeBuilder, final LeafListSchemaNode node,
final ModuleContext context, final boolean inGrouping) {
- if (node == null || typeBuilder == null || node.isAddedByUses()) {
- return false;
+ final Module parentModule = findParentModule(schemaContext, node);
+ final Type listItemsType = resolveLeafListItemsType(typeBuilder, node, context, inGrouping, parentModule);
+ final Type returnType;
+ if (listItemsType.equals(objectType())) {
+ returnType = listTypeWildcard();
+ } else {
+ returnType = listTypeFor(listItemsType);
}
- final QName nodeName = node.getQName();
+ constructGetter(typeBuilder, returnType, node);
- final TypeDefinition<?> typeDef = node.getType();
- final Module parentModule = findParentModule(schemaContext, node);
+ return returnType;
+ }
- Type returnType = null;
+ private Type resolveLeafListItemsType(final GeneratedTypeBuilder typeBuilder, final LeafListSchemaNode node,
+ final ModuleContext context, final boolean inGrouping, final Module parentModule) {
+ final Type returnType;
+ final TypeDefinition<? extends TypeDefinition<?>> typeDef = node.getType();
if (typeDef.getBaseType() == null) {
if (typeDef instanceof EnumTypeDefinition) {
- returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, inGrouping);
final EnumTypeDefinition enumTypeDef = (EnumTypeDefinition) typeDef;
- final EnumBuilder enumBuilder = resolveInnerEnumFromTypeDefinition(enumTypeDef, nodeName,
- typeBuilder, context);
- returnType = DefaultType.of(enumBuilder);
+ returnType = resolveInnerEnumFromTypeDefinition(enumTypeDef, node.getQName(), typeBuilder, context);
typeProvider.putReferencedType(node.getPath(), returnType);
} else if (typeDef instanceof UnionTypeDefinition) {
- final UnionTypeDefinition unionDef = (UnionTypeDefinition)typeDef;
+ final UnionTypeDefinition unionDef = (UnionTypeDefinition) typeDef;
returnType = addTOToTypeBuilder(unionDef, typeBuilder, node, parentModule);
} else if (typeDef instanceof BitsTypeDefinition) {
- final GeneratedTOBuilder genTOBuilder = addTOToTypeBuilder((BitsTypeDefinition)typeDef, typeBuilder,
- node, parentModule);
+ final GeneratedTOBuilder genTOBuilder = addTOToTypeBuilder((BitsTypeDefinition) typeDef, typeBuilder,
+ node, parentModule);
returnType = genTOBuilder.build();
} else {
final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, restrictions, inGrouping);
addPatternConstant(typeBuilder, node.getQName().getLocalName(), restrictions.getPatternConstraints());
}
-
- constructGetter(typeBuilder, listTypeFor(returnType), node);
- return true;
+ return returnType;
}
private Type createReturnTypeForUnion(final GeneratedTOBuilder genTOBuilder, final UnionTypeDefinition typeDef,
name = JavaTypeName.create(packageName, BindingMapping.getClassName(schemaNode.getQName()));
}
- final GeneratedTypeBuilder it = addRawInterfaceDefinition(name, schemaNode);
+ final GeneratedTypeBuilder it = addRawInterfaceDefinition(context, name, schemaNode);
it.addImplementsType(baseInterface);
if (!(schemaNode instanceof GroupingDefinition)) {
it.addImplementsType(augmentable(it));
* <li>if schemaNode name is null</li>
* </ul>
*/
- private GeneratedTypeBuilder addRawInterfaceDefinition(final JavaTypeName identifier, final SchemaNode schemaNode) {
+ private GeneratedTypeBuilder addRawInterfaceDefinition(final ModuleContext context, final JavaTypeName identifier,
+ final SchemaNode schemaNode) {
checkArgument(schemaNode != null, "Data Schema Node cannot be NULL.");
checkArgument(schemaNode.getQName() != null, "QName for Data Schema Node cannot be NULL.");
final String schemaNodeName = schemaNode.getQName().getLocalName();
// FIXME: Validation of name conflict
final GeneratedTypeBuilder newType = typeProvider.newGeneratedTypeBuilder(identifier);
- final Module module = findParentModule(schemaContext, schemaNode);
- qnameConstant(newType, JavaTypeName.create(BindingMapping.getRootPackageName(module.getQNameModule()),
- BindingMapping.MODULE_INFO_CLASS_NAME), schemaNode.getQName().getLocalName());
+ qnameConstant(newType, context.moduleInfoType(), schemaNode.getQName().getLocalName());
+ final Module module = context.module();
addCodegenInformation(newType, module, schemaNode);
newType.setSchemaPath(schemaNode.getPath());
newType.setModuleName(module.getName());
return newType;
}
- /**
- * Creates the name of the getter method name from <code>localName</code>.
- *
- * @param localName string with the name of the getter method
- * @param returnType return type
- * @return string with the name of the getter method for <code>methodName</code> in JAVA method format
- */
- public static String getterMethodName(final String localName, final Type returnType) {
- return BindingMapping.getGetterMethodName(localName, BOOLEAN.equals(returnType));
- }
-
/**
* Created a method signature builder as part of <code>interfaceBuilder</code>. The method signature builder is
* created for the getter method of <code>schemaNodeName</code>. Also <code>comment</code>
private MethodSignatureBuilder constructGetter(final GeneratedTypeBuilder interfaceBuilder, final Type returnType,
final SchemaNode node) {
final MethodSignatureBuilder getMethod = interfaceBuilder.addMethod(
- getterMethodName(node.getQName().getLocalName(), returnType));
+ BindingMapping.getGetterMethodName(node.getQName().getLocalName()));
getMethod.setReturnType(returnType);
annotateDeprecatedIfNecessary(node, getMethod);
addComment(getMethod, node);
+ if (BOOLEAN.equals(returnType)) {
+ // Construct a default 'isFoo()' method for compatibility
+ interfaceBuilder.addMethod(BindingMapping.getGetterMethodName(node.getQName().getLocalName(), true))
+ .setAccessModifier(AccessModifier.PUBLIC)
+ .setDefault(true)
+ .setReturnType(BOOLEAN);
+ }
+
return getMethod;
}
+ private MethodSignatureBuilder constructOverrideGetter(final GeneratedTypeBuilder interfaceBuilder,
+ final Type returnType, final SchemaNode node) {
+ final MethodSignatureBuilder getter = constructGetter(interfaceBuilder, returnType, node);
+ getter.addAnnotation(OVERRIDE_ANNOTATION);
+ return getter;
+ }
+
private static void constructNonnull(final GeneratedTypeBuilder interfaceBuilder, final Type returnType,
final ListSchemaNode node) {
final MethodSignatureBuilder getMethod = interfaceBuilder.addMethod(
if (schemaNode instanceof LeafSchemaNode) {
final LeafSchemaNode leaf = (LeafSchemaNode) schemaNode;
final String leafName = leaf.getQName().getLocalName();
- Type type = resolveLeafSchemaNodeAsMethod(typeBuilder, leaf, context, inGrouping);
+ final Type type;
+ if (!schemaNode.isAddedByUses()) {
+ type = resolveUnambiguousLeafNodeAsMethod(typeBuilder, leaf, context, inGrouping);
+ } else if (needGroupingMethodOverride(leaf, typeBuilder)) {
+ type = resolveLeafLeafrefNodeAsMethod(typeBuilder, leaf, context, inGrouping);
+ } else {
+ type = null;
+ }
+
if (listKeys.contains(leafName)) {
if (type == null) {
resolveLeafSchemaNodeAsProperty(genTOBuilder, leaf, true);
resolveLeafSchemaNodeAsProperty(genTOBuilder, leaf, type, true);
}
}
+ } else if (schemaNode instanceof LeafListSchemaNode) {
+ resolveLeafListNodeAsMethod(typeBuilder, (LeafListSchemaNode) schemaNode, context, inGrouping);
} else if (!schemaNode.isAddedByUses()) {
- if (schemaNode instanceof LeafListSchemaNode) {
- resolveLeafListSchemaNode(typeBuilder, (LeafListSchemaNode) schemaNode, context, inGrouping);
- } else if (schemaNode instanceof ContainerSchemaNode) {
- containerToGenType(context, typeBuilder, childOf(typeBuilder),
- (ContainerSchemaNode) schemaNode, inGrouping);
+ if (schemaNode instanceof ContainerSchemaNode) {
+ containerToGenType(context, typeBuilder, childOf(typeBuilder), (ContainerSchemaNode) schemaNode,
+ inGrouping);
} else if (schemaNode instanceof ChoiceSchemaNode) {
choiceToGeneratedType(context, typeBuilder, (ChoiceSchemaNode) schemaNode, inGrouping);
} else if (schemaNode instanceof ListSchemaNode) {
final GeneratedTypeBuilder genType = findGrouping(usesNode.getSourceGrouping());
if (genType == null) {
throw new IllegalStateException("Grouping " + usesNode.getSourceGrouping().getQName()
- + " is not resolved for " + builder.getName());
+ + " is not resolved for " + builder.getFullyQualifiedName());
}
builder.addImplementsType(genType.build());
}
}
+ private static void addConcreteInterfaceMethods(final GeneratedTypeBuilder typeBuilder) {
+ defaultImplementedInterace(typeBuilder);
+
+ typeBuilder.addMethod(BindingMapping.BINDING_HASHCODE_NAME)
+ .setAccessModifier(AccessModifier.PUBLIC)
+ .setStatic(true)
+ .setReturnType(primitiveIntType());
+ typeBuilder.addMethod(BindingMapping.BINDING_EQUALS_NAME)
+ .setAccessModifier(AccessModifier.PUBLIC)
+ .setStatic(true)
+ .setReturnType(primitiveBooleanType());
+ typeBuilder.addMethod(BindingMapping.BINDING_TO_STRING_NAME)
+ .setAccessModifier(AccessModifier.PUBLIC)
+ .setStatic(true)
+ .setReturnType(STRING);
+ }
+
private static void narrowImplementedInterface(final GeneratedTypeBuilder typeBuilder) {
defineImplementedInterfaceMethod(typeBuilder, wildcardTypeFor(typeBuilder.getIdentifier()));
}