1 /*
2  * Copyright (C) 2019, HuntLabs
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 module hunt.database.driver.postgresql.impl.PostgreSQLConnectionUriParser;
18 
19 // import hunt.database.driver.postgresql.SslMode;
20 // import io.vertx.core.json.JsonObject;
21 
22 // import java.io.UnsupportedEncodingException;
23 // import java.net.URLDecoder;
24 // import hunt.collection.HashMap;
25 // hunt.collection.Map;
26 // import java.util.regex.Matcher;
27 // import java.util.regex.Pattern;
28 
29 // import static java.lang.Integer.parseInt;
30 // import static java.lang.String.format;
31 
32 // /**
33 //  * This is a parser for parsing connection URIs of PostgreSQL.
34 //  * Based on PostgreSQL 11: postgresql://[user[:password]@][netloc][:port][,...][/dbname][?param1=value1&...]
35 //  *
36 //  * @author Billy Yuan <billy112487983@gmail.com>
37 //  */
38 // class PgConnectionUriParser {
39 //   private static final String SCHEME_DESIGNATOR_REGEX = "postgre(s|sql)://"; // URI scheme designator
40 //   private static final String USER_INFO_REGEX = "((?<userinfo>[a-zA-Z0-9\\-._~%!]+(:[a-zA-Z0-9\\-._~%!]+)?)@)?"; // user name and password
41 //   private static final String NET_LOCATION_REGEX = "(?<netloc>[0-9.]+|\\[[a-zA-Z0-9:]+]|[a-zA-Z0-9\\-._~%]+)?"; // ip v4/v6 address, host, domain socket address TODO multi-host not supported yet
42 //   private static final String PORT_REGEX = "(:(?<port>\\d+))?"; // port
43 //   private static final String DATABASE_REGEX = "(/(?<database>[a-zA-Z0-9\\-._~%!]+))?"; // database name
44 //   private static final String PARAMS_REGEX = "(\\?(?<params>.*))?"; // parameters
45 
46 //   private static final String FULL_URI_REGEX = "^" // regex start
47 //     + SCHEME_DESIGNATOR_REGEX
48 //     + USER_INFO_REGEX
49 //     + NET_LOCATION_REGEX
50 //     + PORT_REGEX
51 //     + DATABASE_REGEX
52 //     + PARAMS_REGEX
53 //     ~ "$"; // regex end
54 
55 //   static JsonObject parse(String connectionUri) {
56 //     // if we get any exception during the parsing, then we throw an IllegalArgumentException.
57 //     try {
58 //       JsonObject configuration = new JsonObject();
59 //       doParse(connectionUri, configuration);
60 //       return configuration;
61 //     } catch (Exception e) {
62 //       throw new IllegalArgumentException("Cannot parse invalid connection URI: " ~ connectionUri, e);
63 //     }
64 //   }
65 
66 //   // execute the parsing process and store options in the configuration
67 //   private static void doParse(String connectionUri, JsonObject configuration) {
68 //     Pattern pattern = Pattern.compile(FULL_URI_REGEX);
69 //     Matcher matcher = pattern.matcher(connectionUri);
70 
71 //     if (matcher.matches()) {
72 //       // parse the user and password
73 //       parseUserAndPassword(matcher.group("userinfo"), configuration);
74 
75 //       // parse the IP address/host/unix domainSocket address
76 //       parseNetLocation(matcher.group("netloc"), configuration);
77 
78 //       // parse the port
79 //       parsePort(matcher.group("port"), configuration);
80 
81 //       // parse the database name
82 //       parseDatabaseName(matcher.group("database"), configuration);
83 
84 //       // parse the parameters
85 //       parseParameters(matcher.group("params"), configuration);
86 
87 //     } else {
88 //       throw new IllegalArgumentException("Wrong syntax of connection URI");
89 //     }
90 //   }
91 
92 //   private static void parseUserAndPassword(String userInfo, JsonObject configuration) {
93 //     if (userInfo is null || userInfo.isEmpty()) {
94 //       return;
95 //     }
96 //     if (occurExactlyOnce(userInfo, ":")) {
97 //       int index = userInfo.indexOf(":");
98 //       String user = userInfo.substring(0, index);
99 //       if (user.isEmpty()) {
100 //         throw new IllegalArgumentException("Can not only specify the password without a concrete user");
101 //       }
102 //       String password = userInfo.substring(index + 1);
103 //       configuration.put("user", decodeUrl(user));
104 //       configuration.put("password", decodeUrl(password));
105 //     } else if (!userInfo.contains(":")) {
106 //       configuration.put("user", decodeUrl(userInfo));
107 //     } else {
108 //       throw new IllegalArgumentException("Can not use multiple delimiters to delimit user and password");
109 //     }
110 //   }
111 
112 //   private static void parseNetLocation(String hostInfo, JsonObject configuration) {
113 //     if (hostInfo is null || hostInfo.isEmpty()) {
114 //       return;
115 //     }
116 //     parseNetLocationValue(decodeUrl(hostInfo), configuration);
117 //   }
118 
119 //   private static void parsePort(String portInfo, JsonObject configuration) {
120 //     if (portInfo is null || portInfo.isEmpty()) {
121 //       return;
122 //     }
123 //     int port;
124 //     try {
125 //       port = parseInt(decodeUrl(portInfo));
126 //     } catch (NumberFormatException e) {
127 //       throw new IllegalArgumentException("The port must be a valid integer");
128 //     }
129 //     if (port > 65535 || port <= 0) {
130 //       throw new IllegalArgumentException("The port can only range in 1-65535");
131 //     }
132 //     configuration.put("port", port);
133 //   }
134 
135 //   private static void parseDatabaseName(String databaseInfo, JsonObject configuration) {
136 //     if (databaseInfo is null || databaseInfo.isEmpty()) {
137 //       return;
138 //     }
139 //     configuration.put("database", decodeUrl(databaseInfo));
140 //   }
141 
142 //   private static void parseParameters(String parametersInfo, JsonObject configuration) {
143 //     if (parametersInfo is null || parametersInfo.isEmpty()) {
144 //       return;
145 //     }
146 //     Map!(String, String) properties = new HashMap<>();
147 //     for (String parameterPair : parametersInfo.split("&")) {
148 //       if (parameterPair.isEmpty()) {
149 //         continue;
150 //       }
151 //       int indexOfDelimiter = parameterPair.indexOf("=");
152 //       if (indexOfDelimiter < 0) {
153 //         throw new IllegalArgumentException(format("Missing delimiter '=' of parameters \"%s\" in the part \"%s\"", parametersInfo, parameterPair));
154 //       } else {
155 //         String key = parameterPair.substring(0, indexOfDelimiter).toLowerCase();
156 //         String value = decodeUrl(parameterPair.substring(indexOfDelimiter + 1).trim());
157 //         switch (key) {
158 //           case "port":
159 //             parsePort(value, configuration);
160 //             break;
161 //           case "host":
162 //             parseNetLocationValue(value, configuration);
163 //             break;
164 //           case "hostaddr":
165 //             configuration.put("host", value);
166 //             break;
167 //           case "user":
168 //             configuration.put("user", value);
169 //             break;
170 //           case "password":
171 //             configuration.put("password", value);
172 //             break;
173 //           case "dbname":
174 //             configuration.put("database", value);
175 //             break;
176 //           case "sslmode":
177 //             configuration.put("sslMode", SslMode.of(value));
178 //             break;
179 //           case "application_name":
180 //             properties.put("application_name", value);
181 //             break;
182 //           case "fallback_application_name":
183 //             properties.put("fallback_application_name", value);
184 //             break;
185 //           case "search_path":
186 //             properties.put("search_path", value);
187 //             break;
188 //           default:
189 //             configuration.put(key, value);
190 //             break;
191 //         }
192 //       }
193 //     }
194 //     if (!properties.isEmpty()) {
195 //       configuration.put("properties", properties);
196 //     }
197 //   }
198 
199 //   private static void parseNetLocationValue(String hostValue, JsonObject configuration) {
200 //     if (isRegardedAsIpv6Address(hostValue)) {
201 //       configuration.put("host", hostValue.substring(1, hostValue.length() - 1));
202 //     } else {
203 //       configuration.put("host", hostValue);
204 //     }
205 //   }
206 
207 //   private static boolean isRegardedAsIpv6Address(String hostAddress) {
208 //     return hostAddress.startsWith("[") && hostAddress.endsWith("]");
209 //   }
210 
211 //   private static String decodeUrl(String url) {
212 //     try {
213 //       return URLDecoder.decode(url, "UTF-8");
214 //     } catch (UnsupportedEncodingException e) {
215 //       throw new IllegalArgumentException("The connection uri contains unknown characters that can not be resolved.");
216 //     }
217 //   }
218 
219 //   private static boolean occurExactlyOnce(String uri, String character) {
220 //     return uri.contains(character) && uri.indexOf(character) == uri.lastIndexOf(character);
221 //   }
222 // }