Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* $Id: a73fcf3bb85b9c1a17c01e82f72463c76f9924e8 $
*
* This file is part of the iText (R) project.
* Copyright (c) 1998-2016 iText Group NV
* Authors: Kevin Day, Bruno Lowagie, et al.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3
* as published by the Free Software Foundation with the addition of the
* following permission added to Section 15 as permitted in Section 7(a):
* FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
* ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
* OF THIRD PARTY RIGHTS.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General License for more
* details. You should have received a copy of the GNU Affero General License
* along with this program; if not, see http://www.gnu.org/licenses or write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA, 02110-1301 USA, or download the license from the following URL:
* http://itextpdf.com/terms-of-use/
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General License.
*
* In accordance with Section 7(b) of the GNU Affero General License, a covered
* work must retain the producer line in every PDF that is created or
* manipulated using iText.
*
* You can be released from the requirements of the license by purchasing a
* commercial license. Buying such a license is mandatory as soon as you develop
* commercial activities involving the iText software without disclosing the
* source code of your own applications. These activities include: offering paid
* services to customers as an ASP, serving PDFs on the fly in a web
* application, shipping iText with a closed source product.
*
* For more information, please contact iText Software Corp. at this address:
* [email protected]
*/
package com.itextpdf.text.io;
import java.io.IOException;
/**
* A RandomAccessSource that is based on a set of underlying sources, treating the sources as if they were a contiguous block of data.
* @since 5.3.5
*/
class GroupedRandomAccessSource implements RandomAccessSource {
/**
* The underlying sources (along with some meta data to quickly determine where each source begins and ends)
*/
private final SourceEntry[] sources;
/**
* Cached value to make multiple reads from the same underlying source more efficient
*/
private SourceEntry currentSourceEntry;
/**
* Cached size of the underlying channel
*/
private final long size;
/**
* Constructs a new {@link GroupedRandomAccessSource} based on the specified set of sources
* @param sources the sources used to build this group
*/
public GroupedRandomAccessSource(RandomAccessSource[] sources) throws IOException {
this.sources = new SourceEntry[sources.length];
long totalSize = 0;
for(int i = 0; i < sources.length; i++){
this.sources[i] = new SourceEntry(i, sources[i], totalSize);
totalSize += sources[i].length();
}
size = totalSize;
currentSourceEntry = this.sources[sources.length-1];
sourceInUse(currentSourceEntry.source);
}
/**
* For a given offset, return the index of the source that contains the specified offset.
* This is an optimization feature to help optimize the access of the correct source without having to iterate
* through every single source each time. It is safe to always return 0, in which case the full set of sources will be searched.
* Subclasses should override this method if they are able to compute the source index more efficiently (for example {@link FileChannelRandomAccessSource} takes advantage of fixed size page buffers to compute the index)
* @param offset the offset
* @return the index of the input source that contains the specified offset, or 0 if unknown
*/
protected int getStartingSourceIndex(long offset){
if (offset >= currentSourceEntry.firstByte)
return currentSourceEntry.index;
return 0;
}
/**
* Returns the SourceEntry that contains the byte at the specified offset
* sourceReleased is called as a notification callback so subclasses can take care of cleanup when the source is no longer the active source
* @param offset the offset of the byte to look for
* @return the SourceEntry that contains the byte at the specified offset
* @throws IOException if there is a problem with IO (usually the result of the sourceReleased() call)
*/
private SourceEntry getSourceEntryForOffset(long offset) throws IOException {
if (offset >= size)
return null;
if (offset >= currentSourceEntry.firstByte && offset <= currentSourceEntry.lastByte)
return currentSourceEntry;
// hook to allow subclasses to release resources if necessary
sourceReleased(currentSourceEntry.source);
int startAt = getStartingSourceIndex(offset);
for(int i = startAt; i < sources.length; i++){
if (offset >= sources[i].firstByte && offset <= sources[i].lastByte){
currentSourceEntry = sources[i];
sourceInUse(currentSourceEntry.source);
return currentSourceEntry;
}
}
return null;
}
/**
* Called when a given source is no longer the active source. This gives subclasses the abilty to release resources, if appropriate.
* @param source the source that is no longer the active source
* @throws IOException if there are any problems
*/
protected void sourceReleased(RandomAccessSource source) throws IOException{
// by default, do nothing
}
/**
* Called when a given source is about to become the active source. This gives subclasses the abilty to retrieve resources, if appropriate.
* @param source the source that is about to become the active source
* @throws IOException if there are any problems
*/
protected void sourceInUse(RandomAccessSource source) throws IOException{
// by default, do nothing
}
/**
* {@inheritDoc}
* The source that contains the byte at position is retrieved, the correct offset into that source computed, then the value
* from that offset in the underlying source is returned.
*/
public int get(long position) throws IOException {
SourceEntry entry = getSourceEntryForOffset(position);
if (entry == null) // we have run out of data to read from
return -1;
return entry.source.get(entry.offsetN(position));
}
/**
* {@inheritDoc}
*/
public int get(long position, byte[] bytes, int off, int len) throws IOException {
SourceEntry entry = getSourceEntryForOffset(position);
if (entry == null) // we have run out of data to read from
return -1;
long offN = entry.offsetN(position);
int remaining = len;
while(remaining > 0){
if (entry == null) // we have run out of data to read from
break;
if (offN > entry.source.length())
break;
int count = entry.source.get(offN, bytes, off, remaining);
if (count == -1)
break;
off += count;
position += count;
remaining -= count;
offN = 0;
entry = getSourceEntryForOffset(position);
}
return remaining == len ? -1 : len - remaining;
}
/**
* {@inheritDoc}
*/
public long length() {
return size;
}
/**
* {@inheritDoc}
* Closes all of the underlying sources
*/
public void close() throws IOException {
for (SourceEntry entry : sources) {
entry.source.close();
}
}
/**
* Used to track each source, along with useful meta data
*/
private static class SourceEntry{
/**
* The underlying source
*/
final RandomAccessSource source;
/**
* The first byte (in the coordinates of the GroupedRandomAccessSource) that this source contains
*/
final long firstByte;
/**
* The last byte (in the coordinates of the GroupedRandomAccessSource) that this source contains
*/
final long lastByte;
/**
* The index of this source in the GroupedRandomAccessSource
*/
final int index;
/**
* Standard constructor
* @param index the index
* @param source the source
* @param offset the offset of the source in the GroupedRandomAccessSource
*/
public SourceEntry(int index, RandomAccessSource source, long offset) {
this.index = index;
this.source = source;
this.firstByte = offset;
this.lastByte = offset + source.length() - 1;
}
/**
* Given an absolute offset (in the GroupedRandomAccessSource coordinates), calculate the effective offset in the underlying source
* @param absoluteOffset the offset in the parent GroupedRandomAccessSource
* @return the effective offset in the underlying source
*/
public long offsetN(long absoluteOffset){
return absoluteOffset - firstByte;
}
}
}