Skip to content

Commit 4231198

Browse files
committed
Inheritance support - Method inheritance for classes.
1 parent 8e55f2d commit 4231198

File tree

9 files changed

+103
-25
lines changed

9 files changed

+103
-25
lines changed

docs/Misc-Group/ChildClass.md

+18
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,22 @@ This is a protected string, use carefully.
3434
---
3535
## Methods
3636
### `doSomething()`
37+
### `override overridableMethodOverridden()`
38+
39+
This method was overridden.
40+
41+
#### Return
42+
43+
**Type**
44+
45+
String
46+
47+
**Description**
48+
49+
A String.
50+
51+
### `overridableMethod()`
52+
53+
*Inherited*
54+
3755
---

docs/Misc-Group/ParentClass.md

+1
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,5 @@ This is a protected string, use carefully.
3333
---
3434
## Methods
3535
### `overridableMethod()`
36+
### `overridableMethodOverridden()`
3637
---

examples/force-app/main/default/classes/ChildClass.cls

+8
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,12 @@ public class ChildClass extends ParentClass {
22
public void doSomething() {
33
System.debug('Do something');
44
}
5+
6+
/**
7+
* @description This method was overridden.
8+
* @return A String.
9+
*/
10+
public override String overridableMethodOverridden() {
11+
return '';
12+
}
513
}

examples/force-app/main/default/classes/ParentClass.cls

+4
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,8 @@ public virtual class ParentClass extends GrandparentClass{
99
public virtual String overridableMethod() {
1010
return '';
1111
}
12+
13+
public virtual String overridableMethodOverridden() {
14+
return '';
15+
}
1216
}

src/model/inheritance.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { FieldMirror, MethodMirror, PropertyMirror } from '@cparra/apex-reflection';
2+
3+
export type InheritanceSupport = { inherited: boolean };
4+
export type FieldMirrorWithInheritance = FieldMirror & InheritanceSupport;
5+
export type PropertyMirrorWithInheritance = PropertyMirror & InheritanceSupport;
6+
export type MethodMirrorWithInheritance = MethodMirror & InheritanceSupport;
7+
export type FieldOrProperty = FieldMirrorWithInheritance | PropertyMirrorWithInheritance;

src/model/markdown-generation-util/field-declaration-util.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
import { MarkdownFile } from '../markdown-file';
2-
import { FieldMirror, PropertyMirror } from '@cparra/apex-reflection';
3-
4-
type InheritanceSupport = { inherited: boolean };
5-
type FieldMirrorWithInheritance = FieldMirror & InheritanceSupport;
6-
type PropertyMirrorWithInheritance = PropertyMirror & InheritanceSupport;
7-
type FieldOrProperty = FieldMirrorWithInheritance | PropertyMirrorWithInheritance;
2+
import { FieldMirrorWithInheritance, FieldOrProperty, PropertyMirrorWithInheritance } from '../inheritance';
83

