From 31ff2bb854c096cb6c178b2e80c538db73cb2c34 Mon Sep 17 00:00:00 2001 From: Ahmedeldeeb50 Date: Mon, 21 Sep 2020 10:39:15 +0200 Subject: Support parsing of XML files within Velocity and Jinja Template. Issue-ID: CCSDK-2769 Signed-off-by: Ahmedeldeeb50 Change-Id: Ib79af3f7621d14176700d80e987c0cdf5a8a11a6 --- .../templ-mapp-creation/TemplateType.ts | 5 -- .../templ-mapp-creation.component.html | 16 +++--- .../templ-mapp-creation.component.ts | 66 +++++++--------------- .../templ-mapp-listing.component.ts | 45 ++++++++++----- .../template-mapping/utils/Parser.spec.ts | 28 --------- .../template-mapping/utils/Parser.ts | 3 - .../utils/ParserFactory/JinjaXML.ts | 31 ++++++++++ .../utils/ParserFactory/Parser.spec.ts | 28 +++++++++ .../template-mapping/utils/ParserFactory/Parser.ts | 3 + .../utils/ParserFactory/ParserFactory.ts | 32 +++++++++++ .../utils/ParserFactory/VtlParser.ts | 51 +++++++++++++++++ .../utils/ParserFactory/XmlParser.ts | 33 +++++++++++ .../template-mapping/utils/TemplateType.ts | 13 +++++ .../template-mapping/utils/XmlParser.ts | 34 ----------- .../packages/package-creation/template.store.ts | 2 + 15 files changed, 252 insertions(+), 138 deletions(-) delete mode 100644 cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/TemplateType.ts delete mode 100644 cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/Parser.spec.ts delete mode 100644 cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/Parser.ts create mode 100644 cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/JinjaXML.ts create mode 100644 cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/Parser.spec.ts create mode 100644 cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/Parser.ts create mode 100644 cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/ParserFactory.ts create mode 100644 cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/VtlParser.ts create mode 100644 cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/XmlParser.ts create mode 100644 cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/TemplateType.ts delete mode 100644 cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/XmlParser.ts diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/TemplateType.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/TemplateType.ts deleted file mode 100644 index 17a4bfae6..000000000 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/TemplateType.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum TemplateType { - Velocity = 'vtl', - Koltin = 'kt', - Jinja = 'Unknown' -} diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.html b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.html index dda6231d9..dfd8c31a8 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.html +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.html @@ -44,24 +44,24 @@
-
Use the editor to add parameters or you can also - Import File.
When you import new file, the new attributes will replace diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.ts index 346a06d83..78449fba9 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-creation/templ-mapp-creation.component.ts @@ -10,9 +10,11 @@ import { PackageCreationUtils } from '../../package-creation.utils'; import { JsonConvert, Any } from 'json2typescript'; import { ToastrService } from 'ngx-toastr'; import { SharedService } from '../shared-service'; -import { XmlParser } from '../utils/XmlParser'; +import { XmlParser } from '../utils/ParserFactory/XmlParser'; import { TourService } from 'ngx-tour-md-menu'; import { PackageCreationService } from '../../package-creation.service'; +import { ParserFactory } from '../utils/ParserFactory/ParserFactory'; +import { TemplateType, FileExtension } from '../utils/TemplateType'; declare var $: any; @Component({ @@ -43,7 +45,7 @@ export class TemplMappCreationComponent implements OnInit, OnDestroy { MappingAdapter: MappingAdapter; mapping = new Map(); templateFileContent: string; - templateExt = 'Velcoity'; + templateExt = 'vtl'; dependancies = new Map>(); dependanciesSource = new Map(); mappingRes = []; @@ -51,6 +53,7 @@ export class TemplMappCreationComponent implements OnInit, OnDestroy { currentMapping: any; edit = false; fileToDelete: any = {}; + parserFactory = new ParserFactory(); constructor( private packageCreationStore: PackageCreationStore, @@ -82,12 +85,16 @@ export class TemplMappCreationComponent implements OnInit, OnDestroy { } else { this.mappingRes = []; this.currentMapping = Any; + this.resourceDictionaryRes = []; } this.templateFileContent = templateInfo.fileContent; + this.templateExt = this.templateInfo.ext || this.templateExt ; this.currentTemplate = Object.assign({}, templateInfo); if (templateInfo.type === 'template' || templateInfo.type.includes('template')) { - this.currentTemplate.fileName = 'Templates/' + this.fileName + '-template.vtl'; + console.log('template extension ' + this.templateExt); + this.currentTemplate.fileName = 'Templates/' + this.fileName + '-template.' + this.templateExt; + console.log(this.currentTemplate.fileName); } else { this.currentTemplate = Any; } @@ -95,7 +102,7 @@ export class TemplMappCreationComponent implements OnInit, OnDestroy { }); this.sharedService.isEdit().subscribe(res => { - console.log('------------------------'); + console.log('------------------------....'); console.log(res); this.edit = res; @@ -126,11 +133,11 @@ export class TemplMappCreationComponent implements OnInit, OnDestroy { getFileExtension() { switch (this.templateExt) { - case 'Velcoity': + case 'vtl': return '.vtl'; - case 'Koltin': + case 'kt': return '.ktl'; - case 'Jinja': + case 'j2': return '.j2'; default: return '.vtl'; @@ -143,34 +150,10 @@ export class TemplMappCreationComponent implements OnInit, OnDestroy { } public getTemplateVariable(fileContent: string) { - const variables: string[] = []; - const stringsSlittedByBraces = fileContent.split('${'); - const stringsDefaultByDollarSignOnly = fileContent.split('"$'); - - for (let i = 1; i < stringsSlittedByBraces.length; i++) { - const element = stringsSlittedByBraces[i]; - if (element) { - const firstElement = element.split('}')[0]; - if (!variables.includes(firstElement)) { - variables.push(firstElement); - } else { - console.log(firstElement); - } - } - } - - for (let i = 1; i < stringsDefaultByDollarSignOnly.length; i++) { - const element = stringsDefaultByDollarSignOnly[i]; - if (element && !element.includes('$')) { - const firstElement = element.split('"')[0] - .replace('{', '') - .replace('}', '').trim(); - if (!variables.includes(firstElement)) { - variables.push(firstElement); - } - } - } - return variables; + // TODO: implement factory Pattern for parser + console.log('start parsing........ ' + this.templateExt); + const parser = this.parserFactory.getParser(fileContent, this.templateExt); + return parser.getVariables(fileContent); } public dropped(files: NgxFileDropEntry[]) { @@ -253,6 +236,7 @@ export class TemplMappCreationComponent implements OnInit, OnDestroy { fileReader.onload = (e) => { this.templateFileContent = fileReader.result.toString(); this.variables = this.getTemplateVariable(this.templateFileContent); + console.log(this.variables); }; fileReader.readAsText(file); @@ -395,18 +379,6 @@ export class TemplMappCreationComponent implements OnInit, OnDestroy { rerender(): void { this.dtTrigger.next(); - - // if (this.dtElement.dtInstance) { - // console.log('rerender'); - // this.dtElement.dtInstance.then((dtInstance: DataTables.Api) => { - // dtInstance.destroy(); - // this.dtElement.dtOptions = this.dtOptions; - // this.dtElement.dtTrigger.next(); - // dtInstance.draw(); - // }); - // } else { - // this.dtTrigger.next(); - // } } ngOnDestroy(): void { diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-listing/templ-mapp-listing.component.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-listing/templ-mapp-listing.component.ts index 5750bf430..722be321c 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-listing/templ-mapp-listing.component.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/templ-mapp-listing/templ-mapp-listing.component.ts @@ -6,6 +6,8 @@ import { TemplateAndMapping } from '../TemplateAndMapping'; import { ActivatedRoute } from '@angular/router'; import { SharedService } from '../shared-service'; import { TourService } from 'ngx-tour-md-menu'; +import { TemplateType } from '../utils/TemplateType'; +import { of } from 'rxjs'; @Component({ @@ -22,7 +24,7 @@ export class TemplMappListingComponent implements OnInit { isCreate = true; currentFile: string; edit = false; - fileToDelete: any = {}; + fileToDelete = ''; constructor( private packageCreationStore: PackageCreationStore, @@ -117,19 +119,25 @@ export class TemplMappListingComponent implements OnInit { setSourceCodeEditor(key: string) { this.currentFile = key; - const templateKey = 'Templates/' + key + '-template.vtl'; + const templateKey = 'Templates/' + key + '-template'; this.packageCreationStore.state$.subscribe(cba => { console.log('cba ------'); console.log(cba); console.log(key); console.log(this.templateAndMappingMap); const templateInfo = new TemplateInfo(); - if (cba.templates && cba.templates.files.has(templateKey)) { - const fileContent = cba.templates.getValue(templateKey.trim()); - console.log(fileContent); - templateInfo.fileContent = fileContent; - templateInfo.fileName = templateKey; - templateInfo.type = 'template'; + // tslint:disable-next-line: forin + for (const templateType in TemplateType) { + const fileName = templateKey + '.' + TemplateType[templateType]; + if (cba.templates && cba.templates.files.has(fileName)) { + const fileContent = cba.templates.getValue(fileName.trim()); + console.log(templateType + '......ccccccc.... ' + fileName); + templateInfo.fileContent = fileContent; + templateInfo.fileName = fileName; + templateInfo.ext = TemplateType[templateType]; + templateInfo.type = 'template'; + break; + } } const mappingKey = 'Templates/' + key + '-mapping.json'; if (cba.mapping && cba.mapping.files.has(mappingKey)) { @@ -153,18 +161,29 @@ export class TemplMappListingComponent implements OnInit { } initDelete(file) { console.log(file); - this.fileToDelete = file; + const templateKey = 'Templates/' + file + '-template'; + // tslint:disable-next-line: forin + for (const templateType in TemplateType) { + const fileName = templateKey + '.' + TemplateType[templateType]; + if (this.packageCreationStore.state.templates.files.has(fileName)) { + this.fileToDelete = fileName; + break; + } + } + } condifrmDelete() { - console.log(this.templateAndMappingMap); - this.templateAndMappingMap.delete(this.fileToDelete); + const file = this.fileToDelete.split('/')[1].split('-')[0]; + const ext = this.fileToDelete.split('/')[1].split('.')[1]; + this.templateAndMappingMap.delete(file); if (this.templateAndMappingMap.size <= 0) { this.openCreationView(); } // Delete from templates - this.packageCreationStore.state.templates.files.delete('Templates/' + this.fileToDelete + '-template.vtl'); + this.packageCreationStore.state.templates.files.delete('Templates/' + file + '-template.' + ext); // Delete from Mapping - this.packageCreationStore.state.mapping.files.delete('Templates/' + this.fileToDelete + '-mapping.json'); + this.packageCreationStore.state.mapping.files.delete('Templates/' + file + '-mapping.json'); + console.log(this.templateAndMappingMap); } diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/Parser.spec.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/Parser.spec.ts deleted file mode 100644 index e90377e0c..000000000 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/Parser.spec.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { XmlParser } from './XmlParser'; - -fdescribe('ImportsTabComponent', () => { - const parser: XmlParser = new XmlParser(); - - - beforeEach(() => { - }); - - it('Test xml Parser', () => { - const fileContent = ` - - - $vdns_int_private_ip_0 - $vdns_onap_private_ip_0 - false - dddd - - - `; - - const res = parser.getVariables(fileContent); - console.log(res); - expect(res.length).toEqual(2); - expect(res[0]).toEqual('vdns_int_private_ip_0'); - expect(res[1]).toEqual('vdns_onap_private_ip_0'); - }); -}); diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/Parser.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/Parser.ts deleted file mode 100644 index 495c64307..000000000 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/Parser.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface Parser { - getVariables(fileContent: string): string[]; -} diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/JinjaXML.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/JinjaXML.ts new file mode 100644 index 000000000..7a8042433 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/JinjaXML.ts @@ -0,0 +1,31 @@ +import { Parser } from './Parser'; + +export class JinjaXMLParser implements Parser { + getVariables(fileContent: string): string[] { + const variables = []; + if (fileContent.includes('>[')) { + const xmlSplit = fileContent.split('>['); + for (const val of xmlSplit) { + const res = val.substring(0, val.indexOf('] 0) { + variables.push(res); + } + + } + } + return variables; + } + +} + +/* + + + + + +[hostname] + + + +*/ diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/Parser.spec.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/Parser.spec.ts new file mode 100644 index 000000000..e90377e0c --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/Parser.spec.ts @@ -0,0 +1,28 @@ +import { XmlParser } from './XmlParser'; + +fdescribe('ImportsTabComponent', () => { + const parser: XmlParser = new XmlParser(); + + + beforeEach(() => { + }); + + it('Test xml Parser', () => { + const fileContent = ` + + + $vdns_int_private_ip_0 + $vdns_onap_private_ip_0 + false + dddd + + + `; + + const res = parser.getVariables(fileContent); + console.log(res); + expect(res.length).toEqual(2); + expect(res[0]).toEqual('vdns_int_private_ip_0'); + expect(res[1]).toEqual('vdns_onap_private_ip_0'); + }); +}); diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/Parser.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/Parser.ts new file mode 100644 index 000000000..495c64307 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/Parser.ts @@ -0,0 +1,3 @@ +export interface Parser { + getVariables(fileContent: string): string[]; +} diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/ParserFactory.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/ParserFactory.ts new file mode 100644 index 000000000..6cc62758e --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/ParserFactory.ts @@ -0,0 +1,32 @@ + +import { XmlParser } from './XmlParser'; +import { Parser } from './Parser'; +import { VtlParser } from './VtlParser'; +import { FileExtension } from '../TemplateType'; +import { JinjaXMLParser } from './JinjaXML'; + +export class ParserFactory { + + getParser(fileContent: string, fileExtension: string): Parser { + let parser: Parser; + console.log('file extension =' + fileExtension); + if (fileExtension === FileExtension.Velocity) { + if (this.isXML(fileContent)) { + parser = new XmlParser(); + } else { + parser = new VtlParser(); + } + } else if (fileExtension === FileExtension.Jinja) { + if (this.isXML(fileContent)) { + parser = new JinjaXMLParser(); + } + } else if (fileExtension === FileExtension.XML) { + parser = new XmlParser(); + } + return parser; + } + + private isXML(fileContent: string): boolean { + return fileContent.includes(''); + } +} diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/VtlParser.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/VtlParser.ts new file mode 100644 index 000000000..2b2e17fb9 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/VtlParser.ts @@ -0,0 +1,51 @@ +import { Parser } from './Parser'; + +export class VtlParser implements Parser { + getVariables(fileContent: string): string[] { + const variables: string[] = []; + const stringsSlittedByBraces = fileContent.split('${'); + const stringsDefaultByDollarSignOnly = fileContent.split('"$'); + + for (let i = 1; i < stringsSlittedByBraces.length; i++) { + const element = stringsSlittedByBraces[i]; + if (element) { + const firstElement = element.split('}')[0]; + if (!variables.includes(firstElement)) { + variables.push(firstElement); + } else { + console.log(firstElement); + } + } + } + + for (let i = 1; i < stringsDefaultByDollarSignOnly.length; i++) { + const element = stringsDefaultByDollarSignOnly[i]; + if (element && !element.includes('$')) { + const firstElement = element.split('"')[0] + .replace('{', '') + .replace('}', '').trim(); + if (!variables.includes(firstElement)) { + variables.push(firstElement); + } + } + } + return variables; + } + +} + +/* + + + + + $vdns_int_private_ip_0 + $vdns_onap_private_ip_0 + aaaa + false + dddd + + + + +*/ diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/XmlParser.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/XmlParser.ts new file mode 100644 index 000000000..5cb9c9f81 --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/ParserFactory/XmlParser.ts @@ -0,0 +1,33 @@ +import { Parser } from './Parser'; + +export class XmlParser implements Parser { + getVariables(fileContent: string): string[] { + const variables = []; + const xmlSplit = fileContent.split('$'); + for (const val of xmlSplit) { + const res = val.substring(0, val.indexOf(' 0) { + variables.push(res); + } + + } + return variables; + } + +} + +/* + + + + + $vdns_int_private_ip_0 + $vdns_onap_private_ip_0 + aaaa + false + dddd + + + + +*/ diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/TemplateType.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/TemplateType.ts new file mode 100644 index 000000000..04a829e8a --- /dev/null +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/TemplateType.ts @@ -0,0 +1,13 @@ +export enum TemplateType { + Velocity = 'vtl', + Koltin = 'kt', + Jinja = 'j2', +} + +export enum FileExtension { + Velocity = 'vtl', + Koltin = 'kt', + Jinja = 'j2', + CSV = 'csv', + XML = 'xml' +} diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/XmlParser.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/XmlParser.ts deleted file mode 100644 index 4feb7032a..000000000 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template-mapping/utils/XmlParser.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { Parser } from './Parser'; -import { variable } from '@angular/compiler/src/output/output_ast'; - -export class XmlParser implements Parser { - getVariables(fileContent: string): string[] { - const variables = []; - const xmlSplit = fileContent.split('$'); - for (const val of xmlSplit) { - const res = val.substring(0, val.indexOf(' 0) { - variables.push(res); - } - - } - return variables; - } - -} - -/* - - - - - $vdns_int_private_ip_0 - $vdns_onap_private_ip_0 - aaaa - false - dddd - - - - -*/ diff --git a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template.store.ts b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template.store.ts index 9c8775514..4b12bb130 100644 --- a/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template.store.ts +++ b/cds-ui/designer-client/src/app/modules/feature-modules/packages/package-creation/template.store.ts @@ -28,6 +28,7 @@ export class TemplateInfo { fileName: string; fileContent: string; type: string; + ext: string; mapping = []; @@ -35,6 +36,7 @@ export class TemplateInfo { this.fileName = ''; this.fileContent = ''; this.type = ''; + this.ext = ''; } -- cgit 1.2.3-korg