SegmentedSeekableStream.java
上传用户:btjssb159
上传日期:2018-01-04
资源大小:241k
文件大小:17k
源码类别:

DNA

开发平台:

Java

  1. /*
  2.  * Copyright (c) 2001 Sun Microsystems, Inc. All Rights Reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without 
  5.  * modification, are permitted provided that the following conditions are met:
  6.  * 
  7.  * -Redistributions of source code must retain the above copyright notice, this 
  8.  * list of conditions and the following disclaimer.
  9.  *
  10.  * -Redistribution in binary form must reproduct the above copyright notice,
  11.  * this list of conditions and the following disclaimer in the documentation
  12.  * and/or other materials provided with the distribution.
  13.  * 
  14.  * Neither the name of Sun Microsystems, Inc. or the names of contributors may
  15.  * be used to endorse or promote products derived from this software without
  16.  * specific prior written permission.
  17.  * 
  18.  * This software is provided "AS IS," without a warranty of any kind. ALL
  19.  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
  20.  * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
  21.  * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
  22.  * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
  23.  * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
  24.  * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
  25.  * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
  26.  * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
  27.  * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
  28.  * POSSIBILITY OF SUCH DAMAGES.
  29.  * 
  30.  * You acknowledge that Software is not designed,licensed or intended for use in 
  31.  * the design, construction, operation or maintenance of any nuclear facility.
  32.  */
  33. import java.io.IOException;
  34. /**
  35.  * An implementation of the <code>StreamSegmentMapper</code> interface
  36.  * that requires an explicit list of the starting locations and
  37.  * lengths of the source segments.
  38.  */
  39. class StreamSegmentMapperImpl implements StreamSegmentMapper {
  40.     private long[] segmentPositions;
  41.     private int[] segmentLengths;
  42.     public StreamSegmentMapperImpl(long[] segmentPositions,
  43.                                    int[] segmentLengths) {
  44.         this.segmentPositions = (long[])segmentPositions.clone();
  45.         this.segmentLengths = (int[])segmentLengths.clone();
  46.     }
  47.     public StreamSegment getStreamSegment(long position, int length) {
  48.         int numSegments = segmentLengths.length;
  49.         for (int i = 0; i < numSegments; i++) {
  50.             int len = segmentLengths[i];
  51.             if (position < len) {
  52.                 return new StreamSegment(segmentPositions[i] + position,
  53.                                          Math.min(len - (int)position,
  54.                                                   length));
  55.             }
  56.             position -= len;
  57.         }
  58.         return null;
  59.     }
  60.     public void getStreamSegment(long position, int length,
  61.                                  StreamSegment seg) {
  62.         int numSegments = segmentLengths.length;
  63.         for (int i = 0; i < numSegments; i++) {
  64.             int len = segmentLengths[i];
  65.             if (position < len) {
  66.                 seg.setStartPos(segmentPositions[i] + position);
  67.                 seg.setSegmentLength(Math.min(len - (int)position, length));
  68.                 return;
  69.             }
  70.             position -= len;
  71.         }
  72.         seg.setStartPos(-1);
  73.         seg.setSegmentLength(-1);
  74.         return;
  75.     }
  76. }
  77. /**
  78.  * An implementation of the <code>StreamSegmentMapper</code> interface
  79.  * for segments of equal length.
  80.  */
  81. class SectorStreamSegmentMapper implements StreamSegmentMapper {
  82.     long[] segmentPositions;
  83.     int segmentLength;
  84.     int totalLength;
  85.     int lastSegmentLength;
  86.     public SectorStreamSegmentMapper(long[] segmentPositions,
  87.                                      int segmentLength,
  88.                                      int totalLength) {
  89.         this.segmentPositions = (long[])segmentPositions.clone();
  90.         this.segmentLength = segmentLength;
  91.         this.totalLength = totalLength;
  92.         this.lastSegmentLength = totalLength -
  93.             (segmentPositions.length - 1)*segmentLength;
  94.     }
  95.     public StreamSegment getStreamSegment(long position, int length) {
  96.         int index = (int) (position/segmentLength);
  97.         // Compute segment length
  98.         int len = (index == segmentPositions.length - 1) ?
  99.             lastSegmentLength : segmentLength;
  100.         // Compute position within the segment
  101.         position -= index*segmentLength;
  102.         // Compute maximum legal length
  103.         len -= position;
  104.         if (len > length) {
  105.             len = length;
  106.         }
  107.         return new StreamSegment(segmentPositions[index] + position, len);
  108.     }
  109.     public void getStreamSegment(long position, int length,
  110.                                  StreamSegment seg) {
  111.         int index = (int) (position/segmentLength);
  112.         // Compute segment length
  113.         int len = (index == segmentPositions.length - 1) ?
  114.             lastSegmentLength : segmentLength;
  115.         // Compute position within the segment
  116.         position -= index*segmentLength;
  117.         // Compute maximum legal length
  118.         len -= position;
  119.         if (len > length) {
  120.             len = length;
  121.         }
  122.         seg.setStartPos(segmentPositions[index] + position);
  123.         seg.setSegmentLength(len);
  124.     }
  125. }
  126. /**
  127.  * A <code>SegmentedSeekableStream</code> provides a view of a
  128.  * subset of another <code>SeekableStream</code> consiting of a series
  129.  * of segments with given starting positions in the source stream and
  130.  * lengths.  The resulting stream behaves like an ordinary
  131.  * <code>SeekableStream</code>.
  132.  *
  133.  * <p> For example, given a <code>SeekableStream</code> containing
  134.  * data in a format consisting of a number of sub-streams stored in
  135.  * non-contiguous sectors indexed by a directory, it is possible to
  136.  * construct a set of <code>SegmentedSeekableStream</code>s, one for
  137.  * each sub-stream, that each provide a view of the sectors comprising
  138.  * a particular stream by providing the positions and lengths of the
  139.  * stream's sectors as indicated by the directory.  The complex
  140.  * multi-stream structure of the original stream may be ignored by
  141.  * users of the <code>SegmentedSeekableStream</code>, who see a
  142.  * separate <code>SeekableStream</code> for each sub-stream and do not
  143.  * need to understand the directory structure at all.
  144.  *
  145.  * <p> For further efficiency, a directory structure such as in the
  146.  * example described above need not be fully parsed in order to build
  147.  * a <code>SegmentedSeekableStream</code>.  Instead, the
  148.  * <code>StreamSegmentMapper</code> interface allows the association
  149.  * between a desired region of the output and an input segment to be
  150.  * provided dynamically.  This mapping might be computed by reading
  151.  * from a directory in piecemeal fashion in order to avoid consuming
  152.  * memory resources.
  153.  *
  154.  * <p> It is the responsibility of the user of this class to determine
  155.  * whether backwards seeking should be enabled.  If the source stream
  156.  * supports only forward seeking, backwards seeking must be disabled
  157.  * and the <code>StreamSegmentMapper</code> must be monotone; that is,
  158.  * forward motion in the destination must always result in forward
  159.  * motion within the source.  If the source stream supports backwards
  160.  * seeking, there are no restrictions on the
  161.  * <code>StreamSegmentMapper</code> and backwards seeking may always
  162.  * be enabled for the <code>SegmentedSeekableStream</code>.
  163.  *
  164.  * <p><b> This class is not a committed part of the JAI API.  It may
  165.  * be removed or changed in future releases of JAI.</b>
  166.  */
  167. public class SegmentedSeekableStream extends SeekableStream {
  168.     private SeekableStream stream;
  169.     private StreamSegmentMapper mapper;
  170.     private long pointer = 0;
  171.     private boolean canSeekBackwards;
  172.     
  173.     /**
  174.      * Constructs a <code>SegmentedSeekableStream</code>
  175.      * given a <code>SeekableStream</code> as input,
  176.      * an instance of <code>StreamSegmentMapper</code>,
  177.      * and a <code>boolean</code> indicating whether the
  178.      * output <code>SegmentedSeekableStream</code> should
  179.      * support seeking backwards.  If <code>canSeekBackwards</code>
  180.      * is <code>true</code>, the source stream must itself
  181.      * support seeking backwards.
  182.      *
  183.      * @param stream A source <code>SeekableStream</code>
  184.      * @param mapper An instance of the <code>StreamSegmentMapper</code>
  185.      *        interface.
  186.      * @param canSeekBackwards <code>true</code> if the ability to
  187.      *        seek backwards is desired.   
  188.      */
  189.     public SegmentedSeekableStream(SeekableStream stream,
  190.                                    StreamSegmentMapper mapper,
  191.                                    boolean canSeekBackwards) {
  192.         this.stream = stream;
  193.         this.mapper = mapper;
  194.         this.canSeekBackwards = canSeekBackwards;
  195.  
  196.         if (canSeekBackwards && !stream.canSeekBackwards()) {
  197.             throw new IllegalArgumentException(JaiI18N.getString("SegmentedSeekableStream0"));
  198.         }
  199.     }
  200.     /**
  201.      * Constructs a <code>SegmentedSeekableStream</code> given a
  202.      * <code>SeekableStream</code> as input, a list of the starting
  203.      * positions and lengths of the segments of the source stream, and
  204.      * a <code>boolean</code> indicating whether the output
  205.      * <code>SegmentedSeekableStream</code> should support seeking
  206.      * backwards.  If <code>canSeekBakckwards</code> is
  207.      * <code>true</code>, the source stream must itself support
  208.      * seeking backwards.
  209.      *
  210.      * @param stream A source <code>SeekableStream</code>
  211.      * @param segmentPositions An array of <code>long</code>s 
  212.      *        giving the starting positions of the segments in the
  213.      *        source stream.
  214.      * @param segmentLengths  An array of <code>int</code>s 
  215.      *        giving the lengths of segments in the source stream.
  216.      * @param canSeekBackwards <code>true</code> if the ability to
  217.      *        seek backwards is desired.
  218.      */
  219.     public SegmentedSeekableStream(SeekableStream stream,
  220.                                    long[] segmentPositions,
  221.                                    int[] segmentLengths,
  222.                                    boolean canSeekBackwards) {
  223.         this(stream,
  224.             new StreamSegmentMapperImpl(segmentPositions, segmentLengths),
  225.             canSeekBackwards);
  226.     }
  227.     /**
  228.      * Constructs a <code>SegmentedSeekableStream</code> given a
  229.      * <code>SeekableStream</code> as input, a list of the starting
  230.      * positions of the segments of the source stream, the common
  231.      * length of each segment, the total length of the segments and
  232.      * a <code>boolean</code> indicating whether the output
  233.      * <code>SegmentedSeekableStream</code> should support seeking
  234.      * backwards.  If <code>canSeekBakckwards</code> is
  235.      * <code>true</code>, the source stream must itself support
  236.      * seeking backwards.
  237.      *
  238.      * <p> This constructor is useful for selecting substreams
  239.      *     of sector-oriented file formats in which each segment
  240.      *     of the substream (except possibly the final segment)
  241.      *     occupies a fixed-length sector.
  242.      *
  243.      * @param stream A source <code>SeekableStream</code>
  244.      * @param segmentPositions An array of <code>long</code>s 
  245.      *        giving the starting positions of the segments in the
  246.      *        source stream.
  247.      * @param segmentLength  The common length of each segment.
  248.      * @param totalLength  The total length of the source segments.
  249.      * @param canSeekBackwards <code>true</code> if the ability to
  250.      *        seek backwards is desired.
  251.      */
  252.     public SegmentedSeekableStream(SeekableStream stream,
  253.                                    long[] segmentPositions,
  254.                                    int segmentLength,
  255.                                    int totalLength,
  256.                                    boolean canSeekBackwards) {
  257.         this(stream,
  258.              new SectorStreamSegmentMapper(segmentPositions,
  259.                                            segmentLength,
  260.                                            totalLength),
  261.              canSeekBackwards);
  262.     }
  263.     /**
  264.      * Returns the current offset in this stream.
  265.      *
  266.      * @return     the offset from the beginning of the stream, in bytes,
  267.      *             at which the next read occurs.
  268.      */
  269.     public long getFilePointer() {
  270.         return (long)pointer;
  271.     }
  272.     /**
  273.      * Returns <code>true</code> if seeking backwards is supported.
  274.      * Support is determined by the value of the
  275.      * <code>canSeekBackwards</code> parameter at construction time.
  276.      */
  277.     public boolean canSeekBackwards() {
  278.         return canSeekBackwards;
  279.     }
  280.     /**
  281.      * Sets the offset, measured from the beginning of this 
  282.      * stream, at which the next read occurs.
  283.      *
  284.      * <p> If <code>canSeekBackwards()</code> returns <code>false</code>,
  285.      * then setting <code>pos</code> to an offset smaller than
  286.      * the current value of <code>getFilePointer()</code> will have
  287.      * no effect.
  288.      *
  289.      * @param      pos   the offset position, measured in bytes from the 
  290.      *                   beginning of the stream, at which to set the stream 
  291.      *                   pointer.
  292.      * @exception  IOException  if <code>pos</code> is less than 
  293.      *                          <code>0</code> or if an I/O error occurs.
  294.      */
  295.     public void seek(long pos) throws IOException {
  296.         if (pos < 0) {
  297.             throw new IOException();
  298.         }
  299.         pointer = pos;
  300.     }
  301.     private StreamSegment streamSegment = new StreamSegment();
  302.     
  303.     /**
  304.      * Reads the next byte of data from the input stream. The value byte is
  305.      * returned as an <code>int</code> in the range <code>0</code> to
  306.      * <code>255</code>. If no byte is available because the end of the stream
  307.      * has been reached, the value <code>-1</code> is returned. This method
  308.      * blocks until input data is available, the end of the stream is detected,
  309.      * or an exception is thrown.
  310.      *
  311.      * @return     the next byte of data, or <code>-1</code> if the end of the
  312.      *             stream is reached.
  313.      * @exception  IOException  if an I/O error occurs.
  314.      */
  315.     public int read() throws IOException {
  316.         mapper.getStreamSegment(pointer, 1, streamSegment);
  317.         stream.seek(streamSegment.getStartPos());
  318.         int val = stream.read();
  319.         ++pointer;
  320.         return val;
  321.     }
  322.     /**
  323.      * Reads up to <code>len</code> bytes of data from the input stream into
  324.      * an array of bytes.  An attempt is made to read as many as
  325.      * <code>len</code> bytes, but a smaller number may be read, possibly
  326.      * zero. The number of bytes actually read is returned as an integer.
  327.      *
  328.      * <p> This method blocks until input data is available, end of stream is
  329.      * detected, or an exception is thrown.
  330.      *
  331.      * <p> If <code>b</code> is <code>null</code>, a
  332.      * <code>NullPointerException</code> is thrown.
  333.      *
  334.      * <p> If <code>off</code> is negative, or <code>len</code> is negative, or
  335.      * <code>off+len</code> is greater than the length of the array
  336.      * <code>b</code>, then an <code>IndexOutOfBoundsException</code> is
  337.      * thrown.
  338.      *
  339.      * <p> If <code>len</code> is zero, then no bytes are read and
  340.      * <code>0</code> is returned; otherwise, there is an attempt to read at
  341.      * least one byte. If no byte is available because the stream is at end of
  342.      * stream, the value <code>-1</code> is returned; otherwise, at least one
  343.      * byte is read and stored into <code>b</code>.
  344.      *
  345.      * <p> The first byte read is stored into element <code>b[off]</code>, the
  346.      * next one into <code>b[off+1]</code>, and so on. The number of bytes read
  347.      * is, at most, equal to <code>len</code>. Let <i>k</i> be the number of
  348.      * bytes actually read; these bytes will be stored in elements
  349.      * <code>b[off]</code> through <code>b[off+</code><i>k</i><code>-1]</code>,
  350.      * leaving elements <code>b[off+</code><i>k</i><code>]</code> through
  351.      * <code>b[off+len-1]</code> unaffected.
  352.      *
  353.      * <p> In every case, elements <code>b[0]</code> through
  354.      * <code>b[off]</code> and elements <code>b[off+len]</code> through
  355.      * <code>b[b.length-1]</code> are unaffected.
  356.      *
  357.      * <p> If the first byte cannot be read for any reason other than end of
  358.      * stream, then an <code>IOException</code> is thrown. In particular, an
  359.      * <code>IOException</code> is thrown if the input stream has been closed.
  360.      *
  361.      * @param      b     the buffer into which the data is read.
  362.      * @param      off   the start offset in array <code>b</code>
  363.      *                   at which the data is written.
  364.      * @param      len   the maximum number of bytes to read.
  365.      * @return     the total number of bytes read into the buffer, or
  366.      *             <code>-1</code> if there is no more data because the end of
  367.      *             the stream has been reached.
  368.      * @exception  IOException  if an I/O error occurs.
  369.      */
  370.     public int read(byte[] b, int off, int len) throws IOException {
  371.         if (b == null) {
  372.             throw new NullPointerException();
  373.         }
  374.         if ((off < 0) || (len < 0) || (off + len > b.length)) {
  375.             throw new IndexOutOfBoundsException();
  376.         }
  377.         if (len == 0) {
  378.             return 0;
  379.         }
  380.         mapper.getStreamSegment(pointer, len, streamSegment);
  381.         stream.seek(streamSegment.getStartPos());
  382.         int nbytes = stream.read(b, off, streamSegment.getSegmentLength());
  383.         pointer += nbytes;
  384.         return nbytes;
  385.     }
  386. }