94
export function declareField(
105
markdownFile: MarkdownFile,

src/model/markdown-generation-util/method-declaration-util.ts

+28-10
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,29 @@
1-
import { ConstructorMirror, DocComment, MethodMirror } from '@cparra/apex-reflection';
1+
import { ConstructorMirror, DocComment } from '@cparra/apex-reflection';
22
import { MarkdownFile } from '../markdown-file';
33
import { ParameterMirror } from '@cparra/apex-reflection/index';
44
import { addCustomDocCommentAnnotations } from './doc-comment-annotation-util';
5+
import { MethodMirrorWithInheritance } from '../inheritance';
56

67
export function declareMethod(
78
markdownFile: MarkdownFile,
8-
methods: ConstructorMirror[] | MethodMirror[],
9+
methods: ConstructorMirror[] | MethodMirrorWithInheritance[],
910
startingHeadingLevel: number,
1011
className = '',
1112
): void {
1213
methods.forEach((currentMethod) => {
13-
const signatureName = isMethod(currentMethod) ? (currentMethod as MethodMirror).name : className;
14+
const signatureName = isMethod(currentMethod) ? (currentMethod as MethodMirrorWithInheritance).name : className;
1415
markdownFile.addTitle(`\`${buildSignature(signatureName, currentMethod)}\``, startingHeadingLevel + 2);
16+
17+
// Inheritance tag
18+
if (isMethod(currentMethod)) {
19+
const asMethodMirror = currentMethod as MethodMirrorWithInheritance;
20+
if (asMethodMirror.inherited) {
21+
markdownFile.addBlankLine();
22+
markdownFile.addText('*Inherited*');
23+
markdownFile.addBlankLine();
24+
}
25+
}
26+
1527
currentMethod.annotations.forEach((annotation) => {
1628
markdownFile.addBlankLine();
1729
markdownFile.addText(`\`${annotation.type.toUpperCase()}\``);
@@ -28,7 +40,7 @@ export function declareMethod(
2840
}
2941

3042
if (isMethod(currentMethod)) {
31-
addReturns(markdownFile, currentMethod as MethodMirror, startingHeadingLevel);
43+
addReturns(markdownFile, currentMethod as MethodMirrorWithInheritance, startingHeadingLevel);
3244
}
3345

3446
addThrowsBlock(markdownFile, currentMethod, startingHeadingLevel);
@@ -53,8 +65,8 @@ type DocCommentAware = {
5365

5466
function buildSignature(name: string, parameterAware: ParameterAware): string {
5567
let signature = `${name}(`;
56-
if (isMethod(parameterAware) && (parameterAware as MethodMirror).memberModifiers.length) {
57-
signature = (parameterAware as MethodMirror).memberModifiers.join(' ') + ' ' + signature;
68+
if (isMethod(parameterAware) && (parameterAware as MethodMirrorWithInheritance).memberModifiers.length) {
69+
signature = (parameterAware as MethodMirrorWithInheritance).memberModifiers.join(' ') + ' ' + signature;
5870
}
5971
const signatureParameters = parameterAware.parameters.map((param) => `${param.type} ${param.name}`);
6072
signature += signatureParameters.join(', ');
@@ -63,7 +75,7 @@ function buildSignature(name: string, parameterAware: ParameterAware): string {
6375

6476
function addParameters(
6577
markdownFile: MarkdownFile,
66-
methodModel: MethodMirror | ConstructorMirror,
78+
methodModel: MethodMirrorWithInheritance | ConstructorMirror,
6779
startingHeadingLevel: number,
6880
) {
6981
if (!methodModel.docComment?.paramAnnotations.length) {
@@ -83,7 +95,11 @@ function addParameters(
8395
markdownFile.addBlankLine();
8496
}
8597

86-
function addReturns(markdownFile: MarkdownFile, methodModel: MethodMirror, startingHeadingLevel: number) {
98+
function addReturns(
99+
markdownFile: MarkdownFile,
100+
methodModel: MethodMirrorWithInheritance,
101+
startingHeadingLevel: number,
102+
) {
87103
if (!methodModel.docComment?.returnAnnotation) {
88104
return;
89105
}
@@ -127,6 +143,8 @@ function addExample(markdownFile: MarkdownFile, docCommentAware: DocCommentAware
127143
markdownFile.addBlankLine();
128144
}
129145

130-
function isMethod(method: MethodMirror | ConstructorMirror | ParameterAware): method is ConstructorMirror {
131-
return (method as MethodMirror).type !== undefined;
146+
function isMethod(
147+
method: MethodMirrorWithInheritance | ConstructorMirror | ParameterAware,
148+
): method is ConstructorMirror {
149+
return (method as MethodMirrorWithInheritance).type !== undefined;
132150
}

src/model/markdown-type-file.ts

+11-7
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,16 @@ import {
22
ClassMirror,
33
ConstructorMirror,
44
EnumMirror,
5-
FieldMirror,
65
InterfaceMirror,
76
MethodMirror,
8-
PropertyMirror,
97
Type,
108
} from '@cparra/apex-reflection';
119
import { WalkerFactory } from '../service/walkers/walker-factory';
1210
import { WalkerListener } from '../service/walkers/walker';
1311
import { MarkdownFile } from './markdown-file';
1412
import { declareType, declareMethod, declareField } from './markdown-generation-util';
1513
import ClassFileGeneratorHelper from '../transpiler/markdown/class-file-generatorHelper';
14+
import { FieldMirrorWithInheritance, MethodMirrorWithInheritance, PropertyMirrorWithInheritance } from './inheritance';
1615

1716
interface GroupAware {
1817
group?: string;
@@ -42,12 +41,12 @@ export class MarkdownTypeFile extends MarkdownFile implements WalkerListener {
4241
this.declareMethodWithGroupings(constructors, className);
4342
}
4443

45-
public onFieldsDeclaration(fields: FieldMirror[]): void {
44+
public onFieldsDeclaration(fields: FieldMirrorWithInheritance[]): void {
4645
this.addTitle('Fields', this.headingLevel + 1);
4746
this.declareFieldOrProperty(fields);
4847
}
4948

50-
public onPropertiesDeclaration(properties: PropertyMirror[]): void {
49+
public onPropertiesDeclaration(properties: PropertyMirrorWithInheritance[]): void {
5150
this.addTitle('Properties', this.headingLevel + 1);
5251
this.declareFieldOrProperty(properties);
5352
}
@@ -90,7 +89,10 @@ export class MarkdownTypeFile extends MarkdownFile implements WalkerListener {
9089
return !!groupAware.find((current) => !!current.group);
9190
}
9291

93-
private declareMethodWithGroupings(methods: ConstructorMirror[] | MethodMirror[], className = ''): void {
92+
private declareMethodWithGroupings(
93+
methods: ConstructorMirror[] | MethodMirrorWithInheritance[],
94+
className = '',
95+
): void {
9496
const hasGroupings = this.hasGroupings(methods);
9597
if (!hasGroupings) {
9698
declareMethod(this, methods, this.headingLevel, className);
@@ -105,15 +107,17 @@ export class MarkdownTypeFile extends MarkdownFile implements WalkerListener {
105107
}
106108
}
107109

108-
private declareFieldOrProperty(fieldsOrProperties: FieldMirror[] | PropertyMirror[]): void {
110+
private declareFieldOrProperty(
111+
fieldsOrProperties: FieldMirrorWithInheritance[] | PropertyMirrorWithInheritance[],
112+
): void {
109113
const hasGroupings = this.hasGroupings(fieldsOrProperties);
110114
if (!hasGroupings) {
111115
declareField(this, fieldsOrProperties, this.headingLevel, false);
112116
} else {
113117
const groupedFields = this.group(fieldsOrProperties);
114118
for (const key in groupedFields) {
115119
this.startGroup(key);
116-
const fieldsForGroup = groupedFields[key] as FieldMirror[];
120+
const fieldsForGroup = groupedFields[key] as FieldMirrorWithInheritance[];
117121
declareField(this, fieldsForGroup, this.headingLevel, true);
118122
this.endGroup();
119123
}

src/service/parser.ts

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { Type, ReflectionResult, ClassMirror, FieldMirror, PropertyMirror } from '@cparra/apex-reflection';
1+
import {
2+
Type,
3+
ReflectionResult,
4+
ClassMirror,
5+
FieldMirror,
6+
PropertyMirror,
7+
MethodMirror,
8+
} from '@cparra/apex-reflection';
29
import ApexBundle from '../model/apex-bundle';
310
import MetadataProcessor from './metadata-processor';
411
import { Logger } from '../util/logger';
@@ -7,6 +14,8 @@ export interface TypeParser {
714
parse(reflect: (apexBundle: ApexBundle) => ReflectionResult): Type[];
815
}
916

17+
type NameAware = { name: string };
18+
1019
export class RawBodyParser implements TypeParser {
1120
constructor(public typeBundles: ApexBundle[]) {}
1221

@@ -74,6 +83,7 @@ export class RawBodyParser implements TypeParser {
7483

7584
currentClass.fields = [...currentClass.fields, ...this.getInheritedFields(parentAsClass, currentClass)];
7685
currentClass.properties = [...currentClass.properties, ...this.getInheritedProperties(parentAsClass, currentClass)];
86+
currentClass.methods = [...currentClass.methods, ...this.getInheritedMethods(parentAsClass, currentClass)];
7787
return currentClass;
7888
}
7989

@@ -103,7 +113,20 @@ export class RawBodyParser implements TypeParser {
103113
return parentProperties;
104114
}
105115

106-
memberExists(members: FieldMirror[] | PropertyMirror[], fieldName: string): boolean {
116+
private getInheritedMethods(parentAsClass: ClassMirror, currentClass: ClassMirror) {
117+
const parentMethods = parentAsClass.methods
118+
// Filter out private methods
119+
.filter((currentMethod) => currentMethod.access_modifier.toLowerCase() !== 'private')
120+
// Filter out methods that also exist on the child
121+
.filter((currentMethod) => !this.memberExists(currentClass.methods, currentMethod.name))
122+
.map((currentMethod) => ({
123+
...currentMethod,
124+
inherited: true,
125+
}));
126+
return parentMethods;
127+
}
128+
129+
memberExists(members: NameAware[], fieldName: string): boolean {
107130
const fieldNames = members.map((currentMember) => currentMember.name);
108131
return fieldNames.includes(fieldName);
109132
}

0 commit comments

Comments
 (0)