1/*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15interface NativeUri {
16  new(input: string): NativeUri;
17  normalize(): string;
18  equals(other: NativeUri): boolean;
19  equalsTo(other: NativeUri): boolean;
20  checkIsAbsolute(): boolean;
21  toString(): string;
22  checkIsRelative(): boolean;
23  checkIsOpaque(): boolean;
24  checkIsHierarchical(): boolean;
25  addQueryValue(key: string, value: string): string;
26  addSegment(pathSegment: string):string;
27  getSegment(): string[];
28  clearQuery(): string;
29  scheme: string | null;
30  authority: string | null;
31  ssp: string;
32  userInfo: string | null;
33  host: string | null;
34  port: string;
35  path: string | null;
36  query: string | null;
37  fragment: string | null;
38  isFailed: string;
39  encodedUserInfo: string | null;
40  encodedPath: string | null;
41  encodedQuery: string | null;
42  encodedFragment: string | null;
43  encodedAuthority: string | null;
44  encodedSSP: string | null;
45}
46interface UriInterface {
47  Uri: NativeUri;
48}
49declare function requireInternal(s: string): UriInterface;
50const uri = requireInternal('uri');
51
52const TypeErrorCodeId = 401;
53const SyntaxErrorCodeId = 10200002;
54
55class BusinessError extends Error {
56  code: number;
57  constructor(msg: string) {
58    super(msg);
59    this.name = 'BusinessError';
60    this.code = TypeErrorCodeId;
61  }
62}
63
64class URI {
65  uricalss: NativeUri;
66  constructor(input: string) {
67    if (typeof input !== 'string' || input.length === 0) {
68      throw new BusinessError(`Parameter error. The type of ${input} must be string`);
69    }
70    this.uricalss = new uri.Uri(input);
71    let errStr: string = this.uricalss.isFailed;
72    if (errStr.length !== 0) {
73      let err : BusinessError = new BusinessError(`Syntax Error. Invalid Uri string: The ${errStr}`);
74      err.code = SyntaxErrorCodeId;
75      throw err;
76    }
77  }
78
79  static createFromParts(scheme: string, ssp: string, fragment: string): URI {
80    if (scheme === null || typeof scheme !== 'string') {
81      throw new BusinessError(`Parameter error. The type of ${scheme} must be string`);
82    }
83    if (ssp === null || typeof ssp !== 'string') {
84      throw new BusinessError(`Parameter error. The type of ${ssp} must be string`);
85    }
86    if (typeof fragment !== 'string') {
87      throw new BusinessError(`Parameter error. The type of ${fragment} must be string`);
88    }
89    let uriStr: string = scheme;
90    uriStr += ':' + encodeURIComponent(ssp);
91    if (fragment !== null && fragment !== '') {
92      uriStr += '#' + encodeURIComponent(fragment);
93    }
94    return createNewUri(uriStr);
95  }
96
97  toString(): string {
98    return toAscllString(this.uricalss.toString());
99  }
100
101  equals(other: URI): boolean {
102    return this.uricalss.equals(other.uricalss);
103  }
104
105  equalsTo(other: URI): boolean {
106    if (other instanceof URI) {
107      return this.uricalss.equals(other.uricalss);
108    }
109    throw new BusinessError(`Parameter error. The type of ${other} must be URI`);
110  }
111
112  checkIsAbsolute(): boolean {
113    return this.uricalss.checkIsAbsolute();
114  }
115
116  checkRelative(): boolean {
117    return this.uricalss.checkIsRelative();
118  }
119
120  checkOpaque(): boolean {
121    return this.uricalss.checkIsOpaque();
122  }
123
124  checkHierarchical(): boolean {
125    return this.uricalss.checkIsHierarchical();
126  }
127
128  addQueryValue(key: string, value: string): URI {
129    if (key === null || typeof key !== 'string') {
130      throw new BusinessError(`Parameter error. The type of ${key} must be string`);
131    }
132    if (value === null || typeof value !== 'string') {
133      throw new BusinessError(`Parameter error. The type of ${value} must be string`);
134    }
135    let uriStr = this.uricalss.addQueryValue(encodeURIComponent(key), encodeURIComponent(value));
136    return createNewUri(uriStr);
137  }
138
139  addEncodedSegment(pathSegment: string): URI {
140    if (pathSegment === null || typeof pathSegment !== 'string') {
141      throw new BusinessError(`Parameter error. The type of ${pathSegment} must be string`);
142    }
143    let uriStr: string = this.uricalss.addSegment(pathSegment);
144    return createNewUri(uriStr);
145  }
146
147  addSegment(pathSegment: string): URI {
148    if (pathSegment === null || typeof pathSegment !== 'string') {
149      throw new BusinessError(`Parameter error. The type of ${pathSegment} must be string`);
150    }
151    let uriStr = this.uricalss.addSegment(encodeURIComponent(pathSegment));
152    return createNewUri(uriStr);
153  }
154
155  getQueryValue(key: string): string | null {
156    if (key === null || typeof key !== 'string') {
157      throw new BusinessError(`Parameter error. The type of ${key} must be string`);
158    }
159    let value: string | null = null;
160    if (this.uricalss.query === null) {
161      return null;
162    }
163    let queryStrs: string[] = this.uricalss.query.split('&') || [];
164    for (let item of queryStrs) {
165      if (key === '' && item === '') {
166        return '';
167      }
168      let str = item.split('=') || [];
169      if (str.length === 1 && this.decodeSafelyInner(str[0]) === key) {
170        return '';
171      } else if (this.decodeSafelyInner(str[0]) === key) {
172        return this.decodeSafelyInner(item.substring(str[0].length + 1).replace(/\+/g, ' '));
173      }
174    }
175    return value;
176  }
177
178  decodeSafelyOut(input: string): string {
179    let decodedString: string = '';
180    let decodedTemp: string = '';
181    let index: number = 0;
182    while (index < input.length) {
183      if (input[index] === '%' && /[0-9A-Fa-f]{2}/.test(input.slice(index + 1, index + 3))) {
184        const encodedChar = input.slice(index, index + 3);
185        try {
186          decodedString += decodeURIComponent(decodedTemp + encodedChar);
187          decodedTemp = '';
188        } catch (e) {
189          decodedTemp += encodedChar;
190        }
191        index += 3;
192        continue;
193      }
194      if (decodedTemp === '') {
195        decodedString += input[index];
196      } else {
197        decodedString += decodedTemp;
198        decodedString += input[index];
199        decodedTemp = '';
200      }
201      index++;
202    }
203    return decodedTemp === '' ? decodedString : decodedString += decodedTemp;
204  }
205
206  decodeSafelyInner(input: string): string {
207    if (input === undefined || input === '') {
208      return input;
209    }
210    let strVal: string = '';
211    try {
212      strVal = decodeURIComponent(input);
213    } catch (e) {
214      strVal = this.decodeSafelyOut(input);
215    }
216    return strVal;
217  }
218
219  getQueryNames(): string[] {
220    let names: Set<string> = new Set<string>();
221    if (this.uricalss.query === null) {
222      return [];
223    }
224    let start: number = 0;
225    while (start < this.uricalss.query.length) {
226      let next: number = this.uricalss.query.indexOf('&', start);
227      let end: number = (next === -1) ? this.uricalss.query.length : next;
228      let separator: number = this.uricalss.query.indexOf('=', start);
229      if (separator > end || separator === -1) {
230        separator = end;
231      }
232      let name: string = this.uricalss.query.substring(start, separator);
233      names.add(this.decodeSafelyInner(name));
234      start = end + 1;
235    }
236    return Array.from(names);
237  }
238
239  getQueryValues(key: string): string[] {
240    if (key === null || typeof key !== 'string') {
241      throw new BusinessError(`Parameter error. The type of ${key} must be string`);
242    }
243    let values = new Array();
244    if (this.uricalss.query === null) {
245      return values;
246    }
247    let queryStrs: string[] = this.uricalss.query.split('&') || [];
248    for (let item of queryStrs) {
249      if (key === '' && item === '') {
250        values.push(item);
251      }
252      let str = item.split('=') || [];
253      if (str.length === 1 && this.decodeSafelyInner(str[0]) === key) {
254        values.push('');
255      } else if (this.decodeSafelyInner(str[0]) === key) {
256        values.push(this.decodeSafelyInner(item.substring(str[0].length + 1)));
257      }
258    }
259    return values;
260  }
261
262  getBooleanQueryValue(key: string, defaultValue: boolean): boolean {
263    if (key === null || typeof key !== 'string') {
264      throw new BusinessError(`Parameter error. The type of ${key} must be string`);
265    }
266    if (defaultValue === null || typeof defaultValue !== 'boolean') {
267      throw new BusinessError(`Parameter error. The type of ${key} must be boolean`);
268    }
269    let flag = this.getQueryValue(key);
270    if (flag == null) {
271      return defaultValue;
272    }
273    flag = flag.toLocaleLowerCase();
274    return 'false' !== flag && '0' !== flag;
275  }
276
277  getLastSegment(): string {
278    let segments = this.uricalss.getSegment();
279    if (!segments) {
280      return '';
281    }
282    return this.decodeSafelyInner(segments[segments.length - 1]);
283  }
284
285  getSegment(): string[] {
286    let array = new Array();
287    let segments = this.uricalss.getSegment();
288    if (segments) {
289      segments.forEach(element => {
290        array.push(this.decodeSafelyInner(element));
291      });
292    }
293    return array;
294  }
295
296  clearQuery(): URI {
297    let uriStr: string = this.uricalss.clearQuery();
298    return createNewUri(uriStr);
299  }
300
301  normalize(): URI {
302    let uriStr: string = this.uricalss.normalize();
303    return createNewUri(uriStr);
304  }
305
306  get scheme(): string | null {
307    return this.uricalss.scheme;
308  }
309
310  get authority(): string | null {
311    if (this.uricalss.authority === null) {
312      return null;
313    }
314    let thisAuthority: string = this.uricalss.authority;
315    if (thisAuthority.indexOf('[') !== -1) {
316      let arr: string[] = thisAuthority.split('[');
317      let brr: string[] = arr[1].split(']');
318      arr[1] = '[' + brr[0] + ']';
319      arr[2] = brr[1];
320      arr[0] = this.decodeSafelyInner(arr[0]);
321      arr[2] = this.decodeSafelyInner(arr[2]);
322      return arr.join('');
323    } else {
324      return this.decodeSafelyInner(thisAuthority);
325    }
326  }
327
328  get ssp(): string {
329    let thisSsp: string = this.uricalss.ssp;
330    if (thisSsp.indexOf('[') !== -1) {
331      let arr: string[] = thisSsp.split('[');
332      let brr: string[] = arr[1].split(']');
333      arr[1] = '[' + brr[0] + ']';
334      arr[2] = brr[1];
335      arr[0] = this.decodeSafelyInner(arr[0]);
336      arr[2] = this.decodeSafelyInner(arr[2]);
337      return arr.join('');
338    } else {
339      return this.decodeSafelyInner(thisSsp);
340    }
341  }
342
343  get userInfo(): string | null {
344    return this.uricalss.userInfo === null ? null : this.decodeSafelyInner(this.uricalss.userInfo);
345  }
346
347  get host(): string | null {
348    return this.uricalss.host;
349  }
350
351  get port(): string {
352    return this.uricalss.port;
353  }
354
355  get path(): string | null {
356    return this.uricalss.path === null ? null : this.decodeSafelyInner(this.uricalss.path);
357  }
358
359  get query(): string | null {
360    return this.uricalss.query === null ? null : this.decodeSafelyInner(this.uricalss.query);
361  }
362
363  get fragment(): string | null {
364    return this.uricalss.fragment === null ? null : this.decodeSafelyInner(this.uricalss.fragment);
365  }
366
367  get encodedUserInfo(): string | null {
368    return this.uricalss.userInfo;
369  }
370
371  get encodedPath(): string | null {
372    return this.uricalss.path;
373  }
374
375  get encodedQuery(): string | null {
376    return this.uricalss.query;
377  }
378
379  get encodedFragment(): string | null {
380    return this.uricalss.fragment;
381  }
382
383  get encodedAuthority(): string | null {
384    if (this.uricalss.authority === null) {
385      return null;
386    }
387    let thisAuthority: string = this.uricalss.authority;
388    if (thisAuthority.indexOf('[') !== -1) {
389      let arr: string[] = thisAuthority.split('[');
390      let brr: string[] = arr[1].split(']');
391      arr[1] = '[' + brr[0] + ']';
392      arr[2] = brr[1];
393      return arr.join('');
394    } else {
395      return thisAuthority;
396    }
397  }
398
399  get encodedSSP(): string {
400    let thisSsp: string = this.uricalss.ssp;
401    if (thisSsp.indexOf('[') !== -1) {
402      let arr: string[] = thisSsp.split('[');
403      let brr: string[] = arr[1].split(']');
404      arr[1] = '[' + brr[0] + ']';
405      arr[2] = brr[1];
406      return arr.join('');
407    } else {
408      return thisSsp;
409    }
410  }
411}
412
413function toAscllString(uriStr: string): string {
414  return encodeURI(uriStr).replace(/%5B/g, '[').replace(/%5D/g, ']').replace(/%25/g, '%');
415}
416
417function createNewUri(uriStr: string): URI {
418  return new URI(uriStr);
419}
420
421export default {
422  URI: URI
423};