PageRenderTime 57ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/src/tufts/Util.java

https://bitbucket.org/elloyd/cnpvue
Java | 2943 lines | 2035 code | 399 blank | 509 comment | 382 complexity | 2b4672dcddb0d4a661f9aa9b6c212404 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * Copyright 2003-2010 Tufts University Licensed under the
  3. * Educational Community License, Version 2.0 (the "License"); you may
  4. * not use this file except in compliance with the License. You may
  5. * obtain a copy of the License at
  6. *
  7. * http://www.osedu.org/licenses/ECL-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing,
  10. * software distributed under the License is distributed on an "AS IS"
  11. * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
  12. * or implied. See the License for the specific language governing
  13. * permissions and limitations under the License.
  14. */
  15. package tufts;
  16. import java.awt.Color;
  17. import java.awt.Shape;
  18. import java.awt.Transparency;
  19. import java.awt.geom.Point2D;
  20. import java.awt.geom.Rectangle2D;
  21. import java.awt.geom.CubicCurve2D;
  22. import java.awt.geom.QuadCurve2D;
  23. import java.awt.image.BufferedImage;
  24. import java.io.BufferedReader;
  25. import java.io.File;
  26. import java.io.IOException;
  27. import java.io.InputStream;
  28. import java.io.InputStreamReader;
  29. import java.io.PrintWriter;
  30. import java.io.StringWriter;
  31. import java.io.Writer;
  32. import java.lang.ref.Reference;
  33. import java.lang.ref.SoftReference;
  34. import java.net.InetAddress;
  35. import java.net.NetworkInterface;
  36. import java.net.URLEncoder;
  37. import java.text.BreakIterator;
  38. import java.util.AbstractList;
  39. import java.util.ArrayList;
  40. import java.util.Arrays;
  41. import java.util.Collection;
  42. import java.util.Enumeration;
  43. import java.util.HashMap;
  44. import java.util.Hashtable;
  45. import java.util.Iterator;
  46. import java.util.List;
  47. import java.util.Locale;
  48. import java.util.Map;
  49. import java.util.NoSuchElementException;
  50. import java.util.RandomAccess;
  51. import java.util.jar.JarEntry;
  52. import java.util.jar.JarFile;
  53. import javax.swing.JComponent;
  54. import javax.swing.JFrame;
  55. import javax.swing.JLabel;
  56. import javax.swing.JPanel;
  57. import javax.swing.border.LineBorder;
  58. import tufts.macosx.MacOSX;
  59. /**
  60. * Utility class. Provides code for determining what platform we're on,
  61. * platform-specific code for opening URL's, as well as various convenience
  62. * functions.
  63. *
  64. * @version $Revision: $ / $Date: $ / $Author: sfraize $
  65. */
  66. public class Util
  67. {
  68. private static final org.apache.log4j.Logger Log = org.apache.log4j.Logger.getLogger(Util.class);
  69. private static boolean WindowsPlatform = false;
  70. private static boolean MacPlatform = false;
  71. private static boolean MacAquaLAF = false;
  72. private static boolean MacAquaLAF_set = false;
  73. private static boolean UnixPlatform = false;
  74. private static final String PlatformName;
  75. private static final String OSVersion;
  76. private static final boolean OSisMacLeopard;
  77. private static float javaVersion = 1.0f;
  78. private static int MacMRJVersion = -1;
  79. private static boolean DEBUG = false;
  80. private static String PlatformEncoding;
  81. private static boolean JVM_is_64_bit;
  82. static {
  83. if (!DEBUG)
  84. DEBUG = System.getProperty("tufts.Util.debug") != null;
  85. final String osName = System.getProperty("os.name");
  86. final String osArch = System.getProperty("os.arch");
  87. OSVersion = System.getProperty("os.version");
  88. final String javaSpec = System.getProperty("java.specification.version");
  89. final String bits = System.getProperty("sun.arch.data.model");
  90. if (bits.equals("64"))
  91. JVM_is_64_bit = true;
  92. else
  93. JVM_is_64_bit = false;
  94. PlatformName = osName + " " + OSVersion + " " + osArch;
  95. if (DEBUG) out(String.format("Platform: %s / %s / %s", osName, OSVersion, osArch));
  96. //if (DEBUG) out(String.format("Platform: %s / %s / %s; Leopard=%s", osName, OSVersion, osArch, OSisMacLeopard));
  97. try {
  98. javaVersion = Float.parseFloat(javaSpec);
  99. if (DEBUG) out("Java Version: " + javaVersion + "; JVM-bits=" + bits + "; 64bit=" + JVM_is_64_bit);
  100. } catch (Exception e) {
  101. errorOut("couldn't parse java.specifcaion.version: [" + javaSpec + "]");
  102. errorOut(e.toString());
  103. }
  104. final String osn = osName.toUpperCase();
  105. /*
  106. * fix for forums problem. one of the strangest bugs i've seen in a while.
  107. * something to keep in mind for future, calling toUpperCase() in a
  108. * locale that uses diacritics will add diacritics to a string that
  109. * did not originally contain them. so, "windows vista" gets
  110. * diacritics on the i in both words, the fix is so put the match
  111. * we're looking for as the lower case word mac and winodws, and call
  112. * toUpperCase on them so both sides of the comparison are always being
  113. * processed the same way. note that if you call to upper on a string
  114. * that is already upper case it does not place in the diacritic characters.
  115. */
  116. if (osn.startsWith("mac".toUpperCase())) {
  117. MacPlatform = true;
  118. OSisMacLeopard =
  119. OSVersion.startsWith("10.5") || // Leopard
  120. OSVersion.startsWith("10.6"); // Snow Leopard
  121. if (DEBUG) out(String.format("Mac: Leopard/Snow-Leopard=%s", OSisMacLeopard));
  122. String mrj = System.getProperty("mrj.version");
  123. int i = 0;
  124. while (i < mrj.length()) {
  125. if (!Character.isDigit(mrj.charAt(i)))
  126. break;
  127. i++;
  128. }
  129. try {
  130. MacMRJVersion = Integer.parseInt(mrj.substring(0, i));
  131. } catch (NumberFormatException e) {
  132. errorOut("couldn't parse mrj.version: " + e);
  133. errorOut(e.toString());
  134. }
  135. if (DEBUG) out("Mac mrj.version: \"" + mrj + "\" = " + MacMRJVersion);
  136. } else if (osn.indexOf("windows".toUpperCase()) >= 0) {
  137. WindowsPlatform = true;
  138. OSisMacLeopard = false;
  139. //if (DEBUG) out("Windows Platform: " + PlatformName);
  140. } else {
  141. UnixPlatform = true;
  142. OSisMacLeopard = false;
  143. }
  144. if (DEBUG) out(String.format("OSFlags: isWin=%b; isMac=%b(leopard=%b); isUnix=%b;", WindowsPlatform, MacPlatform, OSisMacLeopard, UnixPlatform));
  145. String term = System.getenv("TERM");
  146. boolean allowColor = false;
  147. if (term == null)
  148. term = "";
  149. allowColor = term.indexOf("color") >= 0;
  150. if (!allowColor) {
  151. term = System.getenv("SSH_TERM");
  152. if (term != null && term.indexOf("color") >= 0)
  153. allowColor = true;
  154. }
  155. if (allowColor) {
  156. TERM_RED = "\033[1;31m";
  157. TERM_GREEN = "\033[1;32m";
  158. TERM_YELLOW = "\033[1;33m";
  159. TERM_BLUE = "\033[1;34m";
  160. TERM_PURPLE = "\033[1;35m";
  161. TERM_CYAN = "\033[1;36m";
  162. TERM_CLEAR = "\033[m";
  163. } else {
  164. TERM_RED = TERM_GREEN = TERM_YELLOW = TERM_BLUE = TERM_PURPLE = TERM_CYAN = TERM_CLEAR = "";
  165. }
  166. //printStackTrace("TERM[" + term + "]");
  167. }
  168. /** Common escape codes for terminal text colors. Set to empty string unless on a color terminal */
  169. public static final String TERM_RED, TERM_GREEN, TERM_YELLOW, TERM_BLUE, TERM_PURPLE, TERM_CYAN, TERM_CLEAR;
  170. private static void out(String s) {
  171. if (Log.isDebugEnabled())
  172. Log.debug(s);
  173. else
  174. System.out.println("tufts.Util: " + s);
  175. }
  176. private static void errorOut(String s) {
  177. System.err.println("tufts.Util: " + s);
  178. Log.error(s);
  179. }
  180. public static String formatLines(String target, int maxLength)
  181. {
  182. Locale currentLocale = new Locale ("en","US");
  183. return formatLines(target,maxLength,currentLocale);
  184. }
  185. public static String formatLines(String target, int maxLength,
  186. Locale locale)
  187. {
  188. if (target == null) {
  189. Log.warn("attempt to line-break null string");
  190. return null;
  191. }
  192. try {
  193. target = breakLines(target, maxLength, locale);
  194. } catch (Throwable t) {
  195. Log.error("failed to line-break [" + target + "]; ", t);
  196. }
  197. return target;
  198. }
  199. public static String maxDisplay(final String s, int len) {
  200. if (s == null)
  201. return null;
  202. if (len < 4)
  203. len = 4;
  204. if (s.length() > len) {
  205. return s.substring(0,len-3) + "...";
  206. } else {
  207. return s;
  208. }
  209. }
  210. private static String breakLines(final String target, int maxLength, Locale currentLocale)
  211. {
  212. final StringBuilder s = new StringBuilder(target.length() + 4);
  213. final BreakIterator boundary = BreakIterator.getLineInstance(currentLocale);
  214. boundary.setText(target);
  215. int start = boundary.first();
  216. int end = boundary.next();
  217. int lineLength = 0;
  218. boolean didBreak = false;
  219. while (end != BreakIterator.DONE) {
  220. final String word = target.substring(start,end);
  221. lineLength += word.length();
  222. // Log.debug(String.format("len: %2d, word %s%s",
  223. // lineLength,
  224. // tags(word),
  225. // atExistingNewline ? ", @newline" : ""));
  226. if (lineLength >= maxLength) {
  227. final boolean atExistingNewline =
  228. s.length() > 1 && s.charAt(s.length() - 1) == '\n';
  229. if (!atExistingNewline) {
  230. //Log.debug("adding newline");
  231. s.append('\n');
  232. didBreak = true;
  233. }
  234. lineLength = word.length();
  235. }
  236. //Log.debug("adding word " + tags(word));
  237. s.append(word);
  238. start = end;
  239. end = boundary.next();
  240. }
  241. return didBreak ? s.toString() : target;
  242. }
  243. /*
  244. * Be sure to compare this value to a constant *float*
  245. * value -- e.g. "1.4f" -- just "1.4" as double value may
  246. * actually appear to be less than 1.4, as that can't
  247. * be exactly represented by a double.
  248. */
  249. public static float getJavaVersion() {
  250. return javaVersion;
  251. }
  252. /** return mac runtime for java version. Will return -1 if we're not running on mac platform. */
  253. public static int getMacMRJVersion() {
  254. return MacMRJVersion;
  255. }
  256. public static String getPlatformName() {
  257. return PlatformName == null ? (System.getProperty("os.name") + "?") : PlatformName;
  258. }
  259. public static String getOSVersion() {
  260. return OSVersion;
  261. }
  262. public static boolean isMacLeopard() {
  263. return OSisMacLeopard;
  264. }
  265. public static boolean isMacTiger() {
  266. return MacPlatform && !OSisMacLeopard;
  267. }
  268. /** @return true if we're running on a version Microsoft Windows */
  269. public static boolean isWindowsPlatform() {
  270. return WindowsPlatform;
  271. }
  272. /** @return true if we're running on an Apple Mac OS */
  273. public static boolean isMacPlatform() {
  274. return MacPlatform;
  275. }
  276. /** @return true if we're running on unix only platform (e.g., Linux -- Mac OS X not included) */
  277. public static boolean isUnixPlatform(){
  278. return UnixPlatform;
  279. }
  280. /**
  281. * @return true of Mac Cocoa extensions are supported.
  282. * Currently only true if running in a 32bit Java 1.5 VM.
  283. */
  284. public static boolean isMacCocoaSupported()
  285. {
  286. return isMacPlatform() && !JVM_is_64_bit && javaVersion < 1.6;
  287. }
  288. public static String getDefaultPlatformEncoding() {
  289. if (PlatformEncoding == null)
  290. PlatformEncoding = (new java.io.OutputStreamWriter(new java.io.ByteArrayOutputStream())).getEncoding();
  291. return PlatformEncoding;
  292. }
  293. /** @return true if the current Look & Feel is Mac Aqua (not always true just because you're on a mac)
  294. * Note: do NOT call this from any static initializers the result may be changed by application startup
  295. * code. */
  296. public static boolean isMacAquaLookAndFeel()
  297. {
  298. // we can't set this at static init time because the LAF can be set after that
  299. if (MacAquaLAF_set == false) {
  300. MacAquaLAF =
  301. isMacPlatform() &&
  302. javax.swing.UIManager.getLookAndFeel().getName().toLowerCase().indexOf("aqua") >= 0;
  303. MacAquaLAF_set = true;
  304. }
  305. return MacAquaLAF;
  306. }
  307. /** @param url -- a url assumed to be in the platform default format (e.g., it may be
  308. * formatted in a way only understood on the current platform).
  309. */
  310. public static void openURL(String url)
  311. throws java.io.IOException
  312. {
  313. // TODO: isLocalFile should check for inital '\' also (File.separator) -- but follow-on code will now need re-testing
  314. boolean isLocalFile = (url.length() >= 5 && "file:".equalsIgnoreCase(url.substring(0,5))) || url.startsWith("/");
  315. boolean isMailTo = url.length() >= 7 && "mailto:".equalsIgnoreCase(url.substring(0,7));
  316. if (DEBUG) System.err.println("openURL_PLAT [" + url + "] isLocalFile=" + isLocalFile + " isMailTo=" + isMailTo);
  317. if (isMacPlatform())
  318. openURL_Mac(url, isLocalFile, isMailTo);
  319. else if (isUnixPlatform())
  320. openURL_Unix(url, isLocalFile, isMailTo);
  321. else // default is a windows platform
  322. openURL_Windows(url, isLocalFile, isMailTo);
  323. }
  324. private static final String PC_OPENURL_CMD = "rundll32 url.dll,FileProtocolHandler";
  325. private static void openURL_Windows(String url, boolean isLocalFile, boolean isMailTo)
  326. throws java.io.IOException
  327. {
  328. System.err.println("openURL_Win [" + url + "]");
  329. // On at least Windows 2000, %20's don't work when given to url.dll FileProtocolHandler
  330. // (Should be using some kind of URLProtocolHanlder ?)
  331. // Also at least Win2K: file:///C:/foo/bar.html works, but start that with file:/ or file://
  332. // and it DOES NOT work -- you MUST have the three slashes, OR, you can have NO SLASHES,
  333. // and it will work... (file:C:/foo/bar.html)
  334. // ALSO, file:// or // file:/// works BY ITSELF (file:/ by itself still doesn't work).
  335. //url = url.replaceAll("%20", " ");
  336. if (!isMailTo) {
  337. url = decodeURL(url);
  338. url = decodeURL(url); // run twice in case any %2520 double encoded spaces
  339. }
  340. if (isLocalFile) {
  341. // below works, but we're doing a full conversion to windows path names now
  342. //url = url.replaceFirst("^file:/+", "file:");
  343. url = url.replaceFirst("^file:/+", "");
  344. url = url.replace('/', '\\');
  345. System.err.println("openURL_patch[" + url + "]");
  346. char c1 = 0;
  347. try { c1 = url.charAt(1); } catch (StringIndexOutOfBoundsException e) {}
  348. if (c1 == ':' || c1 == '|') {
  349. // Windows drive letter specification, e.g., "C:".
  350. // Also, I've seen D|/dir/file.txt in a shortcut file, so we allow that too.
  351. // In any case, we do noting: this string should be workable to url.dll
  352. } else {
  353. if (url.startsWith("file:")) {
  354. // DO NOTHING.
  355. // If we had "file:" above and NOT "file:/", we need this check here.
  356. // This suggests this code needs refactoring. SMF 2008-04-02
  357. } else {
  358. // if this does NOT start with a drive specification, assume
  359. // the first component is a host for a windows network
  360. // drive, and thus we have to pre-pend \\ to it, to get \\host\file
  361. url = "\\\\" + url;
  362. }
  363. }
  364. // at this point, "url" is really just a local windows file
  365. // in full windows syntax (backslashes, drive specs, etc)
  366. System.err.println("openURL_WinLF[" + url + "]");
  367. }
  368. String cmd = PC_OPENURL_CMD + " " + url;
  369. final int sizeURL = url.length();
  370. final int sizeCommand = cmd.length();
  371. final int sizeWinArgs = sizeCommand - 17; // subtract "rundll32 url.dll,"
  372. boolean debug = DEBUG;
  373. if (sizeURL > 2027 || sizeCommand > 2064 || sizeWinArgs >= 2048) {
  374. System.err.println("\nWarning: WinXP buffer lengths likely exceeded: command not likely to run (arglen=" + sizeWinArgs + ")");
  375. debug = true;
  376. } else {
  377. System.err.println();
  378. }
  379. if (debug) {
  380. System.err.println("exec url length: " + sizeURL);
  381. System.err.println("exec command length: " + sizeCommand);
  382. System.err.println("exec url.dll args len: " + sizeWinArgs);
  383. }
  384. if (sizeCommand > 2064) {
  385. System.err.println("exec: truncating command and hoping for the best...");
  386. cmd = cmd.substring(0,2063);
  387. }
  388. System.err.println("exec[" + cmd + "]");
  389. Runtime.getRuntime().exec(cmd);
  390. // final String mailto = "mailto:"
  391. // + "foo@bar.com?"
  392. // + "subject=TestSubject"
  393. // + "&attachment="
  394. // //+ "&Attach="
  395. // + "\""
  396. // + "c:\\\\foo.txt"
  397. // + "\""
  398. // ;
  399. // System.err.println("MAILTO: ["+mailto+"]");
  400. // Runtime.getRuntime().exec(new String[] {"rundll32", "url.dll,FileProtocolHandler", mailto
  401. // "mailto:"
  402. // + "&subject=TestSubject"
  403. // + "&attachment=" + "\"" + "c:\\\\foo.txt" + "\""
  404. // },
  405. // null);
  406. // if (false) {
  407. // Process p = Runtime.getRuntime().exec(cmd);
  408. // try {
  409. // System.err.println("waiting...");
  410. // p.waitFor();
  411. // } catch (Exception ex) {
  412. // System.err.println(ex);
  413. // }
  414. // System.err.println("exit value=" + p.exitValue());
  415. // }
  416. }
  417. /**
  418. * Call a named static method with the given args.
  419. * The method signature is inferred from the argument types. We map the primitive
  420. * type wrapper class to their primitive types (an auto un-boxing), so this
  421. * won't work for method calls that take args of Boolean, Integer, etc,
  422. * only boolean, int, etc.
  423. */
  424. public static Object execute(Object object, String className, String methodName, Object[] args)
  425. throws ClassNotFoundException,
  426. NoSuchMethodException,
  427. IllegalArgumentException,
  428. IllegalAccessException,
  429. java.lang.reflect.InvocationTargetException
  430. {
  431. if (DEBUG) {
  432. // use multiple prints in remote case of a class-load or some such error along the way
  433. System.err.println("Util.execute:");
  434. System.err.println("\t className: [" + className + "]");
  435. System.err.println("\tmethodName: [" + methodName + "]");
  436. String desc;
  437. if (args == null)
  438. desc = "null";
  439. else if (args.length == 0)
  440. desc = "(none)";
  441. else if (args.length == 1)
  442. desc = objectTag(args[0]) + " [" + args[0] + "]";
  443. else
  444. desc = Arrays.asList(args).toString();
  445. System.err.println("\t args: " + desc);
  446. System.err.println("\t object: " + objectTag(object) + " [" + object + "]");
  447. }
  448. final Class clazz = Class.forName(className);
  449. final Class[] argTypes;
  450. if (args == null) {
  451. argTypes = null;
  452. } else {
  453. argTypes = new Class[args.length];
  454. for (int i = 0; i < args.length; i++) {
  455. final Class argClass = args[i].getClass();
  456. if (argClass == Boolean.class) argTypes[i] = Boolean.TYPE;
  457. else if (argClass == Integer.class) argTypes[i] = Integer.TYPE;
  458. else if (argClass == Long.class) argTypes[i] = Long.TYPE;
  459. else if (argClass == Short.class) argTypes[i] = Short.TYPE;
  460. else if (argClass == Float.class) argTypes[i] = Float.TYPE;
  461. else if (argClass == Double.class) argTypes[i] = Double.TYPE;
  462. else if (argClass == Byte.class) argTypes[i] = Byte.TYPE;
  463. else if (argClass == Character.class) argTypes[i] = Character.TYPE;
  464. else
  465. argTypes[i] = args[i].getClass();
  466. }
  467. }
  468. java.lang.reflect.Method method = clazz.getMethod(methodName, argTypes);
  469. if (DEBUG) System.err.print("\t invoking: " + method + " ... ");
  470. Object result = method.invoke(object, args);
  471. if (DEBUG) System.err.println("returned.");
  472. if (DEBUG && method.getReturnType() != void.class) {
  473. // use multiple prints in remote case of a class-load or some such error along the way
  474. //System.err.println("Util.execute:");
  475. System.err.println("\t return: " + objectTag(result) + " [" + result + "]");
  476. }
  477. //System.out.println("Sucessfully invoked " + className + "." + methodName + ", result=" + result);
  478. return result;
  479. }
  480. public static Object execute(String className, String methodName, Object[] args)
  481. throws ClassNotFoundException,
  482. NoSuchMethodException,
  483. IllegalArgumentException,
  484. IllegalAccessException,
  485. java.lang.reflect.InvocationTargetException
  486. {
  487. return execute(null, className, methodName, args);
  488. }
  489. /** Attempt a method call. If it fails, quietly fail, printing a stack trace and returning null.
  490. * @param object - cannot be null (implying a static method), as is also used to get the class name
  491. * If object is a Class object, make this a static invocation in that class.
  492. */
  493. public static Object invoke(Object object, String methodName, Object[] args)
  494. {
  495. String className;
  496. if (object instanceof Class) {
  497. className = ((Class)object).getName();
  498. object = null;
  499. } else
  500. className = object.getClass().getName();
  501. try {
  502. return execute(object, className, methodName, args);
  503. } catch (Exception e) {
  504. e.printStackTrace();
  505. return e;
  506. }
  507. }
  508. /**
  509. * Attempt a method call. If it fails, quietly fail, printing a stack trace and returning null.
  510. * This is a convenience version that allows the passing in of a single argument directly.
  511. **/
  512. public static Object invoke(Object object, String methodName, Object arg0) {
  513. return invoke(object, methodName, new Object[] { arg0 });
  514. }
  515. public static Object invoke(Object object, String methodName) {
  516. return invoke(object, methodName, null);
  517. }
  518. /**
  519. * Attempt to invoke the given method with the given args. If
  520. * any exception occurs, (e.g., ClassNotFoundException because a
  521. * particular library isn't present) we quietly catch it and
  522. * pass it back as the return value;
  523. */
  524. public static Object executeIfFound(String className, String methodName, Object[] args) {
  525. try {
  526. return execute(className, methodName, args);
  527. } catch (Exception e) {
  528. return e;
  529. }
  530. }
  531. /** Encode a query pair URL such that it will work with the current platform openURL code.
  532. * E.g., construct a mailto: whose subject/body args are encoded such that they successfully
  533. * pass through to the local mail client.
  534. */
  535. public static String makeQueryURL(String urlStart, String ... nameValuePairs) {
  536. StringBuffer url = new StringBuffer(urlStart);
  537. boolean isKey = true;
  538. boolean isFirstKey = true;
  539. String curKey = "<no-key!>";
  540. for (String s : nameValuePairs) {
  541. //System.out.format("TOKEN[%s]\n", s);
  542. if (isKey) {
  543. if (isFirstKey) {
  544. url.append('?');
  545. isFirstKey = false;
  546. } else
  547. url.append('&');
  548. curKey = s;
  549. System.out.format("KEY[%s]\n", s);
  550. url.append(s);
  551. url.append('=');
  552. } else if ("attachment".equals(curKey)) {
  553. // append windows pathname raw:
  554. // An "attachment" arg has been rumoured to work on Windows,
  555. // but as far as I can tell, these rumours are total BS.
  556. // Have yet to see this work in any configuration.
  557. url.append('"' + s + '"');
  558. } else {
  559. // value field:
  560. final String encodedValue = encodeURLData(s);
  561. if (DEBUG) {
  562. System.out.format(" %-17s [%s]\n", curKey + "(RAW):", s);
  563. System.out.format(" %-17s [%s]\n", curKey + "(ENCODED):", encodedValue);
  564. }
  565. url.append(encodedValue);
  566. }
  567. isKey = !isKey;
  568. }
  569. //if (DEBUG) System.out.println("QUERY URL: " + url);
  570. return url.toString();
  571. }
  572. public static String encodeURLData(String url) {
  573. try {
  574. url = URLEncoder.encode(url, "UTF-8");
  575. url = url.replaceAll("\\+", "%20"); // Mail clients don't understand '+' as space
  576. } catch (Throwable t) {
  577. // should never happen:
  578. printStackTrace(t);
  579. }
  580. return url;
  581. }
  582. // GAK! Move this code to URLResource, as it's going to have to be smart
  583. // about local file drops for mac URL's, to be sure to UTF-8 DECODE them
  584. // first, so we get RID of special characters or spaces, which come
  585. // when the local file was dragged Safari, as opposed to the Finder
  586. // (e.g., Desktop), which already decodes them for us.
  587. // Also, we need to generically handle the decoding of ":" into "/"
  588. // for these URL's. (God forbid there is HTML in the name: e.g. <i>foo</i>
  589. // will send us a ':' in place of the '/', which we also don't
  590. // want to mistake for a protocol below. Actually, do fix the ':'
  591. // in the display name (title), but not raw meta-data: could be confusing.
  592. private static java.lang.reflect.Method macOpenURL_Method = null;
  593. private static void openURL_Mac(String url, boolean isLocalFile, boolean isMailTo)
  594. {
  595. //if (DEBUG) System.err.println("openURL_Mac0 [" + url + "]");
  596. System.err.println("openURL_Mac0 [" + url + "], isLocal=" + isLocalFile);
  597. if (isLocalFile) {
  598. boolean changed = true;
  599. if (url.startsWith("file:////")) {
  600. // don't think we have to do this, but just in case
  601. // (was getting a complaint and couldn't tell if this was why or not,
  602. // so now we won't see it)
  603. url = "file:///" + url.substring(9);
  604. } else if (url.startsWith("/")) {
  605. // must have file: at head to be sure it works on mac
  606. url = "file:" + url;
  607. } else
  608. changed = false;
  609. if (DEBUG && changed) System.err.println("openURL_Mac1 [" + url + "]");
  610. }
  611. if (isMailTo) {
  612. // final String flatURL = url.toLowerCase();
  613. // final int queryIndex = flatURL.indexOf('?') + 1;
  614. // if (queryIndex > 1) {
  615. // final String query = flatURL.substring(queryIndex);
  616. // if (DEBUG) System.out.println("QUERY: [" + query + "]");
  617. // final int subjectIndex = query.indexOf("subject=");
  618. // }
  619. //url = url.replaceAll(" ", "%20");
  620. // try {
  621. // url = URLEncoder.encode(url, "UTF-8");
  622. // } catch (Throwable t) {
  623. // printStackTrace(t);
  624. // }
  625. } else {
  626. try {
  627. url = encodeSpecialChars_Mac(url, isMailTo);
  628. } catch (Throwable t) {
  629. printStackTrace(t);
  630. }
  631. }
  632. // In Mac OS X, local files MUST have %20 and NO spaces in the URL's -- reverse of Windows.
  633. // But enforcing this for regular HTTP URL's breaks them: turns existing %20's into %2520's
  634. // If no protocol in the URL string, assume it's a pathname. Normally, there
  635. // would be nothing to do as openURL handles that fine on MacOSX, unless it's
  636. // not absolute, it which case we make it absolute by assuming the users home
  637. // directory.
  638. if (!isMailTo && url.indexOf(':') < 0 && !url.startsWith("/")) {
  639. // Hack to make relative references relative to user home directory. OSX
  640. // won't default to use current directory for a relative references, so
  641. // we're prepending the home directory manually as a bail out try-for-it.
  642. url = "file://" + System.getProperty("user.home") + "/" + url;
  643. if (DEBUG) System.err.println(" OUM HOME [" + url + "]");
  644. }
  645. execMacOpenURL(url);
  646. }
  647. // WHAT WE KNOW WORKS FOR MAILTO: '@' encoded, but ? / & decoded, except WITHIN
  648. // the value of &field=value, which of course messes it up.
  649. // Also in the working state: All spaces as %20, newlines %0A
  650. // If we take out the colons tho, we're screwed.
  651. private static String encodeSpecialChars_Mac(String url, boolean isMailTo)
  652. throws java.io.IOException
  653. {
  654. // if (url.indexOf('%') >= 0) {
  655. // // If we were to make sure we're always handed URI's (at
  656. // // least on the mac) this would prevent accidentally
  657. // // decoding actual '+' characters in the file-name,
  658. // // as they strings would already come with %20 for spaces.
  659. // // However, if there was a special (e.g. Unicode) character
  660. // // anywhere in the string, we would still need to encode it,
  661. // // but then we'd have to determine if the '+' we see at this
  662. // // point is from the URLEncoder, or from the real File name.
  663. // // Eventually, this Util code should take a real File object
  664. // // so we can be more sure about what we're doing in these
  665. // // corner cases.
  666. // if (DEBUG) System.err.println(" NO-CLEANUP [" + url + "]");
  667. // return url;
  668. // }
  669. // In case there are any special characters (e.g., Unicode chars) in the
  670. // file name, we must first encode them for MacOSX (local files only?)
  671. // FYI, MacOSX openURL uses UTF-8, NOT the native MacRoman encoding.
  672. // URLEncoder encodes EVERYTHING other than alphas tho, so we need
  673. // to put it back.
  674. // Note: using the less intense encoding of URI's may make this simpler
  675. if (url.indexOf('%') >= 0) {
  676. // DECODE it, in case there are already any encodings,
  677. // we don't want to double-encode.
  678. url = java.net.URLDecoder.decode(url, "UTF-8");
  679. if (DEBUG) System.err.println(" DECODE UTF [" + url + "]");
  680. }
  681. url = java.net.URLEncoder.encode(url, "UTF-8"); // URLEncoder is way overzealous...
  682. if (DEBUG) System.err.println(" ENCODE UTF [" + url + "]");
  683. // now decode the over-coded stuff -- these are all crucial characters
  684. // that must be present...
  685. url = url.replaceAll("%3A", ":");
  686. url = url.replaceAll("%2F", "/");
  687. url = url.replaceAll("%3F", "?");
  688. url = url.replaceAll("%3D", "=");
  689. url = url.replaceAll("%26", "&");
  690. // Mac doesn't undestand '+' means ' ' in it's openURL support method:
  691. // url = url.replace('+', ' '); // nor does it understand actual spaces
  692. url = url.replaceAll("\\+", "%20");
  693. if (DEBUG) System.err.println(" CLEANUP [" + url + "]");
  694. return url;
  695. }
  696. private static void execMacOpenURL(String url)
  697. {
  698. if (isMacLeopard()) {
  699. final String cmd = "/usr/bin/open";
  700. try {
  701. if (DEBUG) System.err.println(" Leopard: " + cmd + " " + url);
  702. Runtime.getRuntime().exec(new String[]{ cmd, url });
  703. } catch (Throwable t) {
  704. printStackTrace(t, cmd + " " + url);
  705. }
  706. } else if (getJavaVersion() >= 1.4f) {
  707. // Can't call this directly because wont compile on the PC
  708. //com.apple.eio.FileManager.openURL(url);
  709. if (macOpenURL_Method == null) {
  710. try {
  711. Class macFileManager = Class.forName("com.apple.eio.FileManager");
  712. //Class macFileManager = ClassLoader.getSystemClassLoader().loadClass("com.apple.eio.FileManager");
  713. macOpenURL_Method = macFileManager.getMethod("openURL", new Class[] { String.class });
  714. } catch (Exception e) {
  715. System.err.println("Failed to find Mac FileManager or openURL method: will not be able to display URL content.");
  716. e.printStackTrace();
  717. throw new UnsupportedOperationException("com.apple.eio.FileManager:openURL " + e);
  718. }
  719. }
  720. if (macOpenURL_Method != null) {
  721. try {
  722. macOpenURL_Method.invoke(null, new Object[] { url });
  723. } catch (Exception e) {
  724. e.printStackTrace();
  725. throw new UnsupportedOperationException("com.apple.eio.FileManager.openURL " + e);
  726. }
  727. } else
  728. throw new UnsupportedOperationException("openURL_Mac");
  729. } else {
  730. throw new UnsupportedOperationException("mac java <= 1.3 openURL unimplemented");
  731. // put this back if want to suppor mac java 1.3
  732. // this has been deprecated in mac java 1.4, so
  733. // just ignore the warning if using a 1.4 or beyond
  734. // compiler
  735. // com.apple.mrj.MRJFileUtils.openURL(url);
  736. }
  737. if (DEBUG) System.err.println("execMacOpenURL returns for (" + url + ")");
  738. }
  739. private static boolean HasGnomeOpen = false;
  740. private static boolean GnomeOpenSet = false;
  741. private static void openURL_Unix(String url, boolean isLocalFile, boolean isMailTo)
  742. throws java.io.IOException
  743. {
  744. // For now we just assume Netscape is installed.
  745. // todo: run down list of netscape, mozilla, konqueror (KDE
  746. // browser), gnome version? KDE/Gnome may have their own
  747. // services for getting a default browser.
  748. /*
  749. The problem I had with my 2 linux installations was that even though netscape wasn't
  750. installed the distribution provided a symlink named netscape which pointed to firefox
  751. however firefox couldn't interpet the netscape parameters. So below I've modified it
  752. to try to load firefox first and then if that fails load netscape, i suppose konqueror
  753. should be in the mix too maybe but this is better than it was and catches alot of cases.
  754. */
  755. final Runtime runtime = Runtime.getRuntime();
  756. Process process = null;
  757. int waitFor = -1;
  758. if (!GnomeOpenSet) {
  759. // test for gnome
  760. try {
  761. process = runtime.exec("gnome-open");
  762. waitFor = process.waitFor();
  763. }
  764. catch (InterruptedException e) {
  765. e.printStackTrace();
  766. }
  767. catch (IOException ioe2) {
  768. ioe2.printStackTrace();
  769. waitFor = 2;
  770. }
  771. GnomeOpenSet = true;
  772. if (waitFor != 2)
  773. HasGnomeOpen = true;
  774. }
  775. if (HasGnomeOpen) {
  776. //open file with gnome
  777. if (isLocalFile) {
  778. url = decodeURL(url);
  779. // SMF 2008-04-30: commented this out: was ensuring that most
  780. // local file links would not work -- not sure what cases
  781. // this was handling.
  782. //
  783. //url = "file:///" + url.substring(6);
  784. // old:
  785. ////jjurl = url.replaceAll(" ","\\ ");
  786. ////url ="\""+url+"\"";
  787. }
  788. Log.info("gnome-open " + url);
  789. process = runtime.exec(new String[] {"gnome-open" , url});
  790. // out("process: " + process);
  791. InputStream stdin = process.getErrorStream();
  792. InputStreamReader isr = new InputStreamReader(stdin);
  793. BufferedReader br = new BufferedReader(isr);
  794. String line = null;
  795. System.out.println("<OUTPUT>");
  796. while ((line = br.readLine()) != null)
  797. System.out.println(line);
  798. System.out.println("<OUTPUT>");
  799. try
  800. {
  801. int exitVal = process.waitFor();
  802. System.out.println("exit val : " + exitVal);
  803. }
  804. catch(InterruptedException inte)
  805. {
  806. inte.printStackTrace();
  807. }
  808. return;
  809. }
  810. else
  811. waitFor=-1;
  812. // kde
  813. try
  814. {
  815. System.out.println("TESTING KDE");
  816. process =runtime.exec("kfmexec");
  817. waitFor = process.waitFor();
  818. } catch (InterruptedException e)
  819. {
  820. e.printStackTrace();
  821. }
  822. catch (IOException ioe2)
  823. {
  824. ioe2.printStackTrace();
  825. waitFor=2;
  826. }
  827. if (waitFor != 2)
  828. {
  829. if (isLocalFile)
  830. {
  831. url = decodeURL(url);
  832. //jjurl = url.replaceAll(" ","\\ ");
  833. url = "file:///" + url.substring(6);
  834. //url ="\""+url+"\"";
  835. System.out.println(url);
  836. }
  837. //open file with kde
  838. process = runtime.exec(new String[] { "kfmclient","exec", url});
  839. InputStream stdin = process.getErrorStream();
  840. InputStreamReader isr = new InputStreamReader(stdin);
  841. BufferedReader br = new BufferedReader(isr);
  842. String line = null;
  843. System.out.println("<OUTPUT>");
  844. while ((line = br.readLine()) != null)
  845. System.out.println(line);
  846. System.out.println("<OUTPUT>");
  847. System.out.println("<OUTPUT2>");
  848. stdin=process.getInputStream();
  849. isr = new InputStreamReader(stdin);
  850. br = new BufferedReader(isr);
  851. line = null;
  852. while ((line = br.readLine()) != null)
  853. System.out.println(line);
  854. System.out.println("<OUTPUT2>");
  855. try
  856. {
  857. int exitVal = process.waitFor();
  858. System.out.println("exit val : " + exitVal);
  859. }
  860. catch(InterruptedException inte)
  861. {
  862. inte.printStackTrace();
  863. }
  864. return;
  865. }
  866. //if (waitFor == 2)
  867. try
  868. {
  869. process = runtime.exec(new String[] { "firefox", url});
  870. waitFor = process.waitFor();
  871. System.out.println("WAIT FOR : " + waitFor);
  872. }
  873. catch (java.io.IOException ioe3)
  874. {
  875. //firefox not available try netscape instead.
  876. process = Runtime.getRuntime().exec(new String[] { "netscape",
  877. "-remove",
  878. "'openURL('" + url + "')" });
  879. } catch (InterruptedException e) {
  880. // TODO Auto-generated catch block
  881. e.printStackTrace();
  882. }
  883. out("process: " + process);
  884. }
  885. /**
  886. * Execute the given system command (arg0 is command, subsequent args are command arguments).
  887. * @param runDirectory -- if non-null, the directory to run the command in.
  888. *
  889. * The current impl will only return up to the first 256 chars of output, and will
  890. * use String.trim on it, to remove any trailing newline.
  891. *
  892. * @see java.lang.Runtime
  893. */
  894. public static String getSystemCommandOutput(String[] args, String runDirectory)
  895. {
  896. String output = null;
  897. try {
  898. File dir = null;
  899. if (runDirectory != null) {
  900. try {
  901. dir = new File(runDirectory);
  902. } catch (Throwable t) {
  903. printStackTrace(t, "Warning: couldn't create file from: " + runDirectory);
  904. }
  905. }
  906. Log.info("exec " + args[0] + ": " + Arrays.asList(args) + " in dir " + dir + " (" + runDirectory + ")");
  907. final Process proc = Runtime.getRuntime().exec(args, null, dir);
  908. final java.io.InputStream stream = proc.getInputStream();
  909. final byte[] buf = new byte[256];
  910. final int got = stream.read(buf);
  911. output = new String(buf, 0, got).trim();
  912. Log.debug("exec " + args[0] + ": got output[" + output + "]");
  913. } catch (Throwable t) {
  914. printStackTrace(t, "getSystemCommandOutput");
  915. }
  916. return output;
  917. }
  918. /**
  919. * Fast impl of replacing %xx hexidecimal codes with actual characters (e.g., %20 is a space, %2F is '/').
  920. * Leaves untouched any bad hex digits, or %xx strings that represent control characters (less than space/0x20
  921. * or greater than tilde/0x7E).
  922. *
  923. * @return String with replaced %xx codes. If there are no '%' characters in the input string,
  924. * the original String object is returned.
  925. */
  926. public static String decodeURL(final String s)
  927. {
  928. //System.out.println("DECODING " + s);
  929. int i = s.indexOf('%');
  930. if (i < 0)
  931. return s;
  932. final int len = s.length();
  933. final StringBuffer buf = new StringBuffer(len);
  934. // copy in everything we've skipped in the original string up to now
  935. buf.append(s.substring(0, i));
  936. //System.out.println("DECODE START " + buf);
  937. for ( ; i < len; i++) {
  938. final char c = s.charAt(i);
  939. if (c == '%' && i+2 < len) {
  940. final int hex1 = Character.digit(s.charAt(++i), 16);
  941. final int hex2 = Character.digit(s.charAt(++i), 16);
  942. final char charValue = (char) (hex1 * 16 + hex2);
  943. //System.out.println("Got char value " + charValue + " from " + c1 + c2);
  944. if (hex1 < 1 || hex2 < 0 || charValue < 0x20 || charValue > 0x7E) {
  945. // Pass through untouched if not two good hex characters (and first can't be 0),
  946. // or if result is a control character (anything less than space, or greater than '~')
  947. buf.append('%');
  948. buf.append(s.charAt(i-1));
  949. buf.append(s.charAt(i));
  950. } else {
  951. buf.append(charValue);
  952. }
  953. } else {
  954. buf.append(c);
  955. }
  956. }
  957. if (DEBUG) {
  958. System.out.println("DECODED [" + s + "]");
  959. System.out.println(" TO [" + buf + "]");
  960. }
  961. return buf.toString();
  962. }
  963. public static final Iterator EmptyIterator = new EmptyIterable();
  964. public static final Iterable EmptyIterable = (Iterable) EmptyIterator;
  965. private static final class EmptyIterable implements java.util.Iterator, java.lang.Iterable {
  966. public boolean hasNext() { return false; }
  967. public Object next() { throw new NoSuchElementException(); }
  968. public void remove() { throw new UnsupportedOperationException(); }
  969. public String toString() { return "EmptyIterator"; }
  970. public Iterator iterator() { return this; }
  971. }
  972. public interface Itering<T> extends java.util.Iterator<T>, Iterable<T> {}
  973. public static abstract class AbstractItering<T> implements Itering<T> {
  974. public Iterator<T> iterator() {
  975. return this;
  976. }
  977. public void remove() {
  978. throw new UnsupportedOperationException();
  979. }
  980. }
  981. private static abstract class IndexedItering<T> extends AbstractItering<T> {
  982. final int length;
  983. int index = 0;
  984. IndexedItering(int len) { length = len; }
  985. public boolean hasNext() {
  986. return index < length;
  987. }
  988. // could reset index if another "instance" is requested via iterator()
  989. }
  990. public static <T> Itering<T> iterable(T o) {
  991. return new SingletonIterable<T>(o);
  992. }
  993. public static Itering<org.w3c.dom.Node> iterable(final org.w3c.dom.NodeList nl) {
  994. return new IndexedItering<org.w3c.dom.Node>(nl.getLength()) {
  995. public org.w3c.dom.Node next() {
  996. return nl.item(index++);
  997. }
  998. };
  999. }
  1000. public static Itering<org.w3c.dom.Node> iterable(final org.w3c.dom.NamedNodeMap nm) {
  1001. return new IndexedItering<org.w3c.dom.Node>(nm.getLength()) {
  1002. public org.w3c.dom.Node next() {
  1003. return nm.item(index++);
  1004. }
  1005. };
  1006. }
  1007. public static Itering<org.w3c.dom.Node> iterable(final org.w3c.dom.Node n) {
  1008. return iterable(n.getChildNodes());
  1009. }
  1010. /** Convenience class: provides a single element iterator. Is also an iterable, returning self.
  1011. * Each request for an iterable resets us to be iterated again (not threadsafe) */
  1012. private static final class SingletonIterable<T> implements Itering<T> {
  1013. private final T object;
  1014. private boolean done;
  1015. public SingletonIterable(T o) {
  1016. object = o;
  1017. }
  1018. public boolean hasNext() { return !done; }
  1019. public T next() { if (done) throw new NoSuchElementException(); done = true; return object; }
  1020. public void remove() { throw new UnsupportedOperationException(); }
  1021. public Iterator<T> iterator() { done = false; return this; }
  1022. public String toString() { return "[" + object + "]"; }
  1023. };
  1024. public static <T> List<T> asList(T[] array) {
  1025. return new ExposedArrayList(array);
  1026. }
  1027. public static <T> T[] toArray(Collection<? extends T> bag, Class<T> clazz) {
  1028. // return (T[]) bag.toArray(); class cast exception
  1029. // can't create a new instance of the array w/out a known type -- unreliable to pull from collection (and could be empty)
  1030. //return bag.toArray((T[])java.lang.reflect.Array.newInstance(?.getClass(), bag.size()));
  1031. return bag.toArray((T[])java.lang.reflect.Array.newInstance(clazz, bag.size()));
  1032. }
  1033. /**
  1034. * Identical to Arrays.asList, except that toArray() returns the internal array,
  1035. * which allows for Collection.addAll(ExposedArrayList) to be used w/out triggering an array clone
  1036. */
  1037. private static final class ExposedArrayList<E> extends AbstractList<E>
  1038. implements RandomAccess
  1039. {
  1040. private final Object[] a;
  1041. ExposedArrayList(E[] array) {
  1042. if (array == null) throw new NullPointerException();
  1043. a = array;
  1044. }
  1045. @Override
  1046. public int size() { return a.length; }
  1047. /** returns the internal array -- allows for Collection.addAll(ExposedArrayList) to be called w/out triggering a clone */
  1048. @Override
  1049. public Object[] toArray() { return a; }
  1050. @Override
  1051. public E get(int index) { return (E)a[index]; }
  1052. @Override
  1053. public E set(int index, E element) {
  1054. Object oldValue = a[index];
  1055. a[index] = element;
  1056. return (E)oldValue;
  1057. }
  1058. @Override
  1059. public int indexOf(Object o) {
  1060. if (o==null) {
  1061. for (int i=0; i<a.length; i++)
  1062. if (a[i]==null)
  1063. return i;
  1064. } else {
  1065. for (int i=0; i<a.length; i++)
  1066. if (o.equals(a[i]))
  1067. return i;
  1068. }
  1069. return -1;
  1070. }
  1071. @Override
  1072. public boolean contains(Object o) {
  1073. return indexOf(o) != -1;
  1074. }
  1075. }
  1076. private static final class SkipNullsArrayList<T> extends ArrayList<T> {
  1077. SkipNullsArrayList() {}
  1078. @Override
  1079. public boolean add(T c) {
  1080. if (c == null) {
  1081. //Util.printStackTrace("null not allowed");
  1082. } else {
  1083. super.add(c);
  1084. }
  1085. return true;
  1086. }
  1087. }
  1088. public stat

Large files files are truncated, but you can click here to view the full file