perks/codegen-csharp/property.ts

265 строки
8.8 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { intersect, DeepPartial } from '@azure-tools/codegen';
import { docComment, EOL, indent } from '@azure-tools/codegen';
import { Dictionary, length } from '@azure-tools/linq';
import { Abstract, Access, Extern, highestAccess, Modifier, New, Override, Sealed, Static, Virtual } from './access-modifier';
import { Attribute } from './attribute';
import { summary } from './doc-comments';
import { Expression, ExpressionOrLiteral, toExpression, valueOf, isAnExpression } from './expression';
import { OneOrMoreStatements, Statement, Statements, StatementPossibilities } from './statements/statement';
import { TypeDeclaration } from './type-declaration';
import { ExpressionStatement, Instance, Variable } from './variable';
export class Property extends Variable implements Instance {
public 'new': New = Modifier.None;
public getAccess = Access.Public;
public setAccess = Access.Public;
public 'static': Static = Modifier.None;
public virtual: Virtual = Modifier.None;
public sealed: Sealed = Modifier.None;
public override: Override = Modifier.None;
public abstract: Abstract = Modifier.None;
public extern: Extern = Modifier.None;
public attributes = new Array<Attribute>();
public metadata: Dictionary<any> = {};
public description = '';
public get?: StatementPossibilities | Expression;
public set?: StatementPossibilities | Expression;
protected get visibility(): Access {
return highestAccess(this.getAccess, this.setAccess) || Access.Explicit;
}
public add<T extends object>(item: T & (Attribute)): T {
if (item instanceof Attribute) {
this.attributes.push(item);
return item;
}
throw Error(`FATAL - UNABLE TO ADD UNKNOWN TYPE for '${JSON.stringify(item)}'`);
}
protected get attributeDeclaration(): string {
return length(this.attributes) > 0 ? `${this.attributes.joinWith(each => `${each.value}`, EOL)}${EOL}` : '';
}
constructor(public name: string, public type: TypeDeclaration, objectInitializer?: DeepPartial<Property>) {
super();
name = name.trim();
this.apply(objectInitializer);
if (!this.description.trim()) {
this.description = '';
}
}
protected get getterDeclaration(): string {
return this.getAccess === this.visibility ? 'get' : `${this.getAccess} get`;
}
protected get setterDeclaration(): string {
return this.setAccess === this.visibility ? 'set' : `${this.setAccess} set`;
}
protected get getter(): string {
if (!this.get) {
// if there is a set expression/body then this can't bet auto
return this.set ? '' : `${this.getterDeclaration};`;
}
if (isAnExpression(this.get)) {
return `${this.getterDeclaration} => ${valueOf(this.get)};`;
}
const gi = new Statements(this.get).implementation;
return gi.indexOf('\n') == -1 ? `${this.getterDeclaration} { ${gi} }` :
`${this.getterDeclaration}
{
${indent(gi, 2)}
}
`;
}
protected get setter(): string {
if (!this.set) {
// if there is a get expression/body then this can't bet auto
return this.get ? '' : `${this.setterDeclaration};`;
}
if (isAnExpression(this.set)) {
return `${this.setterDeclaration} => ${valueOf(this.set)};`;
}
const si = new Statements(this.set).implementation;
return si.indexOf('\n') == -1 ? `${this.setterDeclaration} { ${si} }` :
`${this.setterDeclaration}
{
${indent(si, 2)}
}
`;
}
public get declaration(): string {
const s = this.setter;
const g = this.getter;
const decl = `
${docComment(summary(this.description))}
${this.attributeDeclaration}${this.new}${this.visibility} ${this.static} ${this.virtual} ${this.sealed} ${this.override} ${this.abstract} ${this.extern} ${this.type.declaration} ${this.name}`;
if (g && s) {
return (g.indexOf('\n') > -1 || s.indexOf('\n') > -1) ?
// at least one is more that one line
`${decl}
{
${indent(g, 2)}
${indent(s, 2)}
}`.slim() :
// both are single line
`${decl} { ${g} ${s} }`.slim();
}
if (s) {
// no getter?
return (s.indexOf('\n') > -1) ?
`${decl}
{
${indent(s, 2)}
}`.slim() :
// both are single line
`${decl} { ${s} }`.slim();
}
if (g) {
// no setter
return (g.indexOf('\n') > -1) ?
`${decl}
{
${indent(g, 2)}
}`.slim() :
// single line
`${decl} { ${g} }`.slim();
}
return `${decl} { get; set; }`.slim();
}
public get value(): string {
return `${this.name}`.trim();
}
public get valuePrivate(): string {
return `${this.name}`.trim();
}
public assign(expression: ExpressionOrLiteral): OneOrMoreStatements {
return `${this.name} = ${valueOf(expression)};`;
}
public assignPrivate(expression: ExpressionOrLiteral): OneOrMoreStatements {
return this.assign(expression);
}
public get declarationExpression(): Expression {
return this;
}
public get declarationStatement(): Statement {
throw new Error('Property can not be a declaration statement');
}
public invokeMethod(methodName: string, ...parameters: Array<Expression>): ExpressionStatement {
const e = `${this.value}.${methodName}(${parameters.joinWith(valueOf)})`;
return intersect(
toExpression(e), {
implementation: `${e};`
});
}
}
export class Indexer extends Property {
constructor(public keyType: TypeDeclaration, valueType: TypeDeclaration, objectInitializer: Partial<Indexer>) {
super(`this[${keyType.declaration} index]`, valueType);
this.apply(objectInitializer);
}
get index() {
return 'index';
}
}
export class LambdaProperty extends Property {
constructor(public name: string, public type: TypeDeclaration, public expression: Expression, objectInitializer?: DeepPartial<LambdaProperty>) {
super(name, type);
this.apply(objectInitializer);
}
public get declaration(): string {
return `
${docComment(summary(this.description))}
${this.attributeDeclaration}${this.new}${this.visibility} ${this.static} ${this.virtual} ${this.sealed} ${this.override} ${this.abstract} ${this.extern} ${this.type.declaration} ${this.name} => ${valueOf(this.expression)};
`.slim();
}
}
export class LazyProperty extends Property {
private backingName: string;
public instanceAccess = 'this';
constructor(public name: string, public type: TypeDeclaration, public expression: Expression, objectInitializer?: DeepPartial<LazyProperty>) {
super(name, type);
this.backingName = `_${this.name.uncapitalize()}`;
this.apply(objectInitializer);
}
public get declaration(): string {
return `
${ docComment(summary(`Backing field for <see cref="${this.name}" /> property.`))}
private ${this.static} ${this.type.declaration} ${this.backingName};
EOL
${docComment(summary(this.description))}
${this.attributeDeclaration}${this.new}${this.visibility} ${this.static} ${this.virtual} ${this.sealed} ${this.override} ${this.abstract} ${this.extern} ${this.type.declaration} ${this.name} => ${this.instanceAccess}.${this.backingName}?? (${this.instanceAccess}.${this.backingName} = ${this.expression.value});
`.slim();
}
}
export class BackedProperty extends Property {
public backingName: string;
public initializer?: ExpressionOrLiteral;
constructor(name: string, type: TypeDeclaration, objectInitializer?: DeepPartial<BackedProperty>) {
const backingName = `_${name.uncapitalize()}`;
super(name, type, {
get: toExpression(`this.${backingName}`),
set: toExpression(`this.${backingName} = value`)
});
this.backingName = backingName;
this.apply(objectInitializer);
}
public get declaration(): string {
return `
${docComment(summary(`Backing field for <see cref="${this.name}" /> property.`))}
private ${this.type.declaration} ${this.backingName}${this.initializer ? `= ${valueOf(this.initializer)}` : ''};
EOL
${super.declaration}
`.trim();
// ${docComment(summary(this.description))}
// ${ this.attributeDeclaration } ${ this.new } ${ this.visibility } ${ this.static } ${ this.virtual } ${ this.sealed } ${ this.override } ${ this.abstract } ${ this.extern } ${ this.type.declaration } ${ this.name } { ${ this.getterDeclaration } { return this.${ this.backingName }; } ${ this.setterDeclaration } { this.${ this.backingName } = value; } }
// EOL
}
public get value(): string {
return `${this.name}`;
}
public get valuePrivate(): string {
return `this.${this.backingName}`;
}
public assignPrivate(expression: ExpressionOrLiteral): OneOrMoreStatements {
return `{${this.backingName} = ${valueOf(expression)};}`;
}
}