PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/src/org/olap4j/impl/ConnectStringParser.java

https://github.com/cherry-wb/olap4j
Java | 301 lines | 204 code | 19 blank | 78 comment | 44 complexity | 892cbbfce8558f77b6542c9432abf4fc MD5 | raw file
Possible License(s): Apache-2.0
  1. /*
  2. // Licensed to Julian Hyde under one or more contributor license
  3. // agreements. See the NOTICE file distributed with this work for
  4. // additional information regarding copyright ownership.
  5. //
  6. // Julian Hyde licenses this file to you under the Apache License,
  7. // Version 2.0 (the "License"); you may not use this file except in
  8. // compliance with the License. You may obtain a copy of the License at:
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // Unless required by applicable law or agreed to in writing, software
  13. // distributed under the License is distributed on an "AS IS" BASIS,
  14. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. // See the License for the specific language governing permissions and
  16. // limitations under the License.
  17. */
  18. package org.olap4j.impl;
  19. import java.util.*;
  20. /**
  21. * Parser for olap4j connect strings.
  22. *
  23. * @author jhyde
  24. * @version $Id$
  25. * @since Dec 12, 2007
  26. */
  27. // Copied from mondrian.olap.Util.ConnectStringParser
  28. public class ConnectStringParser {
  29. private final String s;
  30. private final int n;
  31. private int i;
  32. private final StringBuilder nameBuf;
  33. private final StringBuilder valueBuf;
  34. /**
  35. * Converts an OLE DB connect string into a {@link java.util.Map}.
  36. *
  37. * <p> For example, <code>"Provider=MSOLAP; DataSource=LOCALHOST;"</code>
  38. * becomes the set of (key, value) pairs <code>{("Provider","MSOLAP"),
  39. * ("DataSource", "LOCALHOST")}</code>. Another example is
  40. * <code>Provider='sqloledb';Data Source='MySqlServer';Initial
  41. * Catalog='Pubs';Integrated Security='SSPI';</code>.
  42. *
  43. * <p> This method implements as much as possible of the <a
  44. * href="http://msdn.microsoft.com/library/en-us/oledb/htm/oledbconnectionstringsyntax.asp"
  45. * target="_blank">OLE DB connect string syntax
  46. * specification</a>.
  47. *
  48. * <p>The return value is a map which:
  49. * <ul>
  50. * <li>preserves the order in that the entries occurred;</li>
  51. * <li>is not case-sensitive when looking up properties</li>
  52. * </ul>
  53. *
  54. * @param s Connect string
  55. *
  56. * @return Map containing (name, value) pairs, stored in the order that
  57. * they occurred in the connect string
  58. */
  59. public static Map<String, String> parseConnectString(String s) {
  60. return new ConnectStringParser(s).parse();
  61. }
  62. private ConnectStringParser(String s) {
  63. this.s = s;
  64. this.i = 0;
  65. this.n = s.length();
  66. this.nameBuf = new StringBuilder(64);
  67. this.valueBuf = new StringBuilder(64);
  68. }
  69. private PropertyMap parse() {
  70. PropertyMap map = new PropertyMap();
  71. while (i < n) {
  72. parsePair(map);
  73. }
  74. return map;
  75. }
  76. /**
  77. * Reads "name=value;" or "name=value<EOF>".
  78. *
  79. * @param map Map to append value to
  80. */
  81. private void parsePair(PropertyMap map) {
  82. String name = parseName();
  83. if (name == null) {
  84. return;
  85. }
  86. String value;
  87. if (i >= n) {
  88. value = "";
  89. } else if (s.charAt(i) == ';') {
  90. i++;
  91. value = "";
  92. } else {
  93. value = parseValue();
  94. }
  95. map.put(name, value);
  96. }
  97. /**
  98. * Reads "name=". Name can contain equals sign if equals sign is
  99. * doubled.
  100. *
  101. * @return Next name in the connect string being parsed, or null if there
  102. * is no further name
  103. */
  104. private String parseName() {
  105. nameBuf.setLength(0);
  106. while (true) {
  107. char c = s.charAt(i);
  108. switch (c) {
  109. case '=':
  110. i++;
  111. if (i < n && (c = s.charAt(i)) == '=') {
  112. // doubled equals sign; take one of them, and carry on
  113. i++;
  114. nameBuf.append(c);
  115. break;
  116. }
  117. String name = nameBuf.toString();
  118. name = name.trim();
  119. return name;
  120. case ' ':
  121. if (nameBuf.length() == 0) {
  122. // ignore preceding spaces
  123. i++;
  124. if (i >= n) {
  125. // there is no name, e.g. trailing spaces after
  126. // semicolon, 'x=1; y=2; '
  127. return null;
  128. }
  129. break;
  130. } else {
  131. // fall through
  132. }
  133. default:
  134. nameBuf.append(c);
  135. i++;
  136. if (i >= n) {
  137. return nameBuf.toString().trim();
  138. }
  139. }
  140. }
  141. }
  142. /**
  143. * Reads "value;" or "value<EOF>"
  144. *
  145. * @return next value from connect string being parsed
  146. */
  147. private String parseValue() {
  148. char c;
  149. // skip over leading white space
  150. while ((c = s.charAt(i)) == ' ') {
  151. i++;
  152. if (i >= n) {
  153. return "";
  154. }
  155. }
  156. if (c == '"' || c == '\'') {
  157. String value = parseQuoted(c);
  158. // skip over trailing white space
  159. while (i < n && (c = s.charAt(i)) == ' ') {
  160. i++;
  161. }
  162. if (i >= n) {
  163. return value;
  164. } else if (c == ';') {
  165. i++;
  166. return value;
  167. } else {
  168. throw new RuntimeException(
  169. "quoted value ended too soon, at position " + i
  170. + " in '" + s + "'");
  171. }
  172. } else {
  173. String value;
  174. int semi = s.indexOf(';', i);
  175. if (semi >= 0) {
  176. value = s.substring(i, semi);
  177. i = semi + 1;
  178. } else {
  179. value = s.substring(i);
  180. i = n;
  181. }
  182. return value.trim();
  183. }
  184. }
  185. /**
  186. * Reads a string quoted by a given character. Occurrences of the
  187. * quoting character must be doubled. For example,
  188. * <code>parseQuoted('"')</code> reads <code>"a ""new"" string"</code>
  189. * and returns <code>a "new" string</code>.
  190. *
  191. * @param q Quoting character (usually single or double quote)
  192. * @return quoted string
  193. */
  194. private String parseQuoted(char q) {
  195. char c = s.charAt(i++);
  196. assert c == q;
  197. valueBuf.setLength(0);
  198. while (i < n) {
  199. c = s.charAt(i);
  200. if (c == q) {
  201. i++;
  202. if (i < n) {
  203. c = s.charAt(i);
  204. if (c == q) {
  205. valueBuf.append(c);
  206. i++;
  207. continue;
  208. }
  209. }
  210. return valueBuf.toString();
  211. } else {
  212. valueBuf.append(c);
  213. i++;
  214. }
  215. }
  216. throw new RuntimeException(
  217. "Connect string '" + s
  218. + "' contains unterminated quoted value '"
  219. + valueBuf.toString() + "'");
  220. }
  221. private static class PropertyMap extends LinkedHashMap<String, String> {
  222. private final Map<String, String> originalKeys =
  223. new HashMap<String, String>();
  224. private static final String PROVIDER = normalize("Provider");
  225. public String get(Object key) {
  226. return super.get(normalize((String) key));
  227. }
  228. public String remove(Object key) {
  229. return super.remove(normalize((String) key));
  230. }
  231. public String put(String key, String value) {
  232. final String normalizedKey = normalize(key);
  233. if (normalizedKey.equals(PROVIDER)
  234. && containsKey(normalizedKey))
  235. {
  236. // "Provider" is the sole property which does not override.
  237. // The first occurrence of "Provider" is the one which is used.
  238. return null;
  239. }
  240. originalKeys.put(normalizedKey, key);
  241. return super.put(normalizedKey, value);
  242. }
  243. public boolean containsKey(Object key) {
  244. return super.containsKey(normalize((String) key));
  245. }
  246. private static String normalize(String key) {
  247. return key.toUpperCase();
  248. }
  249. public String toString() {
  250. StringBuilder sb = new StringBuilder(64);
  251. int i = 0;
  252. for (Map.Entry<String, String> entry : entrySet()) {
  253. if (i++ > 0) {
  254. sb.append("; ");
  255. }
  256. final String key = entry.getKey();
  257. final String originalKey = originalKeys.get(key);
  258. sb.append(originalKey);
  259. sb.append('=');
  260. final String value = entry.getValue();
  261. if (value == null) {
  262. sb.append("'null'");
  263. } else {
  264. // Quote a property value if is has a semi colon in it
  265. // 'xxx;yyy'
  266. if (value.indexOf(';') >= 0 && value.charAt(0) != '\'') {
  267. sb.append("'");
  268. }
  269. sb.append(value);
  270. if (value.indexOf(';') >= 0
  271. && value.charAt(value.length() - 1) != '\'')
  272. {
  273. sb.append("'");
  274. }
  275. }
  276. }
  277. return sb.toString();
  278. }
  279. }
  280. }
  281. // End ConnectStringParser.java