PageRenderTime 58ms CodeModel.GetById 31ms RepoModel.GetById 1ms app.codeStats 0ms

/trunk/org.mwc.cmap.legacy/src/MWC/GUI/S57/support/BinaryFile.java

https://bitbucket.org/haris_peco/debrief
Java | 739 lines | 316 code | 83 blank | 340 comment | 75 complexity | 7ed56c666786527e77057cdccbae11e0 MD5 | raw file
  1. // **********************************************************************
  2. //
  3. // <copyright>
  4. //
  5. // BBN Technologies
  6. // 10 Moulton Street
  7. // Cambridge, MA 02138
  8. // (617) 873-8000
  9. //
  10. // Copyright (C) BBNT Solutions LLC. All rights reserved.
  11. //
  12. // </copyright>
  13. // **********************************************************************
  14. //
  15. // $Source: i:/mwc/coag/asset/cvsroot/util/MWC/GUI/S57/support/BinaryFile.java,v $
  16. // $RCSfile: BinaryFile.java,v $
  17. // $Revision: 1.1 $
  18. // $Date: 2007/04/27 09:20:00 $
  19. // $Author: ian.mayo $
  20. //
  21. // **********************************************************************
  22. package MWC.GUI.S57.support;
  23. import java.io.EOFException;
  24. import java.io.File;
  25. import java.io.FileNotFoundException;
  26. import java.io.IOException;
  27. import java.io.InputStream;
  28. import java.lang.ref.WeakReference;
  29. import java.net.URL;
  30. import java.net.URLClassLoader;
  31. import java.util.Vector;
  32. import com.bbn.openmap.Environment;
  33. import com.bbn.openmap.MoreMath;
  34. import com.bbn.openmap.util.Debug;
  35. /**
  36. * The BinaryFile is the standard object used to access data files. It
  37. * acts like a RandomAccessFile, but will work on jar file contents
  38. * and URLs, too. The source of the data is isolated through the
  39. * InputReader interface.
  40. */
  41. public class BinaryFile {
  42. private InputReader inputReader = null;
  43. /**
  44. * The byte order of the underlying file. (<code>true</code>==
  45. * MSB-First == big-endian)
  46. */
  47. protected boolean MSBFirst = false;
  48. /**
  49. * Constructs a new BinaryFile with the specified file as the
  50. * input. The default byte-order is LSB first. Reads start at the
  51. * first byte of the file.
  52. *
  53. * @param f the file to be opened for reading
  54. * @exception IOException pass-through errors from opening a
  55. * RandomAccessFile with f
  56. * @see java.io.RandomAccessFile
  57. */
  58. public BinaryFile(final File f) throws IOException {
  59. inputReader = new FileInputReader(f);
  60. }
  61. /**
  62. * Constructs a new BinaryFile with the specified file as the
  63. * input. The byte-order is undefined. Reads start at the first
  64. * byte of the file. This constructor looks for the file with the
  65. * string given, and will call the correct constructor as
  66. * appropriate. If the string represents a file available locally,
  67. * then the BinaryFile will be accessed with a FileInputReader
  68. * using a RandomAccessFile. If it's only available as a resource,
  69. * then a StreamInputReader will be used. The name should be a
  70. * path to a file, or the name of a resource that can be found in
  71. * the classpath, or a URL.
  72. *
  73. * @param name the name of the file to be opened for reading
  74. * @exception IOException pass-through errors from opening the
  75. * file.
  76. */
  77. public BinaryFile(final String name) throws IOException {
  78. boolean showDebug = false;
  79. if (Debug.debugging("binaryfile")) {
  80. showDebug = true;
  81. }
  82. if (showDebug) {
  83. Debug.output("BinaryFile: trying to figure out how to handle "
  84. + name);
  85. }
  86. try {
  87. File file = null;
  88. URL url = null;
  89. if (!Environment.isApplet()) {
  90. file = new File(name);
  91. }
  92. if (file != null && file.exists()) {
  93. // If the string represents a file, then we want to
  94. // use the RandomAccessFile aspect of the BinaryFile.
  95. setInputReader(new FileInputReader(file));
  96. } else {
  97. // url = ClassLoader.getSystemResource(name);
  98. url = Thread.currentThread()
  99. .getContextClassLoader()
  100. .getResource(name);
  101. // OK, now we want to look around for the file, in the
  102. // classpaths, and as a resource. It may be a file in
  103. // a classpath, available for direct access.
  104. if (url != null) {
  105. final String newname = url.getFile();
  106. if (showDebug) {
  107. Debug.output("BinaryFile: looking for " + newname);
  108. }
  109. if (!Environment.isApplet()) {
  110. file = new File(newname);
  111. }
  112. if (file != null && file.exists()) {
  113. // It's still a file, available directly.
  114. // Access it with the RandomAccessFile
  115. setInputReader(new FileInputReader(file));
  116. } else {
  117. // Need to get it as a resource. Needs
  118. // special handling if it's coming in a jar
  119. // file. Jar file references have a "!" in
  120. // them
  121. if (!setJarInputReader(newname)) {
  122. if (showDebug) {
  123. Debug.output(" trying as url: " + url);
  124. }
  125. setInputReader(new URLInputReader(url));
  126. }
  127. }
  128. } else if (Environment.isApplet()) {
  129. if (showDebug) {
  130. Debug.output(" As applet, checking codebase...");
  131. }
  132. // Look in the codebase for applets...
  133. final URL[] cba = new URL[1];
  134. cba[0] = Environment.getApplet().getCodeBase();
  135. final URLClassLoader ucl = URLClassLoader.newInstance(cba);
  136. url = ucl.getResource(name);
  137. if (url != null) {
  138. setInputReader(new URLInputReader(url));
  139. }
  140. }
  141. // It's not in the classpath, so try it as a URL.
  142. if (inputReader == null) {
  143. if (showDebug) {
  144. Debug.output(" lastly, trying as URL: " + name);
  145. }
  146. try {
  147. setInputReader(new URLInputReader(new URL(name)));
  148. } catch (final java.security.AccessControlException ace) {
  149. Debug.output("BinaryFile: " + name
  150. + " couldn't be accessed.");
  151. throw new IOException("AccessControlException trying to fetch "
  152. + name + " as a URL");
  153. }
  154. }
  155. }
  156. if (inputReader == null) {
  157. throw new FileNotFoundException("BinaryFile can't find: "
  158. + name);
  159. }
  160. } catch (final IOException ioe) {
  161. throw ioe;
  162. }
  163. }
  164. /**
  165. * Takes a name of a file, and checks to see if it reflects an
  166. * entry in a jar file. (Check the filename and see if it looks
  167. * like "jarfile!jarfileentry".) If it is, it separates the path,
  168. * and set the inputReader to a JarInputReader and returns true.
  169. * If not, it returns false.
  170. */
  171. protected boolean setJarInputReader(final String name) throws IOException {
  172. try {
  173. final int index = name.indexOf("!");
  174. if (index != -1) {
  175. // Used to be this, modified by Erik Sanders to work
  176. // with jdk 1.4 plugin
  177. // String jarFileName =
  178. // name.substring(name.indexOf(":") + 1, index);
  179. // changed to this...
  180. String jarFileName;
  181. if (name.startsWith("file:")) {
  182. // java-plugin 1.3 returns local file: strip file:
  183. // from string
  184. jarFileName = name.substring(name.indexOf(":") + 1, index);
  185. } else {
  186. // java-plugin 1.4 returns reference to server, so
  187. // leave http:// part
  188. // Used to start the substring from 1, but changed
  189. // to 0 thanks to DGK
  190. jarFileName = name.substring(0, index);
  191. }
  192. // skip !/ "
  193. final String jarEntryName = name.substring(index + 2);
  194. if (Debug.debugging("binaryfile")) {
  195. Debug.output(" got: \n" + jarFileName + "\n" + jarEntryName);
  196. }
  197. // If the jar doesn't exist, should return something
  198. // that indicates this. Should check the performance
  199. // impllications of this call, though, at some point.
  200. // DGK added
  201. final File f = new File(jarFileName);
  202. if (f.exists() == false) {
  203. return false;
  204. }
  205. setInputReader(new JarInputReader(jarFileName, jarEntryName));
  206. return true;
  207. }
  208. } catch (final java.security.AccessControlException ace) {
  209. if (Debug.debugging("binaryfile")) {
  210. Debug.output("BinaryFile.setJarInputFile: AccessControlException for "
  211. + name);
  212. }
  213. }
  214. return false;
  215. }
  216. /**
  217. * A simple test method to determine if a file or directory,
  218. * represented by a string, can be found by the current Java
  219. * environment. Uses the same tests as BinaryFile constructor for
  220. * tracking down a file.
  221. *
  222. * @param name A path to a file, a URL, or a path to a jar file
  223. * entry.
  224. */
  225. public static boolean exists(final String name) {
  226. boolean exists = false;
  227. try {
  228. File file = null;
  229. URL url = null;
  230. if (!Environment.isApplet()) {
  231. file = new File(name);
  232. }
  233. if (file != null && file.exists()) {
  234. exists = true;
  235. } else {
  236. // url = ClassLoader.getSystemResource(name);
  237. url = Thread.currentThread()
  238. .getContextClassLoader()
  239. .getResource(name);
  240. // OK, now we want to look around for the file, in the
  241. // classpaths, and as a resource. It may be a file in
  242. // a classpath, available for direct access.
  243. if (url != null) {
  244. exists = true;
  245. } else if (Environment.isApplet()) {
  246. if (Debug.debugging("binaryfile")) {
  247. Debug.output(" As applet, checking codebase...");
  248. }
  249. // Look in the codebase for applets...
  250. final URL[] cba = new URL[1];
  251. cba[0] = Environment.getApplet().getCodeBase();
  252. final URLClassLoader ucl = URLClassLoader.newInstance(cba);
  253. if (ucl.getResource(name) != null) {
  254. exists = true;
  255. // This has been commented out because the
  256. // AppletDataNugget has been deprecated, and
  257. // is not needed.
  258. // } else {
  259. // url = AppletDataNugget.findResource(name);
  260. // if (url != null) {
  261. // exists = true;
  262. // }
  263. }
  264. }
  265. // It's not in the classpath, so try it as a URL to a
  266. // webserver.
  267. if (!exists && name.indexOf("http:") != -1) {
  268. try {
  269. final InputStream stream = new URL(name).openStream();
  270. stream.close();
  271. exists = true;
  272. } catch (final java.security.AccessControlException ace) {
  273. exists = false;
  274. }
  275. }
  276. }
  277. } catch (final IOException ioe) {
  278. Debug.message("binaryfile",
  279. "BinaryFile.exists() caught IOException");
  280. exists = false;
  281. }
  282. if (Debug.debugging("binaryfile")) {
  283. Debug.output("BinaryFile.exists(" + name + ") = " + exists);
  284. }
  285. return exists;
  286. }
  287. /**
  288. * Get the source name from the input reader.
  289. */
  290. public String getName() {
  291. if (inputReader != null) {
  292. return inputReader.getName();
  293. }
  294. return null;
  295. }
  296. /**
  297. * Get the inputReader used for accessing the file, for quering
  298. * purposes. Don't use it to get data, or the file pointers may
  299. * get messed up.
  300. */
  301. public InputReader getInputReader() {
  302. return inputReader;
  303. }
  304. /**
  305. * Set the input reader used by the BinaryFile. Make sure it's
  306. * intialized properly.
  307. */
  308. public void setInputReader(final InputReader reader) {
  309. if (Debug.debugging("binaryfile")) {
  310. Debug.output("Setting inputReader");
  311. }
  312. inputReader = reader;
  313. }
  314. /**
  315. * Set the byte-ordering used to read shorts, int, etc.
  316. *
  317. * @param msbfirst <code>true</code>= MSB first,
  318. * <code>false</code>= LSB first
  319. */
  320. public void byteOrder(final boolean msbfirst) {
  321. MSBFirst = msbfirst;
  322. }
  323. /**
  324. * Accessor for the byte ordering used to read multibyte types.
  325. *
  326. * @return byte ordering
  327. */
  328. public boolean byteOrder() {
  329. return MSBFirst;
  330. }
  331. /**
  332. * Skip over n bytes in the input file
  333. *
  334. * @param n the number of bytes to skip
  335. * @return the actual number of bytes skipped. annoying, isn't it?
  336. * @exception IOException Any IO errors that occur in skipping
  337. * bytes in the underlying file
  338. */
  339. public long skipBytes(final long n) throws IOException {
  340. return inputReader.skipBytes(n);
  341. }
  342. /**
  343. * Get the index of the next character to be read
  344. *
  345. * @return the index
  346. * @exception IOException Any IO errors that occur in accessing
  347. * the underlying file
  348. */
  349. public long getFilePointer() throws IOException {
  350. return inputReader.getFilePointer();
  351. }
  352. /**
  353. * Set the index of the next character to be read.
  354. *
  355. * @param pos the position to seek to.
  356. * @exception IOException Any IO Errors that occur in seeking the
  357. * underlying file.
  358. */
  359. public void seek(final long pos) throws IOException {
  360. inputReader.seek(pos);
  361. }
  362. /**
  363. * The length of the InputReader source.
  364. */
  365. public long length() throws IOException {
  366. return inputReader.length();
  367. }
  368. /**
  369. * Return how many bytes left to be read in the file.
  370. *
  371. * @return the number of bytes remaining to be read (counted in
  372. * bytes)
  373. * @exception IOException Any IO errors encountered in accessing
  374. * the file
  375. */
  376. public long available() throws IOException {
  377. return inputReader.available();
  378. }
  379. /**
  380. * Closes the underlying file
  381. *
  382. * @exception IOException Any IO errors encountered in accessing
  383. * the file
  384. */
  385. public void close() throws IOException {
  386. if (inputReader != null) {
  387. inputReader.close();
  388. }
  389. inputReader = null;
  390. }
  391. /**
  392. * Read from the file.
  393. *
  394. * @return one byte from the file. -1 for EOF
  395. * @exception IOException Any IO errors encountered in reading
  396. * from the file
  397. */
  398. public int read() throws IOException {
  399. return inputReader.read();
  400. }
  401. /**
  402. * Read from the file
  403. *
  404. * @param b The byte array to read into
  405. * @param off the first array position to read into
  406. * @param len the number of bytes to read
  407. * @return the number of bytes read
  408. * @exception IOException Any IO errors encountered in reading
  409. * from the file
  410. */
  411. public int read(final byte b[], final int off, final int len) throws IOException {
  412. return inputReader.read(b, off, len);
  413. }
  414. /**
  415. * Read from the file.
  416. *
  417. * @param b the byte array to read into. Equivelent to
  418. * <code>read(b, 0, b.length)</code>
  419. * @return the number of bytes read
  420. * @exception IOException Any IO errors encountered in reading
  421. * from the file
  422. * @see java.io.RandomAccessFile#read(byte[])
  423. */
  424. public int read(final byte b[]) throws IOException {
  425. return inputReader.read(b);
  426. }
  427. /**
  428. * Read from the file.
  429. *
  430. * @param howmany the number of bytes to read
  431. * @param allowless if we can return fewer bytes than requested
  432. * @return the array of bytes read.
  433. * @exception FormatException Any IO Exceptions, plus an
  434. * end-of-file encountered after reading some, but now
  435. * enough, bytes when allowless was <code>false</code>
  436. * @exception EOFException Encountered an end-of-file while
  437. * allowless was <code>false</code>, but NO bytes
  438. * had been read.
  439. */
  440. public byte[] readBytes(final int howmany, final boolean allowless)
  441. throws EOFException, FormatException {
  442. return inputReader.readBytes(howmany, allowless);
  443. }
  444. /**
  445. * Reads and returns a single byte, cast to a char
  446. *
  447. * @return the byte read from the file, cast to a char
  448. * @exception EOFException the end-of-file has been reached, so no
  449. * chars where available
  450. * @exception FormatException a rethrown IOException
  451. */
  452. public char readChar() throws EOFException, FormatException {
  453. try {
  454. final int retv = inputReader.read();
  455. if (retv == -1) {
  456. throw new EOFException("Error in ReadChar, EOF reached");
  457. }
  458. return (char) retv;
  459. } catch (final IOException i) {
  460. throw new FormatException("readChar IOException: " + i.getMessage());
  461. }
  462. }
  463. /**
  464. * Reads and returns a short.
  465. *
  466. * @return the 2 bytes merged into a short, according to the
  467. * current byte ordering
  468. * @exception EOFException there were less than 2 bytes left in
  469. * the file
  470. * @exception FormatException rethrow of IOExceptions encountered
  471. * while reading the bytes for the short
  472. * @see #read(byte[])
  473. */
  474. public short readShort() throws EOFException, FormatException {
  475. // MSBFirst must be set when we are called
  476. return MoreMath.BuildShort(readBytes(2, false), MSBFirst);
  477. }
  478. /**
  479. * Reads and returns a long
  480. *
  481. * @return the 4 bytes merged into a long, according to the
  482. * current byte ordering
  483. * @exception EOFException there were less than 4 bytes left in
  484. * the file
  485. * @exception FormatException rethrow of IOExceptions encountered
  486. * while reading the bytes for the integer
  487. * @see #read(byte[])
  488. */
  489. public int readInteger() throws EOFException, FormatException {
  490. // MSBFirst must be set when we are called
  491. return MoreMath.BuildInteger(readBytes(4, false), MSBFirst);
  492. }
  493. public void readIntegerArray(final int vec[], final int offset, final int len)
  494. throws EOFException, FormatException {
  495. int idx = offset;
  496. for (int i = 0; i < len; i++) {
  497. vec[idx++] = readInteger();
  498. }
  499. }
  500. /**
  501. * Reads and returns a long
  502. *
  503. * @return the 8 bytes merged into a long, according to the
  504. * current byte ordering
  505. * @exception EOFException there were less than 8 bytes left in
  506. * the file
  507. * @exception FormatException rethrow of IOExceptions encountered
  508. * while reading the bytes for the long
  509. * @see #read(byte[])
  510. */
  511. public long readLong() throws EOFException, FormatException {
  512. return MoreMath.BuildLong(readBytes(8, false), MSBFirst);
  513. }
  514. /**
  515. * Reads and returns a float
  516. *
  517. * @return the 4 bytes merged into a float, according to the
  518. * current byte ordering
  519. * @exception EOFException there were less than 4 bytes left in
  520. * the file
  521. * @exception FormatException rethrow of IOExceptions encountered
  522. * while reading the bytes for the float
  523. * @see #read(byte[])
  524. */
  525. public float readFloat() throws EOFException, FormatException {
  526. return Float.intBitsToFloat(readInteger());
  527. }
  528. public void readFloatArray(final float vec[], final int offset, final int len)
  529. throws EOFException, FormatException {
  530. int idx = offset;
  531. for (int i = 0; i < len; i++) {
  532. vec[idx++] = readFloat();
  533. }
  534. }
  535. /**
  536. * Reads and returns a double
  537. *
  538. * @return the 8 bytes merged into a double, according to the
  539. * current byte ordering
  540. * @exception EOFException there were less than 8 bytes left in
  541. * the file
  542. * @exception FormatException rethrow of IOExceptions encountered
  543. * while reading the bytes for the short
  544. * @see #read(byte[])
  545. */
  546. public double readDouble() throws EOFException, FormatException {
  547. return Double.longBitsToDouble(readLong());
  548. }
  549. /**
  550. * Reads <code>length</code> bytes and returns a string composed
  551. * of the bytes cast to chars
  552. *
  553. * @param length the number of bytes to read into the string
  554. * @return the composed string
  555. * @exception EOFException there were less than
  556. * <code>length</code> bytes left in the file
  557. * @exception FormatException rethrow of IOExceptions encountered
  558. * while reading the bytes for the short
  559. */
  560. public String readFixedLengthString(final int length) throws EOFException,
  561. FormatException {
  562. final byte foo[] = readBytes(length, false);
  563. return new String(foo, 0, length);
  564. }
  565. /**
  566. * Read a bytes and throw an InvalidCharException if it doesn't
  567. * match <code>expected</code>
  568. *
  569. * @param expected what the next char is claimed to be
  570. * @exception EOFException there wasn't a byte, so we can't check
  571. * for a match
  572. * @exception InvalidCharException throws when the character read
  573. * doesn't match <code>expected</code> The .c member
  574. * of the thrown exception is the actual char read
  575. * @exception FormatException some other error from reading the
  576. * file
  577. */
  578. public void assertChar(final char expected) throws EOFException, FormatException {
  579. final char c = readChar();
  580. if (c != expected) {
  581. throw new InvalidCharException("AssertChar: expected " + expected
  582. + " got " + c, c);
  583. }
  584. }
  585. /**
  586. * Reads a string until the specified delimiter or EOF is
  587. * encountered
  588. *
  589. * @param delim the end-of-string delimiter
  590. * @return the string that was read
  591. * @exception FormatException rethrow of IOExceptions from the
  592. * read methods
  593. */
  594. public String readToDelimiter(final char delim) throws FormatException {
  595. final StringBuffer buildretval = new StringBuffer();
  596. char tmp;
  597. try {
  598. while ((tmp = readChar()) != delim)
  599. buildretval.append(tmp);
  600. } catch (final EOFException e) {
  601. // allowable
  602. } catch (final FormatException fe) {
  603. if (buildretval.length() == 0) {
  604. throw fe;
  605. }
  606. }
  607. return buildretval.toString();
  608. }
  609. /**
  610. * Makes sure that the file has been closed.
  611. *
  612. * @exception Throwable what it throws.
  613. */
  614. public void finalize() throws Throwable {
  615. close();
  616. }
  617. /**
  618. * Maintains a list of objects that can be closed so that other
  619. * files can be opened.
  620. */
  621. private static Vector<WeakReference<Closable>> closableList = new Vector<WeakReference<Closable>>();
  622. /**
  623. * Add an object that can be closed if needed. Duplicates are
  624. * allowed. Only holds a WeakReference, so that the object can
  625. * still be garbage-collected.
  626. *
  627. * @param it the object that can be closed
  628. */
  629. public static synchronized void addClosable(final Closable it) {
  630. closableList.add(new WeakReference<Closable>(it));
  631. }
  632. /**
  633. * Remove an object from the closable list.
  634. *
  635. * @param it the object to remove
  636. */
  637. public static synchronized void removeClosable(final Closable it) {
  638. for (int i = 0; i < closableList.size(); i++) {
  639. final Object o = ((WeakReference<Closable>) closableList.elementAt(i)).get();
  640. if ((o == it) || (o == null)) {
  641. closableList.removeElementAt(i);
  642. i--; // in case its in the list more than once
  643. }
  644. }
  645. }
  646. public static synchronized void closeClosable() {
  647. System.out.println("closeClosable " + closableList.size());
  648. for (int i = 0; i < closableList.size(); i++) {
  649. final Closable c = (Closable) (closableList.elementAt(i)).get();
  650. if ((c == null) || !c.close(false)) {
  651. closableList.removeElementAt(i);
  652. i--;
  653. }
  654. }
  655. }
  656. }