]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.history/src/org/simantics/history/csv/URIs.java
Safer URI unescaping for CSVFormatter subscription CSV exporter
[simantics/platform.git] / bundles / org.simantics.history / src / org / simantics / history / csv / URIs.java
1 /*******************************************************************************
2  * Copyright (c) 2017 Association for Decentralized Information Management in
3  * Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     Semantum Oy - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.history.csv;
13
14 /**
15  * @author Tuukka Lehtonen
16  * @since 1.30.0, 1.28.1
17  */
18 class URIs {
19
20     public static String safeUnescape(String uri) {
21         try {
22             return unescape(uri);
23         } catch (IllegalArgumentException e) {
24             return uri;
25         }
26     }
27
28     public static String unescape(String uri) {
29         try {
30             if (!needsUnescaping(uri))
31                 return uri;
32
33             int len = uri.length();
34             String unicode = uri;
35             char result[] = new char[len];
36             int in = 0;
37             int out = 0;
38             while (in < len) {
39                 char inCh = unicode.charAt(in++);
40                 if (inCh == '%' && in+1 < len) {
41                     char d1 = unicode.charAt(in);
42                     char d2 = unicode.charAt(in+1);
43                     if (d1 > 127 || d2 > 127)
44                         throw new IllegalArgumentException("Invalid hex digit escape sequence in " + uri + " at " + in);
45                     result[out++] = (char) (hexDecode(d1) * 16 | hexDecode(d2));
46                     in += 2;
47                 } else {
48                     result[out++] = inCh;
49                 }
50             }
51             return new String(result, 0, out);
52         } catch (IllegalArgumentException e) {
53             throw new IllegalArgumentException("Problem while unescaping string: " + uri, e);
54         } catch (IndexOutOfBoundsException ee) {
55             throw new IllegalArgumentException("Incomplete hex digit escape sequence in " + uri);
56         }
57     }
58
59     private static boolean needsUnescaping(String s) {
60         int l = s.length();
61         for (int i = -1; i < l;) {
62             i = s.indexOf('%', i+1);
63             if (i < 0)
64                 break;
65             if (i+2 < l
66                     && isHexDigit(s.charAt(i+1))
67                     && isHexDigit(s.charAt(i+2)))
68                 return true;
69         }
70         return false;
71     }
72
73     private static int hexDecode(char c) {
74         switch (c) {
75             case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
76                 return ((c) & 255) - 'a' + 10;
77             case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
78                 return c -  'A' + 10;
79             case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
80                 return c -  '0';
81             default:
82                 throw new IllegalArgumentException("Bad Hex escape character: " + ((c)&255) );
83         }
84     }
85
86     private static boolean isHexDigit(char c) {
87         return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
88     }
89
90 //    public static void main(String[] args) {
91 //        System.out.println(unescape("%"));
92 //        System.out.println(unescape("%.AI"));
93 //        System.out.println(unescape("%6B"));
94 //        System.out.println(unescape("%6g"));
95 //        System.out.println(unescape("%g5"));
96 //        System.out.println(unescape("%f5"));
97 //        System.out.println(unescape("%A1"));
98 //        System.out.println(unescape("%A"));
99 //        System.out.println(unescape("%A."));
100 //        System.out.println(unescape("%AI"));
101 //    }
102
103 }