PageRenderTime 42ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/src/main/mondrian/olap/Util.java

https://github.com/Berico-Technologies/mondrian
Java | 4488 lines | 2857 code | 333 blank | 1298 comment | 550 complexity | a95c531aeee77621ed993495fa86a3eb MD5 | raw file
  1. /*
  2. // This software is subject to the terms of the Eclipse Public License v1.0
  3. // Agreement, available at the following URL:
  4. // http://www.eclipse.org/legal/epl-v10.html.
  5. // You must accept the terms of that agreement to use this software.
  6. //
  7. // Copyright (C) 2001-2005 Julian Hyde
  8. // Copyright (C) 2005-2013 Pentaho and others
  9. // All Rights Reserved.
  10. */
  11. package mondrian.olap;
  12. import mondrian.mdx.*;
  13. import mondrian.olap.fun.FunUtil;
  14. import mondrian.olap.fun.Resolver;
  15. import mondrian.olap.type.Type;
  16. import mondrian.resource.MondrianResource;
  17. import mondrian.rolap.*;
  18. import mondrian.spi.UserDefinedFunction;
  19. import mondrian.util.*;
  20. import org.apache.commons.collections.keyvalue.AbstractMapEntry;
  21. import org.apache.commons.vfs.*;
  22. import org.apache.commons.vfs.provider.http.HttpFileObject;
  23. import org.apache.log4j.Logger;
  24. import org.eigenbase.xom.XOMUtil;
  25. import org.olap4j.impl.Olap4jUtil;
  26. import org.olap4j.mdx.*;
  27. import java.io.*;
  28. import java.lang.ref.Reference;
  29. import java.lang.reflect.*;
  30. import java.lang.reflect.Array;
  31. import java.math.BigDecimal;
  32. import java.net.MalformedURLException;
  33. import java.net.URL;
  34. import java.security.MessageDigest;
  35. import java.security.NoSuchAlgorithmException;
  36. import java.sql.*;
  37. import java.sql.Connection;
  38. import java.util.*;
  39. import java.util.concurrent.*;
  40. import java.util.concurrent.atomic.AtomicInteger;
  41. import java.util.regex.Matcher;
  42. import java.util.regex.Pattern;
  43. /**
  44. * Utility functions used throughout mondrian. All methods are static.
  45. *
  46. * @author jhyde
  47. * @since 6 August, 2001
  48. */
  49. public class Util extends XOMUtil {
  50. public static final String nl = System.getProperty("line.separator");
  51. private static final Logger LOGGER = Logger.getLogger(Util.class);
  52. /**
  53. * Placeholder which indicates a value NULL.
  54. */
  55. public static final Object nullValue = new Double(FunUtil.DoubleNull);
  56. /**
  57. * Placeholder which indicates an EMPTY value.
  58. */
  59. public static final Object EmptyValue = new Double(FunUtil.DoubleEmpty);
  60. /**
  61. * Cumulative time spent accessing the database.
  62. */
  63. private static long databaseMillis = 0;
  64. /**
  65. * Random number generator to provide seed for other random number
  66. * generators.
  67. */
  68. private static final Random metaRandom =
  69. createRandom(MondrianProperties.instance().TestSeed.get());
  70. /** Unique id for this JVM instance. Part of a key that ensures that if
  71. * two JVMs in the same cluster have a data-source with the same
  72. * identity-hash-code, they will be treated as different data-sources,
  73. * and therefore caches will not be incorrectly shared. */
  74. public static final UUID JVM_INSTANCE_UUID = UUID.randomUUID();
  75. /**
  76. * Whether we are running a version of Java before 1.5.
  77. *
  78. * <p>If (but not only if) this variable is true, {@link #Retrowoven} will
  79. * also be true.
  80. */
  81. public static final boolean PreJdk15 =
  82. System.getProperty("java.version").startsWith("1.4");
  83. /**
  84. * Whether we are running a version of Java before 1.6.
  85. */
  86. public static final boolean PreJdk16 =
  87. PreJdk15
  88. || System.getProperty("java.version").startsWith("1.5");
  89. /**
  90. * Whether this is an IBM JVM.
  91. */
  92. public static final boolean IBM_JVM =
  93. System.getProperties().getProperty("java.vendor").equals(
  94. "IBM Corporation");
  95. /**
  96. * What version of JDBC?
  97. * Returns:<ul>
  98. * <li>0x0401 in JDK 1.7 and higher</li>
  99. * <li>0x0400 in JDK 1.6</li>
  100. * <li>0x0300 otherwise</li>
  101. * </ul>
  102. */
  103. public static final int JdbcVersion =
  104. System.getProperty("java.version").compareTo("1.7") >= 0
  105. ? 0x0401
  106. : System.getProperty("java.version").compareTo("1.6") >= 0
  107. ? 0x0400
  108. : 0x0300;
  109. /**
  110. * Whether the code base has re-engineered using retroweaver.
  111. * If this is the case, some functionality is not available, but a lot of
  112. * things are available via {@link mondrian.util.UtilCompatible}.
  113. * Retroweaver has some problems involving {@link java.util.EnumSet}.
  114. */
  115. public static final boolean Retrowoven =
  116. Access.class.getSuperclass().getName().equals(
  117. "net.sourceforge.retroweaver.runtime.java.lang.Enum");
  118. private static final UtilCompatible compatible;
  119. /**
  120. * Flag to control expensive debugging. (More expensive than merely
  121. * enabling assertions: as we know, a lot of people run with assertions
  122. * enabled.)
  123. */
  124. public static final boolean DEBUG = false;
  125. static {
  126. String className;
  127. if (PreJdk15 || Retrowoven) {
  128. className = "mondrian.util.UtilCompatibleJdk14";
  129. } else if (PreJdk16) {
  130. className = "mondrian.util.UtilCompatibleJdk15";
  131. } else {
  132. className = "mondrian.util.UtilCompatibleJdk16";
  133. }
  134. compatible = ClassResolver.INSTANCE.instantiateSafe(className);
  135. }
  136. public static boolean isNull(Object o) {
  137. return o == null || o == nullValue;
  138. }
  139. /**
  140. * Returns whether a list is strictly sorted.
  141. *
  142. * @param list List
  143. * @return whether list is sorted
  144. */
  145. public static <T> boolean isSorted(List<T> list) {
  146. T prev = null;
  147. for (T t : list) {
  148. if (prev != null
  149. && ((Comparable<T>) prev).compareTo(t) >= 0)
  150. {
  151. return false;
  152. }
  153. prev = t;
  154. }
  155. return true;
  156. }
  157. /**
  158. * Parses a string and returns a SHA-256 checksum of it.
  159. *
  160. * @param value The source string to parse.
  161. * @return A checksum of the source string.
  162. */
  163. public static byte[] digestSha256(String value) {
  164. final MessageDigest algorithm;
  165. try {
  166. algorithm = MessageDigest.getInstance("SHA-256");
  167. } catch (NoSuchAlgorithmException e) {
  168. throw new RuntimeException(e);
  169. }
  170. return algorithm.digest(value.getBytes());
  171. }
  172. /**
  173. * Creates an MD5 hash of a String.
  174. *
  175. * @param value String to create one way hash upon.
  176. * @return MD5 hash.
  177. */
  178. public static byte[] digestMd5(final String value) {
  179. final MessageDigest algorithm;
  180. try {
  181. algorithm = MessageDigest.getInstance("MD5");
  182. } catch (NoSuchAlgorithmException e) {
  183. throw new RuntimeException(e);
  184. }
  185. return algorithm.digest(value.getBytes());
  186. }
  187. /**
  188. * Creates an {@link ExecutorService} object backed by a thread pool.
  189. * @param maximumPoolSize Maximum number of concurrent
  190. * threads.
  191. * @param corePoolSize Minimum number of concurrent
  192. * threads to maintain in the pool, even if they are
  193. * idle.
  194. * @param keepAliveTime Time, in seconds, for which to
  195. * keep alive unused threads.
  196. * @param name The name of the threads.
  197. * @param rejectionPolicy The rejection policy to enforce.
  198. * @return An executor service preconfigured.
  199. */
  200. public static ExecutorService getExecutorService(
  201. int maximumPoolSize,
  202. int corePoolSize,
  203. long keepAliveTime,
  204. final String name,
  205. RejectedExecutionHandler rejectionPolicy)
  206. {
  207. if (Util.PreJdk16) {
  208. // On JDK1.5, if you specify corePoolSize=0, nothing gets executed.
  209. // Bummer.
  210. corePoolSize = Math.max(corePoolSize, 1);
  211. }
  212. // We must create a factory where the threads
  213. // have the right name and are marked as daemon threads.
  214. final ThreadFactory factory =
  215. new ThreadFactory() {
  216. private final AtomicInteger counter = new AtomicInteger(0);
  217. public Thread newThread(Runnable r) {
  218. final Thread t =
  219. Executors.defaultThreadFactory().newThread(r);
  220. t.setDaemon(true);
  221. t.setName(name + '_' + counter.incrementAndGet());
  222. return t;
  223. }
  224. };
  225. // Ok, create the executor
  226. final ThreadPoolExecutor executor =
  227. new ThreadPoolExecutor(
  228. corePoolSize,
  229. maximumPoolSize > 0
  230. ? maximumPoolSize
  231. : Integer.MAX_VALUE,
  232. keepAliveTime,
  233. TimeUnit.SECONDS,
  234. // we use a sync queue. any other type of queue
  235. // will prevent the tasks from running concurrently
  236. // because the executors API requires blocking queues.
  237. // Important to pass true here. This makes the
  238. // order of tasks deterministic.
  239. // TODO Write a non-blocking queue which implements
  240. // the blocking queue API so we can pass that to the
  241. // executor.
  242. new SynchronousQueue<Runnable>(true),
  243. factory);
  244. // Set the rejection policy if required.
  245. if (rejectionPolicy != null) {
  246. executor.setRejectedExecutionHandler(
  247. rejectionPolicy);
  248. }
  249. // Done
  250. return executor;
  251. }
  252. /**
  253. * Creates an {@link ScheduledExecutorService} object backed by a
  254. * thread pool with a fixed number of threads..
  255. * @param maxNbThreads Maximum number of concurrent
  256. * threads.
  257. * @param name The name of the threads.
  258. * @return An scheduled executor service preconfigured.
  259. */
  260. public static ScheduledExecutorService getScheduledExecutorService(
  261. final int maxNbThreads,
  262. final String name)
  263. {
  264. return Executors.newScheduledThreadPool(
  265. maxNbThreads,
  266. new ThreadFactory() {
  267. final AtomicInteger counter = new AtomicInteger(0);
  268. public Thread newThread(Runnable r) {
  269. final Thread thread =
  270. Executors.defaultThreadFactory().newThread(r);
  271. thread.setDaemon(true);
  272. thread.setName(name + '_' + counter.incrementAndGet());
  273. return thread;
  274. }
  275. }
  276. );
  277. }
  278. /**
  279. * Encodes string for MDX (escapes ] as ]] inside a name).
  280. *
  281. * @deprecated Will be removed in 4.0
  282. */
  283. public static String mdxEncodeString(String st) {
  284. StringBuilder retString = new StringBuilder(st.length() + 20);
  285. for (int i = 0; i < st.length(); i++) {
  286. char c = st.charAt(i);
  287. if ((c == ']')
  288. && ((i + 1) < st.length())
  289. && (st.charAt(i + 1) != '.'))
  290. {
  291. retString.append(']'); // escaping character
  292. }
  293. retString.append(c);
  294. }
  295. return retString.toString();
  296. }
  297. /**
  298. * Converts a string into a double-quoted string.
  299. */
  300. public static String quoteForMdx(String val) {
  301. StringBuilder buf = new StringBuilder(val.length() + 20);
  302. quoteForMdx(buf, val);
  303. return buf.toString();
  304. }
  305. /**
  306. * Appends a double-quoted string to a string builder.
  307. */
  308. public static StringBuilder quoteForMdx(StringBuilder buf, String val) {
  309. buf.append("\"");
  310. String s0 = replace(val, "\"", "\"\"");
  311. buf.append(s0);
  312. buf.append("\"");
  313. return buf;
  314. }
  315. /**
  316. * Return string quoted in [...]. For example, "San Francisco" becomes
  317. * "[San Francisco]"; "a [bracketed] string" becomes
  318. * "[a [bracketed]] string]".
  319. */
  320. public static String quoteMdxIdentifier(String id) {
  321. StringBuilder buf = new StringBuilder(id.length() + 20);
  322. quoteMdxIdentifier(id, buf);
  323. return buf.toString();
  324. }
  325. public static void quoteMdxIdentifier(String id, StringBuilder buf) {
  326. buf.append('[');
  327. int start = buf.length();
  328. buf.append(id);
  329. replace(buf, start, "]", "]]");
  330. buf.append(']');
  331. }
  332. /**
  333. * Return identifiers quoted in [...].[...]. For example, {"Store", "USA",
  334. * "California"} becomes "[Store].[USA].[California]".
  335. */
  336. public static String quoteMdxIdentifier(List<Id.Segment> ids) {
  337. StringBuilder sb = new StringBuilder(64);
  338. quoteMdxIdentifier(ids, sb);
  339. return sb.toString();
  340. }
  341. public static void quoteMdxIdentifier(
  342. List<Id.Segment> ids,
  343. StringBuilder sb)
  344. {
  345. for (int i = 0; i < ids.size(); i++) {
  346. if (i > 0) {
  347. sb.append('.');
  348. }
  349. ids.get(i).toString(sb);
  350. }
  351. }
  352. /**
  353. * Quotes a string literal for Java or JavaScript.
  354. *
  355. * @param s Unquoted literal
  356. * @return Quoted string literal
  357. */
  358. public static String quoteJavaString(String s) {
  359. return s == null
  360. ? "null"
  361. : "\""
  362. + s.replaceAll("\\\\", "\\\\\\\\")
  363. .replaceAll("\\\"", "\\\\\"")
  364. + "\"";
  365. }
  366. /**
  367. * Returns true if two objects are equal, or are both null.
  368. *
  369. * @param s First object
  370. * @param t Second object
  371. * @return Whether objects are equal or both null
  372. */
  373. public static boolean equals(Object s, Object t) {
  374. if (s == t) {
  375. return true;
  376. }
  377. if (s == null || t == null) {
  378. return false;
  379. }
  380. return s.equals(t);
  381. }
  382. /**
  383. * Returns true if two strings are equal, or are both null.
  384. *
  385. * <p>The result is not affected by
  386. * {@link MondrianProperties#CaseSensitive the case sensitive option}; if
  387. * you wish to compare names, use {@link #equalName(String, String)}.
  388. */
  389. public static boolean equals(String s, String t) {
  390. return equals((Object) s, (Object) t);
  391. }
  392. /**
  393. * Returns whether two names are equal.
  394. * Takes into account the
  395. * {@link MondrianProperties#CaseSensitive case sensitive option}.
  396. * Names may be null.
  397. */
  398. public static boolean equalName(String s, String t) {
  399. if (s == null) {
  400. return t == null;
  401. }
  402. boolean caseSensitive =
  403. MondrianProperties.instance().CaseSensitive.get();
  404. return caseSensitive ? s.equals(t) : s.equalsIgnoreCase(t);
  405. }
  406. /**
  407. * Tests two strings for equality, optionally ignoring case.
  408. *
  409. * @param s First string
  410. * @param t Second string
  411. * @param matchCase Whether to perform case-sensitive match
  412. * @return Whether strings are equal
  413. */
  414. public static boolean equal(String s, String t, boolean matchCase) {
  415. return matchCase ? s.equals(t) : s.equalsIgnoreCase(t);
  416. }
  417. /**
  418. * Compares two names. if case sensitive flag is false,
  419. * apply finer grain difference with case sensitive
  420. * Takes into account the {@link MondrianProperties#CaseSensitive case
  421. * sensitive option}.
  422. * Names must not be null.
  423. */
  424. public static int caseSensitiveCompareName(String s, String t) {
  425. boolean caseSensitive =
  426. MondrianProperties.instance().CaseSensitive.get();
  427. if (caseSensitive) {
  428. return s.compareTo(t);
  429. } else {
  430. int v = s.compareToIgnoreCase(t);
  431. // if ignore case returns 0 compare in a case sensitive manner
  432. // this was introduced to solve an issue with Member.equals()
  433. // and Member.compareTo() not agreeing with each other
  434. return v == 0 ? s.compareTo(t) : v;
  435. }
  436. }
  437. /**
  438. * Compares two names.
  439. * Takes into account the {@link MondrianProperties#CaseSensitive case
  440. * sensitive option}.
  441. * Names must not be null.
  442. */
  443. public static int compareName(String s, String t) {
  444. boolean caseSensitive =
  445. MondrianProperties.instance().CaseSensitive.get();
  446. return caseSensitive ? s.compareTo(t) : s.compareToIgnoreCase(t);
  447. }
  448. /**
  449. * Generates a normalized form of a name, for use as a key into a map.
  450. * Returns the upper case name if
  451. * {@link MondrianProperties#CaseSensitive} is true, the name unchanged
  452. * otherwise.
  453. */
  454. public static String normalizeName(String s) {
  455. return MondrianProperties.instance().CaseSensitive.get()
  456. ? s
  457. : s.toUpperCase();
  458. }
  459. /**
  460. * Returns the result of ((Comparable) k1).compareTo(k2), with
  461. * special-casing for the fact that Boolean only became
  462. * comparable in JDK 1.5.
  463. *
  464. * @see Comparable#compareTo
  465. */
  466. public static int compareKey(Object k1, Object k2) {
  467. if (k1 instanceof Boolean) {
  468. // Luckily, "F" comes before "T" in the alphabet.
  469. k1 = k1.toString();
  470. k2 = k2.toString();
  471. }
  472. return ((Comparable) k1).compareTo(k2);
  473. }
  474. /**
  475. * Compares integer values.
  476. *
  477. * @param i0 First integer
  478. * @param i1 Second integer
  479. * @return Comparison of integers
  480. */
  481. public static int compare(int i0, int i1) {
  482. return i0 < i1 ? -1 : (i0 == i1 ? 0 : 1);
  483. }
  484. /**
  485. * Returns a string with every occurrence of a seek string replaced with
  486. * another.
  487. */
  488. public static String replace(String s, String find, String replace) {
  489. // let's be optimistic
  490. int found = s.indexOf(find);
  491. if (found == -1) {
  492. return s;
  493. }
  494. StringBuilder sb = new StringBuilder(s.length() + 20);
  495. int start = 0;
  496. char[] chars = s.toCharArray();
  497. final int step = find.length();
  498. if (step == 0) {
  499. // Special case where find is "".
  500. sb.append(s);
  501. replace(sb, 0, find, replace);
  502. } else {
  503. for (;;) {
  504. sb.append(chars, start, found - start);
  505. if (found == s.length()) {
  506. break;
  507. }
  508. sb.append(replace);
  509. start = found + step;
  510. found = s.indexOf(find, start);
  511. if (found == -1) {
  512. found = s.length();
  513. }
  514. }
  515. }
  516. return sb.toString();
  517. }
  518. /**
  519. * Replaces all occurrences of a string in a buffer with another.
  520. *
  521. * @param buf String buffer to act on
  522. * @param start Ordinal within <code>find</code> to start searching
  523. * @param find String to find
  524. * @param replace String to replace it with
  525. * @return The string buffer
  526. */
  527. public static StringBuilder replace(
  528. StringBuilder buf,
  529. int start,
  530. String find,
  531. String replace)
  532. {
  533. // Search and replace from the end towards the start, to avoid O(n ^ 2)
  534. // copying if the string occurs very commonly.
  535. int findLength = find.length();
  536. if (findLength == 0) {
  537. // Special case where the seek string is empty.
  538. for (int j = buf.length(); j >= 0; --j) {
  539. buf.insert(j, replace);
  540. }
  541. return buf;
  542. }
  543. int k = buf.length();
  544. while (k > 0) {
  545. int i = buf.lastIndexOf(find, k);
  546. if (i < start) {
  547. break;
  548. }
  549. buf.replace(i, i + find.length(), replace);
  550. // Step back far enough to ensure that the beginning of the section
  551. // we just replaced does not cause a match.
  552. k = i - findLength;
  553. }
  554. return buf;
  555. }
  556. /**
  557. * Parses an MDX identifier such as <code>[Foo].[Bar].Baz.&Key&Key2</code>
  558. * and returns the result as a list of segments.
  559. *
  560. * @param s MDX identifier
  561. * @return List of segments
  562. */
  563. public static List<Id.Segment> parseIdentifier(String s) {
  564. return convert(
  565. org.olap4j.impl.IdentifierParser.parseIdentifier(s));
  566. }
  567. /**
  568. * Converts an array of name parts {"part1", "part2"} into a single string
  569. * "[part1].[part2]". If the names contain "]" they are escaped as "]]".
  570. */
  571. public static String implode(List<Id.Segment> names) {
  572. StringBuilder sb = new StringBuilder(64);
  573. for (int i = 0; i < names.size(); i++) {
  574. if (i > 0) {
  575. sb.append(".");
  576. }
  577. // FIXME: should be:
  578. // names.get(i).toString(sb);
  579. // but that causes some tests to fail
  580. Id.Segment segment = names.get(i);
  581. switch (segment.getQuoting()) {
  582. case UNQUOTED:
  583. segment = new Id.NameSegment(((Id.NameSegment) segment).name);
  584. }
  585. segment.toString(sb);
  586. }
  587. return sb.toString();
  588. }
  589. public static String makeFqName(String name) {
  590. return quoteMdxIdentifier(name);
  591. }
  592. public static String makeFqName(OlapElement parent, String name) {
  593. if (parent == null) {
  594. return Util.quoteMdxIdentifier(name);
  595. } else {
  596. StringBuilder buf = new StringBuilder(64);
  597. buf.append(parent.getUniqueName());
  598. buf.append('.');
  599. Util.quoteMdxIdentifier(name, buf);
  600. return buf.toString();
  601. }
  602. }
  603. public static String makeFqName(String parentUniqueName, String name) {
  604. if (parentUniqueName == null) {
  605. return quoteMdxIdentifier(name);
  606. } else {
  607. StringBuilder buf = new StringBuilder(64);
  608. buf.append(parentUniqueName);
  609. buf.append('.');
  610. Util.quoteMdxIdentifier(name, buf);
  611. return buf.toString();
  612. }
  613. }
  614. public static OlapElement lookupCompound(
  615. SchemaReader schemaReader,
  616. OlapElement parent,
  617. List<Id.Segment> names,
  618. boolean failIfNotFound,
  619. int category)
  620. {
  621. return lookupCompound(
  622. schemaReader, parent, names, failIfNotFound, category,
  623. MatchType.EXACT);
  624. }
  625. /**
  626. * Resolves a name such as
  627. * '[Products]&#46;[Product Department]&#46;[Produce]' by resolving the
  628. * components ('Products', and so forth) one at a time.
  629. *
  630. * @param schemaReader Schema reader, supplies access-control context
  631. * @param parent Parent element to search in
  632. * @param names Exploded compound name, such as {"Products",
  633. * "Product Department", "Produce"}
  634. * @param failIfNotFound If the element is not found, determines whether
  635. * to return null or throw an error
  636. * @param category Type of returned element, a {@link Category} value;
  637. * {@link Category#Unknown} if it doesn't matter.
  638. *
  639. * @pre parent != null
  640. * @post !(failIfNotFound && return == null)
  641. *
  642. * @see #parseIdentifier(String)
  643. */
  644. public static OlapElement lookupCompound(
  645. SchemaReader schemaReader,
  646. OlapElement parent,
  647. List<Id.Segment> names,
  648. boolean failIfNotFound,
  649. int category,
  650. MatchType matchType)
  651. {
  652. Util.assertPrecondition(parent != null, "parent != null");
  653. if (LOGGER.isDebugEnabled()) {
  654. StringBuilder buf = new StringBuilder(64);
  655. buf.append("Util.lookupCompound: ");
  656. buf.append("parent.name=");
  657. buf.append(parent.getName());
  658. buf.append(", category=");
  659. buf.append(Category.instance.getName(category));
  660. buf.append(", names=");
  661. quoteMdxIdentifier(names, buf);
  662. LOGGER.debug(buf.toString());
  663. }
  664. // First look up a member from the cache of calculated members
  665. // (cubes and queries both have them).
  666. switch (category) {
  667. case Category.Member:
  668. case Category.Unknown:
  669. Member member = schemaReader.getCalculatedMember(names);
  670. if (member != null) {
  671. return member;
  672. }
  673. }
  674. // Likewise named set.
  675. switch (category) {
  676. case Category.Set:
  677. case Category.Unknown:
  678. NamedSet namedSet = schemaReader.getNamedSet(names);
  679. if (namedSet != null) {
  680. return namedSet;
  681. }
  682. }
  683. // Now resolve the name one part at a time.
  684. for (int i = 0; i < names.size(); i++) {
  685. OlapElement child;
  686. Id.NameSegment name;
  687. if (names.get(i) instanceof Id.NameSegment) {
  688. name = (Id.NameSegment) names.get(i);
  689. child = schemaReader.getElementChild(parent, name, matchType);
  690. } else if (parent instanceof RolapLevel
  691. && names.get(i) instanceof Id.KeySegment
  692. && names.get(i).getKeyParts().size() == 1)
  693. {
  694. // The following code is for SsasCompatibleNaming=false.
  695. // Continues the very limited support for key segments in
  696. // mondrian-3.x. To be removed in mondrian-4, when
  697. // SsasCompatibleNaming=true is the only option.
  698. final Id.KeySegment keySegment = (Id.KeySegment) names.get(i);
  699. name = keySegment.getKeyParts().get(0);
  700. final List<Member> levelMembers =
  701. schemaReader.getLevelMembers(
  702. (Level) parent, false);
  703. child = null;
  704. for (Member member : levelMembers) {
  705. if (((RolapMember) member).getKey().toString().equals(
  706. name.getName()))
  707. {
  708. child = member;
  709. break;
  710. }
  711. }
  712. } else {
  713. name = null;
  714. child = schemaReader.getElementChild(parent, name, matchType);
  715. }
  716. // if we're doing a non-exact match, and we find a non-exact
  717. // match, then for an after match, return the first child
  718. // of each subsequent level; for a before match, return the
  719. // last child
  720. if (child instanceof Member
  721. && !matchType.isExact()
  722. && !Util.equalName(child.getName(), name.getName()))
  723. {
  724. Member bestChild = (Member) child;
  725. for (int j = i + 1; j < names.size(); j++) {
  726. List<Member> childrenList =
  727. schemaReader.getMemberChildren(bestChild);
  728. FunUtil.hierarchizeMemberList(childrenList, false);
  729. if (matchType == MatchType.AFTER) {
  730. bestChild = childrenList.get(0);
  731. } else {
  732. bestChild =
  733. childrenList.get(childrenList.size() - 1);
  734. }
  735. if (bestChild == null) {
  736. child = null;
  737. break;
  738. }
  739. }
  740. parent = bestChild;
  741. break;
  742. }
  743. if (child == null) {
  744. if (LOGGER.isDebugEnabled()) {
  745. LOGGER.debug(
  746. "Util.lookupCompound: "
  747. + "parent.name="
  748. + parent.getName()
  749. + " has no child with name="
  750. + name);
  751. }
  752. if (!failIfNotFound) {
  753. return null;
  754. } else if (category == Category.Member) {
  755. throw MondrianResource.instance().MemberNotFound.ex(
  756. quoteMdxIdentifier(names));
  757. } else {
  758. throw MondrianResource.instance().MdxChildObjectNotFound
  759. .ex(name.toString(), parent.getQualifiedName());
  760. }
  761. }
  762. parent = child;
  763. if (matchType == MatchType.EXACT_SCHEMA) {
  764. matchType = MatchType.EXACT;
  765. }
  766. }
  767. if (LOGGER.isDebugEnabled()) {
  768. LOGGER.debug(
  769. "Util.lookupCompound: "
  770. + "found child.name="
  771. + parent.getName()
  772. + ", child.class="
  773. + parent.getClass().getName());
  774. }
  775. switch (category) {
  776. case Category.Dimension:
  777. if (parent instanceof Dimension) {
  778. return parent;
  779. } else if (parent instanceof Hierarchy) {
  780. return parent.getDimension();
  781. } else if (failIfNotFound) {
  782. throw Util.newError(
  783. "Can not find dimension '" + implode(names) + "'");
  784. } else {
  785. return null;
  786. }
  787. case Category.Hierarchy:
  788. if (parent instanceof Hierarchy) {
  789. return parent;
  790. } else if (parent instanceof Dimension) {
  791. return parent.getHierarchy();
  792. } else if (failIfNotFound) {
  793. throw Util.newError(
  794. "Can not find hierarchy '" + implode(names) + "'");
  795. } else {
  796. return null;
  797. }
  798. case Category.Level:
  799. if (parent instanceof Level) {
  800. return parent;
  801. } else if (failIfNotFound) {
  802. throw Util.newError(
  803. "Can not find level '" + implode(names) + "'");
  804. } else {
  805. return null;
  806. }
  807. case Category.Member:
  808. if (parent instanceof Member) {
  809. return parent;
  810. } else if (failIfNotFound) {
  811. throw MondrianResource.instance().MdxCantFindMember.ex(
  812. implode(names));
  813. } else {
  814. return null;
  815. }
  816. case Category.Unknown:
  817. assertPostcondition(parent != null, "return != null");
  818. return parent;
  819. default:
  820. throw newInternal("Bad switch " + category);
  821. }
  822. }
  823. public static OlapElement lookup(Query q, List<Id.Segment> nameParts) {
  824. final Exp exp = lookup(q, nameParts, false);
  825. if (exp instanceof MemberExpr) {
  826. MemberExpr memberExpr = (MemberExpr) exp;
  827. return memberExpr.getMember();
  828. } else if (exp instanceof LevelExpr) {
  829. LevelExpr levelExpr = (LevelExpr) exp;
  830. return levelExpr.getLevel();
  831. } else if (exp instanceof HierarchyExpr) {
  832. HierarchyExpr hierarchyExpr = (HierarchyExpr) exp;
  833. return hierarchyExpr.getHierarchy();
  834. } else if (exp instanceof DimensionExpr) {
  835. DimensionExpr dimensionExpr = (DimensionExpr) exp;
  836. return dimensionExpr.getDimension();
  837. } else {
  838. throw Util.newInternal("Not an olap element: " + exp);
  839. }
  840. }
  841. /**
  842. * Converts an identifier into an expression by resolving its parts into
  843. * an OLAP object (dimension, hierarchy, level or member) within the
  844. * context of a query.
  845. *
  846. * <p>If <code>allowProp</code> is true, also allows property references
  847. * from valid members, for example
  848. * <code>[Measures].[Unit Sales].FORMATTED_VALUE</code>.
  849. * In this case, the result will be a {@link mondrian.mdx.ResolvedFunCall}.
  850. *
  851. * @param q Query expression belongs to
  852. * @param nameParts Parts of the identifier
  853. * @param allowProp Whether to allow property references
  854. * @return OLAP object or property reference
  855. */
  856. public static Exp lookup(
  857. Query q,
  858. List<Id.Segment> nameParts,
  859. boolean allowProp)
  860. {
  861. return lookup(q, q.getSchemaReader(true), nameParts, allowProp);
  862. }
  863. /**
  864. * Converts an identifier into an expression by resolving its parts into
  865. * an OLAP object (dimension, hierarchy, level or member) within the
  866. * context of a query.
  867. *
  868. * <p>If <code>allowProp</code> is true, also allows property references
  869. * from valid members, for example
  870. * <code>[Measures].[Unit Sales].FORMATTED_VALUE</code>.
  871. * In this case, the result will be a {@link ResolvedFunCall}.
  872. *
  873. * @param q Query expression belongs to
  874. * @param schemaReader Schema reader
  875. * @param segments Parts of the identifier
  876. * @param allowProp Whether to allow property references
  877. * @return OLAP object or property reference
  878. */
  879. public static Exp lookup(
  880. Query q,
  881. SchemaReader schemaReader,
  882. List<Id.Segment> segments,
  883. boolean allowProp)
  884. {
  885. // First, look for a calculated member defined in the query.
  886. final String fullName = quoteMdxIdentifier(segments);
  887. // Look for any kind of object (member, level, hierarchy,
  888. // dimension) in the cube. Use a schema reader without restrictions.
  889. final SchemaReader schemaReaderSansAc =
  890. schemaReader.withoutAccessControl().withLocus();
  891. final Cube cube = q.getCube();
  892. OlapElement olapElement =
  893. schemaReaderSansAc.lookupCompound(
  894. cube, segments, false, Category.Unknown);
  895. if (olapElement != null) {
  896. Role role = schemaReader.getRole();
  897. if (!role.canAccess(olapElement)) {
  898. olapElement = null;
  899. }
  900. if (olapElement instanceof Member) {
  901. olapElement =
  902. schemaReader.substitute((Member) olapElement);
  903. }
  904. }
  905. if (olapElement == null) {
  906. if (allowProp && segments.size() > 1) {
  907. List<Id.Segment> segmentsButOne =
  908. segments.subList(0, segments.size() - 1);
  909. final Id.Segment lastSegment = last(segments);
  910. final String propertyName =
  911. lastSegment instanceof Id.NameSegment
  912. ? ((Id.NameSegment) lastSegment).getName()
  913. : null;
  914. final Member member =
  915. (Member) schemaReaderSansAc.lookupCompound(
  916. cube, segmentsButOne, false, Category.Member);
  917. if (member != null
  918. && propertyName != null
  919. && isValidProperty(propertyName, member.getLevel()))
  920. {
  921. return new UnresolvedFunCall(
  922. propertyName, Syntax.Property, new Exp[] {
  923. createExpr(member)});
  924. }
  925. final Level level =
  926. (Level) schemaReaderSansAc.lookupCompound(
  927. cube, segmentsButOne, false, Category.Level);
  928. if (level != null
  929. && propertyName != null
  930. && isValidProperty(propertyName, level))
  931. {
  932. return new UnresolvedFunCall(
  933. propertyName, Syntax.Property, new Exp[] {
  934. createExpr(level)});
  935. }
  936. }
  937. // if we're in the middle of loading the schema, the property has
  938. // been set to ignore invalid members, and the member is
  939. // non-existent, return the null member corresponding to the
  940. // hierarchy of the element we're looking for; locate the
  941. // hierarchy by incrementally truncating the name of the element
  942. if (q.ignoreInvalidMembers()) {
  943. int nameLen = segments.size() - 1;
  944. olapElement = null;
  945. while (nameLen > 0 && olapElement == null) {
  946. List<Id.Segment> partialName =
  947. segments.subList(0, nameLen);
  948. olapElement = schemaReaderSansAc.lookupCompound(
  949. cube, partialName, false, Category.Unknown);
  950. nameLen--;
  951. }
  952. if (olapElement != null) {
  953. olapElement = olapElement.getHierarchy().getNullMember();
  954. } else {
  955. throw MondrianResource.instance().MdxChildObjectNotFound.ex(
  956. fullName, cube.getQualifiedName());
  957. }
  958. } else {
  959. throw MondrianResource.instance().MdxChildObjectNotFound.ex(
  960. fullName, cube.getQualifiedName());
  961. }
  962. }
  963. // keep track of any measure members referenced; these will be used
  964. // later to determine if cross joins on virtual cubes can be
  965. // processed natively
  966. q.addMeasuresMembers(olapElement);
  967. return createExpr(olapElement);
  968. }
  969. /**
  970. * Looks up a cube in a schema reader.
  971. *
  972. * @param cubeName Cube name
  973. * @param fail Whether to fail if not found.
  974. * @return Cube, or null if not found
  975. */
  976. static Cube lookupCube(
  977. SchemaReader schemaReader,
  978. String cubeName,
  979. boolean fail)
  980. {
  981. for (Cube cube : schemaReader.getCubes()) {
  982. if (Util.compareName(cube.getName(), cubeName) == 0) {
  983. return cube;
  984. }
  985. }
  986. if (fail) {
  987. throw MondrianResource.instance().MdxCubeNotFound.ex(cubeName);
  988. }
  989. return null;
  990. }
  991. /**
  992. * Converts an olap element (dimension, hierarchy, level or member) into
  993. * an expression representing a usage of that element in an MDX statement.
  994. */
  995. public static Exp createExpr(OlapElement element)
  996. {
  997. if (element instanceof Member) {
  998. Member member = (Member) element;
  999. return new MemberExpr(member);
  1000. } else if (element instanceof Level) {
  1001. Level level = (Level) element;
  1002. return new LevelExpr(level);
  1003. } else if (element instanceof Hierarchy) {
  1004. Hierarchy hierarchy = (Hierarchy) element;
  1005. return new HierarchyExpr(hierarchy);
  1006. } else if (element instanceof Dimension) {
  1007. Dimension dimension = (Dimension) element;
  1008. return new DimensionExpr(dimension);
  1009. } else if (element instanceof NamedSet) {
  1010. NamedSet namedSet = (NamedSet) element;
  1011. return new NamedSetExpr(namedSet);
  1012. } else {
  1013. throw Util.newInternal("Unexpected element type: " + element);
  1014. }
  1015. }
  1016. public static Member lookupHierarchyRootMember(
  1017. SchemaReader reader, Hierarchy hierarchy, Id.NameSegment memberName)
  1018. {
  1019. return lookupHierarchyRootMember(
  1020. reader, hierarchy, memberName, MatchType.EXACT);
  1021. }
  1022. /**
  1023. * Finds a root member of a hierarchy with a given name.
  1024. *
  1025. * @param hierarchy Hierarchy
  1026. * @param memberName Name of root member
  1027. * @return Member, or null if not found
  1028. */
  1029. public static Member lookupHierarchyRootMember(
  1030. SchemaReader reader,
  1031. Hierarchy hierarchy,
  1032. Id.NameSegment memberName,
  1033. MatchType matchType)
  1034. {
  1035. // Lookup member at first level.
  1036. //
  1037. // Don't use access control. Suppose we cannot see the 'nation' level,
  1038. // we still want to be able to resolve '[Customer].[USA].[CA]'.
  1039. List<Member> rootMembers = reader.getHierarchyRootMembers(hierarchy);
  1040. // if doing an inexact search on a non-all hierarchy, create
  1041. // a member corresponding to the name we're searching for so
  1042. // we can use it in a hierarchical search
  1043. Member searchMember = null;
  1044. if (!matchType.isExact()
  1045. && !hierarchy.hasAll()
  1046. && !rootMembers.isEmpty())
  1047. {
  1048. searchMember =
  1049. hierarchy.createMember(
  1050. null,
  1051. rootMembers.get(0).getLevel(),
  1052. memberName.name,
  1053. null);
  1054. }
  1055. int bestMatch = -1;
  1056. int k = -1;
  1057. for (Member rootMember : rootMembers) {
  1058. ++k;
  1059. int rc;
  1060. // when searching on the ALL hierarchy, match must be exact
  1061. if (matchType.isExact() || hierarchy.hasAll()) {
  1062. rc = rootMember.getName().compareToIgnoreCase(memberName.name);
  1063. } else {
  1064. rc = FunUtil.compareSiblingMembers(
  1065. rootMember,
  1066. searchMember);
  1067. }
  1068. if (rc == 0) {
  1069. return rootMember;
  1070. }
  1071. if (!hierarchy.hasAll()) {
  1072. if (matchType == MatchType.BEFORE) {
  1073. if (rc < 0
  1074. && (bestMatch == -1
  1075. || FunUtil.compareSiblingMembers(
  1076. rootMember,
  1077. rootMembers.get(bestMatch)) > 0))
  1078. {
  1079. bestMatch = k;
  1080. }
  1081. } else if (matchType == MatchType.AFTER) {
  1082. if (rc > 0
  1083. && (bestMatch == -1
  1084. || FunUtil.compareSiblingMembers(
  1085. rootMember,
  1086. rootMembers.get(bestMatch)) < 0))
  1087. {
  1088. bestMatch = k;
  1089. }
  1090. }
  1091. }
  1092. }
  1093. if (matchType == MatchType.EXACT_SCHEMA) {
  1094. return null;
  1095. }
  1096. if (matchType != MatchType.EXACT && bestMatch != -1) {
  1097. return rootMembers.get(bestMatch);
  1098. }
  1099. // If the first level is 'all', lookup member at second level. For
  1100. // example, they could say '[USA]' instead of '[(All
  1101. // Customers)].[USA]'.
  1102. return (rootMembers.size() > 0 && rootMembers.get(0).isAll())
  1103. ? reader.lookupMemberChildByName(
  1104. rootMembers.get(0),
  1105. memberName,
  1106. matchType)
  1107. : null;
  1108. }
  1109. /**
  1110. * Finds a named level in this hierarchy. Returns null if there is no
  1111. * such level.
  1112. */
  1113. public static Level lookupHierarchyLevel(Hierarchy hierarchy, String s) {
  1114. final Level[] levels = hierarchy.getLevels();
  1115. for (Level level : levels) {
  1116. if (level.getName().equalsIgnoreCase(s)) {
  1117. return level;
  1118. }
  1119. }
  1120. return null;
  1121. }
  1122. /**
  1123. * Finds the zero based ordinal of a Member among its siblings.
  1124. */
  1125. public static int getMemberOrdinalInParent(
  1126. SchemaReader reader,
  1127. Member member)
  1128. {
  1129. Member parent = member.getParentMember();
  1130. List<Member> siblings =
  1131. (parent == null)
  1132. ? reader.getHierarchyRootMembers(member.getHierarchy())
  1133. : reader.getMemberChildren(parent);
  1134. for (int i = 0; i < siblings.size(); i++) {
  1135. if (siblings.get(i).equals(member)) {
  1136. return i;
  1137. }
  1138. }
  1139. throw Util.newInternal(
  1140. "could not find member " + member + " amongst its siblings");
  1141. }
  1142. /**
  1143. * returns the first descendant on the level underneath parent.
  1144. * If parent = [Time].[1997] and level = [Time].[Month], then
  1145. * the member [Time].[1997].[Q1].[1] will be returned
  1146. */
  1147. public static Member getFirstDescendantOnLevel(
  1148. SchemaReader reader,
  1149. Member parent,
  1150. Level level)
  1151. {
  1152. Member m = parent;
  1153. while (m.getLevel() != level) {
  1154. List<Member> children = reader.getMemberChildren(m);
  1155. m = children.get(0);
  1156. }
  1157. return m;
  1158. }
  1159. /**
  1160. * Returns whether a string is null or empty.
  1161. */
  1162. public static boolean isEmpty(String s) {
  1163. return (s == null) || (s.length() == 0);
  1164. }
  1165. /**
  1166. * Encloses a value in single-quotes, to make a SQL string value. Examples:
  1167. * <code>singleQuoteForSql(null)</code> yields <code>NULL</code>;
  1168. * <code>singleQuoteForSql("don't")</code> yields <code>'don''t'</code>.
  1169. */
  1170. public static String singleQuoteString(String val) {
  1171. StringBuilder buf = new StringBuilder(64);
  1172. singleQuoteString(val, buf);
  1173. return buf.toString();
  1174. }
  1175. /**
  1176. * Encloses a value in single-quotes, to make a SQL string value. Examples:
  1177. * <code>singleQuoteForSql(null)</code> yields <code>NULL</code>;
  1178. * <code>singleQuoteForSql("don't")</code> yields <code>'don''t'</code>.
  1179. */
  1180. public static void singleQuoteString(String val, StringBuilder buf) {
  1181. buf.append('\'');
  1182. String s0 = replace(val, "'", "''");
  1183. buf.append(s0);
  1184. buf.append('\'');
  1185. }
  1186. /**
  1187. * Creates a random number generator.
  1188. *
  1189. * @param seed Seed for random number generator.
  1190. * If 0, generate a seed from the system clock and print the value
  1191. * chosen. (This is effectively non-deterministic.)
  1192. * If -1, generate a seed from an internal random number generator.
  1193. * (This is deterministic, but ensures that different tests have
  1194. * different seeds.)
  1195. *
  1196. * @return A random number generator.
  1197. */
  1198. public static Random createRandom(long seed) {
  1199. if (seed == 0) {
  1200. seed = new Random().nextLong();
  1201. System.out.println("random: seed=" + seed);
  1202. } else if (seed == -1 && metaRandom != null) {
  1203. seed = metaRandom.nextLong();
  1204. }
  1205. return new Random(seed);
  1206. }
  1207. /**
  1208. * Returns whether a property is valid for a member of a given level.
  1209. * It is valid if the property is defined at the level or at
  1210. * an ancestor level, or if the property is a standard property such as
  1211. * "FORMATTED_VALUE".
  1212. *
  1213. * @param propertyName Property name
  1214. * @param level Level
  1215. * @return Whether property is valid
  1216. */
  1217. public static boolean isValidProperty(
  1218. String propertyName,
  1219. Level level)
  1220. {
  1221. return lookupProperty(level, propertyName) != null;
  1222. }
  1223. /**
  1224. * Finds a member property called <code>propertyName</code> at, or above,
  1225. * <code>level</code>.
  1226. */
  1227. public static Property lookupProperty(
  1228. Level level,
  1229. String propertyName)
  1230. {
  1231. do {
  1232. Property[] properties = level.getProperties();
  1233. for (Property property : properties) {
  1234. if (property.getName().equals(propertyName)) {
  1235. return property;
  1236. }
  1237. }
  1238. level = level.getParentLevel();
  1239. } while (level != null);
  1240. // Now try a standard property.
  1241. boolean caseSensitive =
  1242. MondrianProperties.instance().CaseSensitive.get();
  1243. final Property property = Property.lookup(propertyName, caseSensitive);
  1244. if (property != null
  1245. && property.isMemberProperty()
  1246. && property.isStandard())
  1247. {
  1248. return property;
  1249. }
  1250. return null;
  1251. }
  1252. /**
  1253. * Insert a call to this method if you want to flag a piece of
  1254. * undesirable code.
  1255. *
  1256. * @deprecated
  1257. */
  1258. public static <T> T deprecated(T reason) {
  1259. throw new UnsupportedOperationException(reason.toString());
  1260. }
  1261. /**
  1262. * Insert a call to this method if you want to flag a piece of
  1263. * undesirable code.
  1264. *
  1265. * @deprecated
  1266. */
  1267. public static <T> T deprecated(T reason, boolean fail) {
  1268. if (fail) {
  1269. throw new UnsupportedOperationException(reason.toString());
  1270. } else {
  1271. return reason;
  1272. }
  1273. }
  1274. public static List<Member> addLevelCalculatedMembers(
  1275. SchemaReader reader,
  1276. Level level,
  1277. List<Member> members)
  1278. {
  1279. List<Member> calcMembers =
  1280. reader.getCalculatedMembers(level.getHierarchy());
  1281. List<Member> calcMembersInThisLevel = new ArrayList<Member>();
  1282. for (Member calcMember : calcMembers) {
  1283. if (calcMember.getLevel().equals(level)) {
  1284. calcMembersInThisLevel.add(calcMember);
  1285. }
  1286. }
  1287. if (!calcMembersInThisLevel.isEmpty()) {
  1288. List<Member> newMemberList =
  1289. new ConcatenableList<Member>();
  1290. newMemberList.addAll(members);
  1291. newMemberList.addAll(calcMembersInThisLevel);
  1292. return newMemberList;
  1293. }
  1294. return members;
  1295. }
  1296. /**
  1297. * Returns an exception which indicates that a particular piece of
  1298. * functionality should work, but a developer has not implemented it yet.
  1299. */
  1300. public static RuntimeException needToImplement(Object o) {
  1301. throw new UnsupportedOperationException("need to implement " + o);
  1302. }
  1303. /**
  1304. * Returns an exception indicating that we didn't expect to find this value
  1305. * here.
  1306. */
  1307. public static <T extends Enum<T>> RuntimeException badValue(
  1308. Enum<T> anEnum)
  1309. {
  1310. return Util.newInternal(
  1311. "Was not expecting value '" + anEnum
  1312. + "' for enumeration '" + anEnum.getDeclaringClass().getName()
  1313. + "' in this context");
  1314. }
  1315. /**
  1316. * Masks Mondrian's version number from a string.
  1317. *
  1318. * @param str String
  1319. * @return String with each occurrence of mondrian's version number
  1320. * (e.g. "2.3.0.0") replaced with "${mondrianVersion}"
  1321. */
  1322. public static String maskVersion(String str) {
  1323. MondrianServer.MondrianVersion mondrianVersion =
  1324. MondrianServer.forId(null).getVersion();
  1325. String versionString = mondrianVersion.getVersionString();
  1326. return replace(str, versionString, "${mondrianVersion}");
  1327. }
  1328. /**
  1329. * Converts a list of SQL-style patterns into a Java regular expression.
  1330. *
  1331. * <p>For example, {"Foo_", "Bar%BAZ"} becomes "Foo.|Bar.*BAZ".
  1332. *
  1333. * @param wildcards List of SQL-style wildcard expressions
  1334. * @return Regular expression
  1335. */
  1336. public static String wildcardToRegexp(List<String> wildcards) {
  1337. StringBuilder buf = new StringBuilder();
  1338. for (String value : wildcards) {
  1339. if (buf.length() > 0) {
  1340. buf.append('|');
  1341. }
  1342. int i = 0;
  1343. while (true) {
  1344. int percent = value.indexOf('%', i);
  1345. int underscore = value.indexOf('_', i);
  1346. if (percent == -1 && underscore == -1) {
  1347. if (i < value.length()) {
  1348. buf.append(quotePattern(value.substring(i)));
  1349. }
  1350. break;
  1351. }
  1352. if (underscore >= 0 && (underscore < percent || percent < 0)) {
  1353. if (i < underscore) {
  1354. buf.append(
  1355. quotePattern(value.substring(i, underscore)));
  1356. }
  1357. buf.append('.');
  1358. i = underscore + 1;
  1359. } else if (percent >= 0
  1360. && (percent < underscore || underscore < 0))
  1361. {
  1362. if (i < percent) {
  1363. buf.append(
  1364. quotePattern(value.substring(i, percent)));
  1365. }
  1366. buf.append(".*");
  1367. i = percent + 1;
  1368. } else {
  1369. throw new IllegalArgumentException();
  1370. }
  1371. }
  1372. }
  1373. return buf.toString();
  1374. }
  1375. /**
  1376. * Converts a camel-case name to an upper-case name with underscores.
  1377. *
  1378. * <p>For example, <code>camelToUpper("FooBar")</code> returns "FOO_BAR".
  1379. *
  1380. * @param s Camel-case string
  1381. * @return Upper-case string
  1382. */
  1383. public static String camelToUpper(String s) {
  1384. StringBuilder buf = new StringBuilder(s.length() + 10);
  1385. int prevUpper = -1;
  1386. for (int i = 0; i < s.length(); ++i) {
  1387. char c = s.charAt(i);
  1388. if (Character.isUpperCase(c)) {
  1389. if (i > prevUpper + 1) {
  1390. buf.append('_');
  1391. }
  1392. prevUpper = i;
  1393. } else {
  1394. c = Character.toUpperCase(c);
  1395. }
  1396. buf.append(c);
  1397. }
  1398. return buf.toString();
  1399. }
  1400. /**
  1401. * Parses a comma-separated list.
  1402. *
  1403. * <p>If a value contains a comma, escape it with a second comma. For
  1404. * example, <code>parseCommaList("x,y,,z")</code> returns
  1405. * <code>{"x", "y,z"}</code>.
  1406. *
  1407. * @param nameCommaList List of names separated by commas
  1408. * @return List of names
  1409. */
  1410. public static List<String> parseCommaList(String nameCommaList) {
  1411. if (nameCommaList.equals("")) {
  1412. return Collections.emptyList();
  1413. }
  1414. if (nameCommaList.endsWith(",")) {
  1415. // Special treatment for list ending in ",", because split ignores
  1416. // entries after separator.
  1417. final String zzz = "zzz";
  1418. final List<String> list = parseCommaList(nameCommaList + zzz);
  1419. String last = list.get(list.size() - 1);
  1420. if (last.equals(zzz)) {
  1421. list.remove(list.size() - 1);
  1422. } else {
  1423. list.set(
  1424. list.size() - 1,
  1425. last.substring(0, last.length() - zzz.length()));
  1426. }
  1427. return list;
  1428. }
  1429. List<String> names = new ArrayList<String>();
  1430. final String[] strings = nameCommaList.split(",");
  1431. for (String string : strings) {
  1432. final int count = names.size();
  1433. if (count > 0
  1434. && names.get(count - 1).equals(""))
  1435. {
  1436. if (count == 1) {
  1437. if (string.equals("")) {
  1438. names.add("");
  1439. } else {
  1440. names.set(
  1441. 0,
  1442. "," + string);
  1443. }
  1444. } else {
  1445. names.set(
  1446. count - 2,
  1447. names.get(count - 2) + "," + string);
  1448. names.remove(count - 1);
  1449. }
  1450. } else {
  1451. names.add(string);
  1452. }
  1453. }
  1454. return names;
  1455. }
  1456. /**
  1457. * Returns an annotation of a particular class on a method. Returns the
  1458. * default value if the annotation is not present, or in JDK 1.4.
  1459. *
  1460. * @param method Method containing annotation
  1461. * @param annotationClassName Name of annotation class to find
  1462. * @param defaultValue Value to return if annotation is not present
  1463. * @return value of annotation
  1464. */
  1465. public static <T> T getAnnotation(
  1466. Method method,
  1467. String annotationClassName,
  1468. T defaultValue)
  1469. {
  1470. return compatible.getAnnotation(
  1471. method, annotationClassName, defaultValue);
  1472. }
  1473. /**
  1474. * Closes and cancels a {@link Statement} using the correct methods
  1475. * available on the current Java runtime.
  1476. * <p>If errors are encountered while canceling a statement,
  1477. * the message is logged in {@link Util}.
  1478. * @param stmt The statement to cancel.
  1479. */
  1480. public static void cancelStatement(Statement stmt) {
  1481. compatible.cancelStatement(stmt);
  1482. }
  1483. public static MemoryInfo getMemoryInfo() {
  1484. return compatible.getMemoryInfo();
  1485. }
  1486. /**
  1487. * Converts a list of a string.
  1488. *
  1489. * For example,
  1490. * <code>commaList("foo", Arrays.asList({"a", "b"}))</code>
  1491. * returns "foo(a, b)".
  1492. *
  1493. * @param s Prefix
  1494. * @param list List
  1495. * @return String representation of string
  1496. */
  1497. public static <T> String commaList(
  1498. String s,
  1499. List<T> list)
  1500. {
  1501. final StringBuilder buf = new StringBuilder(s);
  1502. buf.append("(");
  1503. int k = -1;
  1504. for (T t : list) {
  1505. if (++k > 0) {
  1506. buf.append(", ");
  1507. }
  1508. buf.append(t);
  1509. }
  1510. buf.append(")");
  1511. return buf.toString();
  1512. }
  1513. /**
  1514. * Makes a name distinct from other names which have already been used
  1515. * and shorter than a length limit, adds it to the list, and returns it.
  1516. *
  1517. * @param name Suggested name, may not be unique
  1518. * @param maxLength Maximum length of generated name
  1519. * @param nameList Collection of names already used
  1520. *
  1521. * @return Unique name
  1522. */
  1523. public static String uniquify(
  1524. String name,
  1525. int maxLength,
  1526. Collection<String> nameList)
  1527. {
  1528. assert name != null;
  1529. if (name.length() > maxLength) {
  1530. name = name.substring(0, maxLength);
  1531. }
  1532. if (nameList.contains(name)) {
  1533. String aliasBase = name;
  1534. int j = 0;
  1535. while (true) {
  1536. name = aliasBase + j;
  1537. if (name.length() > maxLength) {
  1538. aliasBase = aliasBase.substring(0, aliasBase.length() - 1);
  1539. continue;
  1540. }
  1541. if (!nameList.contains(name)) {
  1542. break;
  1543. }
  1544. j++;
  1545. }
  1546. }
  1547. nameList.add(name);
  1548. return name;
  1549. }
  1550. /**
  1551. * Returns whether a collection contains precisely one distinct element.
  1552. * Returns false if the collection is empty, or if it contains elements
  1553. * that are not the same as each other.
  1554. *
  1555. * @param collection Collection
  1556. * @return boolean true if all values are same
  1557. */
  1558. public static <T> boolean areOccurencesEqual(
  1559. Collection<T> collection)
  1560. {
  1561. Iterator<T> it = collection.iterator();
  1562. if (!it.hasNext()) {
  1563. // Collection is empty
  1564. return false;
  1565. }
  1566. T first = it.next();
  1567. while (it.hasNext()) {
  1568. T t = it.next();
  1569. if (!t.equals(first)) {
  1570. return false;
  1571. }
  1572. }
  1573. return true;
  1574. }
  1575. /**
  1576. * Creates a memory-, CPU- and cache-efficient immutable list.
  1577. *
  1578. * @param t Array of members of list
  1579. * @param <T> Element type
  1580. * @return List containing the given members
  1581. */
  1582. public static <T> List<T> flatList(T... t) {
  1583. return _flatList(t, false);
  1584. }
  1585. /**
  1586. * Creates a memory-, CPU- and cache-efficient immutable list,
  1587. * always copying the contents.
  1588. *
  1589. * @param t Array of members of list
  1590. * @param <T> Element type
  1591. * @return List containing the given members
  1592. */
  1593. public static <T> List<T> flatListCopy(T... t) {
  1594. return _flatList(t, true);
  1595. }
  1596. /**
  1597. * Creates a memory-, CPU- and cache-efficient immutable list, optionally
  1598. * copying the list.
  1599. *
  1600. * @param copy Whether to always copy the list
  1601. * @param t Array of members of list
  1602. * @return List containing the given members
  1603. */
  1604. private static <T> List<T> _flatList(T[] t, boolean copy) {
  1605. switch (t.length) {
  1606. case 0:
  1607. return Collections.emptyList();
  1608. case 1:
  1609. return Collections.singletonList(t[0]);
  1610. case 2:
  1611. return new Flat2List<T>(t[0], t[1]);
  1612. case 3:
  1613. return new Flat3List<T>(t[0], t[1], t[2]);
  1614. default:
  1615. // REVIEW: AbstractList contains a modCount field; we could
  1616. // write our own implementation and reduce creation overhead a
  1617. // bit.
  1618. if (copy) {
  1619. return Arrays.asList(t.clone());
  1620. } else {
  1621. return Arrays.asList(t);
  1622. }
  1623. }
  1624. }
  1625. /**
  1626. * Creates a memory-, CPU- and cache-efficient immutable list from an
  1627. * existing list. The list is always copied.
  1628. *
  1629. * @param t Array of members of list
  1630. * @param <T> Element type
  1631. * @return List containing the given members
  1632. */
  1633. public static <T> List<T> flatList(List<T> t) {
  1634. switch (t.size()) {
  1635. case 0:
  1636. return Collections.emptyList();
  1637. case 1:
  1638. return Collections.singletonList(t.get(0));
  1639. case 2:
  1640. return new Flat2List<T>(t.get(0), t.get(1));
  1641. case 3:
  1642. return new Flat3List<T>(t.get(0), t.get(1), t.get(2));
  1643. default:
  1644. // REVIEW: AbstractList contains a modCount field; we could
  1645. // write our own implementation and reduce creation overhead a
  1646. // bit.
  1647. //noinspection unchecked
  1648. return (List<T>) Arrays.asList(t.toArray());
  1649. }
  1650. }
  1651. /**
  1652. * Parses a locale string.
  1653. *
  1654. * <p>The inverse operation of {@link java.util.Locale#toString()}.
  1655. *
  1656. * @param localeString Locale string, e.g. "en" or "en_US"
  1657. * @return Java locale object
  1658. */
  1659. public static Locale parseLocale(String localeString) {
  1660. String[] strings = localeString.split("_");
  1661. switch (strings.length) {
  1662. case 1:
  1663. return new Locale(strings[0]);
  1664. case 2:
  1665. return new Locale(strings[0], strings[1]);
  1666. case 3:
  1667. return new Locale(strings[0], strings[1], strings[2]);
  1668. default:
  1669. throw newInternal(
  1670. "bad locale string '" + localeString + "'");
  1671. }
  1672. }
  1673. private static final Map<String, String> TIME_UNITS =
  1674. Olap4jUtil.mapOf(
  1675. "ns", "NANOSECONDS",
  1676. "us", "MICROSECONDS",
  1677. "ms", "MILLISECONDS",
  1678. "s", "SECONDS",
  1679. "m", "MINUTES",
  1680. "h", "HOURS",
  1681. "d", "DAYS");
  1682. /**
  1683. * Parses an interval.
  1684. *
  1685. * <p>For example, "30s" becomes (30, {@link TimeUnit#SECONDS});
  1686. * "2us" becomes (2, {@link TimeUnit#MICROSECONDS}).</p>
  1687. *
  1688. * <p>Units m (minutes), h (hours) and d (days) are only available
  1689. * in JDK 1.6 or later, because the corresponding constants are missing
  1690. * from {@link TimeUnit} in JDK 1.5.</p>
  1691. *
  1692. * @param s String to parse
  1693. * @param unit Default time unit; may be null
  1694. *
  1695. * @return Pair of value and time unit. Neither pair or its components are
  1696. * null
  1697. *
  1698. * @throws NumberFormatException if unit is not present and there is no
  1699. * default, or if number is not valid
  1700. */
  1701. public static Pair<Long, TimeUnit> parseInterval(
  1702. String s,
  1703. TimeUnit unit)
  1704. throws NumberFormatException
  1705. {
  1706. final String original = s;
  1707. for (Map.Entry<String, String> entry : TIME_UNITS.entrySet()) {
  1708. final String abbrev = entry.getKey();
  1709. if (s.endsWith(abbrev)) {
  1710. final String full = entry.getValue();
  1711. try {
  1712. unit = TimeUnit.valueOf(full);
  1713. s = s.substring(0, s.length() - abbrev.length());
  1714. break;
  1715. } catch (IllegalArgumentException e) {
  1716. // ignore - MINUTES, HOURS, DAYS are not defined in JDK1.5
  1717. }
  1718. }
  1719. }
  1720. if (unit == null) {
  1721. throw new NumberFormatException(
  1722. "Invalid time interval '" + original + "'. Does not contain a "
  1723. + "time unit. (Suffix may be ns (nanoseconds), "
  1724. + "us (microseconds), ms (milliseconds), s (seconds), "
  1725. + "h (hours), d (days). For example, '20s' means 20 seconds.)");
  1726. }
  1727. try {
  1728. return Pair.of(new BigDecimal(s).longValue(), unit);
  1729. } catch (NumberFormatException e) {
  1730. throw new NumberFormatException(
  1731. "Invalid time interval '" + original + "'");
  1732. }
  1733. }
  1734. /**
  1735. * Converts a list of olap4j-style segments to a list of mondrian-style
  1736. * segments.
  1737. *
  1738. * @param olap4jSegmentList List of olap4j segments
  1739. * @return List of mondrian segments
  1740. */
  1741. public static List<Id.Segment> convert(
  1742. List<IdentifierSegment> olap4jSegmentList)
  1743. {
  1744. final List<Id.Segment> list = new ArrayList<Id.Segment>();
  1745. for (IdentifierSegment olap4jSegment : olap4jSegmentList) {
  1746. list.add(convert(olap4jSegment));
  1747. }
  1748. return list;
  1749. }
  1750. /**
  1751. * Converts an olap4j-style segment to a mondrian-style segment.
  1752. *
  1753. * @param olap4jSegment olap4j segment
  1754. * @return mondrian segment
  1755. */
  1756. public static Id.Segment convert(IdentifierSegment olap4jSegment) {
  1757. if (olap4jSegment instanceof NameSegment) {
  1758. return convert((NameSegment) olap4jSegment);
  1759. } else {
  1760. return convert((KeySegment) olap4jSegment);
  1761. }
  1762. }
  1763. private static Id.KeySegment convert(final KeySegment keySegment) {
  1764. return new Id.KeySegment(
  1765. new AbstractList<Id.NameSegment>() {
  1766. public Id.NameSegment get(int index) {
  1767. return convert(keySegment.getKeyParts().get(index));
  1768. }
  1769. public int size() {
  1770. return keySegment.getKeyParts().size();
  1771. }
  1772. });
  1773. }
  1774. private static Id.NameSegment convert(NameSegment nameSegment) {
  1775. return new Id.NameSegment(
  1776. nameSegment.getName(),
  1777. convert(nameSegment.getQuoting()));
  1778. }
  1779. private static Id.Quoting convert(Quoting quoting) {
  1780. switch (quoting) {
  1781. case QUOTED:
  1782. return Id.Quoting.QUOTED;
  1783. case UNQUOTED:
  1784. return Id.Quoting.UNQUOTED;
  1785. case KEY:
  1786. return Id.Quoting.KEY;
  1787. default:
  1788. throw Util.unexpected(quoting);
  1789. }
  1790. }
  1791. /**
  1792. * Applies a collection of filters to an iterable.
  1793. *
  1794. * @param iterable Iterable
  1795. * @param conds Zero or more conditions
  1796. * @param <T>
  1797. * @return Iterable that returns only members of underlying iterable for
  1798. * for which all conditions evaluate to true
  1799. */
  1800. public static <T> Iterable<T> filter(
  1801. final Iterable<T> iterable,
  1802. final Functor1<Boolean, T>... conds)
  1803. {
  1804. final Functor1<Boolean, T>[] conds2 = optimizeConditions(conds);
  1805. if (conds2.length == 0) {
  1806. return iterable;
  1807. }
  1808. return new Iterable<T>() {
  1809. public Iterator<T> iterator() {
  1810. return new Iterator<T>() {
  1811. final Iterator<T> iterator = iterable.iterator();
  1812. T next;
  1813. boolean hasNext = moveToNext();
  1814. private boolean moveToNext() {
  1815. outer:
  1816. while (iterator.hasNext()) {
  1817. next = iterator.next();
  1818. for (Functor1<Boolean, T> cond : conds2) {
  1819. if (!cond.apply(next)) {
  1820. continue outer;
  1821. }
  1822. }
  1823. return true;
  1824. }
  1825. return false;
  1826. }
  1827. public boolean hasNext() {
  1828. return hasNext;
  1829. }
  1830. public T next() {
  1831. T t = next;
  1832. hasNext = moveToNext();
  1833. return t;
  1834. }
  1835. public void remove() {
  1836. throw new UnsupportedOperationException();
  1837. }
  1838. };
  1839. }
  1840. };
  1841. }
  1842. private static <T> Functor1<Boolean, T>[] optimizeConditions(
  1843. Functor1<Boolean, T>[] conds)
  1844. {
  1845. final List<Functor1<Boolean, T>> functor1List =
  1846. new ArrayList<Functor1<Boolean, T>>(Arrays.asList(conds));
  1847. for (Iterator<Functor1<Boolean, T>> funcIter =
  1848. functor1List.iterator(); funcIter.hasNext();)
  1849. {
  1850. Functor1<Boolean, T> booleanTFunctor1 = funcIter.next();
  1851. if (booleanTFunctor1 == trueFunctor()) {
  1852. funcIter.remove();
  1853. }
  1854. }
  1855. if (functor1List.size() < conds.length) {
  1856. //noinspection unchecked
  1857. return functor1List.toArray(new Functor1[functor1List.size()]);
  1858. } else {
  1859. return conds;
  1860. }
  1861. }
  1862. /**
  1863. * Sorts a collection of {@link Comparable} objects and returns a list.
  1864. *
  1865. * @param collection Collection
  1866. * @param <T> Element type
  1867. * @return Sorted list
  1868. */
  1869. public static <T extends Comparable> List<T> sort(
  1870. Collection<T> collection)
  1871. {
  1872. Object[] a = collection.toArray(new Object[collection.size()]);
  1873. Arrays.sort(a);
  1874. return cast(Arrays.asList(a));
  1875. }
  1876. /**
  1877. * Sorts a collection of objects using a {@link java.util.Comparator} and returns a
  1878. * list.
  1879. *
  1880. * @param collection Collection
  1881. * @param comparator Comparator
  1882. * @param <T> Element type
  1883. * @return Sorted list
  1884. */
  1885. public static <T> List<T> sort(
  1886. Collection<T> collection,
  1887. Comparator<T> comparator)
  1888. {
  1889. Object[] a = collection.toArray(new Object[collection.size()]);
  1890. //noinspection unchecked
  1891. Arrays.sort(a, (Comparator<? super Object>) comparator);
  1892. return cast(Arrays.asList(a));
  1893. }
  1894. public static List<IdentifierSegment> toOlap4j(
  1895. final List<Id.Segment> segments)
  1896. {
  1897. return new AbstractList<IdentifierSegment>() {
  1898. public IdentifierSegment get(int index) {
  1899. return toOlap4j(segments.get(index));
  1900. }
  1901. public int size() {
  1902. return segments.size();
  1903. }
  1904. };
  1905. }
  1906. public static IdentifierSegment toOlap4j(Id.Segment segment) {
  1907. switch (segment.quoting) {
  1908. case KEY:
  1909. return toOlap4j((Id.KeySegment) segment);
  1910. default:
  1911. return toOlap4j((Id.NameSegment) segment);
  1912. }
  1913. }
  1914. private static KeySegment toOlap4j(final Id.KeySegment keySegment) {
  1915. return new KeySegment(
  1916. new AbstractList<NameSegment>() {
  1917. public NameSegment get(int index) {
  1918. return toOlap4j(keySegment.subSegmentList.get(index));
  1919. }
  1920. public int size() {
  1921. return keySegment.subSegmentList.size();
  1922. }
  1923. });
  1924. }
  1925. private static NameSegment toOlap4j(Id.NameSegment nameSegment) {
  1926. return new NameSegment(
  1927. null,
  1928. nameSegment.name,
  1929. toOlap4j(nameSegment.quoting));
  1930. }
  1931. public static Quoting toOlap4j(Id.Quoting quoting) {
  1932. return Quoting.valueOf(quoting.name());
  1933. }
  1934. // TODO: move to IdentifierSegment
  1935. public static boolean matches(IdentifierSegment segment, String name) {
  1936. switch (segment.getQuoting()) {
  1937. case KEY:
  1938. return false; // FIXME
  1939. case QUOTED:
  1940. return equalName(segment.getName(), name);
  1941. case UNQUOTED:
  1942. return segment.getName().equalsIgnoreCase(name);
  1943. default:
  1944. throw unexpected(segment.getQuoting());
  1945. }
  1946. }
  1947. public static RuntimeException newElementNotFoundException(
  1948. int category,
  1949. IdentifierNode identifierNode)
  1950. {
  1951. String type;
  1952. switch (category) {
  1953. case Category.Member:
  1954. return MondrianResource.instance().MemberNotFound.ex(
  1955. identifierNode.toString());
  1956. case Category.Unknown:
  1957. type = "Element";
  1958. break;
  1959. default:
  1960. type = Category.instance().getDescription(category);
  1961. }
  1962. return newError(type + " '" + identifierNode + "' not found");
  1963. }
  1964. /**
  1965. * Calls {@link java.util.concurrent.Future#get()} and converts any
  1966. * throwable into a non-checked exception.
  1967. *
  1968. * @param future Future
  1969. * @param message Message to qualify wrapped exception
  1970. * @param <T> Result type
  1971. * @return Result
  1972. */
  1973. public static <T> T safeGet(Future<T> future, String message) {
  1974. try {
  1975. return future.get();
  1976. } catch (InterruptedException e) {
  1977. throw newError(e, message);
  1978. } catch (ExecutionException e) {
  1979. final Throwable cause = e.getCause();
  1980. if (cause instanceof RuntimeException) {
  1981. throw (RuntimeException) cause;
  1982. } else if (cause instanceof Error) {
  1983. throw (Error) cause;
  1984. } else {
  1985. throw newError(cause, message);
  1986. }
  1987. }
  1988. }
  1989. public static <T> Set<T> newIdentityHashSetFake() {
  1990. final HashMap<T, Boolean> map = new HashMap<T, Boolean>();
  1991. return new Set<T>() {
  1992. public int size() {
  1993. return map.size();
  1994. }
  1995. public boolean isEmpty() {
  1996. return map.isEmpty();
  1997. }
  1998. public boolean contains(Object o) {
  1999. return map.containsKey(o);
  2000. }
  2001. public Iterator<T> iterator() {
  2002. return map.keySet().iterator();
  2003. }
  2004. public Object[] toArray() {
  2005. return map.keySet().toArray();
  2006. }
  2007. public <T> T[] toArray(T[] a) {
  2008. return map.keySet().toArray(a);
  2009. }
  2010. public boolean add(T t) {
  2011. return map.put(t, Boolean.TRUE) == null;
  2012. }
  2013. public boolean remove(Object o) {
  2014. return map.remove(o) == Boolean.TRUE;
  2015. }
  2016. public boolean containsAll(Collection<?> c) {
  2017. return map.keySet().containsAll(c);
  2018. }
  2019. public boolean addAll(Collection<? extends T> c) {
  2020. throw new UnsupportedOperationException();
  2021. }
  2022. public boolean retainAll(Collection<?> c) {
  2023. throw new UnsupportedOperationException();
  2024. }
  2025. public boolean removeAll(Collection<?> c) {
  2026. throw new UnsupportedOperationException();
  2027. }
  2028. public void clear() {
  2029. map.clear();
  2030. }
  2031. };
  2032. }
  2033. /**
  2034. * Equivalent to {@link Timer#Timer(String, boolean)}.
  2035. * (Introduced in JDK 1.5.)
  2036. *
  2037. * @param name the name of the associated thread
  2038. * @param isDaemon true if the associated thread should run as a daemon
  2039. * @return timer
  2040. */
  2041. public static Timer newTimer(String name, boolean isDaemon) {
  2042. return compatible.newTimer(name, isDaemon);
  2043. }
  2044. /**
  2045. * As Arrays#binarySearch(Object[], int, int, Object), but
  2046. * available pre-JDK 1.6.
  2047. */
  2048. public static <T extends Comparable<T>> int binarySearch(
  2049. T[] ts, int start, int end, T t)
  2050. {
  2051. return compatible.binarySearch(ts, start, end, t);
  2052. }
  2053. /**
  2054. * Returns the intersection of two sorted sets. Does not modify either set.
  2055. *
  2056. * <p>Optimized for the case that both sets are {@link ArraySortedSet}.</p>
  2057. *
  2058. * @param set1 First set
  2059. * @param set2 Second set
  2060. * @return Intersection of the sets
  2061. */
  2062. public static <E extends Comparable> SortedSet<E> intersect(
  2063. SortedSet<E> set1,
  2064. SortedSet<E> set2)
  2065. {
  2066. if (set1.isEmpty()) {
  2067. return set1;
  2068. }
  2069. if (set2.isEmpty()) {
  2070. return set2;
  2071. }
  2072. if (!(set1 instanceof ArraySortedSet)
  2073. || !(set2 instanceof ArraySortedSet))
  2074. {
  2075. final TreeSet<E> set = new TreeSet<E>(set1);
  2076. set.retainAll(set2);
  2077. return set;
  2078. }
  2079. final Comparable<?>[] result =
  2080. new Comparable[Math.min(set1.size(), set2.size())];
  2081. final Iterator<E> it1 = set1.iterator();
  2082. final Iterator<E> it2 = set2.iterator();
  2083. int i = 0;
  2084. E e1 = it1.next();
  2085. E e2 = it2.next();
  2086. for (;;) {
  2087. final int compare = e1.compareTo(e2);
  2088. if (compare == 0) {
  2089. result[i++] = e1;
  2090. if (!it1.hasNext() || !it2.hasNext()) {
  2091. break;
  2092. }
  2093. e1 = it1.next();
  2094. e2 = it2.next();
  2095. } else if (compare == 1) {
  2096. if (!it2.hasNext()) {
  2097. break;
  2098. }
  2099. e2 = it2.next();
  2100. } else {
  2101. if (!it1.hasNext()) {
  2102. break;
  2103. }
  2104. e1 = it1.next();
  2105. }
  2106. }
  2107. return new ArraySortedSet(result, 0, i);
  2108. }
  2109. /**
  2110. * Compares two integers using the same algorithm as
  2111. * {@link Integer#compareTo(Integer)}.
  2112. *
  2113. * @param i0 First integer
  2114. * @param i1 Second integer
  2115. * @return Comparison
  2116. */
  2117. public static int compareIntegers(int i0, int i1) {
  2118. return (i0 < i1 ? -1 : (i0 == i1 ? 0 : 1));
  2119. }
  2120. /**
  2121. * Returns the last item in a list.
  2122. *
  2123. * @param list List
  2124. * @param <T> Element type
  2125. * @return Last item in the list
  2126. * @throws IndexOutOfBoundsException if list is empty
  2127. */
  2128. public static <T> T last(List<T> list) {
  2129. return list.get(list.size() - 1);
  2130. }
  2131. /**
  2132. * Returns the sole item in a list.
  2133. *
  2134. * <p>If the list has 0 or more than one element, throws.</p>
  2135. *
  2136. * @param list List
  2137. * @param <T> Element type
  2138. * @return Sole item in the list
  2139. * @throws IndexOutOfBoundsException if list is empty or has more than 1 elt
  2140. */
  2141. public static <T> T only(List<T> list) {
  2142. if (list.size() != 1) {
  2143. throw new IndexOutOfBoundsException(
  2144. "list " + list + " has " + list.size()
  2145. + " elements, expected 1");
  2146. }
  2147. return list.get(0);
  2148. }
  2149. /**
  2150. * Closes a JDBC result set, statement, and connection, ignoring any errors.
  2151. * If any of them are null, that's fine.
  2152. *
  2153. * <p>If any of them throws a {@link SQLException}, returns the first
  2154. * such exception, but always executes all closes.</p>
  2155. *
  2156. * @param resultSet Result set
  2157. * @param statement Statement
  2158. * @param connection Connection
  2159. */
  2160. public static SQLException close(
  2161. ResultSet resultSet,
  2162. Statement statement,
  2163. Connection connection)
  2164. {
  2165. SQLException firstException = null;
  2166. if (resultSet != null) {
  2167. try {
  2168. if (statement == null) {
  2169. statement = resultSet.getStatement();
  2170. }
  2171. resultSet.close();
  2172. } catch (Throwable t) {
  2173. firstException = new SQLException();
  2174. firstException.initCause(t);
  2175. }
  2176. }
  2177. if (statement != null) {
  2178. try {
  2179. statement.close();
  2180. } catch (Throwable t) {
  2181. if (firstException == null) {
  2182. firstException = new SQLException();
  2183. firstException.initCause(t);
  2184. }
  2185. }
  2186. }
  2187. if (connection != null) {
  2188. try {
  2189. connection.close();
  2190. } catch (Throwable t) {
  2191. if (firstException == null) {
  2192. firstException = new SQLException();
  2193. firstException.initCause(t);
  2194. }
  2195. }
  2196. }
  2197. return firstException;
  2198. }
  2199. /**
  2200. * Creates a bitset with bits from {@code fromIndex} (inclusive) to
  2201. * specified {@code toIndex} (exclusive) set to {@code true}.
  2202. *
  2203. * <p>For example, {@code bitSetBetween(0, 3)} returns a bit set with bits
  2204. * {0, 1, 2} set.
  2205. *
  2206. * @param fromIndex Index of the first bit to be set.
  2207. * @param toIndex Index after the last bit to be set.
  2208. * @return Bit set
  2209. */
  2210. public static BitSet bitSetBetween(int fromIndex, int toIndex) {
  2211. final BitSet bitSet = new BitSet();
  2212. if (toIndex > fromIndex) {
  2213. // Avoid http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6222207
  2214. // "BitSet internal invariants may be violated"
  2215. bitSet.set(fromIndex, toIndex);
  2216. }
  2217. return bitSet;
  2218. }
  2219. public static class ErrorCellValue {
  2220. public String toString() {
  2221. return "#ERR";
  2222. }
  2223. }
  2224. @SuppressWarnings({"unchecked"})
  2225. public static <T> T[] genericArray(Class<T> clazz, int size) {
  2226. return (T[]) Array.newInstance(clazz, size);
  2227. }
  2228. /**
  2229. * Throws an internal error if condition is not true. It would be called
  2230. * <code>assert</code>, but that is a keyword as of JDK 1.4.
  2231. */
  2232. public static void assertTrue(boolean b) {
  2233. if (!b) {
  2234. throw newInternal("assert failed");
  2235. }
  2236. }
  2237. /**
  2238. * Throws an internal error with the given messagee if condition is not
  2239. * true. It would be called <code>assert</code>, but that is a keyword as
  2240. * of JDK 1.4.
  2241. */
  2242. public static void assertTrue(boolean b, String message) {
  2243. if (!b) {
  2244. throw newInternal("assert failed: " + message);
  2245. }
  2246. }
  2247. /**
  2248. * Creates an internal error with a given message.
  2249. */
  2250. public static RuntimeException newInternal(String message) {
  2251. return MondrianResource.instance().Internal.ex(message);
  2252. }
  2253. /**
  2254. * Creates an internal error with a given message and cause.
  2255. */
  2256. public static RuntimeException newInternal(Throwable e, String message) {
  2257. return MondrianResource.instance().Internal.ex(message, e);
  2258. }
  2259. /**
  2260. * Creates a non-internal error. Currently implemented in terms of
  2261. * internal errors, but later we will create resourced messages.
  2262. */
  2263. public static RuntimeException newError(String message) {
  2264. return newInternal(message);
  2265. }
  2266. /**
  2267. * Creates a non-internal error. Currently implemented in terms of
  2268. * internal errors, but later we will create resourced messages.
  2269. */
  2270. public static RuntimeException newError(Throwable e, String message) {
  2271. return newInternal(e, message);
  2272. }
  2273. /**
  2274. * Returns an exception indicating that we didn't expect to find this value
  2275. * here.
  2276. *
  2277. * @param value Value
  2278. */
  2279. public static RuntimeException unexpected(Enum value) {
  2280. return Util.newInternal(
  2281. "Was not expecting value '" + value
  2282. + "' for enumeration '" + value.getClass().getName()
  2283. + "' in this context");
  2284. }
  2285. /**
  2286. * Checks that a precondition (declared using the javadoc <code>@pre</code>
  2287. * tag) is satisfied.
  2288. *
  2289. * @param b The value of executing the condition
  2290. */
  2291. public static void assertPrecondition(boolean b) {
  2292. assertTrue(b);
  2293. }
  2294. /**
  2295. * Checks that a precondition (declared using the javadoc <code>@pre</code>
  2296. * tag) is satisfied. For example,
  2297. *
  2298. * <blockquote><pre>void f(String s) {
  2299. * Util.assertPrecondition(s != null, "s != null");
  2300. * ...
  2301. * }</pre></blockquote>
  2302. *
  2303. * @param b The value of executing the condition
  2304. * @param condition The text of the condition
  2305. */
  2306. public static void assertPrecondition(boolean b, String condition) {
  2307. assertTrue(b, condition);
  2308. }
  2309. /**
  2310. * Checks that a postcondition (declared using the javadoc
  2311. * <code>@post</code> tag) is satisfied.
  2312. *
  2313. * @param b The value of executing the condition
  2314. */
  2315. public static void assertPostcondition(boolean b) {
  2316. assertTrue(b);
  2317. }
  2318. /**
  2319. * Checks that a postcondition (declared using the javadoc
  2320. * <code>@post</code> tag) is satisfied.
  2321. *
  2322. * @param b The value of executing the condition
  2323. */
  2324. public static void assertPostcondition(boolean b, String condition) {
  2325. assertTrue(b, condition);
  2326. }
  2327. /**
  2328. * Converts an error into an array of strings, the most recent error first.
  2329. *
  2330. * @param e the error; may be null. Errors are chained according to their
  2331. * {@link Throwable#getCause cause}.
  2332. */
  2333. public static String[] convertStackToString(Throwable e) {
  2334. List<String> list = new ArrayList<String>();
  2335. while (e != null) {
  2336. String sMsg = getErrorMessage(e);
  2337. list.add(sMsg);
  2338. e = e.getCause();
  2339. }
  2340. return list.toArray(new String[list.size()]);
  2341. }
  2342. /**
  2343. * Constructs the message associated with an arbitrary Java error, making
  2344. * up one based on the stack trace if there is none. As
  2345. * {@link #getErrorMessage(Throwable,boolean)}, but does not print the
  2346. * class name if the exception is derived from {@link java.sql.SQLException}
  2347. * or is exactly a {@link java.lang.Exception}.
  2348. */
  2349. public static String getErrorMessage(Throwable err) {
  2350. boolean prependClassName =
  2351. !(err instanceof java.sql.SQLException
  2352. || err.getClass() == java.lang.Exception.class);
  2353. return getErrorMessage(err, prependClassName);
  2354. }
  2355. /**
  2356. * Constructs the message associated with an arbitrary Java error, making
  2357. * up one based on the stack trace if there is none.
  2358. *
  2359. * @param err the error
  2360. * @param prependClassName should the error be preceded by the
  2361. * class name of the Java exception? defaults to false, unless the error
  2362. * is derived from {@link java.sql.SQLException} or is exactly a {@link
  2363. * java.lang.Exception}
  2364. */
  2365. public static String getErrorMessage(
  2366. Throwable err,
  2367. boolean prependClassName)
  2368. {
  2369. String errMsg = err.getMessage();
  2370. if ((errMsg == null) || (err instanceof RuntimeException)) {
  2371. StringWriter sw = new StringWriter();
  2372. PrintWriter pw = new PrintWriter(sw);
  2373. err.printStackTrace(pw);
  2374. return sw.toString();
  2375. } else {
  2376. return (prependClassName)
  2377. ? err.getClass().getName() + ": " + errMsg
  2378. : errMsg;
  2379. }
  2380. }
  2381. /**
  2382. * If one of the causes of an exception is of a particular class, returns
  2383. * that cause. Otherwise returns null.
  2384. *
  2385. * @param e Exception
  2386. * @param clazz Desired class
  2387. * @param <T> Class
  2388. * @return Cause of given class, or null
  2389. */
  2390. public static <T extends Throwable>
  2391. T getMatchingCause(Throwable e, Class<T> clazz) {
  2392. for (;;) {
  2393. if (clazz.isInstance(e)) {
  2394. return clazz.cast(e);
  2395. }
  2396. final Throwable cause = e.getCause();
  2397. if (cause == null || cause == e) {
  2398. return null;
  2399. }
  2400. e = cause;
  2401. }
  2402. }
  2403. /**
  2404. * Converts an expression to a string.
  2405. */
  2406. public static String unparse(Exp exp) {
  2407. StringWriter sw = new StringWriter();
  2408. PrintWriter pw = new PrintWriter(sw);
  2409. exp.unparse(pw);
  2410. return sw.toString();
  2411. }
  2412. /**
  2413. * Converts an query to a string.
  2414. */
  2415. public static String unparse(Query query) {
  2416. StringWriter sw = new StringWriter();
  2417. PrintWriter pw = new QueryPrintWriter(sw);
  2418. query.unparse(pw);
  2419. return sw.toString();
  2420. }
  2421. /**
  2422. * Creates a file-protocol URL for the given file.
  2423. */
  2424. public static URL toURL(File file) throws MalformedURLException {
  2425. String path = file.getAbsolutePath();
  2426. // This is a bunch of weird code that is required to
  2427. // make a valid URL on the Windows platform, due
  2428. // to inconsistencies in what getAbsolutePath returns.
  2429. String fs = System.getProperty("file.separator");
  2430. if (fs.length() == 1) {
  2431. char sep = fs.charAt(0);
  2432. if (sep != '/') {
  2433. path = path.replace(sep, '/');
  2434. }
  2435. if (path.charAt(0) != '/') {
  2436. path = '/' + path;
  2437. }
  2438. }
  2439. path = "file://" + path;
  2440. return new URL(path);
  2441. }
  2442. /**
  2443. * <code>PropertyList</code> is an order-preserving list of key-value
  2444. * pairs. Lookup is case-insensitive, but the case of keys is preserved.
  2445. */
  2446. public static class PropertyList
  2447. implements Iterable<Pair<String, String>>, Serializable
  2448. {
  2449. List<Pair<String, String>> list =
  2450. new ArrayList<Pair<String, String>>();
  2451. public PropertyList() {
  2452. this.list = new ArrayList<Pair<String, String>>();
  2453. }
  2454. private PropertyList(List<Pair<String, String>> list) {
  2455. this.list = list;
  2456. }
  2457. @SuppressWarnings({"CloneDoesntCallSuperClone"})
  2458. @Override
  2459. public PropertyList clone() {
  2460. return new PropertyList(new ArrayList<Pair<String, String>>(list));
  2461. }
  2462. public String get(String key) {
  2463. return get(key, null);
  2464. }
  2465. public String get(String key, String defaultValue) {
  2466. for (int i = 0, n = list.size(); i < n; i++) {
  2467. Pair<String, String> pair = list.get(i);
  2468. if (pair.left.equalsIgnoreCase(key)) {
  2469. return pair.right;
  2470. }
  2471. }
  2472. return defaultValue;
  2473. }
  2474. public String put(String key, String value) {
  2475. for (int i = 0, n = list.size(); i < n; i++) {
  2476. Pair<String, String> pair = list.get(i);
  2477. if (pair.left.equalsIgnoreCase(key)) {
  2478. String old = pair.right;
  2479. if (key.equalsIgnoreCase("Provider")) {
  2480. // Unlike all other properties, later values of
  2481. // "Provider" do not supersede
  2482. } else {
  2483. pair.right = value;
  2484. }
  2485. return old;
  2486. }
  2487. }
  2488. list.add(new Pair<String, String>(key, value));
  2489. return null;
  2490. }
  2491. public boolean remove(String key) {
  2492. boolean found = false;
  2493. for (int i = 0; i < list.size(); i++) {
  2494. Pair<String, String> pair = list.get(i);
  2495. if (pair.getKey().equalsIgnoreCase(key)) {
  2496. list.remove(i);
  2497. found = true;
  2498. --i;
  2499. }
  2500. }
  2501. return found;
  2502. }
  2503. public String toString() {
  2504. StringBuilder sb = new StringBuilder(64);
  2505. for (int i = 0, n = list.size(); i < n; i++) {
  2506. Pair<String, String> pair = list.get(i);
  2507. if (i > 0) {
  2508. sb.append("; ");
  2509. }
  2510. sb.append(pair.left);
  2511. sb.append('=');
  2512. final String right = pair.right;
  2513. if (right == null) {
  2514. sb.append("'null'");
  2515. } else {
  2516. // Quote a property value if is has a semi colon in it
  2517. // 'xxx;yyy'. Escape any single-quotes by doubling them.
  2518. final int needsQuote = right.indexOf(';');
  2519. if (needsQuote >= 0) {
  2520. // REVIEW: This logic leaves off the leading/trailing
  2521. // quote if the property value already has a
  2522. // leading/trailing quote. Doesn't seem right to me.
  2523. if (right.charAt(0) != '\'') {
  2524. sb.append("'");
  2525. }
  2526. sb.append(replace(right, "'", "''"));
  2527. if (right.charAt(right.length() - 1) != '\'') {
  2528. sb.append("'");
  2529. }
  2530. } else {
  2531. sb.append(right);
  2532. }
  2533. }
  2534. }
  2535. return sb.toString();
  2536. }
  2537. public Iterator<Pair<String, String>> iterator() {
  2538. return list.iterator();
  2539. }
  2540. }
  2541. /**
  2542. * Converts an OLE DB connect string into a {@link PropertyList}.
  2543. *
  2544. * <p> For example, <code>"Provider=MSOLAP; DataSource=LOCALHOST;"</code>
  2545. * becomes the set of (key, value) pairs <code>{("Provider","MSOLAP"),
  2546. * ("DataSource", "LOCALHOST")}</code>. Another example is
  2547. * <code>Provider='sqloledb';Data Source='MySqlServer';Initial
  2548. * Catalog='Pubs';Integrated Security='SSPI';</code>.
  2549. *
  2550. * <p> This method implements as much as possible of the <a
  2551. * href="http://msdn.microsoft.com/library/en-us/oledb/htm/oledbconnectionstringsyntax.asp"
  2552. * target="_blank">OLE DB connect string syntax
  2553. * specification</a>. To find what it <em>actually</em> does, take
  2554. * a look at the <code>mondrian.olap.UtilTestCase</code> test case.
  2555. */
  2556. public static PropertyList parseConnectString(String s) {
  2557. return new ConnectStringParser(s).parse();
  2558. }
  2559. private static class ConnectStringParser {
  2560. private final String s;
  2561. private final int n;
  2562. private int i;
  2563. private final StringBuilder nameBuf;
  2564. private final StringBuilder valueBuf;
  2565. private ConnectStringParser(String s) {
  2566. this.s = s;
  2567. this.i = 0;
  2568. this.n = s.length();
  2569. this.nameBuf = new StringBuilder(64);
  2570. this.valueBuf = new StringBuilder(64);
  2571. }
  2572. PropertyList parse() {
  2573. PropertyList list = new PropertyList();
  2574. while (i < n) {
  2575. parsePair(list);
  2576. }
  2577. return list;
  2578. }
  2579. /**
  2580. * Reads "name=value;" or "name=value<EOF>".
  2581. */
  2582. void parsePair(PropertyList list) {
  2583. String name = parseName();
  2584. if (name == null) {
  2585. return;
  2586. }
  2587. String value;
  2588. if (i >= n) {
  2589. value = "";
  2590. } else if (s.charAt(i) == ';') {
  2591. i++;
  2592. value = "";
  2593. } else {
  2594. value = parseValue();
  2595. }
  2596. list.put(name, value);
  2597. }
  2598. /**
  2599. * Reads "name=". Name can contain equals sign if equals sign is
  2600. * doubled. Returns null if there is no name to read.
  2601. */
  2602. String parseName() {
  2603. nameBuf.setLength(0);
  2604. while (true) {
  2605. char c = s.charAt(i);
  2606. switch (c) {
  2607. case '=':
  2608. i++;
  2609. if (i < n && (c = s.charAt(i)) == '=') {
  2610. // doubled equals sign; take one of them, and carry on
  2611. i++;
  2612. nameBuf.append(c);
  2613. break;
  2614. }
  2615. String name = nameBuf.toString();
  2616. name = name.trim();
  2617. return name;
  2618. case ' ':
  2619. if (nameBuf.length() == 0) {
  2620. // ignore preceding spaces
  2621. i++;
  2622. if (i >= n) {
  2623. // there is no name, e.g. trailing spaces after
  2624. // semicolon, 'x=1; y=2; '
  2625. return null;
  2626. }
  2627. break;
  2628. } else {
  2629. // fall through
  2630. }
  2631. default:
  2632. nameBuf.append(c);
  2633. i++;
  2634. if (i >= n) {
  2635. return nameBuf.toString().trim();
  2636. }
  2637. }
  2638. }
  2639. }
  2640. /**
  2641. * Reads "value;" or "value<EOF>"
  2642. */
  2643. String parseValue() {
  2644. char c;
  2645. // skip over leading white space
  2646. while ((c = s.charAt(i)) == ' ') {
  2647. i++;
  2648. if (i >= n) {
  2649. return "";
  2650. }
  2651. }
  2652. if (c == '"' || c == '\'') {
  2653. String value = parseQuoted(c);
  2654. // skip over trailing white space
  2655. while (i < n && (c = s.charAt(i)) == ' ') {
  2656. i++;
  2657. }
  2658. if (i >= n) {
  2659. return value;
  2660. } else if (s.charAt(i) == ';') {
  2661. i++;
  2662. return value;
  2663. } else {
  2664. throw new RuntimeException(
  2665. "quoted value ended too soon, at position " + i
  2666. + " in '" + s + "'");
  2667. }
  2668. } else {
  2669. String value;
  2670. int semi = s.indexOf(';', i);
  2671. if (semi >= 0) {
  2672. value = s.substring(i, semi);
  2673. i = semi + 1;
  2674. } else {
  2675. value = s.substring(i);
  2676. i = n;
  2677. }
  2678. return value.trim();
  2679. }
  2680. }
  2681. /**
  2682. * Reads a string quoted by a given character. Occurrences of the
  2683. * quoting character must be doubled. For example,
  2684. * <code>parseQuoted('"')</code> reads <code>"a ""new"" string"</code>
  2685. * and returns <code>a "new" string</code>.
  2686. */
  2687. String parseQuoted(char q) {
  2688. char c = s.charAt(i++);
  2689. Util.assertTrue(c == q);
  2690. valueBuf.setLength(0);
  2691. while (i < n) {
  2692. c = s.charAt(i);
  2693. if (c == q) {
  2694. i++;
  2695. if (i < n) {
  2696. c = s.charAt(i);
  2697. if (c == q) {
  2698. valueBuf.append(c);
  2699. i++;
  2700. continue;
  2701. }
  2702. }
  2703. return valueBuf.toString();
  2704. } else {
  2705. valueBuf.append(c);
  2706. i++;
  2707. }
  2708. }
  2709. throw new RuntimeException(
  2710. "Connect string '" + s
  2711. + "' contains unterminated quoted value '" + valueBuf.toString()
  2712. + "'");
  2713. }
  2714. }
  2715. /**
  2716. * Combines two integers into a hash code.
  2717. */
  2718. public static int hash(int i, int j) {
  2719. return (i << 4) ^ j;
  2720. }
  2721. /**
  2722. * Computes a hash code from an existing hash code and an object (which
  2723. * may be null).
  2724. */
  2725. public static int hash(int h, Object o) {
  2726. int k = (o == null) ? 0 : o.hashCode();
  2727. return ((h << 4) | h) ^ k;
  2728. }
  2729. /**
  2730. * Computes a hash code from an existing hash code and an array of objects
  2731. * (which may be null).
  2732. */
  2733. public static int hashArray(int h, Object [] a) {
  2734. // The hashcode for a null array and an empty array should be different
  2735. // than h, so use magic numbers.
  2736. if (a == null) {
  2737. return hash(h, 19690429);
  2738. }
  2739. if (a.length == 0) {
  2740. return hash(h, 19690721);
  2741. }
  2742. for (Object anA : a) {
  2743. h = hash(h, anA);
  2744. }
  2745. return h;
  2746. }
  2747. /**
  2748. * Concatenates one or more arrays.
  2749. *
  2750. * <p>Resulting array has same element type as first array. Each arrays may
  2751. * be empty, but must not be null.
  2752. *
  2753. * @param a0 First array
  2754. * @param as Zero or more subsequent arrays
  2755. * @return Array containing all elements
  2756. */
  2757. public static <T> T[] appendArrays(
  2758. T[] a0,
  2759. T[]... as)
  2760. {
  2761. int n = a0.length;
  2762. for (T[] a : as) {
  2763. n += a.length;
  2764. }
  2765. T[] copy = Util.copyOf(a0, n);
  2766. n = a0.length;
  2767. for (T[] a : as) {
  2768. System.arraycopy(a, 0, copy, n, a.length);
  2769. n += a.length;
  2770. }
  2771. return copy;
  2772. }
  2773. /**
  2774. * Adds an object to the end of an array. The resulting array is of the
  2775. * same type (e.g. <code>String[]</code>) as the input array.
  2776. *
  2777. * @param a Array
  2778. * @param o Element
  2779. * @return New array containing original array plus element
  2780. *
  2781. * @see #appendArrays
  2782. */
  2783. public static <T> T[] append(T[] a, T o) {
  2784. T[] a2 = Util.copyOf(a, a.length + 1);
  2785. a2[a.length] = o;
  2786. return a2;
  2787. }
  2788. /**
  2789. * Like <code>{@link java.util.Arrays}.copyOf(double[], int)</code>, but
  2790. * exists prior to JDK 1.6.
  2791. *
  2792. * @param original the array to be copied
  2793. * @param newLength the length of the copy to be returned
  2794. * @return a copy of the original array, truncated or padded with zeros
  2795. * to obtain the specified length
  2796. */
  2797. public static double[] copyOf(double[] original, int newLength) {
  2798. double[] copy = new double[newLength];
  2799. System.arraycopy(
  2800. original, 0, copy, 0, Math.min(original.length, newLength));
  2801. return copy;
  2802. }
  2803. /**
  2804. * Like <code>{@link java.util.Arrays}.copyOf(int[], int)</code>, but
  2805. * exists prior to JDK 1.6.
  2806. *
  2807. * @param original the array to be copied
  2808. * @param newLength the length of the copy to be returned
  2809. * @return a copy of the original array, truncated or padded with zeros
  2810. * to obtain the specified length
  2811. */
  2812. public static int[] copyOf(int[] original, int newLength) {
  2813. int[] copy = new int[newLength];
  2814. System.arraycopy(
  2815. original, 0, copy, 0, Math.min(original.length, newLength));
  2816. return copy;
  2817. }
  2818. /**
  2819. * Like <code>{@link java.util.Arrays}.copyOf(long[], int)</code>, but
  2820. * exists prior to JDK 1.6.
  2821. *
  2822. * @param original the array to be copied
  2823. * @param newLength the length of the copy to be returned
  2824. * @return a copy of the original array, truncated or padded with zeros
  2825. * to obtain the specified length
  2826. */
  2827. public static long[] copyOf(long[] original, int newLength) {
  2828. long[] copy = new long[newLength];
  2829. System.arraycopy(
  2830. original, 0, copy, 0, Math.min(original.length, newLength));
  2831. return copy;
  2832. }
  2833. /**
  2834. * Like <code>{@link java.util.Arrays}.copyOf(Object[], int)</code>, but
  2835. * exists prior to JDK 1.6.
  2836. *
  2837. * @param original the array to be copied
  2838. * @param newLength the length of the copy to be returned
  2839. * @return a copy of the original array, truncated or padded with zeros
  2840. * to obtain the specified length
  2841. */
  2842. public static <T> T[] copyOf(T[] original, int newLength) {
  2843. //noinspection unchecked
  2844. return (T[]) copyOf(original, newLength, original.getClass());
  2845. }
  2846. /**
  2847. * Copies the specified array.
  2848. *
  2849. * @param original the array to be copied
  2850. * @param newLength the length of the copy to be returned
  2851. * @param newType the class of the copy to be returned
  2852. * @return a copy of the original array, truncated or padded with nulls
  2853. * to obtain the specified length
  2854. */
  2855. public static <T, U> T[] copyOf(
  2856. U[] original, int newLength, Class<? extends T[]> newType)
  2857. {
  2858. @SuppressWarnings({"unchecked", "RedundantCast"})
  2859. T[] copy = ((Object)newType == (Object)Object[].class)
  2860. ? (T[]) new Object[newLength]
  2861. : (T[]) Array.newInstance(newType.getComponentType(), newLength);
  2862. //noinspection SuspiciousSystemArraycopy
  2863. System.arraycopy(
  2864. original, 0, copy, 0,
  2865. Math.min(original.length, newLength));
  2866. return copy;
  2867. }
  2868. /**
  2869. * Returns the cumulative amount of time spent accessing the database.
  2870. *
  2871. * @deprecated Use {@link mondrian.server.monitor.Monitor#getServer()} and
  2872. * {@link mondrian.server.monitor.ServerInfo#sqlStatementExecuteNanos};
  2873. * will be removed in 4.0.
  2874. */
  2875. public static long dbTimeMillis() {
  2876. return databaseMillis;
  2877. }
  2878. /**
  2879. * Adds to the cumulative amount of time spent accessing the database.
  2880. *
  2881. * @deprecated Will be removed in 4.0.
  2882. */
  2883. public static void addDatabaseTime(long millis) {
  2884. databaseMillis += millis;
  2885. }
  2886. /**
  2887. * Returns the system time less the time spent accessing the database.
  2888. * Use this method to figure out how long an operation took: call this
  2889. * method before an operation and after an operation, and the difference
  2890. * is the amount of non-database time spent.
  2891. *
  2892. * @deprecated Will be removed in 4.0.
  2893. */
  2894. public static long nonDbTimeMillis() {
  2895. final long systemMillis = System.currentTimeMillis();
  2896. return systemMillis - databaseMillis;
  2897. }
  2898. /**
  2899. * Creates a very simple implementation of {@link Validator}. (Only
  2900. * useful for resolving trivial expressions.)
  2901. */
  2902. public static Validator createSimpleValidator(final FunTable funTable) {
  2903. return new Validator() {
  2904. public Query getQuery() {
  2905. return null;
  2906. }
  2907. public SchemaReader getSchemaReader() {
  2908. throw new UnsupportedOperationException();
  2909. }
  2910. public Exp validate(Exp exp, boolean scalar) {
  2911. return exp;
  2912. }
  2913. public void validate(ParameterExpr parameterExpr) {
  2914. }
  2915. public void validate(MemberProperty memberProperty) {
  2916. }
  2917. public void validate(QueryAxis axis) {
  2918. }
  2919. public void validate(Formula formula) {
  2920. }
  2921. public FunDef getDef(Exp[] args, String name, Syntax syntax) {
  2922. // Very simple resolution. Assumes that there is precisely
  2923. // one resolver (i.e. no overloading) and no argument
  2924. // conversions are necessary.
  2925. List<Resolver> resolvers = funTable.getResolvers(name, syntax);
  2926. final Resolver resolver = resolvers.get(0);
  2927. final List<Resolver.Conversion> conversionList =
  2928. new ArrayList<Resolver.Conversion>();
  2929. final FunDef def =
  2930. resolver.resolve(args, this, conversionList);
  2931. assert conversionList.isEmpty();
  2932. return def;
  2933. }
  2934. public boolean alwaysResolveFunDef() {
  2935. return false;
  2936. }
  2937. public boolean canConvert(
  2938. int ordinal, Exp fromExp,
  2939. int to,
  2940. List<Resolver.Conversion> conversions)
  2941. {
  2942. return true;
  2943. }
  2944. public boolean requiresExpression() {
  2945. return false;
  2946. }
  2947. public FunTable getFunTable() {
  2948. return funTable;
  2949. }
  2950. public Parameter createOrLookupParam(
  2951. boolean definition,
  2952. String name,
  2953. Type type,
  2954. Exp defaultExp,
  2955. String description)
  2956. {
  2957. return null;
  2958. }
  2959. };
  2960. }
  2961. /**
  2962. * Reads a Reader until it returns EOF and returns the contents as a String.
  2963. *
  2964. * @param rdr Reader to Read.
  2965. * @param bufferSize size of buffer to allocate for reading.
  2966. * @return content of Reader as String
  2967. * @throws IOException on I/O error
  2968. */
  2969. public static String readFully(final Reader rdr, final int bufferSize)
  2970. throws IOException
  2971. {
  2972. if (bufferSize <= 0) {
  2973. throw new IllegalArgumentException(
  2974. "Buffer size must be greater than 0");
  2975. }
  2976. final char[] buffer = new char[bufferSize];
  2977. final StringBuilder buf = new StringBuilder(bufferSize);
  2978. int len;
  2979. while ((len = rdr.read(buffer)) != -1) {
  2980. buf.append(buffer, 0, len);
  2981. }
  2982. return buf.toString();
  2983. }
  2984. /**
  2985. * Reads an input stream until it returns EOF and returns the contents as an
  2986. * array of bytes.
  2987. *
  2988. * @param in Input stream
  2989. * @param bufferSize size of buffer to allocate for reading.
  2990. * @return content of stream as an array of bytes
  2991. * @throws IOException on I/O error
  2992. */
  2993. public static byte[] readFully(final InputStream in, final int bufferSize)
  2994. throws IOException
  2995. {
  2996. if (bufferSize <= 0) {
  2997. throw new IllegalArgumentException(
  2998. "Buffer size must be greater than 0");
  2999. }
  3000. final byte[] buffer = new byte[bufferSize];
  3001. final ByteArrayOutputStream baos =
  3002. new ByteArrayOutputStream(bufferSize);
  3003. int len;
  3004. while ((len = in.read(buffer)) != -1) {
  3005. baos.write(buffer, 0, len);
  3006. }
  3007. return baos.toByteArray();
  3008. }
  3009. /**
  3010. * Returns the contents of a URL, substituting tokens.
  3011. *
  3012. * <p>Replaces the tokens "${key}" if the map is not null and "key" occurs
  3013. * in the key-value map.
  3014. *
  3015. * <p>If the URL string starts with "inline:" the contents are the
  3016. * rest of the URL.
  3017. *
  3018. * @param urlStr URL string
  3019. * @param map Key/value map
  3020. * @return Contents of URL with tokens substituted
  3021. * @throws IOException on I/O error
  3022. */
  3023. public static String readURL(final String urlStr, Map<String, String> map)
  3024. throws IOException
  3025. {
  3026. if (urlStr.startsWith("inline:")) {
  3027. String content = urlStr.substring("inline:".length());
  3028. if (map != null) {
  3029. content = Util.replaceProperties(content, map);
  3030. }
  3031. return content;
  3032. } else {
  3033. final URL url = new URL(urlStr);
  3034. return readURL(url, map);
  3035. }
  3036. }
  3037. /**
  3038. * Returns the contents of a URL.
  3039. *
  3040. * @param url URL
  3041. * @return Contents of URL
  3042. * @throws IOException on I/O error
  3043. */
  3044. public static String readURL(final URL url) throws IOException {
  3045. return readURL(url, null);
  3046. }
  3047. /**
  3048. * Returns the contents of a URL, substituting tokens.
  3049. *
  3050. * <p>Replaces the tokens "${key}" if the map is not null and "key" occurs
  3051. * in the key-value map.
  3052. *
  3053. * @param url URL
  3054. * @param map Key/value map
  3055. * @return Contents of URL with tokens substituted
  3056. * @throws IOException on I/O error
  3057. */
  3058. public static String readURL(
  3059. final URL url,
  3060. Map<String, String> map)
  3061. throws IOException
  3062. {
  3063. final Reader r =
  3064. new BufferedReader(new InputStreamReader(url.openStream()));
  3065. final int BUF_SIZE = 8096;
  3066. try {
  3067. String xmlCatalog = readFully(r, BUF_SIZE);
  3068. xmlCatalog = Util.replaceProperties(xmlCatalog, map);
  3069. return xmlCatalog;
  3070. } finally {
  3071. r.close();
  3072. }
  3073. }
  3074. /**
  3075. * Gets content via Apache VFS. File must exist and have content
  3076. *
  3077. * @param url String
  3078. * @return Apache VFS FileContent for further processing
  3079. * @throws FileSystemException on error
  3080. */
  3081. public static InputStream readVirtualFile(String url)
  3082. throws FileSystemException
  3083. {
  3084. // Treat catalogUrl as an Apache VFS (Virtual File System) URL.
  3085. // VFS handles all of the usual protocols (http:, file:)
  3086. // and then some.
  3087. FileSystemManager fsManager = VFS.getManager();
  3088. if (fsManager == null) {
  3089. throw newError("Cannot get virtual file system manager");
  3090. }
  3091. // Workaround VFS bug.
  3092. if (url.startsWith("file://localhost")) {
  3093. url = url.substring("file://localhost".length());
  3094. }
  3095. if (url.startsWith("file:")) {
  3096. url = url.substring("file:".length());
  3097. }
  3098. // work around for VFS bug not closing http sockets
  3099. // (Mondrian-585)
  3100. if (url.startsWith("http")) {
  3101. try {
  3102. return new URL(url).openStream();
  3103. } catch (IOException e) {
  3104. throw newError(
  3105. "Could not read URL: " + url);
  3106. }
  3107. }
  3108. File userDir = new File("").getAbsoluteFile();
  3109. FileObject file = fsManager.resolveFile(userDir, url);
  3110. FileContent fileContent = null;
  3111. try {
  3112. // Because of VFS caching, make sure we refresh to get the latest
  3113. // file content. This refresh may possibly solve the following
  3114. // workaround for defect MONDRIAN-508, but cannot be tested, so we
  3115. // will leave the work around for now.
  3116. file.refresh();
  3117. // Workaround to defect MONDRIAN-508. For HttpFileObjects, verifies
  3118. // the URL of the file retrieved matches the URL passed in. A VFS
  3119. // cache bug can cause it to treat URLs with different parameters
  3120. // as the same file (e.g. http://blah.com?param=A,
  3121. // http://blah.com?param=B)
  3122. if (file instanceof HttpFileObject
  3123. && !file.getName().getURI().equals(url))
  3124. {
  3125. fsManager.getFilesCache().removeFile(
  3126. file.getFileSystem(), file.getName());
  3127. file = fsManager.resolveFile(userDir, url);
  3128. }
  3129. if (!file.isReadable()) {
  3130. throw newError(
  3131. "Virtual file is not readable: " + url);
  3132. }
  3133. fileContent = file.getContent();
  3134. } finally {
  3135. file.close();
  3136. }
  3137. if (fileContent == null) {
  3138. throw newError(
  3139. "Cannot get virtual file content: " + url);
  3140. }
  3141. return fileContent.getInputStream();
  3142. }
  3143. public static String readVirtualFileAsString(
  3144. String catalogUrl)
  3145. throws IOException
  3146. {
  3147. InputStream in = readVirtualFile(catalogUrl);
  3148. try {
  3149. final byte[] bytes = Util.readFully(in, 1024);
  3150. final char[] chars = new char[bytes.length];
  3151. for (int i = 0; i < chars.length; i++) {
  3152. chars[i] = (char) bytes[i];
  3153. }
  3154. return new String(chars);
  3155. } finally {
  3156. if (in != null) {
  3157. in.close();
  3158. }
  3159. }
  3160. }
  3161. /**
  3162. * Converts a {@link Properties} object to a string-to-string {@link Map}.
  3163. *
  3164. * @param properties Properties
  3165. * @return String-to-string map
  3166. */
  3167. public static Map<String, String> toMap(final Properties properties) {
  3168. return new AbstractMap<String, String>() {
  3169. @SuppressWarnings({"unchecked"})
  3170. public Set<Entry<String, String>> entrySet() {
  3171. return (Set) properties.entrySet();
  3172. }
  3173. };
  3174. }
  3175. /**
  3176. * Replaces tokens in a string.
  3177. *
  3178. * <p>Replaces the tokens "${key}" if "key" occurs in the key-value map.
  3179. * Otherwise "${key}" is left in the string unchanged.
  3180. *
  3181. * @param text Source string
  3182. * @param env Map of key-value pairs
  3183. * @return String with tokens substituted
  3184. */
  3185. public static String replaceProperties(
  3186. String text,
  3187. Map<String, String> env)
  3188. {
  3189. // As of JDK 1.5, cannot use StringBuilder - appendReplacement requires
  3190. // the antediluvian StringBuffer.
  3191. StringBuffer buf = new StringBuffer(text.length() + 200);
  3192. Pattern pattern = Pattern.compile("\\$\\{([^${}]+)\\}");
  3193. Matcher matcher = pattern.matcher(text);
  3194. while (matcher.find()) {
  3195. String varName = matcher.group(1);
  3196. String varValue = env.get(varName);
  3197. if (varValue != null) {
  3198. matcher.appendReplacement(buf, varValue);
  3199. } else {
  3200. matcher.appendReplacement(buf, "\\${$1}");
  3201. }
  3202. }
  3203. matcher.appendTail(buf);
  3204. return buf.toString();
  3205. }
  3206. public static String printMemory() {
  3207. return printMemory(null);
  3208. }
  3209. public static String printMemory(String msg) {
  3210. final Runtime rt = Runtime.getRuntime();
  3211. final long freeMemory = rt.freeMemory();
  3212. final long totalMemory = rt.totalMemory();
  3213. final StringBuilder buf = new StringBuilder(64);
  3214. buf.append("FREE_MEMORY:");
  3215. if (msg != null) {
  3216. buf.append(msg);
  3217. buf.append(':');
  3218. }
  3219. buf.append(' ');
  3220. buf.append(freeMemory / 1024);
  3221. buf.append("kb ");
  3222. long hundredths = (freeMemory * 10000) / totalMemory;
  3223. buf.append(hundredths / 100);
  3224. hundredths %= 100;
  3225. if (hundredths >= 10) {
  3226. buf.append('.');
  3227. } else {
  3228. buf.append(".0");
  3229. }
  3230. buf.append(hundredths);
  3231. buf.append('%');
  3232. return buf.toString();
  3233. }
  3234. /**
  3235. * Casts a Set to a Set with a different element type.
  3236. *
  3237. * @param set Set
  3238. * @return Set of desired type
  3239. */
  3240. @SuppressWarnings({"unchecked"})
  3241. public static <T> Set<T> cast(Set<?> set) {
  3242. return (Set<T>) set;
  3243. }
  3244. /**
  3245. * Casts a List to a List with a different element type.
  3246. *
  3247. * @param list List
  3248. * @return List of desired type
  3249. */
  3250. @SuppressWarnings({"unchecked"})
  3251. public static <T> List<T> cast(List<?> list) {
  3252. return (List<T>) list;
  3253. }
  3254. /**
  3255. * Returns whether it is safe to cast a collection to a collection with a
  3256. * given element type.
  3257. *
  3258. * @param collection Collection
  3259. * @param clazz Target element type
  3260. * @param <T> Element type
  3261. * @return Whether all not-null elements of the collection are instances of
  3262. * element type
  3263. */
  3264. public static <T> boolean canCast(
  3265. Collection<?> collection,
  3266. Class<T> clazz)
  3267. {
  3268. for (Object o : collection) {
  3269. if (o != null && !clazz.isInstance(o)) {
  3270. return false;
  3271. }
  3272. }
  3273. return true;
  3274. }
  3275. /**
  3276. * Casts a collection to iterable.
  3277. *
  3278. * Under JDK 1.4, {@link Collection} objects do not implement
  3279. * {@link Iterable}, so this method inserts a casting wrapper. (Since
  3280. * Iterable does not exist under JDK 1.4, they will have been compiled
  3281. * under JDK 1.5 or later, then retrowoven to 1.4 class format. References
  3282. * to Iterable will have been replaced with references to
  3283. * <code>com.rc.retroweaver.runtime.Retroweaver_</code>.
  3284. *
  3285. * <p>Under later JDKs this method is trivial. This method can be deleted
  3286. * when we discontinue support for JDK 1.4.
  3287. *
  3288. * @param iterable Object which ought to be iterable
  3289. * @param <T> Element type
  3290. * @return Object cast to Iterable
  3291. */
  3292. public static <T> Iterable<T> castToIterable(
  3293. final Object iterable)
  3294. {
  3295. if (Util.Retrowoven
  3296. && !(iterable instanceof Iterable))
  3297. {
  3298. return new Iterable<T>() {
  3299. public Iterator<T> iterator() {
  3300. return ((Collection<T>) iterable).iterator();
  3301. }
  3302. };
  3303. }
  3304. return (Iterable<T>) iterable;
  3305. }
  3306. /**
  3307. * Looks up an enumeration by name, returning null if null or not valid.
  3308. *
  3309. * @param clazz Enumerated type
  3310. * @param name Name of constant
  3311. */
  3312. public static <E extends Enum<E>> E lookup(Class<E> clazz, String name) {
  3313. return lookup(clazz, name, null);
  3314. }
  3315. /**
  3316. * Looks up an enumeration by name, returning a given default value if null
  3317. * or not valid.
  3318. *
  3319. * @param clazz Enumerated type
  3320. * @param name Name of constant
  3321. * @param defaultValue Default value if constant is not found
  3322. * @return Value, or null if name is null or value does not exist
  3323. */
  3324. public static <E extends Enum<E>> E lookup(
  3325. Class<E> clazz, String name, E defaultValue)
  3326. {
  3327. if (name == null) {
  3328. return defaultValue;
  3329. }
  3330. try {
  3331. return Enum.valueOf(clazz, name);
  3332. } catch (IllegalArgumentException e) {
  3333. return defaultValue;
  3334. }
  3335. }
  3336. /**
  3337. * Make a BigDecimal from a double. On JDK 1.5 or later, the BigDecimal
  3338. * precision reflects the precision of the double while with JDK 1.4
  3339. * this is not the case.
  3340. *
  3341. * @param d the input double
  3342. * @return the BigDecimal
  3343. */
  3344. public static BigDecimal makeBigDecimalFromDouble(double d) {
  3345. return compatible.makeBigDecimalFromDouble(d);
  3346. }
  3347. /**
  3348. * Returns a literal pattern String for the specified String.
  3349. *
  3350. * <p>Specification as for {@link Pattern#quote(String)}, which was
  3351. * introduced in JDK 1.5.
  3352. *
  3353. * @param s The string to be literalized
  3354. * @return A literal string replacement
  3355. */
  3356. public static String quotePattern(String s) {
  3357. return compatible.quotePattern(s);
  3358. }
  3359. /**
  3360. * Generates a unique id.
  3361. *
  3362. * <p>From JDK 1.5 onwards, uses a {@code UUID}.
  3363. *
  3364. * @return A unique id
  3365. */
  3366. public static String generateUuidString() {
  3367. return compatible.generateUuidString();
  3368. }
  3369. /**
  3370. * Compiles a script to yield a Java interface.
  3371. *
  3372. * <p>Only valid JDK 1.6 and higher; fails on JDK 1.5 and earlier.</p>
  3373. *
  3374. * @param iface Interface script should implement
  3375. * @param script Script code
  3376. * @param engineName Name of engine (e.g. "JavaScript")
  3377. * @param <T> Interface
  3378. * @return Object that implements given interface
  3379. */
  3380. public static <T> T compileScript(
  3381. Class<T> iface,
  3382. String script,
  3383. String engineName)
  3384. {
  3385. return compatible.compileScript(iface, script, engineName);
  3386. }
  3387. /**
  3388. * Removes a thread local from the current thread.
  3389. *
  3390. * <p>From JDK 1.5 onwards, calls {@link ThreadLocal#remove()}; before
  3391. * that, no-ops.</p>
  3392. *
  3393. * @param threadLocal Thread local
  3394. * @param <T> Type
  3395. */
  3396. public static <T> void threadLocalRemove(ThreadLocal<T> threadLocal) {
  3397. compatible.threadLocalRemove(threadLocal);
  3398. }
  3399. /**
  3400. * Creates a hash set that, like {@link java.util.IdentityHashMap},
  3401. * compares keys using identity.
  3402. *
  3403. * @param <T> Element type
  3404. * @return Set
  3405. */
  3406. public static <T> Set<T> newIdentityHashSet() {
  3407. return compatible.newIdentityHashSet();
  3408. }
  3409. /**
  3410. * Creates a new udf instance from the given udf class.
  3411. *
  3412. * @param udfClass the class to create new instance for
  3413. * @param functionName Function name, or null
  3414. * @return an instance of UserDefinedFunction
  3415. */
  3416. public static UserDefinedFunction createUdf(
  3417. Class<? extends UserDefinedFunction> udfClass,
  3418. String functionName)
  3419. {
  3420. // Instantiate class with default constructor.
  3421. UserDefinedFunction udf;
  3422. String className = udfClass.getName();
  3423. String functionNameOrEmpty =
  3424. functionName == null
  3425. ? ""
  3426. : functionName;
  3427. // Find a constructor.
  3428. Constructor<?> constructor;
  3429. Object[] args = {};
  3430. // 0. Check that class is public and top-level or static.
  3431. // Before JDK 1.5, inner classes are impossible; retroweaver cannot
  3432. // handle the getEnclosingClass method, so skip the check.
  3433. if (!Modifier.isPublic(udfClass.getModifiers())
  3434. || (!PreJdk15
  3435. && udfClass.getEnclosingClass() != null
  3436. && !Modifier.isStatic(udfClass.getModifiers())))
  3437. {
  3438. throw MondrianResource.instance().UdfClassMustBePublicAndStatic.ex(
  3439. functionName,
  3440. className);
  3441. }
  3442. // 1. Look for a constructor "public Udf(String name)".
  3443. try {
  3444. constructor = udfClass.getConstructor(String.class);
  3445. if (Modifier.isPublic(constructor.getModifiers())) {
  3446. args = new Object[] {functionName};
  3447. } else {
  3448. constructor = null;
  3449. }
  3450. } catch (NoSuchMethodException e) {
  3451. constructor = null;
  3452. }
  3453. // 2. Otherwise, look for a constructor "public Udf()".
  3454. if (constructor == null) {
  3455. try {
  3456. constructor = udfClass.getConstructor();
  3457. if (Modifier.isPublic(constructor.getModifiers())) {
  3458. args = new Object[] {};
  3459. } else {
  3460. constructor = null;
  3461. }
  3462. } catch (NoSuchMethodException e) {
  3463. constructor = null;
  3464. }
  3465. }
  3466. // 3. Else, no constructor suitable.
  3467. if (constructor == null) {
  3468. throw MondrianResource.instance().UdfClassWrongIface.ex(
  3469. functionNameOrEmpty,
  3470. className,
  3471. UserDefinedFunction.class.getName());
  3472. }
  3473. // Instantiate class.
  3474. try {
  3475. udf = (UserDefinedFunction) constructor.newInstance(args);
  3476. } catch (InstantiationException e) {
  3477. throw MondrianResource.instance().UdfClassWrongIface.ex(
  3478. functionNameOrEmpty,
  3479. className, UserDefinedFunction.class.getName());
  3480. } catch (IllegalAccessException e) {
  3481. throw MondrianResource.instance().UdfClassWrongIface.ex(
  3482. functionName,
  3483. className,
  3484. UserDefinedFunction.class.getName());
  3485. } catch (ClassCastException e) {
  3486. throw MondrianResource.instance().UdfClassWrongIface.ex(
  3487. functionNameOrEmpty,
  3488. className,
  3489. UserDefinedFunction.class.getName());
  3490. } catch (InvocationTargetException e) {
  3491. throw MondrianResource.instance().UdfClassWrongIface.ex(
  3492. functionName,
  3493. className,
  3494. UserDefinedFunction.class.getName());
  3495. }
  3496. return udf;
  3497. }
  3498. /**
  3499. * Check the resultSize against the result limit setting. Throws
  3500. * LimitExceededDuringCrossjoin exception if limit exceeded.
  3501. *
  3502. * When it is called from RolapNativeSet.checkCrossJoin(), it is only
  3503. * possible to check the known input size, because the final CJ result
  3504. * will come from the DB(and will be checked against the limit when
  3505. * fetching from the JDBC result set, in SqlTupleReader.prepareTuples())
  3506. *
  3507. * @param resultSize Result limit
  3508. * @throws ResourceLimitExceededException
  3509. */
  3510. public static void checkCJResultLimit(long resultSize) {
  3511. int resultLimit = MondrianProperties.instance().ResultLimit.get();
  3512. // Throw an exeption, if the size of the crossjoin exceeds the result
  3513. // limit.
  3514. if (resultLimit > 0 && resultLimit < resultSize) {
  3515. throw MondrianResource.instance().LimitExceededDuringCrossjoin.ex(
  3516. resultSize, resultLimit);
  3517. }
  3518. // Throw an exception if the crossjoin exceeds a reasonable limit.
  3519. // (Yes, 4 billion is a reasonable limit.)
  3520. if (resultSize > Integer.MAX_VALUE) {
  3521. throw MondrianResource.instance().LimitExceededDuringCrossjoin.ex(
  3522. resultSize, Integer.MAX_VALUE);
  3523. }
  3524. }
  3525. /**
  3526. * Converts an olap4j connect string into a legacy mondrian connect string.
  3527. *
  3528. * <p>For example,
  3529. * "jdbc:mondrian:Datasource=jdbc/SampleData;Catalog=foodmart/FoodMart.xml;"
  3530. * becomes
  3531. * "Provider=Mondrian;
  3532. * Datasource=jdbc/SampleData;Catalog=foodmart/FoodMart.xml;"
  3533. *
  3534. * <p>This method is intended to allow legacy applications (such as JPivot
  3535. * and Mondrian's XMLA server) to continue to create connections using
  3536. * Mondrian's legacy connection API even when they are handed an olap4j
  3537. * connect string.
  3538. *
  3539. * @param url olap4j connect string
  3540. * @return mondrian connect string, or null if cannot be converted
  3541. */
  3542. public static String convertOlap4jConnectStringToNativeMondrian(
  3543. String url)
  3544. {
  3545. if (url.startsWith("jdbc:mondrian:")) {
  3546. return "Provider=Mondrian; "
  3547. + url.substring("jdbc:mondrian:".length());
  3548. }
  3549. return null;
  3550. }
  3551. /**
  3552. * Checks if a String is whitespace, empty ("") or null.</p>
  3553. *
  3554. * <pre>
  3555. * StringUtils.isBlank(null) = true
  3556. * StringUtils.isBlank("") = true
  3557. * StringUtils.isBlank(" ") = true
  3558. * StringUtils.isBlank("bob") = false
  3559. * StringUtils.isBlank(" bob ") = false
  3560. * </pre>
  3561. *
  3562. * <p>(Copied from commons-lang.)
  3563. *
  3564. * @param str the String to check, may be null
  3565. * @return <code>true</code> if the String is null, empty or whitespace
  3566. */
  3567. public static boolean isBlank(String str) {
  3568. final int strLen;
  3569. if (str == null || (strLen = str.length()) == 0) {
  3570. return true;
  3571. }
  3572. for (int i = 0; i < strLen; i++) {
  3573. if (!Character.isWhitespace(str.charAt(i))) {
  3574. return false;
  3575. }
  3576. }
  3577. return true;
  3578. }
  3579. /**
  3580. * Returns a role which has access to everything.
  3581. * @param schema A schema to bind this role to.
  3582. * @return A role with root access to the schema.
  3583. */
  3584. public static Role createRootRole(Schema schema) {
  3585. RoleImpl role = new RoleImpl();
  3586. role.grant(schema, Access.ALL);
  3587. role.makeImmutable();
  3588. return role;
  3589. }
  3590. /**
  3591. * Tries to find the cube from which a dimension is taken.
  3592. * It considers private dimensions, shared dimensions and virtual
  3593. * dimensions. If it can't determine with certitude the origin
  3594. * of the dimension, it returns null.
  3595. */
  3596. public static Cube getDimensionCube(Dimension dimension) {
  3597. final Cube[] cubes = dimension.getSchema().getCubes();
  3598. for (Cube cube : cubes) {
  3599. for (Dimension dimension1 : cube.getDimensions()) {
  3600. // If the dimensions have the same identity,
  3601. // we found an access rule.
  3602. if (dimension == dimension1) {
  3603. return cube;
  3604. }
  3605. // If the passed dimension argument is of class
  3606. // RolapCubeDimension, we must validate the cube
  3607. // assignment and make sure the cubes are the same.
  3608. // If not, skip to the next grant.
  3609. if (dimension instanceof RolapCubeDimension
  3610. && dimension.equals(dimension1)
  3611. && !((RolapCubeDimension)dimension1)
  3612. .getCube()
  3613. .equals(cube))
  3614. {
  3615. continue;
  3616. }
  3617. // Last thing is to allow for equality correspondences
  3618. // to work with virtual cubes.
  3619. if (cube instanceof RolapCube
  3620. && ((RolapCube)cube).isVirtual()
  3621. && dimension.equals(dimension1))
  3622. {
  3623. return cube;
  3624. }
  3625. }
  3626. }
  3627. return null;
  3628. }
  3629. public static abstract class AbstractFlatList<T>
  3630. implements List<T>, RandomAccess
  3631. {
  3632. protected final List<T> asArrayList() {
  3633. //noinspection unchecked
  3634. return Arrays.asList((T[]) toArray());
  3635. }
  3636. public Iterator<T> iterator() {
  3637. return asArrayList().iterator();
  3638. }
  3639. public ListIterator<T> listIterator() {
  3640. return asArrayList().listIterator();
  3641. }
  3642. public boolean isEmpty() {
  3643. return false;
  3644. }
  3645. public boolean add(Object t) {
  3646. throw new UnsupportedOperationException();
  3647. }
  3648. public boolean addAll(Collection<? extends T> c) {
  3649. throw new UnsupportedOperationException();
  3650. }
  3651. public boolean addAll(int index, Collection<? extends T> c) {
  3652. throw new UnsupportedOperationException();
  3653. }
  3654. public boolean removeAll(Collection<?> c) {
  3655. throw new UnsupportedOperationException();
  3656. }
  3657. public boolean retainAll(Collection<?> c) {
  3658. throw new UnsupportedOperationException();
  3659. }
  3660. public void clear() {
  3661. throw new UnsupportedOperationException();
  3662. }
  3663. public T set(int index, Object element) {
  3664. throw new UnsupportedOperationException();
  3665. }
  3666. public void add(int index, Object element) {
  3667. throw new UnsupportedOperationException();
  3668. }
  3669. public T remove(int index) {
  3670. throw new UnsupportedOperationException();
  3671. }
  3672. public ListIterator<T> listIterator(int index) {
  3673. return asArrayList().listIterator(index);
  3674. }
  3675. public List<T> subList(int fromIndex, int toIndex) {
  3676. return asArrayList().subList(fromIndex, toIndex);
  3677. }
  3678. public boolean contains(Object o) {
  3679. return indexOf(o) >= 0;
  3680. }
  3681. public boolean containsAll(Collection<?> c) {
  3682. Iterator<?> e = c.iterator();
  3683. while (e.hasNext()) {
  3684. if (!contains(e.next())) {
  3685. return false;
  3686. }
  3687. }
  3688. return true;
  3689. }
  3690. public boolean remove(Object o) {
  3691. throw new UnsupportedOperationException();
  3692. }
  3693. }
  3694. /**
  3695. * List that stores its two elements in the two members of the class.
  3696. * Unlike {@link java.util.ArrayList} or
  3697. * {@link java.util.Arrays#asList(Object[])} there is
  3698. * no array, only one piece of memory allocated, therefore is very compact
  3699. * and cache and CPU efficient.
  3700. *
  3701. * <p>The list is read-only, cannot be modified or resized, and neither
  3702. * of the elements can be null.
  3703. *
  3704. * <p>The list is created via {@link Util#flatList(Object[])}.
  3705. *
  3706. * @see mondrian.olap.Util.Flat3List
  3707. * @param <T>
  3708. */
  3709. protected static class Flat2List<T> extends AbstractFlatList<T> {
  3710. private final T t0;
  3711. private final T t1;
  3712. Flat2List(T t0, T t1) {
  3713. this.t0 = t0;
  3714. this.t1 = t1;
  3715. assert t0 != null;
  3716. assert t1 != null;
  3717. }
  3718. public String toString() {
  3719. return "[" + t0 + ", " + t1 + "]";
  3720. }
  3721. public T get(int index) {
  3722. switch (index) {
  3723. case 0:
  3724. return t0;
  3725. case 1:
  3726. return t1;
  3727. default:
  3728. throw new IndexOutOfBoundsException("index " + index);
  3729. }
  3730. }
  3731. public int size() {
  3732. return 2;
  3733. }
  3734. public boolean equals(Object o) {
  3735. if (o instanceof Flat2List) {
  3736. Flat2List that = (Flat2List) o;
  3737. return Util.equals(this.t0, that.t0)
  3738. && Util.equals(this.t1, that.t1);
  3739. }
  3740. return Arrays.asList(t0, t1).equals(o);
  3741. }
  3742. public int hashCode() {
  3743. int h = 1;
  3744. h = h * 31 + t0.hashCode();
  3745. h = h * 31 + t1.hashCode();
  3746. return h;
  3747. }
  3748. public int indexOf(Object o) {
  3749. if (t0.equals(o)) {
  3750. return 0;
  3751. }
  3752. if (t1.equals(o)) {
  3753. return 1;
  3754. }
  3755. return -1;
  3756. }
  3757. public int lastIndexOf(Object o) {
  3758. if (t1.equals(o)) {
  3759. return 1;
  3760. }
  3761. if (t0.equals(o)) {
  3762. return 0;
  3763. }
  3764. return -1;
  3765. }
  3766. @SuppressWarnings({"unchecked"})
  3767. public <T2> T2[] toArray(T2[] a) {
  3768. a[0] = (T2) t0;
  3769. a[1] = (T2) t1;
  3770. return a;
  3771. }
  3772. public Object[] toArray() {
  3773. return new Object[] {t0, t1};
  3774. }
  3775. }
  3776. /**
  3777. * List that stores its three elements in the three members of the class.
  3778. * Unlike {@link java.util.ArrayList} or
  3779. * {@link java.util.Arrays#asList(Object[])} there is
  3780. * no array, only one piece of memory allocated, therefore is very compact
  3781. * and cache and CPU efficient.
  3782. *
  3783. * <p>The list is read-only, cannot be modified or resized, and none
  3784. * of the elements can be null.
  3785. *
  3786. * <p>The list is created via {@link Util#flatList(Object[])}.
  3787. *
  3788. * @see mondrian.olap.Util.Flat2List
  3789. * @param <T>
  3790. */
  3791. protected static class Flat3List<T> extends AbstractFlatList<T> {
  3792. private final T t0;
  3793. private final T t1;
  3794. private final T t2;
  3795. Flat3List(T t0, T t1, T t2) {
  3796. this.t0 = t0;
  3797. this.t1 = t1;
  3798. this.t2 = t2;
  3799. assert t0 != null;
  3800. assert t1 != null;
  3801. assert t2 != null;
  3802. }
  3803. public String toString() {
  3804. return "[" + t0 + ", " + t1 + ", " + t2 + "]";
  3805. }
  3806. public T get(int index) {
  3807. switch (index) {
  3808. case 0:
  3809. return t0;
  3810. case 1:
  3811. return t1;
  3812. case 2:
  3813. return t2;
  3814. default:
  3815. throw new IndexOutOfBoundsException("index " + index);
  3816. }
  3817. }
  3818. public int size() {
  3819. return 3;
  3820. }
  3821. public boolean equals(Object o) {
  3822. if (o instanceof Flat3List) {
  3823. Flat3List that = (Flat3List) o;
  3824. return Util.equals(this.t0, that.t0)
  3825. && Util.equals(this.t1, that.t1)
  3826. && Util.equals(this.t2, that.t2);
  3827. }
  3828. return o.equals(this);
  3829. }
  3830. public int hashCode() {
  3831. int h = 1;
  3832. h = h * 31 + t0.hashCode();
  3833. h = h * 31 + t1.hashCode();
  3834. h = h * 31 + t2.hashCode();
  3835. return h;
  3836. }
  3837. public int indexOf(Object o) {
  3838. if (t0.equals(o)) {
  3839. return 0;
  3840. }
  3841. if (t1.equals(o)) {
  3842. return 1;
  3843. }
  3844. if (t2.equals(o)) {
  3845. return 2;
  3846. }
  3847. return -1;
  3848. }
  3849. public int lastIndexOf(Object o) {
  3850. if (t2.equals(o)) {
  3851. return 2;
  3852. }
  3853. if (t1.equals(o)) {
  3854. return 1;
  3855. }
  3856. if (t0.equals(o)) {
  3857. return 0;
  3858. }
  3859. return -1;
  3860. }
  3861. @SuppressWarnings({"unchecked"})
  3862. public <T2> T2[] toArray(T2[] a) {
  3863. a[0] = (T2) t0;
  3864. a[1] = (T2) t1;
  3865. a[2] = (T2) t2;
  3866. return a;
  3867. }
  3868. public Object[] toArray() {
  3869. return new Object[] {t0, t1, t2};
  3870. }
  3871. }
  3872. /**
  3873. * Garbage-collecting iterator. Iterates over a collection of references,
  3874. * and if any of the references has been garbage-collected, removes it from
  3875. * the collection.
  3876. *
  3877. * @param <T> Element type
  3878. */
  3879. public static class GcIterator<T> implements Iterator<T> {
  3880. private final Iterator<? extends Reference<T>> iterator;
  3881. private boolean hasNext;
  3882. private T next;
  3883. public GcIterator(Iterator<? extends Reference<T>> iterator) {
  3884. this.iterator = iterator;
  3885. this.hasNext = true;
  3886. moveToNext();
  3887. }
  3888. /**
  3889. * Creates an iterator over a collection of references.
  3890. *
  3891. * @param referenceIterable Collection of references
  3892. * @param <T2> element type
  3893. * @return iterable over collection
  3894. */
  3895. public static <T2> Iterable<T2> over(
  3896. final Iterable<? extends Reference<T2>> referenceIterable)
  3897. {
  3898. return new Iterable<T2>() {
  3899. public Iterator<T2> iterator() {
  3900. return new GcIterator<T2>(referenceIterable.iterator());
  3901. }
  3902. };
  3903. }
  3904. private void moveToNext() {
  3905. while (iterator.hasNext()) {
  3906. final Reference<T> ref = iterator.next();
  3907. next = ref.get();
  3908. if (next != null) {
  3909. return;
  3910. }
  3911. iterator.remove();
  3912. }
  3913. hasNext = false;
  3914. }
  3915. public boolean hasNext() {
  3916. return hasNext;
  3917. }
  3918. public T next() {
  3919. final T next1 = next;
  3920. moveToNext();
  3921. return next1;
  3922. }
  3923. public void remove() {
  3924. throw new UnsupportedOperationException();
  3925. }
  3926. }
  3927. public static interface Functor1<RT, PT> {
  3928. RT apply(PT param);
  3929. }
  3930. public static <T> Functor1<T, T> identityFunctor() {
  3931. //noinspection unchecked
  3932. return (Functor1) IDENTITY_FUNCTOR;
  3933. }
  3934. private static final Functor1 IDENTITY_FUNCTOR =
  3935. new Functor1<Object, Object>() {
  3936. public Object apply(Object param) {
  3937. return param;
  3938. }
  3939. };
  3940. public static <PT> Functor1<Boolean, PT> trueFunctor() {
  3941. //noinspection unchecked
  3942. return (Functor1) TRUE_FUNCTOR;
  3943. }
  3944. public static <PT> Functor1<Boolean, PT> falseFunctor() {
  3945. //noinspection unchecked
  3946. return (Functor1) FALSE_FUNCTOR;
  3947. }
  3948. private static final Functor1 TRUE_FUNCTOR =
  3949. new Functor1<Boolean, Object>() {
  3950. public Boolean apply(Object param) {
  3951. return true;
  3952. }
  3953. };
  3954. private static final Functor1 FALSE_FUNCTOR =
  3955. new Functor1<Boolean, Object>() {
  3956. public Boolean apply(Object param) {
  3957. return false;
  3958. }
  3959. };
  3960. /**
  3961. * Information about memory usage.
  3962. *
  3963. * @see mondrian.olap.Util#getMemoryInfo()
  3964. */
  3965. public interface MemoryInfo {
  3966. Usage get();
  3967. public interface Usage {
  3968. long getUsed();
  3969. long getCommitted();
  3970. long getMax();
  3971. }
  3972. }
  3973. /**
  3974. * A {@link Comparator} implementation which can deal
  3975. * correctly with {@link RolapUtil#sqlNullValue}.
  3976. */
  3977. public static class SqlNullSafeComparator
  3978. implements Comparator<Comparable>
  3979. {
  3980. public static final SqlNullSafeComparator instance =
  3981. new SqlNullSafeComparator();
  3982. private SqlNullSafeComparator() {
  3983. }
  3984. public int compare(Comparable o1, Comparable o2) {
  3985. if (o1 == RolapUtil.sqlNullValue) {
  3986. return -1;
  3987. }
  3988. if (o2 == RolapUtil.sqlNullValue) {
  3989. return 1;
  3990. }
  3991. return o1.compareTo(o2);
  3992. }
  3993. }
  3994. /**
  3995. * This class implements the Knuth-Morris-Pratt algorithm
  3996. * to search within a byte array for a token byte array.
  3997. */
  3998. public static class ByteMatcher {
  3999. private final int[] matcher;
  4000. public final byte[] key;
  4001. public ByteMatcher(byte[] key) {
  4002. this.key = key;
  4003. this.matcher = compile(key);
  4004. }
  4005. /**
  4006. * Matches the pre-compiled byte array token against a
  4007. * byte array variable and returns the index of the key
  4008. * within the array.
  4009. * @param a An array of bytes to search for.
  4010. * @return -1 if not found, or the index (0 based) of the match.
  4011. */
  4012. public int match(byte[] a) {
  4013. int j = 0;
  4014. for (int i = 0; i < a.length; i++) {
  4015. while (j > 0 && key[j] != a[i]) {
  4016. j = matcher[j - 1];
  4017. }
  4018. if (a[i] == key[j]) {
  4019. j++;
  4020. }
  4021. if (key.length == j) {
  4022. return
  4023. i - key.length + 1;
  4024. }
  4025. }
  4026. return -1;
  4027. }
  4028. private int[] compile(byte[] key) {
  4029. int[] matcher = new int[key.length];
  4030. int j = 0;
  4031. for (int i = 1; i < key.length; i++) {
  4032. while (j > 0 && key[j] != key[i]) {
  4033. j = matcher[j - 1];
  4034. }
  4035. if (key[i] == key[j]) {
  4036. j++;
  4037. }
  4038. matcher[i] = j;
  4039. }
  4040. return matcher;
  4041. }
  4042. }
  4043. /**
  4044. * Transforms a list into a map for which all the keys return
  4045. * a null value associated to it.
  4046. *
  4047. * <p>The list passed as an argument will be used to back
  4048. * the map returned and as many methods are overridden as
  4049. * possible to make sure that we don't iterate over the backing
  4050. * list when creating it and when performing operations like
  4051. * .size(), entrySet() and contains().
  4052. *
  4053. * <p>The returned map is to be considered immutable. It will
  4054. * throw an {@link UnsupportedOperationException} if attempts to
  4055. * modify it are made.
  4056. */
  4057. public static <K, V> Map<K, V> toNullValuesMap(List<K> list) {
  4058. return new NullValuesMap<K, V>(list);
  4059. }
  4060. private static class NullValuesMap<K, V> extends AbstractMap<K, V> {
  4061. private final List<K> list;
  4062. private NullValuesMap(List<K> list) {
  4063. super();
  4064. this.list = Collections.unmodifiableList(list);
  4065. }
  4066. public Set<Entry<K, V>> entrySet() {
  4067. return new AbstractSet<Entry<K, V>>() {
  4068. public Iterator<Entry<K, V>>
  4069. iterator()
  4070. {
  4071. return new Iterator<Entry<K, V>>() {
  4072. private int pt = -1;
  4073. public void remove() {
  4074. throw new UnsupportedOperationException();
  4075. }
  4076. @SuppressWarnings("unchecked")
  4077. public Entry<K, V> next() {
  4078. return new AbstractMapEntry(
  4079. list.get(++pt), null) {};
  4080. }
  4081. public boolean hasNext() {
  4082. return pt < list.size();
  4083. }
  4084. };
  4085. }
  4086. public int size() {
  4087. return list.size();
  4088. }
  4089. public boolean contains(Object o) {
  4090. if (o instanceof Entry) {
  4091. if (list.contains(((Entry) o).getKey())) {
  4092. return true;
  4093. }
  4094. }
  4095. return false;
  4096. }
  4097. };
  4098. }
  4099. public Set<K> keySet() {
  4100. return new AbstractSet<K>() {
  4101. public Iterator<K> iterator() {
  4102. return new Iterator<K>() {
  4103. private int pt = -1;
  4104. public void remove() {
  4105. throw new UnsupportedOperationException();
  4106. }
  4107. public K next() {
  4108. return list.get(++pt);
  4109. }
  4110. public boolean hasNext() {
  4111. return pt < list.size();
  4112. }
  4113. };
  4114. }
  4115. public int size() {
  4116. return list.size();
  4117. }
  4118. public boolean contains(Object o) {
  4119. return list.contains(o);
  4120. }
  4121. };
  4122. }
  4123. public Collection<V> values() {
  4124. return new AbstractList<V>() {
  4125. public V get(int index) {
  4126. return null;
  4127. }
  4128. public int size() {
  4129. return list.size();
  4130. }
  4131. public boolean contains(Object o) {
  4132. if (o == null && size() > 0) {
  4133. return true;
  4134. } else {
  4135. return false;
  4136. }
  4137. }
  4138. };
  4139. }
  4140. public V get(Object key) {
  4141. return null;
  4142. }
  4143. public boolean containsKey(Object key) {
  4144. return list.contains(key);
  4145. }
  4146. public boolean containsValue(Object o) {
  4147. if (o == null && size() > 0) {
  4148. return true;
  4149. } else {
  4150. return false;
  4151. }
  4152. }
  4153. }
  4154. }
  4155. // End Util.java