@@ -10,14 +10,25 @@ import { ILocalizedString, ILocFile } from '../interfaces';
1010
1111const STRING_NAME_RESX : RegExp = / ^ [ A - z _ ] [ A - z 0 - 9 _ ] * $ / ;
1212
13+ export interface ILogger {
14+ logError : ( message : string ) => void ;
15+ logWarning : ( message : string ) => void ;
16+ logFileError : ( message : string , filePath : string , line ?: number , position ?: number ) => void ;
17+ logFileWarning : ( message : string , filePath : string , line ?: number , position ?: number ) => void ;
18+ }
19+
20+ export interface IResxReaderOptions extends ILogger {
21+ resxFilePath : string ;
22+ }
23+
1324export class ResxReader {
14- public static readResxFileAsLocFile ( resxFilePath : string ) : ILocFile {
15- const fileContents : string = FileSystem . readFile ( resxFilePath ) ;
25+ public static readResxFileAsLocFile ( options : IResxReaderOptions ) : ILocFile {
26+ const fileContents : string = FileSystem . readFile ( options . resxFilePath ) ;
1627 const xmlDocument : XmlDocument = new XmlDocument ( fileContents ) ;
1728
1829 if ( xmlDocument . name !== 'root' ) {
19- ResxReader . _throwResxExceptionWithLocation (
20- resxFilePath ,
30+ ResxReader . _logErrorWithLocation (
31+ options ,
2132 `Expected RESX to have a "root" element, found "${ xmlDocument . name } "` ,
2233 xmlDocument
2334 ) ;
@@ -32,22 +43,24 @@ export class ResxReader {
3243 case 'data' : {
3344 const stringName : string = childNode . attr . name ;
3445 if ( ! stringName ) {
35- ResxReader . _throwResxExceptionWithLocation (
36- resxFilePath ,
46+ ResxReader . _logErrorWithLocation (
47+ options ,
3748 'Unexpected missing or empty string name' ,
3849 childNode
3950 ) ;
40- }
41-
42- if ( ! STRING_NAME_RESX . test ( stringName ) ) {
43- ResxReader . _throwResxExceptionWithLocation (
44- resxFilePath ,
51+ } else if ( ! STRING_NAME_RESX . test ( stringName ) ) {
52+ ResxReader . _logErrorWithLocation (
53+ options ,
4554 `Invalid string name "${ stringName } "` ,
4655 childNode
4756 ) ;
48- }
57+ } else {
58+ const locString : ILocalizedString | undefined = ResxReader . _readDataElement ( options , childNode ) ;
4959
50- locFile [ stringName ] = ResxReader . _readDataElement ( resxFilePath , childNode ) ;
60+ if ( locString ) {
61+ locFile [ stringName ] = locString ;
62+ }
63+ }
5164
5265 break ;
5366 }
@@ -58,8 +71,8 @@ export class ResxReader {
5871 break ;
5972
6073 default :
61- ResxReader . _throwResxExceptionWithLocation (
62- resxFilePath ,
74+ ResxReader . _logErrorWithLocation (
75+ options ,
6376 `Unexpected RESX element ${ childNode . name } ` ,
6477 childNode
6578 ) ;
@@ -70,172 +83,187 @@ export class ResxReader {
7083
7184 case 'text' : {
7285 if ( childNode . text . trim ( ) !== '' ) {
73- ResxReader . _throwResxException ( resxFilePath , 'Found unexpected non-empty text node in RESX' ) ;
86+ ResxReader . _logErrorWithLocation ( options , 'Found unexpected non-empty text node in RESX' ) ;
7487 }
88+
89+ break ;
7590 }
7691
7792 case 'comment' :
7893 break ;
7994
8095 default :
81- ResxReader . _throwResxException ( resxFilePath , `Unexpected ${ childNode . type } child in RESX` ) ;
96+ ResxReader . _logErrorWithLocation ( options , `Unexpected ${ childNode . type } child in RESX` ) ;
97+ break ;
8298 }
8399 }
84100
85101 return locFile ;
86102 }
87103
88- private static _readDataElement ( resxFilePath : string , dataElement : XmlElement ) : ILocalizedString {
89- let comment : string | undefined = undefined
104+ private static _readDataElement ( options : IResxReaderOptions , dataElement : XmlElement ) : ILocalizedString | undefined {
105+ let foundCommentElement : boolean = false ;
106+ let foundValueElement : boolean = false ;
107+ let comment : string | undefined = undefined ;
90108 let value : string | undefined = undefined ;
91109
92110 for ( const childNode of dataElement . children ) {
93111 switch ( childNode . type ) {
94112 case 'element' : {
95113 switch ( childNode . name ) {
96114 case 'value' : {
97- if ( value !== undefined ) {
98- ResxReader . _throwResxExceptionWithLocation (
99- resxFilePath ,
115+ if ( foundValueElement ) {
116+ ResxReader . _logErrorWithLocation (
117+ options ,
100118 'Duplicate <value> element found' ,
101119 childNode
102120 ) ;
121+ } else {
122+ foundValueElement = true ;
123+ value = ResxReader . _readTextElement ( options , childNode ) ;
103124 }
104125
105- value = ResxReader . _readTextElement ( resxFilePath , childNode ) ;
106126 break ;
107127 }
108128
109129 case 'comment' : {
110- if ( comment !== undefined ) {
111- ResxReader . _throwResxExceptionWithLocation (
112- resxFilePath ,
130+ if ( foundCommentElement ) {
131+ ResxReader . _logErrorWithLocation (
132+ options ,
113133 'Duplicate <comment> element found' ,
114134 childNode
115135 ) ;
136+ } else {
137+ foundCommentElement = true ;
138+ comment = ResxReader . _readTextElement ( options , childNode ) ;
116139 }
117140
118- comment = ResxReader . _readTextElement ( resxFilePath , childNode ) ;
141+ break ;
119142 }
120143
121144 default :
122- ResxReader . _throwResxExceptionWithLocation (
123- resxFilePath ,
145+ ResxReader . _logErrorWithLocation (
146+ options ,
124147 `Unexpected RESX element ${ childNode . name } ` ,
125148 childNode
126149 ) ;
150+ break ;
127151 }
128152
129153 break ;
130154 }
131155
132156 case 'text' : {
133157 if ( childNode . text . trim ( ) !== '' ) {
134- ResxReader . _throwResxExceptionWithLocation (
135- resxFilePath ,
158+ ResxReader . _logErrorWithLocation (
159+ options ,
136160 'Found unexpected non-empty text node in RESX <data> element' ,
137161 dataElement
138162 ) ;
139163 }
164+
165+ break ;
140166 }
141167
142168 case 'comment' :
143169 break ;
144170
145171 default :
146- ResxReader . _throwResxExceptionWithLocation (
147- resxFilePath ,
172+ ResxReader . _logErrorWithLocation (
173+ options ,
148174 `Unexpected ${ childNode . type } child in RESX <data> element` ,
149175 dataElement
150176 ) ;
151177 }
152178 }
153179
154180 if ( value === undefined ) {
155- ResxReader . _throwResxExceptionWithLocation (
156- resxFilePath ,
157- 'Missing <value> element in <data> element' ,
158- dataElement
159- ) ;
160- } else if ( comment === undefined ) {
161- ResxReader . _throwResxExceptionWithLocation (
162- resxFilePath ,
163- 'Missing <comment> element in <data> element' ,
181+ ResxReader . _logErrorWithLocation (
182+ options ,
183+ 'Missing string value in <data> element' ,
164184 dataElement
165185 ) ;
166186 } else {
187+ if ( comment === undefined ) {
188+ ResxReader . _logWarningWithLocation (
189+ options ,
190+ 'Missing string comment in <data> element' ,
191+ dataElement
192+ ) ;
193+ }
194+
167195 return {
168196 value,
169- comment
197+ comment : comment || ''
170198 } ;
171199 }
172200 }
173201
174- private static _readTextElement ( resxFilePath : string , element : XmlElement ) : string {
175- if ( element . children . length !== 1 ) {
176- ResxReader . _throwResxExceptionWithLocation (
177- resxFilePath ,
178- 'Expected text or CDATA' ,
179- element
180- ) ;
181- }
182-
202+ private static _readTextElement ( options : IResxReaderOptions , element : XmlElement ) : string | undefined {
183203 let foundText : string | undefined = undefined ;
184204
185205 for ( const childNode of element . children ) {
186206 switch ( childNode . type ) {
187207 case 'cdata' :
188208 case 'text' : {
189209 if ( foundText !== undefined ) {
190- ResxReader . _throwResxExceptionWithLocation (
191- resxFilePath ,
210+ ResxReader . _logErrorWithLocation (
211+ options ,
192212 'More than one child node found containing text content' ,
193213 element
194214 ) ;
215+ break ;
195216 }
196217
197218 foundText = childNode . type === 'text' ? childNode . text . trim ( ) : childNode . cdata ;
219+ break ;
198220 }
199221
200222 case 'comment' :
201223 break ;
202224
203225 case 'element' :
204- ResxReader . _throwResxExceptionWithLocation (
205- resxFilePath ,
226+ ResxReader . _logErrorWithLocation (
227+ options ,
206228 `Unexpected element` ,
207229 childNode
208230 ) ;
231+ break ;
209232
210233 default :
211- ResxReader . _throwResxExceptionWithLocation (
212- resxFilePath ,
234+ ResxReader . _logErrorWithLocation (
235+ options ,
213236 `Unexpected ${ element . type } child` ,
214237 element
215238 ) ;
239+ break ;
216240 }
217241 }
218242
219- if ( foundText === undefined ) {
220- ResxReader . _throwResxExceptionWithLocation (
221- resxFilePath ,
222- 'Did not find a content node' ,
223- element
224- ) ;
225- } else {
226- return foundText ;
227- }
243+ return foundText ;
228244 }
229245
230- private static _throwResxException ( resxFilePath : string , message : string ) : never {
231- throw new Error ( `${ resxFilePath } : ${ message } ` ) ;
246+ private static _logErrorWithLocation (
247+ options : IResxReaderOptions ,
248+ message : string ,
249+ element ?: XmlElement | XmlDocument
250+ ) : void {
251+ if ( element ) {
252+ options . logFileError ( message , options . resxFilePath , element . line , element . position ) ;
253+ } else {
254+ options . logFileError ( message , options . resxFilePath ) ;
255+ }
232256 }
233257
234- private static _throwResxExceptionWithLocation (
235- resxFilePath : string ,
258+ private static _logWarningWithLocation (
259+ options : IResxReaderOptions ,
236260 message : string ,
237- element : XmlElement | XmlDocument
238- ) : never {
239- throw new Error ( `${ resxFilePath } (${ element . line } ,${ element . position } ): ${ message } ` ) ;
261+ element ?: XmlElement | XmlDocument
262+ ) : void {
263+ if ( element ) {
264+ options . logFileWarning ( message , options . resxFilePath , element . line , element . position ) ;
265+ } else {
266+ options . logFileWarning ( message , options . resxFilePath ) ;
267+ }
240268 }
241269}
0 commit comments