Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StackoverflowException on attribute groups #343

Closed
budul100 opened this issue Jul 21, 2022 · 3 comments
Closed

StackoverflowException on attribute groups #343

budul100 opened this issue Jul 21, 2022 · 3 comments

Comments

@budul100
Copy link

Hi, when I am converting the yEd-graphML source xsd, then it comes to a StackoverflowException. I am calling the generator with the following parameters:

-n http://graphml.graphdrawing.org/xmlns=GraphML -n http://www.w3.org/1999/xlink=XLink -n http://www.yworks.com/xml/graphml=YEd --nu --sf --csm=Public --ct=System.Array --dc ".\ygraphml.xsd"

The problem is an infinite loop in the CreatePropertiesForAttributes function of the model builder.

The problem appears on line 804 ff in ModelBuilder.cs:

if (attributeGroup.RedefinedAttributeGroup != null)
{
	foreach (var attr in attributeGroup.RedefinedAttributeGroup.Attributes.Cast<XmlSchemaObject>())
	{
		var n = attr.GetQualifiedName();

		if (n != null)
			attributes.RemoveAll(a => a.GetQualifiedName() == n);

		attributes.Add(attr);
	}
}

If the item determination is replaced by the following line, then the problem is not appearing anymore (similar approach as line 800 in ModelBuilder.cs:

if (attributeGroup.RedefinedAttributeGroup != null)
{
	var attrs = attributeGroup.RedefinedAttributeGroup.Attributes.Cast<XmlSchemaObject>()
		.Where(a => !(a is XmlSchemaAttributeGroupRef agr && agr.RefName == attributeGroupRef.RefName)).ToList();

	foreach (var attr in attrs)
	{
		var n = attr.GetQualifiedName();

		if (n != null)
			attributes.RemoveAll(a => a.GetQualifiedName() == n);

		attributes.Add(attr);
	}
}
@budul100
Copy link
Author

Actually, I am afraid, that the solution is not really solving the issue. It is rather cutting the output without considering the references.

@budul100
Copy link
Author

The solution in the meantime was to extend the AttributeGroups and the Groups variable of ModelBuilder:

[...]

private readonly Dictionary<XmlQualifiedName, HashSet<XmlSchemaAttributeGroup>> AttributeGroups = new();
private readonly Dictionary<XmlQualifiedName, HashSet<XmlSchemaGroup>> Groups = new();

[...]

foreach (var schema in set.Schemas().Cast<XmlSchema>())
	ResolveDependencies(schema, dependencyOrder, seenSchemas);

foreach (var schema in dependencyOrder)
{
	var currentAttributeGroups = schema.AttributeGroups.Values.Cast<XmlSchemaAttributeGroup>()
		.DistinctBy(g => g.QualifiedName.ToString());

	foreach (var currentAttributeGroup in currentAttributeGroups)
	{
		if (!AttributeGroups.ContainsKey(currentAttributeGroup.QualifiedName))
		{
			AttributeGroups.Add(currentAttributeGroup.QualifiedName, new HashSet<XmlSchemaAttributeGroup>());
		}

		AttributeGroups[currentAttributeGroup.QualifiedName].Add(currentAttributeGroup);
	}

	var currentSchemaGroups = schema.Groups.Values.Cast<XmlSchemaGroup>()
		.DistinctBy(g => g.QualifiedName.ToString());

	foreach (var currentSchemaGroup in currentSchemaGroups)
	{
		if (!Groups.ContainsKey(currentSchemaGroup.QualifiedName))
		{
			Groups.Add(currentSchemaGroup.QualifiedName, new HashSet<XmlSchemaGroup>());
		}

		Groups[currentSchemaGroup.QualifiedName].Add(currentSchemaGroup);
	}
}

foreach (var schema in dependencyOrder)
{
	foreach (var globalType in set.GlobalTypes.Values.Cast<XmlSchemaType>().Where(s => s.GetSchema() == schema))
		CreateTypeModel(globalType.QualifiedName, globalType);

	foreach (var rootElement in set.GlobalElements.Values.Cast<XmlSchemaElement>().Where(s => s.GetSchema() == schema))
		CreateElement(rootElement);
}

The CreatePropertiesForAttributes function has to be changed as follows:

private IEnumerable<PropertyModel> CreatePropertiesForAttributes(Uri source, TypeModel owningTypeModel, IEnumerable<XmlSchemaObject> items)
{
	var properties = new List<PropertyModel>();

	foreach (var item in items)
	{
		switch (item)
		{
			case XmlSchemaAttribute attribute when attribute.Use != XmlSchemaUse.Prohibited:

				properties.Add(PropertyFromAttribute(owningTypeModel, attribute));
				break;

			case XmlSchemaAttributeGroupRef attributeGroupRef:

				foreach (var attributeGroup in AttributeGroups[attributeGroupRef.RefName])
				{
					if (_configuration.GenerateInterfaces)
						CreateTypeModel(attributeGroupRef.RefName, attributeGroup);

					var attributes = attributeGroup.Attributes.Cast<XmlSchemaObject>()
						.Where(a => !(a is XmlSchemaAttributeGroupRef agr && agr.RefName == attributeGroupRef.RefName))
						.ToList();

					if (attributeGroup.RedefinedAttributeGroup != null)
					{
						var attrs = attributeGroup.RedefinedAttributeGroup.Attributes.Cast<XmlSchemaObject>()
							.Where(a => !(a is XmlSchemaAttributeGroupRef agr && agr.RefName == attributeGroupRef.RefName)).ToList();

						if (attrs.Any(a => (a is XmlSchemaAttributeGroupRef agr && agr.RefName == attributeGroupRef.RefName))) { }

						foreach (var attr in attrs)
						{
							var n = attr.GetQualifiedName();

							if (n != null)
								attributes.RemoveAll(a => a.GetQualifiedName() == n);

							attributes.Add(attr);
						}
					}

					var newProperties = CreatePropertiesForAttributes(source, owningTypeModel, attributes);
					properties.AddRange(newProperties);
				}

				break;
		}
	}
	return properties;
}

And the functions had to be adjusted:

private void AddInterfaces(ReferenceTypeModel refTypeModel, IEnumerable<Particle> items)
{
	var interfaces = items.Select(i => i.XmlParticle).OfType<XmlSchemaGroupRef>()
		.Select(i => (InterfaceModel)builder.CreateTypeModel(i.RefName, builder.Groups[i.RefName].First()));
	refTypeModel.AddInterfaces(interfaces);
}

private void AddInterfaces(ReferenceTypeModel refTypeModel, XmlSchemaObjectCollection attributes)
{
	var interfaces = attributes.OfType<XmlSchemaAttributeGroupRef>()
		.Select(a => (InterfaceModel)builder.CreateTypeModel(a.RefName, builder.AttributeGroups[a.RefName].First()));
	refTypeModel.AddInterfaces(interfaces);
}

That has solved the problem.

@mganss mganss closed this as completed in 8fd4172 Jul 26, 2022
@mganss
Copy link
Owner

mganss commented Jul 26, 2022

Thanks a lot. I have integrated your changes. You're also welcome to contribute code by creating a pull request. That way, you are credited as the author of the code changes in the project's history and it makes the workflow easier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants