Skip to content

Commit

Permalink
feat: add support for fields in superclasses (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
tnc1997 committed May 29, 2023
1 parent 4c44991 commit 8679f1c
Show file tree
Hide file tree
Showing 6 changed files with 299 additions and 5 deletions.
18 changes: 18 additions & 0 deletions xml_serializable/lib/src/extensions/class_element_extensions.dart
@@ -0,0 +1,18 @@
import 'package:analyzer/dart/element/element.dart';

extension ClassElementExtensions on ClassElement {
/// Returns a list containing all of the fields declared in this class and its supertypes. This includes superclasses, mixins, interfaces, and superclass constraints.
List<FieldElement> get allFields {
final allFields = {
for (var field in fields) field.name: field,
};

for (var supertype in allSupertypes) {
for (var field in supertype.element2.fields) {
allFields.putIfAbsent(field.name, () => field);
}
}

return allFields.values.toList();
}
}
9 changes: 5 additions & 4 deletions xml_serializable/lib/src/xml_serializable_generator.dart
Expand Up @@ -4,6 +4,7 @@ import 'package:build/build.dart';
import 'package:source_gen/source_gen.dart';
import 'package:xml_annotation/xml_annotation.dart';

import 'extensions/class_element_extensions.dart';
import 'extensions/dart_object_extensions.dart';
import 'extensions/dart_type_extensions.dart';
import 'extensions/element_extensions.dart';
Expand Down Expand Up @@ -115,7 +116,7 @@ class XmlSerializableGenerator extends GeneratorForAnnotation<XmlSerializable> {
'void _\$${element.name}BuildXmlChildren(${element.name} instance, XmlBuilder builder, {Map<String, String> namespaces = const {}}) {',
);

for (final element in element.fields) {
for (final element in element.allFields) {
if (element.hasXmlAttribute ||
element.hasXmlElement ||
element.hasXmlText) {
Expand Down Expand Up @@ -151,7 +152,7 @@ class XmlSerializableGenerator extends GeneratorForAnnotation<XmlSerializable> {

final elements = <FieldElement>{};

for (final element in element.fields) {
for (final element in element.allFields) {
if (element.hasXmlAttribute ||
element.hasXmlElement ||
element.hasXmlText) {
Expand Down Expand Up @@ -215,7 +216,7 @@ class XmlSerializableGenerator extends GeneratorForAnnotation<XmlSerializable> {

buffer.writeln('final attributes = <XmlAttribute>[];');

for (final element in element.fields) {
for (final element in element.allFields) {
if (element.hasXmlAttribute) {
buffer.writeln('final ${element.name} = instance.${element.name};');

Expand Down Expand Up @@ -262,7 +263,7 @@ class XmlSerializableGenerator extends GeneratorForAnnotation<XmlSerializable> {

buffer.writeln('final children = <XmlNode>[];');

for (final element in element.fields) {
for (final element in element.allFields) {
if (element.hasXmlElement || element.hasXmlText) {
buffer.writeln('final ${element.name} = instance.${element.name};');

Expand Down
1 change: 1 addition & 0 deletions xml_serializable/lib/xml_serializable.dart
Expand Up @@ -15,6 +15,7 @@ export 'package:xml_serializable/src/constructor_generators/xml_root_element_con
export 'package:xml_serializable/src/constructor_generators/xml_serializable_xml_element_constructor_generator.dart';
export 'package:xml_serializable/src/constructor_generators/xml_text_constructor_generator.dart';
export 'package:xml_serializable/src/constructor_generators/xml_text_xml_element_constructor_generator.dart';
export 'package:xml_serializable/src/extensions/class_element_extensions.dart';
export 'package:xml_serializable/src/extensions/constant_reader_extensions.dart';
export 'package:xml_serializable/src/extensions/dart_object_extensions.dart';
export 'package:xml_serializable/src/extensions/dart_type_extensions.dart';
Expand Down
219 changes: 219 additions & 0 deletions xml_serializable/test/extensions/class_element_extensions_test.dart
@@ -0,0 +1,219 @@
import 'package:test/test.dart';
import 'package:xml_serializable/xml_serializable.dart';

import '../fake_class_element.dart';
import '../fake_field_element.dart';
import '../fake_interface_type.dart';

void main() {
group(
'ClassElementExtensions',
() {
group(
'allFields',
() {
test(
'should return all of the fields in the class',
() {
final fields = [
FakeFieldElement(
name: 'field1',
),
FakeFieldElement(
name: 'field2',
),
];

expect(
FakeClassElement(
fields: fields,
).allFields,
containsAll(
[
...fields,
],
),
);
},
);

test(
'should return all of the fields in the superclass',
() {
final superclass1Fields = [
FakeFieldElement(
name: 'superclass1Field1',
),
FakeFieldElement(
name: 'superclass1Field2',
),
];

expect(
FakeClassElement(
allSupertypes: [
FakeInterfaceType(
element2: FakeClassElement(
fields: superclass1Fields,
),
),
],
).allFields,
containsAll(
[
...superclass1Fields,
],
),
);
},
);

test(
'should return all of the fields in the class and the superclass',
() {
final fields = [
FakeFieldElement(
name: 'field1',
),
FakeFieldElement(
name: 'field2',
),
];

final superclass1Fields = [
FakeFieldElement(
name: 'superclass1Field1',
),
FakeFieldElement(
name: 'superclass1Field2',
),
];

expect(
FakeClassElement(
fields: fields,
allSupertypes: [
FakeInterfaceType(
element2: FakeClassElement(
fields: superclass1Fields,
),
),
],
).allFields,
containsAll(
[
...fields,
...superclass1Fields,
],
),
);
},
);

test(
'should return all of the fields in all of the superclasses',
() {
final superclass1Fields = [
FakeFieldElement(
name: 'superclass1Field1',
),
FakeFieldElement(
name: 'superclass1Field2',
),
];

final superclass2Fields = [
FakeFieldElement(
name: 'superclass2Field1',
),
FakeFieldElement(
name: 'superclass2Field2',
),
];

expect(
FakeClassElement(
allSupertypes: [
FakeInterfaceType(
element2: FakeClassElement(
fields: superclass1Fields,
),
),
FakeInterfaceType(
element2: FakeClassElement(
fields: superclass2Fields,
),
),
],
).allFields,
containsAll(
[
...superclass1Fields,
...superclass2Fields,
],
),
);
},
);

test(
'should return all of the fields in the class and all of the superclasses',
() {
final fields = [
FakeFieldElement(
name: 'field1',
),
FakeFieldElement(
name: 'field2',
),
];

final superclass1Fields = [
FakeFieldElement(
name: 'superclass1Field1',
),
FakeFieldElement(
name: 'superclass1Field2',
),
];

final superclass2Fields = [
FakeFieldElement(
name: 'superclass2Field1',
),
FakeFieldElement(
name: 'superclass2Field2',
),
];

expect(
FakeClassElement(
fields: fields,
allSupertypes: [
FakeInterfaceType(
element2: FakeClassElement(
fields: superclass1Fields,
),
),
FakeInterfaceType(
element2: FakeClassElement(
fields: superclass2Fields,
),
),
],
).allFields,
containsAll(
[
...fields,
...superclass1Fields,
...superclass2Fields,
],
),
);
},
);
},
);
},
);
}
5 changes: 5 additions & 0 deletions xml_serializable/test/fake_class_element.dart
Expand Up @@ -18,6 +18,9 @@ class FakeClassElement extends Fake implements ClassElement {
@override
final String name;

@override
final List<InterfaceType> allSupertypes;

@override
InterfaceType get thisType =>
_thisType ??
Expand All @@ -32,10 +35,12 @@ class FakeClassElement extends Fake implements ClassElement {
LibraryElement? library,
List<ElementAnnotation>? metadata,
String? name,
List<InterfaceType>? allSupertypes,
InterfaceType? thisType,
}) : fields = fields ?? [],
library = library ?? FakeLibraryElement(),
metadata = metadata ?? [],
name = name ?? 'HelloWorld',
allSupertypes = allSupertypes ?? [],
_thisType = thisType;
}

0 comments on commit 8679f1c

Please sign in to comment.