mirror of
https://github.com/haraldk/TwelveMonkeys.git
synced 2026-05-01 00:00:02 -04:00
Merge branch 'master' of https://github.com/haraldk/TwelveMonkeys
This commit is contained in:
+31
-29
@@ -1,35 +1,37 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>3.0-SNAPSHOT</version>
|
<version>3.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>common-image</artifactId>
|
<artifactId>common-image</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>TwelveMonkeys :: Common :: Image</name>
|
<name>TwelveMonkeys :: Common :: Image</name>
|
||||||
<description>
|
<description>
|
||||||
The TwelveMonkeys Common Image support
|
The TwelveMonkeys Common Image support
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>${project.groupId}</groupId>
|
<groupId>${project.groupId}</groupId>
|
||||||
<artifactId>common-lang</artifactId>
|
<artifactId>common-lang</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>${project.groupId}</groupId>
|
<dependency>
|
||||||
<artifactId>common-io</artifactId>
|
<groupId>${project.groupId}</groupId>
|
||||||
</dependency>
|
<artifactId>common-io</artifactId>
|
||||||
<dependency>
|
</dependency>
|
||||||
<groupId>jmagick</groupId>
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>jmagick</groupId>
|
||||||
<artifactId>jmagick</artifactId>
|
<artifactId>jmagick</artifactId>
|
||||||
<version>6.2.4</version>
|
<version>6.2.4</version>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
<scope>provided</scope>
|
||||||
</dependencies>
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
+19
-15
@@ -41,21 +41,24 @@ import java.util.ArrayList;
|
|||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/AbstractImageSource.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/AbstractImageSource.java#1 $
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractImageSource implements ImageProducer {
|
public abstract class AbstractImageSource implements ImageProducer {
|
||||||
private List<ImageConsumer> mConsumers = new ArrayList<ImageConsumer>();
|
private List<ImageConsumer> consumers = new ArrayList<ImageConsumer>();
|
||||||
protected int mWidth;
|
protected int width;
|
||||||
protected int mHeight;
|
protected int height;
|
||||||
protected int mXOff;
|
protected int xOff;
|
||||||
protected int mYOff;
|
protected int yOff;
|
||||||
|
|
||||||
// ImageProducer interface
|
// ImageProducer interface
|
||||||
public void addConsumer(ImageConsumer pConsumer) {
|
public void addConsumer(final ImageConsumer pConsumer) {
|
||||||
if (mConsumers.contains(pConsumer)) {
|
if (consumers.contains(pConsumer)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mConsumers.add(pConsumer);
|
|
||||||
|
consumers.add(pConsumer);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
initConsumer(pConsumer);
|
initConsumer(pConsumer);
|
||||||
sendPixels(pConsumer);
|
sendPixels(pConsumer);
|
||||||
|
|
||||||
if (isConsumer(pConsumer)) {
|
if (isConsumer(pConsumer)) {
|
||||||
pConsumer.imageComplete(ImageConsumer.STATICIMAGEDONE);
|
pConsumer.imageComplete(ImageConsumer.STATICIMAGEDONE);
|
||||||
|
|
||||||
@@ -68,34 +71,35 @@ public abstract class AbstractImageSource implements ImageProducer {
|
|||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
||||||
if (isConsumer(pConsumer)) {
|
if (isConsumer(pConsumer)) {
|
||||||
pConsumer.imageComplete(ImageConsumer.IMAGEERROR);
|
pConsumer.imageComplete(ImageConsumer.IMAGEERROR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeConsumer(ImageConsumer pConsumer) {
|
public void removeConsumer(final ImageConsumer pConsumer) {
|
||||||
mConsumers.remove(pConsumer);
|
consumers.remove(pConsumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This implementation silently ignores this instruction. If pixeldata is
|
* This implementation silently ignores this instruction. If pixel data is
|
||||||
* not in TDLR order by default, subclasses must override this method.
|
* not in TDLR order by default, subclasses must override this method.
|
||||||
*
|
*
|
||||||
* @param pConsumer the consumer that requested the resend
|
* @param pConsumer the consumer that requested the resend
|
||||||
*
|
*
|
||||||
* @see ImageProducer#requestTopDownLeftRightResend(java.awt.image.ImageConsumer)
|
* @see ImageProducer#requestTopDownLeftRightResend(java.awt.image.ImageConsumer)
|
||||||
*/
|
*/
|
||||||
public void requestTopDownLeftRightResend(ImageConsumer pConsumer) {
|
public void requestTopDownLeftRightResend(final ImageConsumer pConsumer) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startProduction(ImageConsumer pConsumer) {
|
public void startProduction(final ImageConsumer pConsumer) {
|
||||||
addConsumer(pConsumer);
|
addConsumer(pConsumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConsumer(ImageConsumer pConsumer) {
|
public boolean isConsumer(final ImageConsumer pConsumer) {
|
||||||
return mConsumers.contains(pConsumer);
|
return consumers.contains(pConsumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void initConsumer(ImageConsumer pConsumer);
|
protected abstract void initConsumer(ImageConsumer pConsumer);
|
||||||
|
|||||||
@@ -47,33 +47,34 @@ import java.io.IOException;
|
|||||||
*/
|
*/
|
||||||
public class AreaAverageOp implements BufferedImageOp, RasterOp {
|
public class AreaAverageOp implements BufferedImageOp, RasterOp {
|
||||||
|
|
||||||
final private int mWidth;
|
final private int width;
|
||||||
final private int mHeight;
|
final private int height;
|
||||||
|
|
||||||
private Rectangle mSourceRegion;
|
private Rectangle sourceRegion;
|
||||||
|
|
||||||
public AreaAverageOp(final int pWidth, final int pHeight) {
|
public AreaAverageOp(final int pWidth, final int pHeight) {
|
||||||
mWidth = pWidth;
|
width = pWidth;
|
||||||
mHeight = pHeight;
|
height = pHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Rectangle getSourceRegion() {
|
public Rectangle getSourceRegion() {
|
||||||
if (mSourceRegion == null) {
|
if (sourceRegion == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new Rectangle(mSourceRegion);
|
|
||||||
|
return new Rectangle(sourceRegion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSourceRegion(final Rectangle pSourceRegion) {
|
public void setSourceRegion(final Rectangle pSourceRegion) {
|
||||||
if (pSourceRegion == null) {
|
if (pSourceRegion == null) {
|
||||||
mSourceRegion = null;
|
sourceRegion = null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (mSourceRegion == null) {
|
if (sourceRegion == null) {
|
||||||
mSourceRegion = new Rectangle(pSourceRegion);
|
sourceRegion = new Rectangle(pSourceRegion);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mSourceRegion.setBounds(pSourceRegion);
|
sourceRegion.setBounds(pSourceRegion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -93,7 +94,7 @@ public class AreaAverageOp implements BufferedImageOp, RasterOp {
|
|||||||
|
|
||||||
long start = System.currentTimeMillis();
|
long start = System.currentTimeMillis();
|
||||||
// Straight-forward version
|
// Straight-forward version
|
||||||
//Image scaled = src.getScaledInstance(mWidth, mHeight, Image.SCALE_AREA_AVERAGING);
|
//Image scaled = src.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);
|
||||||
//ImageUtil.drawOnto(result, scaled);
|
//ImageUtil.drawOnto(result, scaled);
|
||||||
//result = new BufferedImageFactory(scaled).getBufferedImage();
|
//result = new BufferedImageFactory(scaled).getBufferedImage();
|
||||||
|
|
||||||
@@ -104,7 +105,7 @@ public class AreaAverageOp implements BufferedImageOp, RasterOp {
|
|||||||
AffineTransform xform = null;
|
AffineTransform xform = null;
|
||||||
int w = src.getWidth();
|
int w = src.getWidth();
|
||||||
int h = src.getHeight();
|
int h = src.getHeight();
|
||||||
while (w / 2 > mWidth && h / 2 > mHeight) {
|
while (w / 2 > width && h / 2 > height) {
|
||||||
w /= 2;
|
w /= 2;
|
||||||
h /= 2;
|
h /= 2;
|
||||||
|
|
||||||
@@ -129,7 +130,7 @@ public class AreaAverageOp implements BufferedImageOp, RasterOp {
|
|||||||
src = temp.getSubimage(0, 0, w, h);
|
src = temp.getSubimage(0, 0, w, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
resample(src, result, AffineTransform.getScaleInstance(mWidth / (double) w, mHeight / (double) h));
|
resample(src, result, AffineTransform.getScaleInstance(width / (double) w, height / (double) h));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// The real version
|
// The real version
|
||||||
@@ -160,11 +161,11 @@ public class AreaAverageOp implements BufferedImageOp, RasterOp {
|
|||||||
private WritableRaster filterImpl(Raster src, WritableRaster dest) {
|
private WritableRaster filterImpl(Raster src, WritableRaster dest) {
|
||||||
//System.out.println("src: " + src);
|
//System.out.println("src: " + src);
|
||||||
//System.out.println("dest: " + dest);
|
//System.out.println("dest: " + dest);
|
||||||
if (mSourceRegion != null) {
|
if (sourceRegion != null) {
|
||||||
int cx = mSourceRegion.x;
|
int cx = sourceRegion.x;
|
||||||
int cy = mSourceRegion.y;
|
int cy = sourceRegion.y;
|
||||||
int cw = mSourceRegion.width;
|
int cw = sourceRegion.width;
|
||||||
int ch = mSourceRegion.height;
|
int ch = sourceRegion.height;
|
||||||
|
|
||||||
boolean same = src == dest;
|
boolean same = src == dest;
|
||||||
dest = dest.createWritableChild(cx, cy, cw, ch, 0, 0, null);
|
dest = dest.createWritableChild(cx, cy, cw, ch, 0, 0, null);
|
||||||
@@ -179,11 +180,11 @@ public class AreaAverageOp implements BufferedImageOp, RasterOp {
|
|||||||
// TODO: This don't work too well..
|
// TODO: This don't work too well..
|
||||||
// The thing is that the step length and the scan length will vary, for
|
// The thing is that the step length and the scan length will vary, for
|
||||||
// non-even (1/2, 1/4, 1/8 etc) resampling
|
// non-even (1/2, 1/4, 1/8 etc) resampling
|
||||||
int widthSteps = (width + mWidth - 1) / mWidth;
|
int widthSteps = (width + this.width - 1) / this.width;
|
||||||
int heightSteps = (height + mHeight - 1) / mHeight;
|
int heightSteps = (height + this.height - 1) / this.height;
|
||||||
|
|
||||||
final boolean oddX = width % mWidth != 0;
|
final boolean oddX = width % this.width != 0;
|
||||||
final boolean oddY = height % mHeight != 0;
|
final boolean oddY = height % this.height != 0;
|
||||||
|
|
||||||
final int dataElements = src.getNumDataElements();
|
final int dataElements = src.getNumDataElements();
|
||||||
final int bands = src.getNumBands();
|
final int bands = src.getNumBands();
|
||||||
@@ -210,16 +211,16 @@ public class AreaAverageOp implements BufferedImageOp, RasterOp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int y = 0; y < mHeight; y++) {
|
for (int y = 0; y < this.height; y++) {
|
||||||
if (!oddY || y < mHeight) {
|
if (!oddY || y < this.height) {
|
||||||
scanH = heightSteps;
|
scanH = heightSteps;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
scanH = height - (y * heightSteps);
|
scanH = height - (y * heightSteps);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int x = 0; x < mWidth; x++) {
|
for (int x = 0; x < this.width; x++) {
|
||||||
if (!oddX || x < mWidth) {
|
if (!oddX || x < this.width) {
|
||||||
scanW = widthSteps;
|
scanW = widthSteps;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -243,8 +244,8 @@ public class AreaAverageOp implements BufferedImageOp, RasterOp {
|
|||||||
//
|
//
|
||||||
//System.err.println("width: " + width);
|
//System.err.println("width: " + width);
|
||||||
//System.err.println("height: " + height);
|
//System.err.println("height: " + height);
|
||||||
//System.err.println("mWidth: " + mWidth);
|
//System.err.println("width: " + width);
|
||||||
//System.err.println("mHeight: " + mHeight);
|
//System.err.println("height: " + height);
|
||||||
//
|
//
|
||||||
//e.printStackTrace();
|
//e.printStackTrace();
|
||||||
continue;
|
continue;
|
||||||
@@ -382,20 +383,20 @@ public class AreaAverageOp implements BufferedImageOp, RasterOp {
|
|||||||
public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel destCM) {
|
public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel destCM) {
|
||||||
ColorModel cm = destCM != null ? destCM : src.getColorModel();
|
ColorModel cm = destCM != null ? destCM : src.getColorModel();
|
||||||
return new BufferedImage(cm,
|
return new BufferedImage(cm,
|
||||||
ImageUtil.createCompatibleWritableRaster(src, cm, mWidth, mHeight),
|
ImageUtil.createCompatibleWritableRaster(src, cm, width, height),
|
||||||
cm.isAlphaPremultiplied(), null);
|
cm.isAlphaPremultiplied(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WritableRaster createCompatibleDestRaster(Raster src) {
|
public WritableRaster createCompatibleDestRaster(Raster src) {
|
||||||
return src.createCompatibleWritableRaster(mWidth, mHeight);
|
return src.createCompatibleWritableRaster(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Rectangle2D getBounds2D(Raster src) {
|
public Rectangle2D getBounds2D(Raster src) {
|
||||||
return new Rectangle(mWidth, mHeight);
|
return new Rectangle(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Rectangle2D getBounds2D(BufferedImage src) {
|
public Rectangle2D getBounds2D(BufferedImage src) {
|
||||||
return new Rectangle(mWidth, mHeight);
|
return new Rectangle(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
|
public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
|
||||||
|
|||||||
+5
-5
@@ -67,7 +67,7 @@ public class BrightnessContrastFilter extends RGBImageFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Use a precalculated lookup table for performace
|
// Use a precalculated lookup table for performace
|
||||||
private int[] mLUT = null;
|
private final int[] LUT;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a BrightnessContrastFilter with default values
|
* Creates a BrightnessContrastFilter with default values
|
||||||
@@ -105,7 +105,7 @@ public class BrightnessContrastFilter extends RGBImageFilter {
|
|||||||
* {@code -1.0,..,0.0,..,1.0}.
|
* {@code -1.0,..,0.0,..,1.0}.
|
||||||
*/
|
*/
|
||||||
public BrightnessContrastFilter(float pBrightness, float pContrast) {
|
public BrightnessContrastFilter(float pBrightness, float pContrast) {
|
||||||
mLUT = createLUT(pBrightness, pContrast);
|
LUT = createLUT(pBrightness, pContrast);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int[] createLUT(float pBrightness, float pContrast) {
|
private static int[] createLUT(float pBrightness, float pContrast) {
|
||||||
@@ -157,9 +157,9 @@ public class BrightnessContrastFilter extends RGBImageFilter {
|
|||||||
int b = pARGB & 0xFF;
|
int b = pARGB & 0xFF;
|
||||||
|
|
||||||
// Scale to new contrast
|
// Scale to new contrast
|
||||||
r = mLUT[r];
|
r = LUT[r];
|
||||||
g = mLUT[g];
|
g = LUT[g];
|
||||||
b = mLUT[b];
|
b = LUT[b];
|
||||||
|
|
||||||
// Return ARGB pixel, leave transparency as is
|
// Return ARGB pixel, leave transparency as is
|
||||||
return (pARGB & 0xFF000000) | (r << 16) | (g << 8) | b;
|
return (pARGB & 0xFF000000) | (r << 16) | (g << 8) | b;
|
||||||
|
|||||||
+540
-542
File diff suppressed because it is too large
Load Diff
@@ -28,6 +28,8 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.lang.Validate;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
@@ -41,51 +43,44 @@ import java.awt.geom.AffineTransform;
|
|||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BufferedImageIcon.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BufferedImageIcon.java#2 $
|
||||||
*/
|
*/
|
||||||
public class BufferedImageIcon implements Icon {
|
public class BufferedImageIcon implements Icon {
|
||||||
private final BufferedImage mImage;
|
private final BufferedImage image;
|
||||||
private int mWidth;
|
private int width;
|
||||||
private int mHeight;
|
private int height;
|
||||||
private final boolean mFast;
|
private final boolean fast;
|
||||||
|
|
||||||
public BufferedImageIcon(BufferedImage pImage) {
|
public BufferedImageIcon(BufferedImage pImage) {
|
||||||
this(pImage, pImage.getWidth(), pImage.getHeight());
|
this(pImage, pImage != null ? pImage.getWidth() : 0, pImage != null ? pImage.getHeight() : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferedImageIcon(BufferedImage pImage, int pWidth, int pHeight) {
|
public BufferedImageIcon(BufferedImage pImage, int pWidth, int pHeight) {
|
||||||
if (pImage == null) {
|
image = Validate.notNull(pImage, "image");
|
||||||
throw new IllegalArgumentException("image == null");
|
width = Validate.isTrue(pWidth > 0, pWidth, "width must be positive: %d");
|
||||||
}
|
height = Validate.isTrue(pHeight > 0, pHeight, "height must be positive: %d");
|
||||||
if (pWidth <= 0 || pHeight <= 0) {
|
|
||||||
throw new IllegalArgumentException("Icon size must be positive");
|
|
||||||
}
|
|
||||||
|
|
||||||
mImage = pImage;
|
fast = image.getWidth() == width && image.getHeight() == height;
|
||||||
mWidth = pWidth;
|
|
||||||
mHeight = pHeight;
|
|
||||||
|
|
||||||
mFast = pImage.getWidth() == mWidth && pImage.getHeight() == mHeight;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getIconHeight() {
|
public int getIconHeight() {
|
||||||
return mHeight;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getIconWidth() {
|
public int getIconWidth() {
|
||||||
return mWidth;
|
return width;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||||
if (mFast || !(g instanceof Graphics2D)) {
|
if (fast || !(g instanceof Graphics2D)) {
|
||||||
//System.out.println("Scaling fast");
|
//System.out.println("Scaling fast");
|
||||||
g.drawImage(mImage, x, y, mWidth, mHeight, null);
|
g.drawImage(image, x, y, width, height, null);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//System.out.println("Scaling using interpolation");
|
//System.out.println("Scaling using interpolation");
|
||||||
Graphics2D g2 = (Graphics2D) g;
|
Graphics2D g2 = (Graphics2D) g;
|
||||||
AffineTransform xform = AffineTransform.getTranslateInstance(x, y);
|
AffineTransform xform = AffineTransform.getTranslateInstance(x, y);
|
||||||
xform.scale(mWidth / (double) mImage.getWidth(), mHeight / (double) mImage.getHeight());
|
xform.scale(width / (double) image.getWidth(), height / (double) image.getHeight());
|
||||||
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
|
||||||
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||||
g2.drawImage(mImage, xform, null);
|
g2.drawImage(image, xform, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,14 +73,15 @@ public class ConvolveWithEdgeOp implements BufferedImageOp, RasterOp {
|
|||||||
*/
|
*/
|
||||||
public static final int EDGE_WRAP = 3; // as JAI BORDER_WRAP
|
public static final int EDGE_WRAP = 3; // as JAI BORDER_WRAP
|
||||||
|
|
||||||
private final Kernel mKernel;
|
private final Kernel kernel;
|
||||||
private final int mEdgeCondition;
|
private final int edgeCondition;
|
||||||
|
|
||||||
private final ConvolveOp mConvolve;
|
private final ConvolveOp convolve;
|
||||||
|
|
||||||
public ConvolveWithEdgeOp(final Kernel pKernel, final int pEdgeCondition, final RenderingHints pHints) {
|
public ConvolveWithEdgeOp(final Kernel pKernel, final int pEdgeCondition, final RenderingHints pHints) {
|
||||||
// Create convolution operation
|
// Create convolution operation
|
||||||
int edge;
|
int edge;
|
||||||
|
|
||||||
switch (pEdgeCondition) {
|
switch (pEdgeCondition) {
|
||||||
case EDGE_REFLECT:
|
case EDGE_REFLECT:
|
||||||
case EDGE_WRAP:
|
case EDGE_WRAP:
|
||||||
@@ -90,9 +91,10 @@ public class ConvolveWithEdgeOp implements BufferedImageOp, RasterOp {
|
|||||||
edge = pEdgeCondition;
|
edge = pEdgeCondition;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
mKernel = pKernel;
|
|
||||||
mEdgeCondition = pEdgeCondition;
|
kernel = pKernel;
|
||||||
mConvolve = new ConvolveOp(pKernel, edge, pHints);
|
edgeCondition = pEdgeCondition;
|
||||||
|
convolve = new ConvolveOp(pKernel, edge, pHints);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConvolveWithEdgeOp(final Kernel pKernel) {
|
public ConvolveWithEdgeOp(final Kernel pKernel) {
|
||||||
@@ -107,8 +109,8 @@ public class ConvolveWithEdgeOp implements BufferedImageOp, RasterOp {
|
|||||||
throw new IllegalArgumentException("source image cannot be the same as the destination image");
|
throw new IllegalArgumentException("source image cannot be the same as the destination image");
|
||||||
}
|
}
|
||||||
|
|
||||||
int borderX = mKernel.getWidth() / 2;
|
int borderX = kernel.getWidth() / 2;
|
||||||
int borderY = mKernel.getHeight() / 2;
|
int borderY = kernel.getHeight() / 2;
|
||||||
|
|
||||||
BufferedImage original = addBorder(pSource, borderX, borderY);
|
BufferedImage original = addBorder(pSource, borderX, borderY);
|
||||||
|
|
||||||
@@ -126,7 +128,7 @@ public class ConvolveWithEdgeOp implements BufferedImageOp, RasterOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Do the filtering (if destination is null, a new image will be created)
|
// Do the filtering (if destination is null, a new image will be created)
|
||||||
destination = mConvolve.filter(original, destination);
|
destination = convolve.filter(original, destination);
|
||||||
|
|
||||||
if (pSource != original) {
|
if (pSource != original) {
|
||||||
// Remove the border
|
// Remove the border
|
||||||
@@ -137,7 +139,7 @@ public class ConvolveWithEdgeOp implements BufferedImageOp, RasterOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private BufferedImage addBorder(final BufferedImage pOriginal, final int pBorderX, final int pBorderY) {
|
private BufferedImage addBorder(final BufferedImage pOriginal, final int pBorderX, final int pBorderY) {
|
||||||
if ((mEdgeCondition & 2) == 0) {
|
if ((edgeCondition & 2) == 0) {
|
||||||
return pOriginal;
|
return pOriginal;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +160,7 @@ public class ConvolveWithEdgeOp implements BufferedImageOp, RasterOp {
|
|||||||
g.drawImage(pOriginal, pBorderX, pBorderY, null);
|
g.drawImage(pOriginal, pBorderX, pBorderY, null);
|
||||||
|
|
||||||
// TODO: I guess we need the top/left etc, if the corner pixels are covered by the kernel
|
// TODO: I guess we need the top/left etc, if the corner pixels are covered by the kernel
|
||||||
switch (mEdgeCondition) {
|
switch (edgeCondition) {
|
||||||
case EDGE_REFLECT:
|
case EDGE_REFLECT:
|
||||||
// Top/left (empty)
|
// Top/left (empty)
|
||||||
g.drawImage(pOriginal, pBorderX, 0, pBorderX + w, pBorderY, 0, 0, w, 1, null); // Top/center
|
g.drawImage(pOriginal, pBorderX, 0, pBorderX + w, pBorderY, 0, 0, w, 1, null); // Top/center
|
||||||
@@ -186,7 +188,7 @@ public class ConvolveWithEdgeOp implements BufferedImageOp, RasterOp {
|
|||||||
g.drawImage(pOriginal, w + pBorderX, h + pBorderY, null); // Bottom/right
|
g.drawImage(pOriginal, w + pBorderX, h + pBorderY, null); // Bottom/right
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Illegal edge operation " + mEdgeCondition);
|
throw new IllegalArgumentException("Illegal edge operation " + edgeCondition);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -206,39 +208,39 @@ public class ConvolveWithEdgeOp implements BufferedImageOp, RasterOp {
|
|||||||
* @see #EDGE_WRAP
|
* @see #EDGE_WRAP
|
||||||
*/
|
*/
|
||||||
public int getEdgeCondition() {
|
public int getEdgeCondition() {
|
||||||
return mEdgeCondition;
|
return edgeCondition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WritableRaster filter(final Raster pSource, final WritableRaster pDestination) {
|
public WritableRaster filter(final Raster pSource, final WritableRaster pDestination) {
|
||||||
return mConvolve.filter(pSource, pDestination);
|
return convolve.filter(pSource, pDestination);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferedImage createCompatibleDestImage(final BufferedImage pSource, final ColorModel pDesinationColorModel) {
|
public BufferedImage createCompatibleDestImage(final BufferedImage pSource, final ColorModel pDesinationColorModel) {
|
||||||
return mConvolve.createCompatibleDestImage(pSource, pDesinationColorModel);
|
return convolve.createCompatibleDestImage(pSource, pDesinationColorModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WritableRaster createCompatibleDestRaster(final Raster pSource) {
|
public WritableRaster createCompatibleDestRaster(final Raster pSource) {
|
||||||
return mConvolve.createCompatibleDestRaster(pSource);
|
return convolve.createCompatibleDestRaster(pSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Rectangle2D getBounds2D(final BufferedImage pSource) {
|
public Rectangle2D getBounds2D(final BufferedImage pSource) {
|
||||||
return mConvolve.getBounds2D(pSource);
|
return convolve.getBounds2D(pSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Rectangle2D getBounds2D(final Raster pSource) {
|
public Rectangle2D getBounds2D(final Raster pSource) {
|
||||||
return mConvolve.getBounds2D(pSource);
|
return convolve.getBounds2D(pSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Point2D getPoint2D(final Point2D pSourcePoint, final Point2D pDestinationPoint) {
|
public Point2D getPoint2D(final Point2D pSourcePoint, final Point2D pDestinationPoint) {
|
||||||
return mConvolve.getPoint2D(pSourcePoint, pDestinationPoint);
|
return convolve.getPoint2D(pSourcePoint, pDestinationPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RenderingHints getRenderingHints() {
|
public RenderingHints getRenderingHints() {
|
||||||
return mConvolve.getRenderingHints();
|
return convolve.getRenderingHints();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Kernel getKernel() {
|
public Kernel getKernel() {
|
||||||
return mConvolve.getKernel();
|
return convolve.getKernel();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ import java.awt.image.WritableRaster;
|
|||||||
*/
|
*/
|
||||||
public class CopyDither implements BufferedImageOp, RasterOp {
|
public class CopyDither implements BufferedImageOp, RasterOp {
|
||||||
|
|
||||||
protected IndexColorModel mIndexColorModel = null;
|
protected IndexColorModel indexColorModel = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code CopyDither}, using the given
|
* Creates a {@code CopyDither}, using the given
|
||||||
@@ -61,7 +61,7 @@ public class CopyDither implements BufferedImageOp, RasterOp {
|
|||||||
*/
|
*/
|
||||||
public CopyDither(IndexColorModel pICM) {
|
public CopyDither(IndexColorModel pICM) {
|
||||||
// Store colormodel
|
// Store colormodel
|
||||||
mIndexColorModel = pICM;
|
indexColorModel = pICM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,17 +83,12 @@ public class CopyDither implements BufferedImageOp, RasterOp {
|
|||||||
* @throws ImageFilterException if {@code pDestCM} is not {@code null} or
|
* @throws ImageFilterException if {@code pDestCM} is not {@code null} or
|
||||||
* an instance of {@code IndexColorModel}.
|
* an instance of {@code IndexColorModel}.
|
||||||
*/
|
*/
|
||||||
public final BufferedImage createCompatibleDestImage(BufferedImage pSource,
|
public final BufferedImage createCompatibleDestImage(BufferedImage pSource, ColorModel pDestCM) {
|
||||||
ColorModel pDestCM) {
|
|
||||||
if (pDestCM == null) {
|
if (pDestCM == null) {
|
||||||
return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
return new BufferedImage(pSource.getWidth(), pSource.getHeight(), BufferedImage.TYPE_BYTE_INDEXED, indexColorModel);
|
||||||
BufferedImage.TYPE_BYTE_INDEXED,
|
|
||||||
mIndexColorModel);
|
|
||||||
}
|
}
|
||||||
else if (pDestCM instanceof IndexColorModel) {
|
else if (pDestCM instanceof IndexColorModel) {
|
||||||
return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
return new BufferedImage(pSource.getWidth(), pSource.getHeight(), BufferedImage.TYPE_BYTE_INDEXED, (IndexColorModel) pDestCM);
|
||||||
BufferedImage.TYPE_BYTE_INDEXED,
|
|
||||||
(IndexColorModel) pDestCM);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new ImageFilterException("Only IndexColorModel allowed.");
|
throw new ImageFilterException("Only IndexColorModel allowed.");
|
||||||
@@ -112,13 +107,7 @@ public class CopyDither implements BufferedImageOp, RasterOp {
|
|||||||
return createCompatibleDestRaster(pSrc, getICM(pSrc));
|
return createCompatibleDestRaster(pSrc, getICM(pSrc));
|
||||||
}
|
}
|
||||||
|
|
||||||
public final WritableRaster createCompatibleDestRaster(Raster pSrc,
|
public final WritableRaster createCompatibleDestRaster(Raster pSrc, IndexColorModel pIndexColorModel) {
|
||||||
IndexColorModel pIndexColorModel) {
|
|
||||||
/*
|
|
||||||
return new BufferedImage(pSrc.getWidth(), pSrc.getHeight(),
|
|
||||||
BufferedImage.TYPE_BYTE_INDEXED,
|
|
||||||
pIndexColorModel).getRaster();
|
|
||||||
*/
|
|
||||||
return pIndexColorModel.createCompatibleWritableRaster(pSrc.getWidth(), pSrc.getHeight());
|
return pIndexColorModel.createCompatibleWritableRaster(pSrc.getWidth(), pSrc.getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,8 +196,7 @@ public class CopyDither implements BufferedImageOp, RasterOp {
|
|||||||
* @return the destination image, or a new image, if {@code pDest} was
|
* @return the destination image, or a new image, if {@code pDest} was
|
||||||
* {@code null}.
|
* {@code null}.
|
||||||
*/
|
*/
|
||||||
public final BufferedImage filter(BufferedImage pSource,
|
public final BufferedImage filter(BufferedImage pSource, BufferedImage pDest) {
|
||||||
BufferedImage pDest) {
|
|
||||||
// Create destination image, if none provided
|
// Create destination image, if none provided
|
||||||
if (pDest == null) {
|
if (pDest == null) {
|
||||||
pDest = createCompatibleDestImage(pSource, getICM(pSource));
|
pDest = createCompatibleDestImage(pSource, getICM(pSource));
|
||||||
@@ -238,16 +226,17 @@ public class CopyDither implements BufferedImageOp, RasterOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private IndexColorModel getICM(BufferedImage pSource) {
|
private IndexColorModel getICM(BufferedImage pSource) {
|
||||||
return (mIndexColorModel != null ? mIndexColorModel : IndexImage.getIndexColorModel(pSource, 256, IndexImage.TRANSPARENCY_BITMASK | IndexImage.COLOR_SELECTION_QUALITY));
|
return (indexColorModel != null ? indexColorModel : IndexImage.getIndexColorModel(pSource, 256, IndexImage.TRANSPARENCY_BITMASK | IndexImage.COLOR_SELECTION_QUALITY));
|
||||||
}
|
}
|
||||||
|
|
||||||
private IndexColorModel getICM(Raster pSource) {
|
private IndexColorModel getICM(Raster pSource) {
|
||||||
return (mIndexColorModel != null ? mIndexColorModel : createIndexColorModel(pSource));
|
return (indexColorModel != null ? indexColorModel : createIndexColorModel(pSource));
|
||||||
}
|
}
|
||||||
|
|
||||||
private IndexColorModel createIndexColorModel(Raster pSource) {
|
private IndexColorModel createIndexColorModel(Raster pSource) {
|
||||||
BufferedImage image = new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
BufferedImage image = new BufferedImage(pSource.getWidth(), pSource.getHeight(), BufferedImage.TYPE_INT_ARGB);
|
||||||
BufferedImage.TYPE_INT_ARGB);
|
|
||||||
image.setData(pSource);
|
image.setData(pSource);
|
||||||
|
|
||||||
return IndexImage.getIndexColorModel(image, 256, IndexImage.TRANSPARENCY_BITMASK | IndexImage.COLOR_SELECTION_QUALITY);
|
return IndexImage.getIndexColorModel(image, 256, IndexImage.TRANSPARENCY_BITMASK | IndexImage.COLOR_SELECTION_QUALITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,8 +250,7 @@ public class CopyDither implements BufferedImageOp, RasterOp {
|
|||||||
* @return the destination raster, or a new raster, if {@code pDest} was
|
* @return the destination raster, or a new raster, if {@code pDest} was
|
||||||
* {@code null}.
|
* {@code null}.
|
||||||
*/
|
*/
|
||||||
public final WritableRaster filter(final Raster pSource, WritableRaster pDest,
|
public final WritableRaster filter(final Raster pSource, WritableRaster pDest, IndexColorModel pColorModel) {
|
||||||
IndexColorModel pColorModel) {
|
|
||||||
int width = pSource.getWidth();
|
int width = pSource.getWidth();
|
||||||
int height = pSource.getHeight();
|
int height = pSource.getHeight();
|
||||||
|
|
||||||
@@ -292,6 +280,7 @@ public class CopyDither implements BufferedImageOp, RasterOp {
|
|||||||
pDest.setDataElements(x, y, pixel);
|
pDest.setDataElements(x, y, pixel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pDest;
|
return pDest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,34 +36,36 @@ import java.util.Random;
|
|||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
*
|
*
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/DiffusionDither.java#1 $
|
* @version $Id: DiffusionDither.java#1 $
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class DiffusionDither implements BufferedImageOp, RasterOp {
|
public class DiffusionDither implements BufferedImageOp, RasterOp {
|
||||||
|
|
||||||
protected IndexColorModel mIndexColorModel = null;
|
|
||||||
private boolean mAlternateScans = true;
|
|
||||||
private static final int FS_SCALE = 1 << 8;
|
private static final int FS_SCALE = 1 << 8;
|
||||||
private static final Random RANDOM = new Random();
|
private static final Random RANDOM = new Random();
|
||||||
|
|
||||||
|
protected final IndexColorModel indexColorModel;
|
||||||
|
private boolean alternateScans = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code DiffusionDither}, using the given
|
* Creates a {@code DiffusionDither}, using the given
|
||||||
* {@code IndexColorModel} for dithering into.
|
* {@code IndexColorModel} for dithering into.
|
||||||
*
|
*
|
||||||
* @param pICM an IndexColorModel.
|
* @param pICM an IndexColorModel.
|
||||||
*/
|
*/
|
||||||
public DiffusionDither(IndexColorModel pICM) {
|
public DiffusionDither(final IndexColorModel pICM) {
|
||||||
// Store colormodel
|
// Store color model
|
||||||
mIndexColorModel = pICM;
|
indexColorModel = pICM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code DiffusionDither}, with no fixed
|
* Creates a {@code DiffusionDither}, with no fixed
|
||||||
* {@code IndexColorModel}. The colormodel will be generated for each
|
* {@code IndexColorModel}. The color model will be generated for each
|
||||||
* filtering, unless the dest image allready has an
|
* filtering, unless the destination image already has an
|
||||||
* {@code IndexColorModel}.
|
* {@code IndexColorModel}.
|
||||||
*/
|
*/
|
||||||
public DiffusionDither() {
|
public DiffusionDither() {
|
||||||
|
this(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,7 +76,7 @@ public class DiffusionDither implements BufferedImageOp, RasterOp {
|
|||||||
* @param pUse {@code true} if scan mode should be alternating left/right
|
* @param pUse {@code true} if scan mode should be alternating left/right
|
||||||
*/
|
*/
|
||||||
public void setAlternateScans(boolean pUse) {
|
public void setAlternateScans(boolean pUse) {
|
||||||
mAlternateScans = pUse;
|
alternateScans = pUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -86,8 +88,7 @@ public class DiffusionDither implements BufferedImageOp, RasterOp {
|
|||||||
* @throws ImageFilterException if {@code pDestCM} is not {@code null} or
|
* @throws ImageFilterException if {@code pDestCM} is not {@code null} or
|
||||||
* an instance of {@code IndexColorModel}.
|
* an instance of {@code IndexColorModel}.
|
||||||
*/
|
*/
|
||||||
public final BufferedImage createCompatibleDestImage(BufferedImage pSource,
|
public final BufferedImage createCompatibleDestImage(BufferedImage pSource, ColorModel pDestCM) {
|
||||||
ColorModel pDestCM) {
|
|
||||||
if (pDestCM == null) {
|
if (pDestCM == null) {
|
||||||
return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
|
||||||
BufferedImage.TYPE_BYTE_INDEXED,
|
BufferedImage.TYPE_BYTE_INDEXED,
|
||||||
@@ -107,7 +108,7 @@ public class DiffusionDither implements BufferedImageOp, RasterOp {
|
|||||||
* Creates a compatible {@code Raster} to dither into.
|
* Creates a compatible {@code Raster} to dither into.
|
||||||
* Only {@code IndexColorModel} allowed.
|
* Only {@code IndexColorModel} allowed.
|
||||||
*
|
*
|
||||||
* @param pSrc
|
* @param pSrc the source raster
|
||||||
*
|
*
|
||||||
* @return a {@code WritableRaster}
|
* @return a {@code WritableRaster}
|
||||||
*/
|
*/
|
||||||
@@ -115,14 +116,16 @@ public class DiffusionDither implements BufferedImageOp, RasterOp {
|
|||||||
return createCompatibleDestRaster(pSrc, getICM(pSrc));
|
return createCompatibleDestRaster(pSrc, getICM(pSrc));
|
||||||
}
|
}
|
||||||
|
|
||||||
public final WritableRaster createCompatibleDestRaster(Raster pSrc,
|
/**
|
||||||
IndexColorModel pIndexColorModel) {
|
* Creates a compatible {@code Raster} to dither into.
|
||||||
|
*
|
||||||
|
* @param pSrc the source raster.
|
||||||
|
* @param pIndexColorModel the index color model used to create a {@code Raster}.
|
||||||
|
*
|
||||||
|
* @return a {@code WritableRaster}
|
||||||
|
*/
|
||||||
|
public final WritableRaster createCompatibleDestRaster(Raster pSrc, IndexColorModel pIndexColorModel) {
|
||||||
return pIndexColorModel.createCompatibleWritableRaster(pSrc.getWidth(), pSrc.getHeight());
|
return pIndexColorModel.createCompatibleWritableRaster(pSrc.getWidth(), pSrc.getHeight());
|
||||||
/*
|
|
||||||
return new BufferedImage(pSrc.getWidth(), pSrc.getHeight(),
|
|
||||||
BufferedImage.TYPE_BYTE_INDEXED,
|
|
||||||
pIndexColorModel).getRaster();
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -221,8 +224,7 @@ public class DiffusionDither implements BufferedImageOp, RasterOp {
|
|||||||
* @return the destination image, or a new image, if {@code pDest} was
|
* @return the destination image, or a new image, if {@code pDest} was
|
||||||
* {@code null}.
|
* {@code null}.
|
||||||
*/
|
*/
|
||||||
public final BufferedImage filter(BufferedImage pSource,
|
public final BufferedImage filter(BufferedImage pSource, BufferedImage pDest) {
|
||||||
BufferedImage pDest) {
|
|
||||||
// Create destination image, if none provided
|
// Create destination image, if none provided
|
||||||
if (pDest == null) {
|
if (pDest == null) {
|
||||||
pDest = createCompatibleDestImage(pSource, getICM(pSource));
|
pDest = createCompatibleDestImage(pSource, getICM(pSource));
|
||||||
@@ -252,10 +254,10 @@ public class DiffusionDither implements BufferedImageOp, RasterOp {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private IndexColorModel getICM(BufferedImage pSource) {
|
private IndexColorModel getICM(BufferedImage pSource) {
|
||||||
return (mIndexColorModel != null ? mIndexColorModel : IndexImage.getIndexColorModel(pSource, 256, IndexImage.TRANSPARENCY_BITMASK));
|
return (indexColorModel != null ? indexColorModel : IndexImage.getIndexColorModel(pSource, 256, IndexImage.TRANSPARENCY_BITMASK));
|
||||||
}
|
}
|
||||||
private IndexColorModel getICM(Raster pSource) {
|
private IndexColorModel getICM(Raster pSource) {
|
||||||
return (mIndexColorModel != null ? mIndexColorModel : createIndexColorModel(pSource));
|
return (indexColorModel != null ? indexColorModel : createIndexColorModel(pSource));
|
||||||
}
|
}
|
||||||
|
|
||||||
private IndexColorModel createIndexColorModel(Raster pSource) {
|
private IndexColorModel createIndexColorModel(Raster pSource) {
|
||||||
@@ -265,8 +267,6 @@ public class DiffusionDither implements BufferedImageOp, RasterOp {
|
|||||||
return IndexImage.getIndexColorModel(image, 256, IndexImage.TRANSPARENCY_BITMASK);
|
return IndexImage.getIndexColorModel(image, 256, IndexImage.TRANSPARENCY_BITMASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs a single-input/single-output dither operation, applying basic
|
* Performs a single-input/single-output dither operation, applying basic
|
||||||
* Floyd-Steinberg error-diffusion to the image.
|
* Floyd-Steinberg error-diffusion to the image.
|
||||||
@@ -278,8 +278,7 @@ public class DiffusionDither implements BufferedImageOp, RasterOp {
|
|||||||
* @return the destination raster, or a new raster, if {@code pDest} was
|
* @return the destination raster, or a new raster, if {@code pDest} was
|
||||||
* {@code null}.
|
* {@code null}.
|
||||||
*/
|
*/
|
||||||
public final WritableRaster filter(final Raster pSource, WritableRaster pDest,
|
public final WritableRaster filter(final Raster pSource, WritableRaster pDest, IndexColorModel pColorModel) {
|
||||||
IndexColorModel pColorModel) {
|
|
||||||
int width = pSource.getWidth();
|
int width = pSource.getWidth();
|
||||||
int height = pSource.getHeight();
|
int height = pSource.getHeight();
|
||||||
|
|
||||||
@@ -456,10 +455,11 @@ public class DiffusionDither implements BufferedImageOp, RasterOp {
|
|||||||
mNextErr = temperr;
|
mNextErr = temperr;
|
||||||
|
|
||||||
// Toggle direction
|
// Toggle direction
|
||||||
if (mAlternateScans) {
|
if (alternateScans) {
|
||||||
forward = !forward;
|
forward = !forward;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pDest;
|
return pDest;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,8 +48,8 @@ public class GrayFilter extends RGBImageFilter {
|
|||||||
canFilterIndexColorModel = true;
|
canFilterIndexColorModel = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int mLow = 0;
|
private int low = 0;
|
||||||
private float mRange = 1.0f;
|
private float range = 1.0f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a GrayFilter using ITU color-conversion.
|
* Constructs a GrayFilter using ITU color-conversion.
|
||||||
@@ -82,8 +82,8 @@ public class GrayFilter extends RGBImageFilter {
|
|||||||
pHigh = 1f;
|
pHigh = 1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
mLow = (int) (pLow * 255f);
|
low = (int) (pLow * 255f);
|
||||||
mRange = pHigh - pLow;
|
range = pHigh - pLow;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,9 +118,9 @@ public class GrayFilter extends RGBImageFilter {
|
|||||||
|
|
||||||
//int gray = (int) ((float) (r + g + b) / 3.0f);
|
//int gray = (int) ((float) (r + g + b) / 3.0f);
|
||||||
|
|
||||||
if (mRange != 1.0f) {
|
if (range != 1.0f) {
|
||||||
// Apply range
|
// Apply range
|
||||||
gray = mLow + (int) (gray * mRange);
|
gray = low + (int) (gray * range);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return ARGB pixel
|
// Return ARGB pixel
|
||||||
|
|||||||
+7
-29
@@ -32,42 +32,20 @@ package com.twelvemonkeys.image;
|
|||||||
* This class wraps IllegalArgumentException as thrown by the
|
* This class wraps IllegalArgumentException as thrown by the
|
||||||
* BufferedImageOp interface for more fine-grained control.
|
* BufferedImageOp interface for more fine-grained control.
|
||||||
*
|
*
|
||||||
* @author Harald Kuhr
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
*
|
*
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/ImageFilterException.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/ImageFilterException.java#1 $
|
||||||
*/
|
*/
|
||||||
public class ImageFilterException extends IllegalArgumentException {
|
public class ImageFilterException extends IllegalArgumentException {
|
||||||
private Throwable mCause = null;
|
public ImageFilterException(String message) {
|
||||||
|
super(message);
|
||||||
public ImageFilterException(String pStr) {
|
|
||||||
super(pStr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageFilterException(Throwable pT) {
|
public ImageFilterException(Throwable cause) {
|
||||||
initCause(pT);
|
super(cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImageFilterException(String pStr, Throwable pT) {
|
public ImageFilterException(String message, Throwable cause) {
|
||||||
super(pStr);
|
super(message, cause);
|
||||||
initCause(pT);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Throwable initCause(Throwable pThrowable) {
|
|
||||||
if (mCause != null) {
|
|
||||||
// May only be called once
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
else if (pThrowable == this) {
|
|
||||||
throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
|
|
||||||
mCause = pThrowable;
|
|
||||||
|
|
||||||
// Hmmm...
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Throwable getCause() {
|
|
||||||
return mCause;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,19 +32,17 @@ import java.awt.*;
|
|||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
|
|
||||||
import java.util.Hashtable;
|
import java.util.Hashtable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class contains methods for basic image manipulation and conversion.
|
* This class contains methods for basic image manipulation and conversion.
|
||||||
*
|
*
|
||||||
* @todo Split palette generation out, into ColorModel classes.
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/ImageUtil.java#3 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/ImageUtil.java#3 $
|
||||||
*/
|
*/
|
||||||
public final class ImageUtil {
|
public final class ImageUtil {
|
||||||
|
// TODO: Split palette generation out, into ColorModel classes (?)
|
||||||
|
|
||||||
public final static int ROTATE_90_CCW = -90;
|
public final static int ROTATE_90_CCW = -90;
|
||||||
public final static int ROTATE_90_CW = 90;
|
public final static int ROTATE_90_CW = 90;
|
||||||
@@ -59,12 +57,14 @@ public final class ImageUtil {
|
|||||||
* @see #EDGE_REFLECT
|
* @see #EDGE_REFLECT
|
||||||
*/
|
*/
|
||||||
public static final int EDGE_ZERO_FILL = ConvolveOp.EDGE_ZERO_FILL;
|
public static final int EDGE_ZERO_FILL = ConvolveOp.EDGE_ZERO_FILL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alias for {@link ConvolveOp#EDGE_NO_OP}.
|
* Alias for {@link ConvolveOp#EDGE_NO_OP}.
|
||||||
* @see #convolve(java.awt.image.BufferedImage, java.awt.image.Kernel, int)
|
* @see #convolve(java.awt.image.BufferedImage, java.awt.image.Kernel, int)
|
||||||
* @see #EDGE_REFLECT
|
* @see #EDGE_REFLECT
|
||||||
*/
|
*/
|
||||||
public static final int EDGE_NO_OP = ConvolveOp.EDGE_NO_OP;
|
public static final int EDGE_NO_OP = ConvolveOp.EDGE_NO_OP;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a border to the image while convolving. The border will reflect the
|
* Adds a border to the image while convolving. The border will reflect the
|
||||||
* edges of the original image. This is usually a good default.
|
* edges of the original image. This is usually a good default.
|
||||||
@@ -74,6 +74,7 @@ public final class ImageUtil {
|
|||||||
* @see #convolve(java.awt.image.BufferedImage, java.awt.image.Kernel, int)
|
* @see #convolve(java.awt.image.BufferedImage, java.awt.image.Kernel, int)
|
||||||
*/
|
*/
|
||||||
public static final int EDGE_REFLECT = 2; // as JAI BORDER_REFLECT
|
public static final int EDGE_REFLECT = 2; // as JAI BORDER_REFLECT
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a border to the image while convolving. The border will wrap the
|
* Adds a border to the image while convolving. The border will wrap the
|
||||||
* edges of the original image. This is usually the best choice for tiles.
|
* edges of the original image. This is usually the best choice for tiles.
|
||||||
@@ -229,7 +230,7 @@ public final class ImageUtil {
|
|||||||
* The new image will have the <em>same</em> {@code ColorModel},
|
* The new image will have the <em>same</em> {@code ColorModel},
|
||||||
* {@code Raster} and properties as the original image, if possible.
|
* {@code Raster} and properties as the original image, if possible.
|
||||||
* <p/>
|
* <p/>
|
||||||
* If the image is allready a {@code BufferedImage}, it is simply returned
|
* If the image is already a {@code BufferedImage}, it is simply returned
|
||||||
* and no conversion takes place.
|
* and no conversion takes place.
|
||||||
*
|
*
|
||||||
* @param pOriginal the image to convert.
|
* @param pOriginal the image to convert.
|
||||||
@@ -237,7 +238,7 @@ public final class ImageUtil {
|
|||||||
* @return a {@code BufferedImage}
|
* @return a {@code BufferedImage}
|
||||||
*/
|
*/
|
||||||
public static BufferedImage toBuffered(RenderedImage pOriginal) {
|
public static BufferedImage toBuffered(RenderedImage pOriginal) {
|
||||||
// Don't convert if it allready is a BufferedImage
|
// Don't convert if it already is a BufferedImage
|
||||||
if (pOriginal instanceof BufferedImage) {
|
if (pOriginal instanceof BufferedImage) {
|
||||||
return (BufferedImage) pOriginal;
|
return (BufferedImage) pOriginal;
|
||||||
}
|
}
|
||||||
@@ -283,7 +284,7 @@ public final class ImageUtil {
|
|||||||
* Converts the {@code RenderedImage} to a {@code BufferedImage} of the
|
* Converts the {@code RenderedImage} to a {@code BufferedImage} of the
|
||||||
* given type.
|
* given type.
|
||||||
* <p/>
|
* <p/>
|
||||||
* If the image is allready a {@code BufferedImage} of the given type, it
|
* If the image is already a {@code BufferedImage} of the given type, it
|
||||||
* is simply returned and no conversion takes place.
|
* is simply returned and no conversion takes place.
|
||||||
*
|
*
|
||||||
* @param pOriginal the image to convert.
|
* @param pOriginal the image to convert.
|
||||||
@@ -297,7 +298,7 @@ public final class ImageUtil {
|
|||||||
* @see java.awt.image.BufferedImage#getType()
|
* @see java.awt.image.BufferedImage#getType()
|
||||||
*/
|
*/
|
||||||
public static BufferedImage toBuffered(RenderedImage pOriginal, int pType) {
|
public static BufferedImage toBuffered(RenderedImage pOriginal, int pType) {
|
||||||
// Don't convert if it allready is BufferedImage and correct type
|
// Don't convert if it already is BufferedImage and correct type
|
||||||
if ((pOriginal instanceof BufferedImage) && ((BufferedImage) pOriginal).getType() == pType) {
|
if ((pOriginal instanceof BufferedImage) && ((BufferedImage) pOriginal).getType() == pType) {
|
||||||
return (BufferedImage) pOriginal;
|
return (BufferedImage) pOriginal;
|
||||||
}
|
}
|
||||||
@@ -329,7 +330,7 @@ public final class ImageUtil {
|
|||||||
* given type. The new image will have the same {@code ColorModel},
|
* given type. The new image will have the same {@code ColorModel},
|
||||||
* {@code Raster} and properties as the original image, if possible.
|
* {@code Raster} and properties as the original image, if possible.
|
||||||
* <p/>
|
* <p/>
|
||||||
* If the image is allready a {@code BufferedImage} of the given type, it
|
* If the image is already a {@code BufferedImage} of the given type, it
|
||||||
* is simply returned and no conversion takes place.
|
* is simply returned and no conversion takes place.
|
||||||
* <p/>
|
* <p/>
|
||||||
* This method simply invokes
|
* This method simply invokes
|
||||||
@@ -354,7 +355,7 @@ public final class ImageUtil {
|
|||||||
* The new image will have the same {@code ColorModel}, {@code Raster} and
|
* The new image will have the same {@code ColorModel}, {@code Raster} and
|
||||||
* properties as the original image, if possible.
|
* properties as the original image, if possible.
|
||||||
* <p/>
|
* <p/>
|
||||||
* If the image is allready a {@code BufferedImage}, it is simply returned
|
* If the image is already a {@code BufferedImage}, it is simply returned
|
||||||
* and no conversion takes place.
|
* and no conversion takes place.
|
||||||
*
|
*
|
||||||
* @param pOriginal the image to convert.
|
* @param pOriginal the image to convert.
|
||||||
@@ -365,7 +366,7 @@ public final class ImageUtil {
|
|||||||
* @throws ImageConversionException if the image cannot be converted
|
* @throws ImageConversionException if the image cannot be converted
|
||||||
*/
|
*/
|
||||||
public static BufferedImage toBuffered(Image pOriginal) {
|
public static BufferedImage toBuffered(Image pOriginal) {
|
||||||
// Don't convert if it allready is BufferedImage
|
// Don't convert if it already is BufferedImage
|
||||||
if (pOriginal instanceof BufferedImage) {
|
if (pOriginal instanceof BufferedImage) {
|
||||||
return (BufferedImage) pOriginal;
|
return (BufferedImage) pOriginal;
|
||||||
}
|
}
|
||||||
@@ -536,32 +537,45 @@ public final class ImageUtil {
|
|||||||
*
|
*
|
||||||
* @param pOriginal the orignal image
|
* @param pOriginal the orignal image
|
||||||
* @param pModel the original color model
|
* @param pModel the original color model
|
||||||
* @param mWidth the requested width of the raster
|
* @param width the requested width of the raster
|
||||||
* @param mHeight the requested height of the raster
|
* @param height the requested height of the raster
|
||||||
*
|
*
|
||||||
* @return a new WritableRaster
|
* @return a new WritableRaster
|
||||||
*/
|
*/
|
||||||
static WritableRaster createCompatibleWritableRaster(BufferedImage pOriginal, ColorModel pModel, int mWidth, int mHeight) {
|
static WritableRaster createCompatibleWritableRaster(BufferedImage pOriginal, ColorModel pModel, int width, int height) {
|
||||||
if (pModel == null || equals(pOriginal.getColorModel(), pModel)) {
|
if (pModel == null || equals(pOriginal.getColorModel(), pModel)) {
|
||||||
|
int[] bOffs;
|
||||||
switch (pOriginal.getType()) {
|
switch (pOriginal.getType()) {
|
||||||
case BufferedImage.TYPE_3BYTE_BGR:
|
case BufferedImage.TYPE_3BYTE_BGR:
|
||||||
int[] bOffs = {2, 1, 0}; // NOTE: These are reversed from what the cm.createCompatibleWritableRaster would return
|
bOffs = new int[]{2, 1, 0}; // NOTE: These are reversed from what the cm.createCompatibleWritableRaster would return
|
||||||
return Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
|
return Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
|
||||||
mWidth, mHeight,
|
width, height,
|
||||||
mWidth * 3, 3,
|
width * 3, 3,
|
||||||
bOffs, null);
|
bOffs, null);
|
||||||
case BufferedImage.TYPE_4BYTE_ABGR:
|
case BufferedImage.TYPE_4BYTE_ABGR:
|
||||||
case BufferedImage.TYPE_4BYTE_ABGR_PRE:
|
case BufferedImage.TYPE_4BYTE_ABGR_PRE:
|
||||||
bOffs = new int[] {3, 2, 1, 0}; // NOTE: These are reversed from what the cm.createCompatibleWritableRaster would return
|
bOffs = new int[] {3, 2, 1, 0}; // NOTE: These are reversed from what the cm.createCompatibleWritableRaster would return
|
||||||
return Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
|
return Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE,
|
||||||
mWidth, mHeight,
|
width, height,
|
||||||
mWidth * 4, 4,
|
width * 4, 4,
|
||||||
bOffs, null);
|
bOffs, null);
|
||||||
|
case BufferedImage.TYPE_CUSTOM:
|
||||||
|
// Peek into the sample model to see if we have a sample model that will be incompatible with the default case
|
||||||
|
SampleModel sm = pOriginal.getRaster().getSampleModel();
|
||||||
|
if (sm instanceof ComponentSampleModel) {
|
||||||
|
bOffs = ((ComponentSampleModel) sm).getBandOffsets();
|
||||||
|
return Raster.createInterleavedRaster(sm.getDataType(),
|
||||||
|
width, height,
|
||||||
|
width * bOffs.length, bOffs.length,
|
||||||
|
bOffs, null);
|
||||||
|
}
|
||||||
|
// Else fall through
|
||||||
default:
|
default:
|
||||||
return pOriginal.getColorModel().createCompatibleWritableRaster(mWidth, mHeight);
|
return pOriginal.getColorModel().createCompatibleWritableRaster(width, height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return pModel.createCompatibleWritableRaster(mWidth, mHeight);
|
|
||||||
|
return pModel.createCompatibleWritableRaster(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -569,7 +583,7 @@ public final class ImageUtil {
|
|||||||
* The new image will have the same {@code ColorModel}, {@code Raster} and
|
* The new image will have the same {@code ColorModel}, {@code Raster} and
|
||||||
* properties as the original image, if possible.
|
* properties as the original image, if possible.
|
||||||
* <p/>
|
* <p/>
|
||||||
* If the image is allready a {@code BufferedImage} of the given type, it
|
* If the image is already a {@code BufferedImage} of the given type, it
|
||||||
* is simply returned and no conversion takes place.
|
* is simply returned and no conversion takes place.
|
||||||
*
|
*
|
||||||
* @param pOriginal the image to convert.
|
* @param pOriginal the image to convert.
|
||||||
@@ -597,7 +611,7 @@ public final class ImageUtil {
|
|||||||
* the color model
|
* the color model
|
||||||
*/
|
*/
|
||||||
private static BufferedImage toBuffered(Image pOriginal, int pType, IndexColorModel pICM) {
|
private static BufferedImage toBuffered(Image pOriginal, int pType, IndexColorModel pICM) {
|
||||||
// Don't convert if it allready is BufferedImage and correct type
|
// Don't convert if it already is BufferedImage and correct type
|
||||||
if ((pOriginal instanceof BufferedImage)
|
if ((pOriginal instanceof BufferedImage)
|
||||||
&& ((BufferedImage) pOriginal).getType() == pType
|
&& ((BufferedImage) pOriginal).getType() == pType
|
||||||
&& (pICM == null || equals(((BufferedImage) pOriginal).getColorModel(), pICM))) {
|
&& (pICM == null || equals(((BufferedImage) pOriginal).getColorModel(), pICM))) {
|
||||||
@@ -784,7 +798,7 @@ public final class ImageUtil {
|
|||||||
* Creates a scaled instance of the given {@code Image}, and converts it to
|
* Creates a scaled instance of the given {@code Image}, and converts it to
|
||||||
* a {@code BufferedImage} if needed.
|
* a {@code BufferedImage} if needed.
|
||||||
* If the original image is a {@code BufferedImage} the result will have
|
* If the original image is a {@code BufferedImage} the result will have
|
||||||
* same type and colormodel. Note that this implies overhead, and is
|
* same type and color model. Note that this implies overhead, and is
|
||||||
* probably not useful for anything but {@code IndexColorModel} images.
|
* probably not useful for anything but {@code IndexColorModel} images.
|
||||||
*
|
*
|
||||||
* @param pImage the {@code Image} to scale
|
* @param pImage the {@code Image} to scale
|
||||||
@@ -820,7 +834,7 @@ public final class ImageUtil {
|
|||||||
|
|
||||||
BufferedImage scaled = createResampled(pImage, pWidth, pHeight, pHints);
|
BufferedImage scaled = createResampled(pImage, pWidth, pHeight, pHints);
|
||||||
|
|
||||||
// Convert if colormodels or type differ, to behave as documented
|
// Convert if color models or type differ, to behave as documented
|
||||||
if (type != scaled.getType() && type != BI_TYPE_ANY || !equals(scaled.getColorModel(), cm)) {
|
if (type != scaled.getType() && type != BI_TYPE_ANY || !equals(scaled.getColorModel(), cm)) {
|
||||||
//System.out.print("Converting TYPE " + scaled.getType() + " -> " + type + "... ");
|
//System.out.print("Converting TYPE " + scaled.getType() + " -> " + type + "... ");
|
||||||
//long start = System.currentTimeMillis();
|
//long start = System.currentTimeMillis();
|
||||||
@@ -965,9 +979,6 @@ public final class ImageUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static int convertAWTHints(int pHints) {
|
private static int convertAWTHints(int pHints) {
|
||||||
// TODO: These conversions are broken!
|
|
||||||
// box == area average
|
|
||||||
// point == replicate (or..?)
|
|
||||||
switch (pHints) {
|
switch (pHints) {
|
||||||
case Image.SCALE_FAST:
|
case Image.SCALE_FAST:
|
||||||
case Image.SCALE_REPLICATE:
|
case Image.SCALE_REPLICATE:
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ import java.awt.image.BufferedImage;
|
|||||||
import java.awt.image.ColorModel;
|
import java.awt.image.ColorModel;
|
||||||
import java.awt.image.IndexColorModel;
|
import java.awt.image.IndexColorModel;
|
||||||
import java.awt.image.RenderedImage;
|
import java.awt.image.RenderedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -127,7 +129,7 @@ import java.util.List;
|
|||||||
* @author <A href="mailto:deweese@apache.org">Thomas DeWeese</A>
|
* @author <A href="mailto:deweese@apache.org">Thomas DeWeese</A>
|
||||||
* @author <A href="mailto:jun@oop-reserch.com">Jun Inamori</A>
|
* @author <A href="mailto:jun@oop-reserch.com">Jun Inamori</A>
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/IndexImage.java#1 $
|
* @version $Id: IndexImage.java#1 $
|
||||||
* @see DiffusionDither
|
* @see DiffusionDither
|
||||||
*/
|
*/
|
||||||
class IndexImage {
|
class IndexImage {
|
||||||
@@ -237,19 +239,22 @@ class IndexImage {
|
|||||||
if (this.val != val) {
|
if (this.val != val) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to define a cube of the colorspace. The cube can be split
|
* Used to define a cube of the color space. The cube can be split
|
||||||
* approximagely in half to generate two cubes.
|
* approximately in half to generate two cubes.
|
||||||
*/
|
*/
|
||||||
private static class Cube {
|
private static class Cube {
|
||||||
int[] min = {0, 0, 0}, max = {255, 255, 255};
|
int[] min = {0, 0, 0};
|
||||||
|
int[] max = {255, 255, 255};
|
||||||
boolean done = false;
|
boolean done = false;
|
||||||
List[] colors = null;
|
List<Counter>[] colors = null;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
static final int RED = 0;
|
static final int RED = 0;
|
||||||
static final int GRN = 1;
|
static final int GRN = 1;
|
||||||
@@ -261,7 +266,7 @@ class IndexImage {
|
|||||||
* @param colors contains the 3D color histogram to be subdivided
|
* @param colors contains the 3D color histogram to be subdivided
|
||||||
* @param count the total number of pixels in the 3D histogram.
|
* @param count the total number of pixels in the 3D histogram.
|
||||||
*/
|
*/
|
||||||
public Cube(List[] colors, int count) {
|
public Cube(List<Counter>[] colors, int count) {
|
||||||
this.colors = colors;
|
this.colors = colors;
|
||||||
this.count = count;
|
this.count = count;
|
||||||
}
|
}
|
||||||
@@ -312,20 +317,27 @@ class IndexImage {
|
|||||||
c0 = RED;
|
c0 = RED;
|
||||||
c1 = GRN;
|
c1 = GRN;
|
||||||
}
|
}
|
||||||
|
|
||||||
Cube ret;
|
Cube ret;
|
||||||
|
|
||||||
ret = splitChannel(splitChannel, c0, c1);
|
ret = splitChannel(splitChannel, c0, c1);
|
||||||
|
|
||||||
if (ret != null) {
|
if (ret != null) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = splitChannel(c0, splitChannel, c1);
|
ret = splitChannel(c0, splitChannel, c1);
|
||||||
|
|
||||||
if (ret != null) {
|
if (ret != null) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = splitChannel(c1, splitChannel, c0);
|
ret = splitChannel(c1, splitChannel, c0);
|
||||||
|
|
||||||
if (ret != null) {
|
if (ret != null) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
done = true;
|
done = true;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -381,16 +393,13 @@ class IndexImage {
|
|||||||
|
|
||||||
for (int k = minIdx[c1]; k <= maxIdx[c1]; k++) {
|
for (int k = minIdx[c1]; k <= maxIdx[c1]; k++) {
|
||||||
int idx = idx2 | (k << c1Sh4);
|
int idx = idx2 | (k << c1Sh4);
|
||||||
List v = colors[idx];
|
List<Counter> v = colors[idx];
|
||||||
|
|
||||||
if (v == null) {
|
if (v == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Iterator itr = v.iterator();
|
|
||||||
Counter c;
|
for (Counter c : v) {
|
||||||
|
|
||||||
while (itr.hasNext()) {
|
|
||||||
c = (Counter) itr.next();
|
|
||||||
val = c.val;
|
val = c.val;
|
||||||
vals[0] = (val & 0xFF0000) >> 16;
|
vals[0] = (val & 0xFF0000) >> 16;
|
||||||
vals[1] = (val & 0xFF00) >> 8;
|
vals[1] = (val & 0xFF00) >> 8;
|
||||||
@@ -425,7 +434,6 @@ class IndexImage {
|
|||||||
int c = counts[i];
|
int c = counts[i];
|
||||||
|
|
||||||
if (c == 0) {
|
if (c == 0) {
|
||||||
|
|
||||||
// No counts below this so move up bottom of cube.
|
// No counts below this so move up bottom of cube.
|
||||||
if ((tcount == 0) && (i < max[splitChannel])) {
|
if ((tcount == 0) && (i < max[splitChannel])) {
|
||||||
this.min[splitChannel] = i + 1;
|
this.min[splitChannel] = i + 1;
|
||||||
@@ -438,10 +446,8 @@ class IndexImage {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((half - tcount) <= ((tcount + c) - half)) {
|
if ((half - tcount) <= ((tcount + c) - half)) {
|
||||||
|
|
||||||
// Then lastAdd is a better top idx for this then i.
|
// Then lastAdd is a better top idx for this then i.
|
||||||
if (lastAdd == -1) {
|
if (lastAdd == -1) {
|
||||||
|
|
||||||
// No lower place to break.
|
// No lower place to break.
|
||||||
if (c == this.count) {
|
if (c == this.count) {
|
||||||
|
|
||||||
@@ -465,13 +471,11 @@ class IndexImage {
|
|||||||
else {
|
else {
|
||||||
if (i == this.max[splitChannel]) {
|
if (i == this.max[splitChannel]) {
|
||||||
if (c == this.count) {
|
if (c == this.count) {
|
||||||
|
|
||||||
// would move min up but that should
|
// would move min up but that should
|
||||||
// have happened already.
|
// have happened already.
|
||||||
return null;// no split to make.
|
return null;// no split to make.
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
// Would like to break between i and i+1
|
// Would like to break between i and i+1
|
||||||
// but no i+1 so use lastAdd and i;
|
// but no i+1 so use lastAdd and i;
|
||||||
splitLo = lastAdd;
|
splitLo = lastAdd;
|
||||||
@@ -503,6 +507,7 @@ class IndexImage {
|
|||||||
ret.max[c0] = this.max[c0];
|
ret.max[c0] = this.max[c0];
|
||||||
ret.min[c1] = this.min[c1];
|
ret.min[c1] = this.min[c1];
|
||||||
ret.max[c1] = this.max[c1];
|
ret.max[c1] = this.max[c1];
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -515,6 +520,7 @@ class IndexImage {
|
|||||||
if (this.count == 0) {
|
if (this.count == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
float red = 0, grn = 0, blu = 0;
|
float red = 0, grn = 0, blu = 0;
|
||||||
int minR = min[0], minG = min[1], minB = min[2];
|
int minR = min[0], minG = min[1], minB = min[2];
|
||||||
int maxR = max[0], maxG = max[1], maxB = max[2];
|
int maxR = max[0], maxG = max[1], maxB = max[2];
|
||||||
@@ -531,20 +537,18 @@ class IndexImage {
|
|||||||
|
|
||||||
for (int k = minIdx[2]; k <= maxIdx[2]; k++) {
|
for (int k = minIdx[2]; k <= maxIdx[2]; k++) {
|
||||||
int idx = idx2 | k;
|
int idx = idx2 | k;
|
||||||
List v = colors[idx];
|
List<Counter> v = colors[idx];
|
||||||
|
|
||||||
if (v == null) {
|
if (v == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Iterator itr = v.iterator();
|
|
||||||
Counter c;
|
|
||||||
|
|
||||||
while (itr.hasNext()) {
|
for (Counter c : v) {
|
||||||
c = (Counter) itr.next();
|
|
||||||
val = c.val;
|
val = c.val;
|
||||||
ired = (val & 0xFF0000) >> 16;
|
ired = (val & 0xFF0000) >> 16;
|
||||||
igrn = (val & 0x00FF00) >> 8;
|
igrn = (val & 0x00FF00) >> 8;
|
||||||
iblu = (val & 0x0000FF);
|
iblu = (val & 0x0000FF);
|
||||||
|
|
||||||
if (((ired >= minR) && (ired <= maxR)) && ((igrn >= minG) && (igrn <= maxG)) && ((iblu >= minB) && (iblu <= maxB))) {
|
if (((ired >= minR) && (ired <= maxR)) && ((igrn >= minG) && (igrn <= maxG)) && ((iblu >= minB) && (iblu <= maxB))) {
|
||||||
weight = (c.count / (float) this.count);
|
weight = (c.count / (float) this.count);
|
||||||
red += ((float) ired) * weight;
|
red += ((float) ired) * weight;
|
||||||
@@ -579,10 +583,7 @@ class IndexImage {
|
|||||||
* This version will be removed in a later version of the API.
|
* This version will be removed in a later version of the API.
|
||||||
*/
|
*/
|
||||||
public static IndexColorModel getIndexColorModel(Image pImage, int pNumberOfColors, boolean pFast) {
|
public static IndexColorModel getIndexColorModel(Image pImage, int pNumberOfColors, boolean pFast) {
|
||||||
|
return getIndexColorModel(pImage, pNumberOfColors, pFast ? COLOR_SELECTION_FAST : COLOR_SELECTION_QUALITY);
|
||||||
return getIndexColorModel(pImage, pNumberOfColors, pFast
|
|
||||||
? COLOR_SELECTION_FAST
|
|
||||||
: COLOR_SELECTION_QUALITY);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -636,17 +637,12 @@ class IndexImage {
|
|||||||
// We now have at least a buffered image, create model from it
|
// We now have at least a buffered image, create model from it
|
||||||
if (icm == null) {
|
if (icm == null) {
|
||||||
icm = createIndexColorModel(ImageUtil.toBuffered(image), pNumberOfColors, pHints);
|
icm = createIndexColorModel(ImageUtil.toBuffered(image), pNumberOfColors, pHints);
|
||||||
|
}
|
||||||
//System.out.println("IndexColorModel created from colors.");
|
|
||||||
}
|
|
||||||
else if (!(icm instanceof InverseColorMapIndexColorModel)) {
|
else if (!(icm instanceof InverseColorMapIndexColorModel)) {
|
||||||
// If possible, use faster code
|
// If possible, use faster code
|
||||||
//System.out.println("Wrappimg IndexColorModel in InverseColorMapIndexColorModel");
|
|
||||||
icm = new InverseColorMapIndexColorModel(icm);
|
icm = new InverseColorMapIndexColorModel(icm);
|
||||||
}
|
}
|
||||||
//else {
|
|
||||||
//System.out.println("Allredy InverseColorMapIndexColorModel");
|
|
||||||
//}
|
|
||||||
return icm;
|
return icm;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -674,7 +670,8 @@ class IndexImage {
|
|||||||
int height = pImage.getHeight();
|
int height = pImage.getHeight();
|
||||||
|
|
||||||
// Using 4 bits from R, G & B.
|
// Using 4 bits from R, G & B.
|
||||||
List[] colors = new List[1 << 12];// [4096]
|
@SuppressWarnings("unchecked")
|
||||||
|
List<Counter>[] colors = new List[1 << 12];// [4096]
|
||||||
|
|
||||||
// Speedup, doesn't decrease image quality much
|
// Speedup, doesn't decrease image quality much
|
||||||
int step = 1;
|
int step = 1;
|
||||||
@@ -739,13 +736,16 @@ class IndexImage {
|
|||||||
while (numberOfCubes < pNumberOfColors) {
|
while (numberOfCubes < pNumberOfColors) {
|
||||||
while (cubes[fCube].isDone()) {
|
while (cubes[fCube].isDone()) {
|
||||||
fCube++;
|
fCube++;
|
||||||
|
|
||||||
if (fCube == numberOfCubes) {
|
if (fCube == numberOfCubes) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fCube == numberOfCubes) {
|
if (fCube == numberOfCubes) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Cube cube = cubes[fCube];
|
Cube cube = cubes[fCube];
|
||||||
Cube newCube = cube.split();
|
Cube newCube = cube.split();
|
||||||
|
|
||||||
@@ -756,6 +756,7 @@ class IndexImage {
|
|||||||
cube = newCube;
|
cube = newCube;
|
||||||
newCube = tmp;
|
newCube = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
int j = fCube;
|
int j = fCube;
|
||||||
int count = cube.count;
|
int count = cube.count;
|
||||||
|
|
||||||
@@ -765,17 +766,19 @@ class IndexImage {
|
|||||||
}
|
}
|
||||||
cubes[j++] = cubes[i];
|
cubes[j++] = cubes[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
cubes[j++] = cube;
|
cubes[j++] = cube;
|
||||||
count = newCube.count;
|
count = newCube.count;
|
||||||
|
|
||||||
while (j < numberOfCubes) {
|
while (j < numberOfCubes) {
|
||||||
if (cubes[j].count < count) {
|
if (cubes[j].count < count) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
j++;
|
j++;
|
||||||
}
|
}
|
||||||
for (int i = numberOfCubes; i > j; i--) {
|
|
||||||
cubes[i] = cubes[i - 1];
|
System.arraycopy(cubes, j, cubes, j + 1, numberOfCubes - j);
|
||||||
}
|
|
||||||
cubes[j/*++*/] = newCube;
|
cubes[j/*++*/] = newCube;
|
||||||
numberOfCubes++;
|
numberOfCubes++;
|
||||||
}
|
}
|
||||||
@@ -803,15 +806,13 @@ class IndexImage {
|
|||||||
// - transparency added to all totally black colors?
|
// - transparency added to all totally black colors?
|
||||||
int numOfBits = 8;
|
int numOfBits = 8;
|
||||||
|
|
||||||
// -- haraldK, 20021024, as suggested by Thomas E Deweese
|
// -- haraldK, 20021024, as suggested by Thomas E. Deweese
|
||||||
// plus adding a transparent pixel
|
// plus adding a transparent pixel
|
||||||
IndexColorModel icm;
|
IndexColorModel icm;
|
||||||
if (useTransparency) {
|
if (useTransparency) {
|
||||||
//icm = new IndexColorModel(numOfBits, r.length, r, g, b, r.length - 1);
|
|
||||||
icm = new InverseColorMapIndexColorModel(numOfBits, r.length, r, g, b, r.length - 1);
|
icm = new InverseColorMapIndexColorModel(numOfBits, r.length, r, g, b, r.length - 1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//icm = new IndexColorModel(numOfBits, r.length, r, g, b);
|
|
||||||
icm = new InverseColorMapIndexColorModel(numOfBits, r.length, r, g, b);
|
icm = new InverseColorMapIndexColorModel(numOfBits, r.length, r, g, b);
|
||||||
}
|
}
|
||||||
return icm;
|
return icm;
|
||||||
@@ -925,7 +926,7 @@ class IndexImage {
|
|||||||
* @see IndexColorModel
|
* @see IndexColorModel
|
||||||
*/
|
*/
|
||||||
public static BufferedImage getIndexedImage(BufferedImage pImage, int pNumberOfColors, Color pMatte, int pHints) {
|
public static BufferedImage getIndexedImage(BufferedImage pImage, int pNumberOfColors, Color pMatte, int pHints) {
|
||||||
// NOTE: We need to apply matte before creating colormodel, otherwise we
|
// NOTE: We need to apply matte before creating color model, otherwise we
|
||||||
// won't have colors for potential faded transitions
|
// won't have colors for potential faded transitions
|
||||||
IndexColorModel icm;
|
IndexColorModel icm;
|
||||||
|
|
||||||
@@ -985,15 +986,16 @@ class IndexImage {
|
|||||||
final int width = pImage.getWidth();
|
final int width = pImage.getWidth();
|
||||||
final int height = pImage.getHeight();
|
final int height = pImage.getHeight();
|
||||||
|
|
||||||
// Support transparancy?
|
// Support transparency?
|
||||||
boolean transparency = isTransparent(pHints) && (pImage.getColorModel().getTransparency() != Transparency.OPAQUE) && (pColors.getTransparency() != Transparency.OPAQUE);
|
boolean transparency = isTransparent(pHints) && (pImage.getColorModel().getTransparency() != Transparency.OPAQUE) && (pColors.getTransparency() != Transparency.OPAQUE);
|
||||||
|
|
||||||
// Create image with solid background
|
// Create image with solid background
|
||||||
BufferedImage solid = pImage;
|
BufferedImage solid = pImage;
|
||||||
|
|
||||||
if (pMatte != null) {// transparency doesn't really matter
|
if (pMatte != null) { // transparency doesn't really matter
|
||||||
solid = createSolid(pImage, pMatte);
|
solid = createSolid(pImage, pMatte);
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferedImage indexed;
|
BufferedImage indexed;
|
||||||
|
|
||||||
// Support TYPE_BYTE_BINARY, but only for 2 bit images, as the default
|
// Support TYPE_BYTE_BINARY, but only for 2 bit images, as the default
|
||||||
@@ -1044,12 +1046,12 @@ class IndexImage {
|
|||||||
finally {
|
finally {
|
||||||
g2d.dispose();
|
g2d.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transparency support, this approach seems lame, but it's the only
|
// Transparency support, this approach seems lame, but it's the only
|
||||||
// solution I've found until now (that actually works).
|
// solution I've found until now (that actually works).
|
||||||
// Got anything to do with isPremultiplied? Hmm...
|
|
||||||
if (transparency) {
|
if (transparency) {
|
||||||
// Re-apply the alpha-channel of the original image
|
// Re-apply the alpha-channel of the original image
|
||||||
applyAlpha(indexed, pImage);
|
applyAlpha(indexed, pImage);
|
||||||
@@ -1183,14 +1185,14 @@ class IndexImage {
|
|||||||
* @param pAlpha the image containing the alpha
|
* @param pAlpha the image containing the alpha
|
||||||
*/
|
*/
|
||||||
private static void applyAlpha(BufferedImage pImage, BufferedImage pAlpha) {
|
private static void applyAlpha(BufferedImage pImage, BufferedImage pAlpha) {
|
||||||
// Apply alpha as transparancy, using threshold of 25%
|
// Apply alpha as transparency, using threshold of 25%
|
||||||
for (int y = 0; y < pAlpha.getHeight(); y++) {
|
for (int y = 0; y < pAlpha.getHeight(); y++) {
|
||||||
for (int x = 0; x < pAlpha.getWidth(); x++) {
|
for (int x = 0; x < pAlpha.getWidth(); x++) {
|
||||||
|
|
||||||
// Get alpha component of pixel, if less than 25% opaque
|
// Get alpha component of pixel, if less than 25% opaque
|
||||||
// (0x40 = 64 => 25% of 256), the pixel will be transparent
|
// (0x40 = 64 => 25% of 256), the pixel will be transparent
|
||||||
if (((pAlpha.getRGB(x, y) >> 24) & 0xFF) < 0x40) {
|
if (((pAlpha.getRGB(x, y) >> 24) & 0xFF) < 0x40) {
|
||||||
pImage.setRGB(x, y, 0x00FFFFFF);// 100% transparent
|
pImage.setRGB(x, y, 0x00FFFFFF); // 100% transparent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1200,7 +1202,6 @@ class IndexImage {
|
|||||||
* This class is also a command-line utility.
|
* This class is also a command-line utility.
|
||||||
*/
|
*/
|
||||||
public static void main(String pArgs[]) {
|
public static void main(String pArgs[]) {
|
||||||
|
|
||||||
// Defaults
|
// Defaults
|
||||||
int argIdx = 0;
|
int argIdx = 0;
|
||||||
int speedTest = -1;
|
int speedTest = -1;
|
||||||
@@ -1237,14 +1238,13 @@ class IndexImage {
|
|||||||
speedTest = 10;
|
speedTest = 10;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if ((pArgs[argIdx].charAt(1) == 'w') || pArgs[argIdx].equals("--overwrite")) {
|
||||||
if ((pArgs[argIdx].charAt(1) == 'w') || pArgs[argIdx].equals("--overwrite")) {
|
|
||||||
overWrite = true;
|
overWrite = true;
|
||||||
argIdx++;
|
argIdx++;
|
||||||
}
|
}
|
||||||
else
|
else if ((pArgs[argIdx].charAt(1) == 'c') || pArgs[argIdx].equals("--colors")) {
|
||||||
if ((pArgs[argIdx].charAt(1) == 'c') || pArgs[argIdx].equals("--colors")) {
|
|
||||||
argIdx++;
|
argIdx++;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
numColors = Integer.parseInt(pArgs[argIdx++]);
|
numColors = Integer.parseInt(pArgs[argIdx++]);
|
||||||
}
|
}
|
||||||
@@ -1253,34 +1253,28 @@ class IndexImage {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if ((pArgs[argIdx].charAt(1) == 'g') || pArgs[argIdx].equals("--grayscale")) {
|
||||||
if ((pArgs[argIdx].charAt(1) == 'g') || pArgs[argIdx].equals("--grayscale")) {
|
|
||||||
argIdx++;
|
argIdx++;
|
||||||
gray = true;
|
gray = true;
|
||||||
}
|
}
|
||||||
else
|
else if ((pArgs[argIdx].charAt(1) == 'm') || pArgs[argIdx].equals("--monochrome")) {
|
||||||
if ((pArgs[argIdx].charAt(1) == 'm') || pArgs[argIdx].equals("--monochrome")) {
|
|
||||||
argIdx++;
|
argIdx++;
|
||||||
numColors = 2;
|
numColors = 2;
|
||||||
monochrome = true;
|
monochrome = true;
|
||||||
}
|
}
|
||||||
else
|
else if ((pArgs[argIdx].charAt(1) == 'd') || pArgs[argIdx].equals("--dither")) {
|
||||||
if ((pArgs[argIdx].charAt(1) == 'd') || pArgs[argIdx].equals("--dither")) {
|
|
||||||
argIdx++;
|
argIdx++;
|
||||||
dither = pArgs[argIdx++];
|
dither = pArgs[argIdx++];
|
||||||
}
|
}
|
||||||
else
|
else if ((pArgs[argIdx].charAt(1) == 'p') || pArgs[argIdx].equals("--palette")) {
|
||||||
if ((pArgs[argIdx].charAt(1) == 'p') || pArgs[argIdx].equals("--palette")) {
|
|
||||||
argIdx++;
|
argIdx++;
|
||||||
paletteFileName = pArgs[argIdx++];
|
paletteFileName = pArgs[argIdx++];
|
||||||
}
|
}
|
||||||
else
|
else if ((pArgs[argIdx].charAt(1) == 'q') || pArgs[argIdx].equals("--quality")) {
|
||||||
if ((pArgs[argIdx].charAt(1) == 'q') || pArgs[argIdx].equals("--quality")) {
|
|
||||||
argIdx++;
|
argIdx++;
|
||||||
quality = pArgs[argIdx++];
|
quality = pArgs[argIdx++];
|
||||||
}
|
}
|
||||||
else
|
else if ((pArgs[argIdx].charAt(1) == 'b') || pArgs[argIdx].equals("--bgcolor")) {
|
||||||
if ((pArgs[argIdx].charAt(1) == 'b') || pArgs[argIdx].equals("--bgcolor")) {
|
|
||||||
argIdx++;
|
argIdx++;
|
||||||
try {
|
try {
|
||||||
background = StringUtil.toColor(pArgs[argIdx++]);
|
background = StringUtil.toColor(pArgs[argIdx++]);
|
||||||
@@ -1290,18 +1284,15 @@ class IndexImage {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if ((pArgs[argIdx].charAt(1) == 't') || pArgs[argIdx].equals("--transparency")) {
|
||||||
if ((pArgs[argIdx].charAt(1) == 't') || pArgs[argIdx].equals("--transparency")) {
|
|
||||||
argIdx++;
|
argIdx++;
|
||||||
transparency = true;
|
transparency = true;
|
||||||
}
|
}
|
||||||
else
|
else if ((pArgs[argIdx].charAt(1) == 'f') || pArgs[argIdx].equals("--outputformat")) {
|
||||||
if ((pArgs[argIdx].charAt(1) == 'f') || pArgs[argIdx].equals("--outputformat")) {
|
|
||||||
argIdx++;
|
argIdx++;
|
||||||
format = StringUtil.toLowerCase(pArgs[argIdx++]);
|
format = StringUtil.toLowerCase(pArgs[argIdx++]);
|
||||||
}
|
}
|
||||||
else
|
else if ((pArgs[argIdx].charAt(1) == 'h') || pArgs[argIdx].equals("--help")) {
|
||||||
if ((pArgs[argIdx].charAt(1) == 'h') || pArgs[argIdx].equals("--help")) {
|
|
||||||
argIdx++;
|
argIdx++;
|
||||||
|
|
||||||
// Setting errArgs to true, to print usage
|
// Setting errArgs to true, to print usage
|
||||||
@@ -1321,6 +1312,7 @@ class IndexImage {
|
|||||||
? ", "
|
? ", "
|
||||||
: "\n"));
|
: "\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
System.err.print("Output format names: ");
|
System.err.print("Output format names: ");
|
||||||
String[] writers = ImageIO.getWriterFormatNames();
|
String[] writers = ImageIO.getWriterFormatNames();
|
||||||
|
|
||||||
@@ -1333,7 +1325,7 @@ class IndexImage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read in image
|
// Read in image
|
||||||
java.io.File in = new java.io.File(pArgs[argIdx++]);
|
File in = new File(pArgs[argIdx++]);
|
||||||
|
|
||||||
if (!in.exists()) {
|
if (!in.exists()) {
|
||||||
System.err.println("File \"" + in.getAbsolutePath() + "\" does not exist!");
|
System.err.println("File \"" + in.getAbsolutePath() + "\" does not exist!");
|
||||||
@@ -1341,10 +1333,10 @@ class IndexImage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Read palette if needed
|
// Read palette if needed
|
||||||
java.io.File paletteFile = null;
|
File paletteFile = null;
|
||||||
|
|
||||||
if (paletteFileName != null) {
|
if (paletteFileName != null) {
|
||||||
paletteFile = new java.io.File(paletteFileName);
|
paletteFile = new File(paletteFileName);
|
||||||
if (!paletteFile.exists()) {
|
if (!paletteFile.exists()) {
|
||||||
System.err.println("File \"" + in.getAbsolutePath() + "\" does not exist!");
|
System.err.println("File \"" + in.getAbsolutePath() + "\" does not exist!");
|
||||||
System.exit(5);
|
System.exit(5);
|
||||||
@@ -1352,10 +1344,10 @@ class IndexImage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we can write
|
// Make sure we can write
|
||||||
java.io.File out;
|
File out;
|
||||||
|
|
||||||
if (argIdx < pArgs.length) {
|
if (argIdx < pArgs.length) {
|
||||||
out = new java.io.File(pArgs[argIdx/*++*/]);
|
out = new File(pArgs[argIdx/*++*/]);
|
||||||
|
|
||||||
// Get format from file extension
|
// Get format from file extension
|
||||||
if (format == null) {
|
if (format == null) {
|
||||||
@@ -1363,7 +1355,6 @@ class IndexImage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
// Create new file in current dir, same name + format extension
|
// Create new file in current dir, same name + format extension
|
||||||
String baseName = FileUtil.getBasename(in);
|
String baseName = FileUtil.getBasename(in);
|
||||||
|
|
||||||
@@ -1371,8 +1362,9 @@ class IndexImage {
|
|||||||
if (format == null) {
|
if (format == null) {
|
||||||
format = "png";
|
format = "png";
|
||||||
}
|
}
|
||||||
out = new java.io.File(baseName + '.' + format);
|
out = new File(baseName + '.' + format);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!overWrite && out.exists()) {
|
if (!overWrite && out.exists()) {
|
||||||
System.err.println("The file \"" + out.getAbsolutePath() + "\" allready exists!");
|
System.err.println("The file \"" + out.getAbsolutePath() + "\" allready exists!");
|
||||||
System.exit(5);
|
System.exit(5);
|
||||||
@@ -1396,7 +1388,7 @@ class IndexImage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (java.io.IOException ioe) {
|
catch (IOException ioe) {
|
||||||
ioe.printStackTrace(System.err);
|
ioe.printStackTrace(System.err);
|
||||||
System.exit(5);
|
System.exit(5);
|
||||||
}
|
}
|
||||||
@@ -1441,16 +1433,14 @@ class IndexImage {
|
|||||||
///////////////////////////////
|
///////////////////////////////
|
||||||
// Index
|
// Index
|
||||||
long start = 0;
|
long start = 0;
|
||||||
long end;
|
|
||||||
|
|
||||||
if (speedTest > 0) {
|
if (speedTest > 0) {
|
||||||
|
|
||||||
// SPEED TESTING
|
// SPEED TESTING
|
||||||
System.out.println("Measuring speed!");
|
System.out.println("Measuring speed!");
|
||||||
start = System.currentTimeMillis();
|
start = System.currentTimeMillis();
|
||||||
|
|
||||||
// END SPEED TESTING
|
// END SPEED TESTING
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferedImage indexed;
|
BufferedImage indexed;
|
||||||
IndexColorModel colors;
|
IndexColorModel colors;
|
||||||
|
|
||||||
@@ -1459,7 +1449,6 @@ class IndexImage {
|
|||||||
colors = MonochromeColorModel.getInstance();
|
colors = MonochromeColorModel.getInstance();
|
||||||
}
|
}
|
||||||
else if (gray) {
|
else if (gray) {
|
||||||
|
|
||||||
//indexed = ImageUtil.toBuffered(ImageUtil.grayscale(image), BufferedImage.TYPE_BYTE_GRAY);
|
//indexed = ImageUtil.toBuffered(ImageUtil.grayscale(image), BufferedImage.TYPE_BYTE_GRAY);
|
||||||
image = ImageUtil.toBuffered(ImageUtil.grayscale(image));
|
image = ImageUtil.toBuffered(ImageUtil.grayscale(image));
|
||||||
indexed = getIndexedImage(image, colors = getIndexColorModel(image, numColors, hints), background, hints);
|
indexed = getIndexedImage(image, colors = getIndexColorModel(image, numColors, hints), background, hints);
|
||||||
@@ -1470,7 +1459,6 @@ class IndexImage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (paletteImg != null) {
|
else if (paletteImg != null) {
|
||||||
|
|
||||||
// Get palette from image
|
// Get palette from image
|
||||||
indexed = getIndexedImage(ImageUtil.toBuffered(image, BufferedImage.TYPE_INT_ARGB),
|
indexed = getIndexedImage(ImageUtil.toBuffered(image, BufferedImage.TYPE_INT_ARGB),
|
||||||
colors = getIndexColorModel(paletteImg, numColors, hints), background, hints);
|
colors = getIndexColorModel(paletteImg, numColors, hints), background, hints);
|
||||||
@@ -1479,12 +1467,10 @@ class IndexImage {
|
|||||||
image = ImageUtil.toBuffered(image, BufferedImage.TYPE_INT_ARGB);
|
image = ImageUtil.toBuffered(image, BufferedImage.TYPE_INT_ARGB);
|
||||||
indexed = getIndexedImage(image, colors = getIndexColorModel(image, numColors, hints), background, hints);
|
indexed = getIndexedImage(image, colors = getIndexColorModel(image, numColors, hints), background, hints);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (speedTest > 0) {
|
if (speedTest > 0) {
|
||||||
|
|
||||||
// SPEED TESTING
|
// SPEED TESTING
|
||||||
end = System.currentTimeMillis();
|
System.out.println("Color selection + dither: " + (System.currentTimeMillis() - start) + " ms");
|
||||||
System.out.println("Color selection + dither: " + (end - start) + " ms");
|
|
||||||
|
|
||||||
// END SPEED TESTING
|
// END SPEED TESTING
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1494,11 +1480,11 @@ class IndexImage {
|
|||||||
System.err.println("No writer for format: \"" + format + "\"!");
|
System.err.println("No writer for format: \"" + format + "\"!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (java.io.IOException ioe) {
|
catch (IOException ioe) {
|
||||||
ioe.printStackTrace(System.err);
|
ioe.printStackTrace(System.err);
|
||||||
}
|
}
|
||||||
if (speedTest > 0) {
|
|
||||||
|
|
||||||
|
if (speedTest > 0) {
|
||||||
// SPEED TESTING
|
// SPEED TESTING
|
||||||
System.out.println("Measuring speed!");
|
System.out.println("Measuring speed!");
|
||||||
|
|
||||||
@@ -1513,17 +1499,16 @@ class IndexImage {
|
|||||||
for (int i = 0; i < speedTest; i++) {
|
for (int i = 0; i < speedTest; i++) {
|
||||||
start = System.currentTimeMillis();
|
start = System.currentTimeMillis();
|
||||||
getIndexedImage(image, colors, background, hints);
|
getIndexedImage(image, colors, background, hints);
|
||||||
end = System.currentTimeMillis();
|
time += (System.currentTimeMillis() - start);
|
||||||
time += (end - start);
|
|
||||||
System.out.print('.');
|
System.out.print('.');
|
||||||
if ((i + 1) % 10 == 0) {
|
if ((i + 1) % 10 == 0) {
|
||||||
System.out.println("\nAverage (after " + (i + 1) + " iterations): " + (time / (i + 1)) + "ms");
|
System.out.println("\nAverage (after " + (i + 1) + " iterations): " + (time / (i + 1)) + "ms");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("\nDither only:");
|
System.out.println("\nDither only:");
|
||||||
System.out.println("Total time (" + speedTest + " invocations): " + time + "ms");
|
System.out.println("Total time (" + speedTest + " invocations): " + time + "ms");
|
||||||
System.out.println("Average: " + time / speedTest + "ms");
|
System.out.println("Average: " + time / speedTest + "ms");
|
||||||
|
|
||||||
// END SPEED TESTING
|
// END SPEED TESTING
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,12 +72,12 @@ class InverseColorMap {
|
|||||||
*/
|
*/
|
||||||
final static int MAXQUANTVAL = 1 << 5;
|
final static int MAXQUANTVAL = 1 << 5;
|
||||||
|
|
||||||
byte[] mRGBMapByte;
|
byte[] rgbMapByte;
|
||||||
int[] mRGBMapInt;
|
int[] rgbMapInt;
|
||||||
int mNumColors;
|
int numColors;
|
||||||
int mMaxColor;
|
int maxColor;
|
||||||
byte[] mInverseRGB; // inverse rgb color map
|
byte[] inverseRGB; // inverse rgb color map
|
||||||
int mTransparentIndex = -1;
|
int transparentIndex = -1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
* @param pRGBColorMap the rgb color map to create inverse color map for.
|
||||||
@@ -99,11 +99,11 @@ class InverseColorMap {
|
|||||||
* @param pTransparent the index of the transparent pixel in the map
|
* @param pTransparent the index of the transparent pixel in the map
|
||||||
*/
|
*/
|
||||||
InverseColorMap(byte[] pRGBColorMap, int pTransparent) {
|
InverseColorMap(byte[] pRGBColorMap, int pTransparent) {
|
||||||
mRGBMapByte = pRGBColorMap;
|
rgbMapByte = pRGBColorMap;
|
||||||
mNumColors = mRGBMapByte.length / 4;
|
numColors = rgbMapByte.length / 4;
|
||||||
mTransparentIndex = pTransparent;
|
transparentIndex = pTransparent;
|
||||||
|
|
||||||
mInverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
|
inverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
|
||||||
initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]);
|
initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,11 +112,11 @@ class InverseColorMap {
|
|||||||
* @param pTransparent the index of the transparent pixel in the map
|
* @param pTransparent the index of the transparent pixel in the map
|
||||||
*/
|
*/
|
||||||
InverseColorMap(int[] pRGBColorMap, int pTransparent) {
|
InverseColorMap(int[] pRGBColorMap, int pTransparent) {
|
||||||
mRGBMapInt = pRGBColorMap;
|
rgbMapInt = pRGBColorMap;
|
||||||
mNumColors = mRGBMapInt.length;
|
numColors = rgbMapInt.length;
|
||||||
mTransparentIndex = pTransparent;
|
transparentIndex = pTransparent;
|
||||||
|
|
||||||
mInverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
|
inverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
|
||||||
initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]);
|
initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,8 +130,8 @@ class InverseColorMap {
|
|||||||
final int xsqr = 1 << (TRUNCBITS * 2); // 64 - twice the smallest step size vale of quantized colors
|
final int xsqr = 1 << (TRUNCBITS * 2); // 64 - twice the smallest step size vale of quantized colors
|
||||||
final int xsqr2 = xsqr + xsqr;
|
final int xsqr2 = xsqr + xsqr;
|
||||||
|
|
||||||
for (int i = 0; i < mNumColors; ++i) {
|
for (int i = 0; i < numColors; ++i) {
|
||||||
if (i == mTransparentIndex) {
|
if (i == transparentIndex) {
|
||||||
// Skip the transparent pixel
|
// Skip the transparent pixel
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -141,15 +141,15 @@ class InverseColorMap {
|
|||||||
int blue, b, bdist, binc, bxx;
|
int blue, b, bdist, binc, bxx;
|
||||||
|
|
||||||
// HaraldK 20040801: Added support for int[]
|
// HaraldK 20040801: Added support for int[]
|
||||||
if (mRGBMapByte != null) {
|
if (rgbMapByte != null) {
|
||||||
red = mRGBMapByte[i * 4] & 0xFF;
|
red = rgbMapByte[i * 4] & 0xFF;
|
||||||
green = mRGBMapByte[i * 4 + 1] & 0xFF;
|
green = rgbMapByte[i * 4 + 1] & 0xFF;
|
||||||
blue = mRGBMapByte[i * 4 + 2] & 0xFF;
|
blue = rgbMapByte[i * 4 + 2] & 0xFF;
|
||||||
}
|
}
|
||||||
else if (mRGBMapInt != null) {
|
else if (rgbMapInt != null) {
|
||||||
red = (mRGBMapInt[i] >> 16) & 0xFF;
|
red = (rgbMapInt[i] >> 16) & 0xFF;
|
||||||
green = (mRGBMapInt[i] >> 8) & 0xFF;
|
green = (rgbMapInt[i] >> 8) & 0xFF;
|
||||||
blue = mRGBMapInt[i] & 0xFF;
|
blue = rgbMapInt[i] & 0xFF;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new IllegalStateException("colormap == null");
|
throw new IllegalStateException("colormap == null");
|
||||||
@@ -170,7 +170,7 @@ class InverseColorMap {
|
|||||||
for (b = 0, bdist = gdist, bxx = binc; b < MAXQUANTVAL; bdist += bxx, ++b, ++rgbI, bxx += xsqr2) {
|
for (b = 0, bdist = gdist, bxx = binc; b < MAXQUANTVAL; bdist += bxx, ++b, ++rgbI, bxx += xsqr2) {
|
||||||
if (i == 0 || pTemp[rgbI] > bdist) {
|
if (i == 0 || pTemp[rgbI] > bdist) {
|
||||||
pTemp[rgbI] = bdist;
|
pTemp[rgbI] = bdist;
|
||||||
mInverseRGB[rgbI] = (byte) i;
|
inverseRGB[rgbI] = (byte) i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -187,7 +187,7 @@ class InverseColorMap {
|
|||||||
* created inverse color map.
|
* created inverse color map.
|
||||||
*/
|
*/
|
||||||
public final int getIndexNearest(int pColor) {
|
public final int getIndexNearest(int pColor) {
|
||||||
return mInverseRGB[((pColor >> (3 * TRUNCBITS)) & QUANTMASK_RED) +
|
return inverseRGB[((pColor >> (3 * TRUNCBITS)) & QUANTMASK_RED) +
|
||||||
((pColor >> (2 * TRUNCBITS)) & QUANTMASK_GREEN) +
|
((pColor >> (2 * TRUNCBITS)) & QUANTMASK_GREEN) +
|
||||||
((pColor >> (/* 1 * */ TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
|
((pColor >> (/* 1 * */ TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
|
||||||
}
|
}
|
||||||
@@ -203,7 +203,7 @@ class InverseColorMap {
|
|||||||
*/
|
*/
|
||||||
public final int getIndexNearest(int pRed, int pGreen, int pBlue) {
|
public final int getIndexNearest(int pRed, int pGreen, int pBlue) {
|
||||||
// NOTE: the third line in expression for blue is shifting DOWN not UP.
|
// NOTE: the third line in expression for blue is shifting DOWN not UP.
|
||||||
return mInverseRGB[((pRed << (2 * QUANTBITS - TRUNCBITS)) & QUANTMASK_RED) +
|
return inverseRGB[((pRed << (2 * QUANTBITS - TRUNCBITS)) & QUANTMASK_RED) +
|
||||||
((pGreen << (/* 1 * */ QUANTBITS - TRUNCBITS)) & QUANTMASK_GREEN) +
|
((pGreen << (/* 1 * */ QUANTBITS - TRUNCBITS)) & QUANTMASK_GREEN) +
|
||||||
((pBlue >> (TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
|
((pBlue >> (TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-36
@@ -46,13 +46,13 @@ import java.awt.image.IndexColorModel;
|
|||||||
*/
|
*/
|
||||||
public class InverseColorMapIndexColorModel extends IndexColorModel {
|
public class InverseColorMapIndexColorModel extends IndexColorModel {
|
||||||
|
|
||||||
protected int mRGBs[];
|
protected int rgbs[];
|
||||||
protected int mMapSize;
|
protected int mapSize;
|
||||||
|
|
||||||
protected InverseColorMap mInverseMap = null;
|
protected InverseColorMap inverseMap = null;
|
||||||
private final static int ALPHA_THRESHOLD = 0x80;
|
private final static int ALPHA_THRESHOLD = 0x80;
|
||||||
|
|
||||||
private int mWhiteIndex = -1;
|
private int whiteIndex = -1;
|
||||||
private final static int WHITE = 0x00FFFFFF;
|
private final static int WHITE = 0x00FFFFFF;
|
||||||
private final static int RGB_MASK = 0x00FFFFFF;
|
private final static int RGB_MASK = 0x00FFFFFF;
|
||||||
|
|
||||||
@@ -74,11 +74,11 @@ public class InverseColorMapIndexColorModel extends IndexColorModel {
|
|||||||
ImageUtil.getTransferType(pColorModel),
|
ImageUtil.getTransferType(pColorModel),
|
||||||
pColorModel.getValidPixels());
|
pColorModel.getValidPixels());
|
||||||
|
|
||||||
mRGBs = pRGBs;
|
rgbs = pRGBs;
|
||||||
mMapSize = mRGBs.length;
|
mapSize = rgbs.length;
|
||||||
|
|
||||||
mInverseMap = new InverseColorMap(mRGBs);
|
inverseMap = new InverseColorMap(rgbs);
|
||||||
mWhiteIndex = getWhiteIndex();
|
whiteIndex = getWhiteIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -91,6 +91,7 @@ public class InverseColorMapIndexColorModel extends IndexColorModel {
|
|||||||
private static int[] getRGBs(IndexColorModel pColorModel) {
|
private static int[] getRGBs(IndexColorModel pColorModel) {
|
||||||
int[] rgb = new int[pColorModel.getMapSize()];
|
int[] rgb = new int[pColorModel.getMapSize()];
|
||||||
pColorModel.getRGBs(rgb);
|
pColorModel.getRGBs(rgb);
|
||||||
|
|
||||||
return rgb;
|
return rgb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,15 +112,13 @@ public class InverseColorMapIndexColorModel extends IndexColorModel {
|
|||||||
*
|
*
|
||||||
* @see IndexColorModel#IndexColorModel(int, int, int[], int, boolean, int, int)
|
* @see IndexColorModel#IndexColorModel(int, int, int[], int, boolean, int, int)
|
||||||
*/
|
*/
|
||||||
public InverseColorMapIndexColorModel(int pNumBits, int pSize, int[] pRGBs,
|
public InverseColorMapIndexColorModel(int pNumBits, int pSize, int[] pRGBs, int pStart, boolean pAlpha, int pTransparentIndex, int pTransferType) {
|
||||||
int pStart, boolean pAlpha, int pTransparentIndex,
|
|
||||||
int pTransferType) {
|
|
||||||
super(pNumBits, pSize, pRGBs, pStart, pAlpha, pTransparentIndex, pTransferType);
|
super(pNumBits, pSize, pRGBs, pStart, pAlpha, pTransparentIndex, pTransferType);
|
||||||
mRGBs = getRGBs(this);
|
rgbs = getRGBs(this);
|
||||||
mMapSize = mRGBs.length;
|
mapSize = rgbs.length;
|
||||||
|
|
||||||
mInverseMap = new InverseColorMap(mRGBs, pTransparentIndex);
|
inverseMap = new InverseColorMap(rgbs, pTransparentIndex);
|
||||||
mWhiteIndex = getWhiteIndex();
|
whiteIndex = getWhiteIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -138,15 +137,13 @@ public class InverseColorMapIndexColorModel extends IndexColorModel {
|
|||||||
*
|
*
|
||||||
* @see IndexColorModel#IndexColorModel(int, int, byte[], byte[], byte[], int)
|
* @see IndexColorModel#IndexColorModel(int, int, byte[], byte[], byte[], int)
|
||||||
*/
|
*/
|
||||||
public InverseColorMapIndexColorModel(int pNumBits, int pSize,
|
public InverseColorMapIndexColorModel(int pNumBits, int pSize, byte[] pReds, byte[] pGreens, byte[] pBlues, int pTransparentIndex) {
|
||||||
byte[] pReds, byte[] pGreens, byte[] pBlues,
|
|
||||||
int pTransparentIndex) {
|
|
||||||
super(pNumBits, pSize, pReds, pGreens, pBlues, pTransparentIndex);
|
super(pNumBits, pSize, pReds, pGreens, pBlues, pTransparentIndex);
|
||||||
mRGBs = getRGBs(this);
|
rgbs = getRGBs(this);
|
||||||
mMapSize = mRGBs.length;
|
mapSize = rgbs.length;
|
||||||
|
|
||||||
mInverseMap = new InverseColorMap(mRGBs, pTransparentIndex);
|
inverseMap = new InverseColorMap(rgbs, pTransparentIndex);
|
||||||
mWhiteIndex = getWhiteIndex();
|
whiteIndex = getWhiteIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -164,19 +161,18 @@ public class InverseColorMapIndexColorModel extends IndexColorModel {
|
|||||||
*
|
*
|
||||||
* @see IndexColorModel#IndexColorModel(int, int, byte[], byte[], byte[])
|
* @see IndexColorModel#IndexColorModel(int, int, byte[], byte[], byte[])
|
||||||
*/
|
*/
|
||||||
public InverseColorMapIndexColorModel(int pNumBits, int pSize,
|
public InverseColorMapIndexColorModel(int pNumBits, int pSize, byte[] pReds, byte[] pGreens, byte[] pBlues) {
|
||||||
byte[] pReds, byte[] pGreens, byte[] pBlues) {
|
|
||||||
super(pNumBits, pSize, pReds, pGreens, pBlues);
|
super(pNumBits, pSize, pReds, pGreens, pBlues);
|
||||||
mRGBs = getRGBs(this);
|
rgbs = getRGBs(this);
|
||||||
mMapSize = mRGBs.length;
|
mapSize = rgbs.length;
|
||||||
|
|
||||||
mInverseMap = new InverseColorMap(mRGBs);
|
inverseMap = new InverseColorMap(rgbs);
|
||||||
mWhiteIndex = getWhiteIndex();
|
whiteIndex = getWhiteIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getWhiteIndex() {
|
private int getWhiteIndex() {
|
||||||
for (int i = 0; i < mRGBs.length; i++) {
|
for (int i = 0; i < rgbs.length; i++) {
|
||||||
int color = mRGBs[i];
|
int color = rgbs[i];
|
||||||
if ((color & RGB_MASK) == WHITE) {
|
if ((color & RGB_MASK) == WHITE) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@@ -244,7 +240,6 @@ public class InverseColorMapIndexColorModel extends IndexColorModel {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public Object getDataElements(int rgb, Object pixel) {
|
public Object getDataElements(int rgb, Object pixel) {
|
||||||
|
|
||||||
int alpha = (rgb>>>24);
|
int alpha = (rgb>>>24);
|
||||||
|
|
||||||
int pix;
|
int pix;
|
||||||
@@ -253,11 +248,11 @@ public class InverseColorMapIndexColorModel extends IndexColorModel {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int color = rgb & RGB_MASK;
|
int color = rgb & RGB_MASK;
|
||||||
if (color == WHITE && mWhiteIndex != -1) {
|
if (color == WHITE && whiteIndex != -1) {
|
||||||
pix = mWhiteIndex;
|
pix = whiteIndex;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pix = mInverseMap.getIndexNearest(color);
|
pix = inverseMap.getIndexNearest(color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,8 +292,7 @@ public class InverseColorMapIndexColorModel extends IndexColorModel {
|
|||||||
shortObj[0] = (short) pix;
|
shortObj[0] = (short) pix;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedOperationException("This method has not been " +
|
throw new UnsupportedOperationException("This method has not been implemented for transferType " + transferType);
|
||||||
"implemented for transferType " + transferType);
|
|
||||||
}
|
}
|
||||||
return pixel;
|
return pixel;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,11 +54,11 @@ final class MagickAccelerator {
|
|||||||
|
|
||||||
private static final int RESAMPLE_OP = 0;
|
private static final int RESAMPLE_OP = 0;
|
||||||
|
|
||||||
private static Class[] sNativeOp = new Class[1];
|
private static Class[] nativeOp = new Class[1];
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
sNativeOp[RESAMPLE_OP] = Class.forName("com.twelvemonkeys.image.ResampleOp");
|
nativeOp[RESAMPLE_OP] = Class.forName("com.twelvemonkeys.image.ResampleOp");
|
||||||
}
|
}
|
||||||
catch (ClassNotFoundException e) {
|
catch (ClassNotFoundException e) {
|
||||||
System.err.println("Could not find class: " + e);
|
System.err.println("Could not find class: " + e);
|
||||||
@@ -94,8 +94,8 @@ final class MagickAccelerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static int getNativeOpIndex(Class pOpClass) {
|
private static int getNativeOpIndex(Class pOpClass) {
|
||||||
for (int i = 0; i < sNativeOp.length; i++) {
|
for (int i = 0; i < nativeOp.length; i++) {
|
||||||
if (pOpClass == sNativeOp[i]) {
|
if (pOpClass == nativeOp[i]) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -112,7 +112,7 @@ final class MagickAccelerator {
|
|||||||
switch (getNativeOpIndex(pOperation.getClass())) {
|
switch (getNativeOpIndex(pOperation.getClass())) {
|
||||||
case RESAMPLE_OP:
|
case RESAMPLE_OP:
|
||||||
ResampleOp resample = (ResampleOp) pOperation;
|
ResampleOp resample = (ResampleOp) pOperation;
|
||||||
result = resampleMagick(pInput, resample.mWidth, resample.mHeight, resample.mFilterType);
|
result = resampleMagick(pInput, resample.width, resample.height, resample.filterType);
|
||||||
|
|
||||||
// NOTE: If output parameter is non-null, we have to return that
|
// NOTE: If output parameter is non-null, we have to return that
|
||||||
// image, instead of result
|
// image, instead of result
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import java.awt.image.*;
|
|||||||
/**
|
/**
|
||||||
* Monochrome B/W color model.
|
* Monochrome B/W color model.
|
||||||
*
|
*
|
||||||
* @author Harald Kuhr
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
*/
|
*/
|
||||||
public class MonochromeColorModel extends IndexColorModel {
|
public class MonochromeColorModel extends IndexColorModel {
|
||||||
|
|
||||||
|
|||||||
@@ -48,37 +48,37 @@ public class PixelizeOp implements BufferedImageOp, RasterOp {
|
|||||||
// TODO: support more raster types/color models
|
// TODO: support more raster types/color models
|
||||||
// TODO: This is actually an implementation of Area Averaging, without the scale... Let's extract it...
|
// TODO: This is actually an implementation of Area Averaging, without the scale... Let's extract it...
|
||||||
|
|
||||||
final private int mPixelSizeX;
|
final private int pixelSizeX;
|
||||||
final private int mPixelSizeY;
|
final private int pixelSizeY;
|
||||||
|
|
||||||
private Rectangle mSourceRegion;
|
private Rectangle sourceRegion;
|
||||||
|
|
||||||
public PixelizeOp(final int pPixelSize) {
|
public PixelizeOp(final int pPixelSize) {
|
||||||
this(pPixelSize, pPixelSize);
|
this(pPixelSize, pPixelSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PixelizeOp(final int pPixelSizeX, final int pPixelSizeY) {
|
public PixelizeOp(final int pPixelSizeX, final int pPixelSizeY) {
|
||||||
mPixelSizeX = pPixelSizeX;
|
pixelSizeX = pPixelSizeX;
|
||||||
mPixelSizeY = pPixelSizeY;
|
pixelSizeY = pPixelSizeY;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Rectangle getSourceRegion() {
|
public Rectangle getSourceRegion() {
|
||||||
if (mSourceRegion == null) {
|
if (sourceRegion == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return new Rectangle(mSourceRegion);
|
return new Rectangle(sourceRegion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSourceRegion(final Rectangle pSourceRegion) {
|
public void setSourceRegion(final Rectangle pSourceRegion) {
|
||||||
if (pSourceRegion == null) {
|
if (pSourceRegion == null) {
|
||||||
mSourceRegion = null;
|
sourceRegion = null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (mSourceRegion == null) {
|
if (sourceRegion == null) {
|
||||||
mSourceRegion = new Rectangle(pSourceRegion);
|
sourceRegion = new Rectangle(pSourceRegion);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mSourceRegion.setBounds(pSourceRegion);
|
sourceRegion.setBounds(pSourceRegion);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -107,11 +107,11 @@ public class PixelizeOp implements BufferedImageOp, RasterOp {
|
|||||||
private WritableRaster filterImpl(Raster src, WritableRaster dest) {
|
private WritableRaster filterImpl(Raster src, WritableRaster dest) {
|
||||||
//System.out.println("src: " + src);
|
//System.out.println("src: " + src);
|
||||||
//System.out.println("dest: " + dest);
|
//System.out.println("dest: " + dest);
|
||||||
if (mSourceRegion != null) {
|
if (sourceRegion != null) {
|
||||||
int cx = mSourceRegion.x;
|
int cx = sourceRegion.x;
|
||||||
int cy = mSourceRegion.y;
|
int cy = sourceRegion.y;
|
||||||
int cw = mSourceRegion.width;
|
int cw = sourceRegion.width;
|
||||||
int ch = mSourceRegion.height;
|
int ch = sourceRegion.height;
|
||||||
|
|
||||||
boolean same = src == dest;
|
boolean same = src == dest;
|
||||||
dest = dest.createWritableChild(cx, cy, cw, ch, 0, 0, null);
|
dest = dest.createWritableChild(cx, cy, cw, ch, 0, 0, null);
|
||||||
@@ -122,8 +122,8 @@ public class PixelizeOp implements BufferedImageOp, RasterOp {
|
|||||||
|
|
||||||
final int width = src.getWidth();
|
final int width = src.getWidth();
|
||||||
final int height = src.getHeight();
|
final int height = src.getHeight();
|
||||||
int w = (width + mPixelSizeX - 1) / mPixelSizeX;
|
int w = (width + pixelSizeX - 1) / pixelSizeX;
|
||||||
int h = (height + mPixelSizeY - 1) / mPixelSizeY;
|
int h = (height + pixelSizeY - 1) / pixelSizeY;
|
||||||
|
|
||||||
final boolean oddX = width % w != 0;
|
final boolean oddX = width % w != 0;
|
||||||
final boolean oddY = height % h != 0;
|
final boolean oddY = height % h != 0;
|
||||||
@@ -156,23 +156,23 @@ public class PixelizeOp implements BufferedImageOp, RasterOp {
|
|||||||
|
|
||||||
for (int y = 0; y < h; y++) {
|
for (int y = 0; y < h; y++) {
|
||||||
if (!oddY || y + 1 < h) {
|
if (!oddY || y + 1 < h) {
|
||||||
scanH = mPixelSizeY;
|
scanH = pixelSizeY;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
scanH = height - (y * mPixelSizeY);
|
scanH = height - (y * pixelSizeY);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int x = 0; x < w; x++) {
|
for (int x = 0; x < w; x++) {
|
||||||
if (!oddX || x + 1 < w) {
|
if (!oddX || x + 1 < w) {
|
||||||
scanW = mPixelSizeX;
|
scanW = pixelSizeX;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
scanW = width - (x * mPixelSizeX);
|
scanW = width - (x * pixelSizeX);
|
||||||
}
|
}
|
||||||
final int pixelCount = scanW * scanH;
|
final int pixelCount = scanW * scanH;
|
||||||
final int pixelLength = pixelCount * dataElements;
|
final int pixelLength = pixelCount * dataElements;
|
||||||
|
|
||||||
data = src.getDataElements(x * mPixelSizeX, y * mPixelSizeY, scanW, scanH, data);
|
data = src.getDataElements(x * pixelSizeX, y * pixelSizeY, scanW, scanH, data);
|
||||||
|
|
||||||
// NOTE: These are not neccessarily ARGB..
|
// NOTE: These are not neccessarily ARGB..
|
||||||
double valueA = 0.0;
|
double valueA = 0.0;
|
||||||
@@ -277,7 +277,7 @@ public class PixelizeOp implements BufferedImageOp, RasterOp {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dest.setDataElements(x * mPixelSizeX, y * mPixelSizeY, scanW, scanH, data);
|
dest.setDataElements(x * pixelSizeX, y * pixelSizeY, scanW, scanH, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*/
|
/*/
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ import java.awt.geom.Point2D;
|
|||||||
import java.awt.geom.Rectangle2D;
|
import java.awt.geom.Rectangle2D;
|
||||||
import java.awt.image.*;
|
import java.awt.image.*;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resamples (scales) a {@code BufferedImage} to a new width and height, using
|
* Resamples (scales) a {@code BufferedImage} to a new width and height, using
|
||||||
* high performance and high quality algorithms.
|
* high performance and high quality algorithms.
|
||||||
@@ -138,7 +137,7 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
// MagickAccelerator to work consistently (see magick.FilterType).
|
// MagickAccelerator to work consistently (see magick.FilterType).
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Undefined interpolation, filter method will use default filter
|
* Undefined interpolation, filter method will use default filter.
|
||||||
*/
|
*/
|
||||||
public final static int FILTER_UNDEFINED = 0;
|
public final static int FILTER_UNDEFINED = 0;
|
||||||
/**
|
/**
|
||||||
@@ -194,11 +193,11 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
/**
|
/**
|
||||||
* Mitchell interpolation. High quality.
|
* Mitchell interpolation. High quality.
|
||||||
*/
|
*/
|
||||||
public final static int FILTER_MITCHELL = 12;// IM default scale with palette or alpha, or scale up
|
public final static int FILTER_MITCHELL = 12; // IM default scale with palette or alpha, or scale up
|
||||||
/**
|
/**
|
||||||
* Lanczos interpolation. High quality.
|
* Lanczos interpolation. High quality.
|
||||||
*/
|
*/
|
||||||
public final static int FILTER_LANCZOS = 13;// IM default
|
public final static int FILTER_LANCZOS = 13; // IM default
|
||||||
/**
|
/**
|
||||||
* Blackman-Bessel interpolation. High quality.
|
* Blackman-Bessel interpolation. High quality.
|
||||||
*/
|
*/
|
||||||
@@ -291,10 +290,10 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
|
|
||||||
// Member variables
|
// Member variables
|
||||||
// Package access, to allow access from MagickAccelerator
|
// Package access, to allow access from MagickAccelerator
|
||||||
int mWidth;
|
int width;
|
||||||
int mHeight;
|
int height;
|
||||||
|
|
||||||
int mFilterType;
|
int filterType;
|
||||||
private static final boolean TRANSFORM_OP_BICUBIC_SUPPORT = SystemUtil.isFieldAvailable(AffineTransformOp.class.getName(), "TYPE_BICUBIC");
|
private static final boolean TRANSFORM_OP_BICUBIC_SUPPORT = SystemUtil.isFieldAvailable(AffineTransformOp.class.getName(), "TYPE_BICUBIC");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -302,14 +301,13 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
*/
|
*/
|
||||||
// TODO: Move to abstract class AbstractBufferedImageOp?
|
// TODO: Move to abstract class AbstractBufferedImageOp?
|
||||||
static class Key extends RenderingHints.Key {
|
static class Key extends RenderingHints.Key {
|
||||||
|
|
||||||
static int sIndex = 10000;
|
static int sIndex = 10000;
|
||||||
|
|
||||||
private final String mName;
|
private final String name;
|
||||||
|
|
||||||
public Key(final String pName) {
|
public Key(final String pName) {
|
||||||
super(sIndex++);
|
super(sIndex++);
|
||||||
mName = pName;
|
name = pName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCompatibleValue(Object pValue) {
|
public boolean isCompatibleValue(Object pValue) {
|
||||||
@@ -317,7 +315,7 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return mName;
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,27 +324,27 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
*/
|
*/
|
||||||
// TODO: Extract abstract Value class, and move to AbstractBufferedImageOp
|
// TODO: Extract abstract Value class, and move to AbstractBufferedImageOp
|
||||||
static final class Value {
|
static final class Value {
|
||||||
final private RenderingHints.Key mKey;
|
final private RenderingHints.Key key;
|
||||||
final private String mName;
|
final private String name;
|
||||||
final private int mType;
|
final private int type;
|
||||||
|
|
||||||
public Value(final RenderingHints.Key pKey, final String pName, final int pType) {
|
public Value(final RenderingHints.Key pKey, final String pName, final int pType) {
|
||||||
mKey = pKey;
|
key = pKey;
|
||||||
mName = pName;
|
name = pName;
|
||||||
validateFilterType(pType);
|
validateFilterType(pType);
|
||||||
mType = pType;// TODO: test for duplicates
|
type = pType;// TODO: test for duplicates
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCompatibleKey(Key pKey) {
|
public boolean isCompatibleKey(Key pKey) {
|
||||||
return pKey == mKey;
|
return pKey == key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getFilterType() {
|
public int getFilterType() {
|
||||||
return mType;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return mName;
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,11 +352,11 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
* Creates a {@code ResampleOp} that will resample input images to the
|
* Creates a {@code ResampleOp} that will resample input images to the
|
||||||
* given width and height, using the default interpolation filter.
|
* given width and height, using the default interpolation filter.
|
||||||
*
|
*
|
||||||
* @param pWidth width of the resampled image
|
* @param width width of the re-sampled image
|
||||||
* @param pHeight height of the resampled image
|
* @param height height of the re-sampled image
|
||||||
*/
|
*/
|
||||||
public ResampleOp(int pWidth, int pHeight) {
|
public ResampleOp(int width, int height) {
|
||||||
this(pWidth, pHeight, FILTER_UNDEFINED);
|
this(width, height, FILTER_UNDEFINED);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -394,38 +392,38 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
* </ul>
|
* </ul>
|
||||||
* Other hints have no effect on this filter.
|
* Other hints have no effect on this filter.
|
||||||
*
|
*
|
||||||
* @param pWidth width of the resampled image
|
* @param width width of the re-sampled image
|
||||||
* @param pHeight height of the resampled image
|
* @param height height of the re-sampled image
|
||||||
* @param pHints rendering hints, affecting interpolation algorithm
|
* @param hints rendering hints, affecting interpolation algorithm
|
||||||
* @see #KEY_RESAMPLE_INTERPOLATION
|
* @see #KEY_RESAMPLE_INTERPOLATION
|
||||||
* @see RenderingHints#KEY_INTERPOLATION
|
* @see RenderingHints#KEY_INTERPOLATION
|
||||||
* @see RenderingHints#KEY_RENDERING
|
* @see RenderingHints#KEY_RENDERING
|
||||||
* @see RenderingHints#KEY_COLOR_RENDERING
|
* @see RenderingHints#KEY_COLOR_RENDERING
|
||||||
*/
|
*/
|
||||||
public ResampleOp(int pWidth, int pHeight, RenderingHints pHints) {
|
public ResampleOp(int width, int height, RenderingHints hints) {
|
||||||
this(pWidth, pHeight, getFilterType(pHints));
|
this(width, height, getFilterType(hints));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code ResampleOp} that will resample input images to the
|
* Creates a {@code ResampleOp} that will resample input images to the
|
||||||
* given width and height, using the given interpolation filter.
|
* given width and height, using the given interpolation filter.
|
||||||
*
|
*
|
||||||
* @param pWidth width of the resampled image
|
* @param width width of the re-sampled image
|
||||||
* @param pHeight height of the resampled image
|
* @param height height of the re-sampled image
|
||||||
* @param pFilterType interpolation filter algorithm
|
* @param filterType interpolation filter algorithm
|
||||||
* @see <a href="#field_summary">filter type constants</a>
|
* @see <a href="#field_summary">filter type constants</a>
|
||||||
*/
|
*/
|
||||||
public ResampleOp(int pWidth, int pHeight, int pFilterType) {
|
public ResampleOp(int width, int height, int filterType) {
|
||||||
if (pWidth <= 0 || pHeight <= 0) {
|
if (width <= 0 || height <= 0) {
|
||||||
// NOTE: w/h == 0 makes the Magick DLL crash and the JVM dies.. :-P
|
// NOTE: w/h == 0 makes the Magick DLL crash and the JVM dies.. :-P
|
||||||
throw new IllegalArgumentException("width and height must be positive");
|
throw new IllegalArgumentException("width and height must be positive");
|
||||||
}
|
}
|
||||||
|
|
||||||
mWidth = pWidth;
|
this.width = width;
|
||||||
mHeight = pHeight;
|
this.height = height;
|
||||||
|
|
||||||
validateFilterType(pFilterType);
|
validateFilterType(filterType);
|
||||||
mFilterType = pFilterType;
|
this.filterType = filterType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void validateFilterType(int pFilterType) {
|
private static void validateFilterType(int pFilterType) {
|
||||||
@@ -471,25 +469,21 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
}
|
}
|
||||||
return value != null ? ((Value) value).getFilterType() : FILTER_UNDEFINED;
|
return value != null ? ((Value) value).getFilterType() : FILTER_UNDEFINED;
|
||||||
}
|
}
|
||||||
else
|
else if (RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR.equals(pHints.get(RenderingHints.KEY_INTERPOLATION))
|
||||||
if (RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR.equals(pHints.get(RenderingHints.KEY_INTERPOLATION))
|
|
||||||
|| (!pHints.containsKey(RenderingHints.KEY_INTERPOLATION)
|
|| (!pHints.containsKey(RenderingHints.KEY_INTERPOLATION)
|
||||||
&& (RenderingHints.VALUE_RENDER_SPEED.equals(pHints.get(RenderingHints.KEY_RENDERING))
|
&& (RenderingHints.VALUE_RENDER_SPEED.equals(pHints.get(RenderingHints.KEY_RENDERING))
|
||||||
|| RenderingHints.VALUE_COLOR_RENDER_SPEED.equals(pHints.get(RenderingHints.KEY_COLOR_RENDERING))))) {
|
|| RenderingHints.VALUE_COLOR_RENDER_SPEED.equals(pHints.get(RenderingHints.KEY_COLOR_RENDERING))))) {
|
||||||
// Nearest neighbour, or prioritze speed
|
// Nearest neighbour, or prioritize speed
|
||||||
return FILTER_POINT;
|
return FILTER_POINT;
|
||||||
}
|
}
|
||||||
else
|
else if (RenderingHints.VALUE_INTERPOLATION_BILINEAR.equals(pHints.get(RenderingHints.KEY_INTERPOLATION))) {
|
||||||
if (RenderingHints.VALUE_INTERPOLATION_BILINEAR.equals(pHints.get(RenderingHints.KEY_INTERPOLATION))) {
|
|
||||||
// Triangle equals bi-linear interpolation
|
// Triangle equals bi-linear interpolation
|
||||||
return FILTER_TRIANGLE;
|
return FILTER_TRIANGLE;
|
||||||
}
|
}
|
||||||
else
|
else if (RenderingHints.VALUE_INTERPOLATION_BICUBIC.equals(pHints.get(RenderingHints.KEY_INTERPOLATION))) {
|
||||||
if (RenderingHints.VALUE_INTERPOLATION_BICUBIC.equals(pHints.get(RenderingHints.KEY_INTERPOLATION))) {
|
|
||||||
return FILTER_QUADRATIC;// No idea if this is correct..?
|
return FILTER_QUADRATIC;// No idea if this is correct..?
|
||||||
}
|
}
|
||||||
else
|
else if (RenderingHints.VALUE_RENDER_QUALITY.equals(pHints.get(RenderingHints.KEY_RENDERING))
|
||||||
if (RenderingHints.VALUE_RENDER_QUALITY.equals(pHints.get(RenderingHints.KEY_RENDERING))
|
|
||||||
|| RenderingHints.VALUE_COLOR_RENDER_QUALITY.equals(pHints.get(RenderingHints.KEY_COLOR_RENDERING))) {
|
|| RenderingHints.VALUE_COLOR_RENDER_QUALITY.equals(pHints.get(RenderingHints.KEY_COLOR_RENDERING))) {
|
||||||
// Prioritize quality
|
// Prioritize quality
|
||||||
return FILTER_MITCHELL;
|
return FILTER_MITCHELL;
|
||||||
@@ -500,83 +494,87 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resamples (scales) the image to the size, and using the algorithm
|
* Re-samples (scales) the image to the size, and using the algorithm
|
||||||
* specified in the constructor.
|
* specified in the constructor.
|
||||||
*
|
*
|
||||||
* @param pInput The {@code BufferedImage} to be filtered
|
* @param input The {@code BufferedImage} to be filtered
|
||||||
* @param pOutput The {@code BufferedImage} in which to store the resampled
|
* @param output The {@code BufferedImage} in which to store the resampled
|
||||||
* image
|
* image
|
||||||
* @return The resampled {@code BufferedImage}.
|
* @return The re-sampled {@code BufferedImage}.
|
||||||
* @throws NullPointerException if {@code pInput} is {@code null}
|
* @throws NullPointerException if {@code input} is {@code null}
|
||||||
* @throws IllegalArgumentException if {@code pInput == pOutput}.
|
* @throws IllegalArgumentException if {@code input == output}.
|
||||||
* @see #ResampleOp(int,int,int)
|
* @see #ResampleOp(int,int,int)
|
||||||
*/
|
*/
|
||||||
public final BufferedImage filter(final BufferedImage pInput, final BufferedImage pOutput) {
|
public final BufferedImage filter(final BufferedImage input, final BufferedImage output) {
|
||||||
if (pInput == null) {
|
if (input == null) {
|
||||||
throw new NullPointerException("Input == null");
|
throw new NullPointerException("Input == null");
|
||||||
}
|
}
|
||||||
if (pInput == pOutput) {
|
if (input == output) {
|
||||||
throw new IllegalArgumentException("Output image cannot be the same as the input image");
|
throw new IllegalArgumentException("Output image cannot be the same as the input image");
|
||||||
}
|
}
|
||||||
|
|
||||||
InterpolationFilter filter;
|
InterpolationFilter filter;
|
||||||
|
|
||||||
|
|
||||||
// Special case for POINT, TRIANGLE and QUADRATIC filter, as standard
|
// Special case for POINT, TRIANGLE and QUADRATIC filter, as standard
|
||||||
// Java implementation is very fast (possibly H/W accellerated)
|
// Java implementation is very fast (possibly H/W accelerated)
|
||||||
switch (mFilterType) {
|
switch (filterType) {
|
||||||
case FILTER_POINT:
|
case FILTER_POINT:
|
||||||
return fastResample(pInput, pOutput, mWidth, mHeight, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
|
if (input.getType() != BufferedImage.TYPE_CUSTOM) {
|
||||||
case FILTER_TRIANGLE:
|
return fastResample(input, output, width, height, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
|
||||||
return fastResample(pInput, pOutput, mWidth, mHeight, AffineTransformOp.TYPE_BILINEAR);
|
|
||||||
case FILTER_QUADRATIC:
|
|
||||||
if (TRANSFORM_OP_BICUBIC_SUPPORT) {
|
|
||||||
return fastResample(pInput, pOutput, mWidth, mHeight, 3); // AffineTransformOp.TYPE_BICUBIC
|
|
||||||
}
|
}
|
||||||
// Fall through
|
// Else fall through
|
||||||
|
case FILTER_TRIANGLE:
|
||||||
|
if (input.getType() != BufferedImage.TYPE_CUSTOM) {
|
||||||
|
return fastResample(input, output, width, height, AffineTransformOp.TYPE_BILINEAR);
|
||||||
|
}
|
||||||
|
// Else fall through
|
||||||
|
case FILTER_QUADRATIC:
|
||||||
|
if (input.getType() != BufferedImage.TYPE_CUSTOM && TRANSFORM_OP_BICUBIC_SUPPORT) {
|
||||||
|
return fastResample(input, output, width, height, 3); // AffineTransformOp.TYPE_BICUBIC
|
||||||
|
}
|
||||||
|
// Else fall through
|
||||||
default:
|
default:
|
||||||
filter = createFilter(mFilterType);
|
filter = createFilter(filterType);
|
||||||
// NOTE: Workaround for filter throwing exceptions when input or output is less than support...
|
// NOTE: Workaround for filter throwing exceptions when input or output is less than support...
|
||||||
if (Math.min(pInput.getWidth(), pInput.getHeight()) <= filter.support() || Math.min(mWidth, mHeight) <= filter.support()) {
|
if (Math.min(input.getWidth(), input.getHeight()) <= filter.support() || Math.min(width, height) <= filter.support()) {
|
||||||
return fastResample(pInput, pOutput, mWidth, mHeight, AffineTransformOp.TYPE_BILINEAR);
|
return fastResample(input, output, width, height, AffineTransformOp.TYPE_BILINEAR);
|
||||||
}
|
}
|
||||||
// Fall through
|
// Fall through
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Try to use native ImageMagick code
|
// Try to use native ImageMagick code
|
||||||
BufferedImage result = MagickAccelerator.filter(this, pInput, pOutput);
|
BufferedImage result = MagickAccelerator.filter(this, input, output);
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, continue in pure Java mode
|
// Otherwise, continue in pure Java mode
|
||||||
|
|
||||||
// TODO: What if pOutput != null and wrong size? Create new? Render on only a part? Document?
|
// TODO: What if output != null and wrong size? Create new? Render on only a part? Document?
|
||||||
|
|
||||||
// If filter type != POINT or BOX an input has IndexColorModel, convert
|
// If filter type != POINT or BOX an input has IndexColorModel, convert
|
||||||
// to true color, with alpha reflecting that of the original colormodel.
|
// to true color, with alpha reflecting that of the original color model.
|
||||||
BufferedImage input;
|
BufferedImage temp;
|
||||||
ColorModel cm;
|
ColorModel cm;
|
||||||
if (mFilterType != FILTER_BOX && (cm = pInput.getColorModel()) instanceof IndexColorModel) {
|
if (filterType != FILTER_POINT && filterType != FILTER_BOX && (cm = input.getColorModel()) instanceof IndexColorModel) {
|
||||||
// TODO: OPTIMIZE: If colormodel has only b/w or gray, we could skip color info
|
// TODO: OPTIMIZE: If color model has only b/w or gray, we could skip color info
|
||||||
input = ImageUtil.toBuffered(pInput, cm.hasAlpha() ? BufferedImage.TYPE_4BYTE_ABGR : BufferedImage.TYPE_3BYTE_BGR);
|
temp = ImageUtil.toBuffered(input, cm.hasAlpha() ? BufferedImage.TYPE_4BYTE_ABGR : BufferedImage.TYPE_3BYTE_BGR);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
input = pInput;
|
temp = input;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create or convert output to a suitable image
|
// Create or convert output to a suitable image
|
||||||
// TODO: OPTIMIZE: Don't really need to convert all types to same as input
|
// TODO: OPTIMIZE: Don't really need to convert all types to same as input
|
||||||
result = pOutput != null ? /*pOutput*/ ImageUtil.toBuffered(pOutput, input.getType()) : createCompatibleDestImage(input, null);
|
result = output != null && temp.getType() != BufferedImage.TYPE_CUSTOM ? /*output*/ ImageUtil.toBuffered(output, temp.getType()) : createCompatibleDestImage(temp, null);
|
||||||
|
|
||||||
resample(input, result, filter);
|
resample(temp, result, filter);
|
||||||
|
|
||||||
// If pOutput != null and needed to be converted, draw it back
|
// If output != null and needed to be converted, draw it back
|
||||||
if (pOutput != null && pOutput != result) {
|
if (output != null && output != result) {
|
||||||
//pOutput.setData(output.getRaster());
|
//output.setData(output.getRaster());
|
||||||
ImageUtil.drawOnto(pOutput, result);
|
ImageUtil.drawOnto(output, result);
|
||||||
result = pOutput;
|
result = output;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -672,8 +670,8 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private static BufferedImage fastResample(final BufferedImage pInput, final BufferedImage pOutput, final int pWidth, final int pHeight, final int pType) {
|
private static BufferedImage fastResample(final BufferedImage input, final BufferedImage output, final int width, final int height, final int type) {
|
||||||
BufferedImage temp = pInput;
|
BufferedImage temp = input;
|
||||||
|
|
||||||
double xScale;
|
double xScale;
|
||||||
double yScale;
|
double yScale;
|
||||||
@@ -681,20 +679,20 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
AffineTransform transform;
|
AffineTransform transform;
|
||||||
AffineTransformOp scale;
|
AffineTransformOp scale;
|
||||||
|
|
||||||
if (pType > AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {
|
if (type > AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {
|
||||||
// Initially scale so all remaining operations will halve the image
|
// Initially scale so all remaining operations will halve the image
|
||||||
if (pWidth < pInput.getWidth() || pHeight < pInput.getHeight()) {
|
if (width < input.getWidth() || height < input.getHeight()) {
|
||||||
int w = pWidth;
|
int w = width;
|
||||||
int h = pHeight;
|
int h = height;
|
||||||
while (w < pInput.getWidth() / 2) {
|
while (w < input.getWidth() / 2) {
|
||||||
w *= 2;
|
w *= 2;
|
||||||
}
|
}
|
||||||
while (h < pInput.getHeight() / 2) {
|
while (h < input.getHeight() / 2) {
|
||||||
h *= 2;
|
h *= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
xScale = w / (double) pInput.getWidth();
|
xScale = w / (double) input.getWidth();
|
||||||
yScale = h / (double) pInput.getHeight();
|
yScale = h / (double) input.getHeight();
|
||||||
|
|
||||||
//System.out.println("First scale by x=" + xScale + ", y=" + yScale);
|
//System.out.println("First scale by x=" + xScale + ", y=" + yScale);
|
||||||
|
|
||||||
@@ -704,12 +702,12 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scale = null;// NOTE: This resets!
|
scale = null; // NOTE: This resets!
|
||||||
|
|
||||||
xScale = pWidth / (double) temp.getWidth();
|
xScale = width / (double) temp.getWidth();
|
||||||
yScale = pHeight / (double) temp.getHeight();
|
yScale = height / (double) temp.getHeight();
|
||||||
|
|
||||||
if (pType > AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {
|
if (type > AffineTransformOp.TYPE_NEAREST_NEIGHBOR) {
|
||||||
// TODO: Test skipping first scale (above), and instead scale once
|
// TODO: Test skipping first scale (above), and instead scale once
|
||||||
// more here, and a little less than .5 each time...
|
// more here, and a little less than .5 each time...
|
||||||
// That would probably make the scaling smoother...
|
// That would probably make the scaling smoother...
|
||||||
@@ -740,17 +738,15 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
}
|
}
|
||||||
|
|
||||||
temp = scale.filter(temp, null);
|
temp = scale.filter(temp, null);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//System.out.println("Rest to scale by x=" + xScale + ", y=" + yScale);
|
//System.out.println("Rest to scale by x=" + xScale + ", y=" + yScale);
|
||||||
|
|
||||||
transform = AffineTransform.getScaleInstance(xScale, yScale);
|
transform = AffineTransform.getScaleInstance(xScale, yScale);
|
||||||
scale = new AffineTransformOp(transform, pType);
|
scale = new AffineTransformOp(transform, type);
|
||||||
|
|
||||||
return scale.filter(temp, pOutput);
|
|
||||||
|
|
||||||
|
return scale.filter(temp, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -760,7 +756,7 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
* @see <a href="#field_summary">filter type constants</a>
|
* @see <a href="#field_summary">filter type constants</a>
|
||||||
*/
|
*/
|
||||||
public int getFilterType() {
|
public int getFilterType() {
|
||||||
return mFilterType;
|
return filterType;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static InterpolationFilter createFilter(int pFilterType) {
|
private static InterpolationFilter createFilter(int pFilterType) {
|
||||||
@@ -770,7 +766,8 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (pFilterType) {
|
switch (pFilterType) {
|
||||||
//case FILTER_POINT: // Should never happen
|
case FILTER_POINT:
|
||||||
|
return new PointFilter();
|
||||||
case FILTER_BOX:
|
case FILTER_BOX:
|
||||||
return new BoxFilter();
|
return new BoxFilter();
|
||||||
case FILTER_TRIANGLE:
|
case FILTER_TRIANGLE:
|
||||||
@@ -815,14 +812,13 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
// If indexcolormodel, we probably don't want to use that...
|
// If indexcolormodel, we probably don't want to use that...
|
||||||
// NOTE: Either BOTH or NONE of the images must have ALPHA
|
// NOTE: Either BOTH or NONE of the images must have ALPHA
|
||||||
|
|
||||||
return new BufferedImage(cm, ImageUtil.createCompatibleWritableRaster(pInput, cm, mWidth, mHeight),
|
return new BufferedImage(cm, ImageUtil.createCompatibleWritableRaster(pInput, cm, width, height),
|
||||||
cm.isAlphaPremultiplied(), null);
|
cm.isAlphaPremultiplied(), null);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RenderingHints getRenderingHints() {
|
public RenderingHints getRenderingHints() {
|
||||||
Object value;
|
Object value;
|
||||||
switch (mFilterType) {
|
switch (filterType) {
|
||||||
case FILTER_UNDEFINED:
|
case FILTER_UNDEFINED:
|
||||||
return null;
|
return null;
|
||||||
case FILTER_POINT:
|
case FILTER_POINT:
|
||||||
@@ -871,14 +867,14 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
value = VALUE_INTERPOLATION_BLACKMAN_SINC;
|
value = VALUE_INTERPOLATION_BLACKMAN_SINC;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException("Unknown filter type: " + mFilterType);
|
throw new IllegalStateException("Unknown filter type: " + filterType);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new RenderingHints(KEY_RESAMPLE_INTERPOLATION, value);
|
return new RenderingHints(KEY_RESAMPLE_INTERPOLATION, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Rectangle2D getBounds2D(BufferedImage src) {
|
public Rectangle2D getBounds2D(BufferedImage src) {
|
||||||
return new Rectangle(mWidth, mHeight);
|
return new Rectangle(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
|
public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
|
||||||
@@ -1439,10 +1435,8 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
resample()
|
resample()
|
||||||
|
|
||||||
Resizes bitmaps while resampling them.
|
Resizes bitmaps while resampling them.
|
||||||
Returns -1 if error, 0 if success.
|
|
||||||
*/
|
*/
|
||||||
private BufferedImage resample(BufferedImage pSource, BufferedImage pDest, InterpolationFilter pFilter) {
|
private BufferedImage resample(BufferedImage pSource, BufferedImage pDest, InterpolationFilter pFilter) {
|
||||||
// TODO: Don't work... Could fix by creating a temporary image in filter method
|
|
||||||
final int dstWidth = pDest.getWidth();
|
final int dstWidth = pDest.getWidth();
|
||||||
final int dstHeight = pDest.getHeight();
|
final int dstHeight = pDest.getHeight();
|
||||||
|
|
||||||
@@ -1451,7 +1445,8 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
|
|
||||||
/* create intermediate column to hold horizontal dst column zoom */
|
/* create intermediate column to hold horizontal dst column zoom */
|
||||||
final ColorModel cm = pSource.getColorModel();
|
final ColorModel cm = pSource.getColorModel();
|
||||||
final WritableRaster work = cm.createCompatibleWritableRaster(1, srcHeight);
|
// final WritableRaster work = cm.createCompatibleWritableRaster(1, srcHeight);
|
||||||
|
final WritableRaster work = ImageUtil.createCompatibleWritableRaster(pSource, cm, 1, srcHeight);
|
||||||
|
|
||||||
double xscale = (double) dstWidth / (double) srcWidth;
|
double xscale = (double) dstWidth / (double) srcWidth;
|
||||||
double yscale = (double) dstHeight / (double) srcHeight;
|
double yscale = (double) dstHeight / (double) srcHeight;
|
||||||
@@ -1566,7 +1561,7 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
final WritableRaster out = pDest.getRaster();
|
final WritableRaster out = pDest.getRaster();
|
||||||
|
|
||||||
// TODO: This is not optimal for non-byte-packed rasters...
|
// TODO: This is not optimal for non-byte-packed rasters...
|
||||||
// (What? Maybe I implemented the fix, but forgot to remove the qTODO?)
|
// (What? Maybe I implemented the fix, but forgot to remove the TODO?)
|
||||||
final int numChannels = raster.getNumBands();
|
final int numChannels = raster.getNumBands();
|
||||||
final int[] channelMax = new int[numChannels];
|
final int[] channelMax = new int[numChannels];
|
||||||
for (int k = 0; k < numChannels; k++) {
|
for (int k = 0; k < numChannels; k++) {
|
||||||
@@ -1575,7 +1570,7 @@ public class ResampleOp implements BufferedImageOp/* TODO: RasterOp */ {
|
|||||||
|
|
||||||
for (int xx = 0; xx < dstWidth; xx++) {
|
for (int xx = 0; xx < dstWidth; xx++) {
|
||||||
ContributorList contribX = calcXContrib(xscale, fwidth, srcWidth, pFilter, xx);
|
ContributorList contribX = calcXContrib(xscale, fwidth, srcWidth, pFilter, xx);
|
||||||
/* Apply horz filter to make dst column in tmp. */
|
/* Apply horiz filter to make dst column in tmp. */
|
||||||
for (int k = 0; k < srcHeight; k++) {
|
for (int k = 0; k < srcHeight; k++) {
|
||||||
for (int channel = 0; channel < numChannels; channel++) {
|
for (int channel = 0; channel < numChannels; channel++) {
|
||||||
|
|
||||||
|
|||||||
@@ -42,8 +42,8 @@ import java.awt.image.ReplicateScaleFilter;
|
|||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/SubsamplingFilter.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/SubsamplingFilter.java#1 $
|
||||||
*/
|
*/
|
||||||
public class SubsamplingFilter extends ReplicateScaleFilter {
|
public class SubsamplingFilter extends ReplicateScaleFilter {
|
||||||
private int mXSub;
|
private int xSub;
|
||||||
private int mYSub;
|
private int ySub;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code SubsamplingFilter}.
|
* Creates a {@code SubsamplingFilter}.
|
||||||
@@ -62,16 +62,16 @@ public class SubsamplingFilter extends ReplicateScaleFilter {
|
|||||||
throw new IllegalArgumentException("Subsampling factors must be positive.");
|
throw new IllegalArgumentException("Subsampling factors must be positive.");
|
||||||
}
|
}
|
||||||
|
|
||||||
mXSub = pXSub;
|
xSub = pXSub;
|
||||||
mYSub = pYSub;
|
ySub = pYSub;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@code ImageFilter} implementation, do not invoke. */
|
/** {@code ImageFilter} implementation, do not invoke. */
|
||||||
public void setDimensions(int pWidth, int pHeight) {
|
public void setDimensions(int pWidth, int pHeight) {
|
||||||
destWidth = (pWidth + mXSub - 1) / mXSub;
|
destWidth = (pWidth + xSub - 1) / xSub;
|
||||||
destHeight = (pHeight + mYSub - 1) / mYSub;
|
destHeight = (pHeight + ySub - 1) / ySub;
|
||||||
|
|
||||||
//System.out.println("Subsampling: " + mXSub + "," + mYSub + "-> " + destWidth + ", " + destHeight);
|
//System.out.println("Subsampling: " + xSub + "," + ySub + "-> " + destWidth + ", " + destHeight);
|
||||||
super.setDimensions(pWidth, pHeight);
|
super.setDimensions(pWidth, pHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,6 @@
|
|||||||
* See the class {@link com.twelvemonkeys.image.ImageUtil}.
|
* See the class {@link com.twelvemonkeys.image.ImageUtil}.
|
||||||
*
|
*
|
||||||
* @version 1.0
|
* @version 1.0
|
||||||
* @author <a href="mailto:harald@escenic.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
*/
|
*/
|
||||||
package com.twelvemonkeys.image;
|
package com.twelvemonkeys.image;
|
||||||
+366
@@ -0,0 +1,366 @@
|
|||||||
|
package com.twelvemonkeys.image;
|
||||||
|
|
||||||
|
import org.junit.Ignore;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.color.ColorSpace;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.awt.image.ColorModel;
|
||||||
|
import java.awt.image.ImageProducer;
|
||||||
|
import java.awt.image.IndexColorModel;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BufferedImageFactoryTestCase
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haraldk$
|
||||||
|
* @version $Id: BufferedImageFactoryTestCase.java,v 1.0 May 7, 2010 12:40:08 PM haraldk Exp$
|
||||||
|
*/
|
||||||
|
public class BufferedImageFactoryTestCase {
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testCreateNullImage() {
|
||||||
|
new BufferedImageFactory((Image) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testCreateNullProducer() {
|
||||||
|
new BufferedImageFactory((ImageProducer) null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NPE in Toolkit, ok
|
||||||
|
@Test(expected = RuntimeException.class)
|
||||||
|
public void testGetBufferedImageErrorSourceByteArray() {
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage((byte[]) null);
|
||||||
|
|
||||||
|
new BufferedImageFactory(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalArgumentException.class)
|
||||||
|
public void testGetBufferedImageErrorSourceImageProducer() {
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage((ImageProducer) null);
|
||||||
|
|
||||||
|
new BufferedImageFactory(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This is a quite serious bug, however, the bug is in the Toolkit, allowing such images in the first place...
|
||||||
|
// In any case, there's not much we can do, except until someone is bored and kills the app/thread... :-P
|
||||||
|
@Ignore("Bug in Toolkit")
|
||||||
|
@Test(timeout = 1000, expected = ImageConversionException.class)
|
||||||
|
public void testGetBufferedImageErrorSourceString() {
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage((String) null);
|
||||||
|
|
||||||
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
|
factory.getBufferedImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a little random, and it would be nicer if we could throw an IllegalArgumentException on create.
|
||||||
|
// Unfortunately, the API doesn't allow this...
|
||||||
|
@Test(timeout = 1000, expected = ImageConversionException.class)
|
||||||
|
public void testGetBufferedImageErrorSourceURL() {
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage(getClass().getResource("/META-INF/MANIFEST.MF"));
|
||||||
|
|
||||||
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
|
factory.getBufferedImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetBufferedImageJPEG() {
|
||||||
|
URL resource = getClass().getResource("/sunflower.jpg");
|
||||||
|
assertNotNull(resource);
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage(resource);
|
||||||
|
assertNotNull(source);
|
||||||
|
|
||||||
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
|
BufferedImage image = factory.getBufferedImage();
|
||||||
|
|
||||||
|
assertEquals(187, image.getWidth());
|
||||||
|
assertEquals(283, image.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetColorModelJPEG() {
|
||||||
|
URL resource = getClass().getResource("/sunflower.jpg");
|
||||||
|
assertNotNull(resource);
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage(resource);
|
||||||
|
assertNotNull(source);
|
||||||
|
|
||||||
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
|
ColorModel colorModel = factory.getColorModel();
|
||||||
|
|
||||||
|
assertNotNull(colorModel);
|
||||||
|
assertEquals(3, colorModel.getNumColorComponents()); // getNumComponents may include alpha, we don't care
|
||||||
|
assertEquals(ColorSpace.getInstance(ColorSpace.CS_sRGB), colorModel.getColorSpace());
|
||||||
|
|
||||||
|
for (int i = 0; i < colorModel.getNumComponents(); i++) {
|
||||||
|
assertEquals(8, colorModel.getComponentSize(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetBufferedImageGIF() {
|
||||||
|
URL resource = getClass().getResource("/tux.gif");
|
||||||
|
assertNotNull(resource);
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage(resource);
|
||||||
|
assertNotNull(source);
|
||||||
|
|
||||||
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
|
BufferedImage image = factory.getBufferedImage();
|
||||||
|
|
||||||
|
assertEquals(250, image.getWidth());
|
||||||
|
assertEquals(250, image.getHeight());
|
||||||
|
|
||||||
|
assertEquals(Transparency.BITMASK, image.getTransparency());
|
||||||
|
|
||||||
|
// All corners of image should be fully transparent
|
||||||
|
assertEquals(0, image.getRGB(0, 0) >>> 24);
|
||||||
|
assertEquals(0, image.getRGB(249, 0) >>> 24);
|
||||||
|
assertEquals(0, image.getRGB(0, 249) >>> 24);
|
||||||
|
assertEquals(0, image.getRGB(249, 249) >>> 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetColorModelGIF() {
|
||||||
|
URL resource = getClass().getResource("/tux.gif");
|
||||||
|
assertNotNull(resource);
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage(resource);
|
||||||
|
assertNotNull(source);
|
||||||
|
|
||||||
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
|
ColorModel colorModel = factory.getColorModel();
|
||||||
|
|
||||||
|
assertNotNull(colorModel);
|
||||||
|
|
||||||
|
assertEquals(3, colorModel.getNumColorComponents());
|
||||||
|
assertEquals(ColorSpace.getInstance(ColorSpace.CS_sRGB), colorModel.getColorSpace());
|
||||||
|
assertTrue(colorModel instanceof IndexColorModel);
|
||||||
|
|
||||||
|
assertTrue(colorModel.hasAlpha());
|
||||||
|
assertEquals(4, colorModel.getNumComponents());
|
||||||
|
assertTrue(((IndexColorModel) colorModel).getTransparentPixel() >= 0);
|
||||||
|
assertEquals(Transparency.BITMASK, colorModel.getTransparency());
|
||||||
|
|
||||||
|
for (int i = 0; i < colorModel.getNumComponents(); i++) {
|
||||||
|
assertEquals(8, colorModel.getComponentSize(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetBufferedImageSubsampled() {
|
||||||
|
URL resource = getClass().getResource("/sunflower.jpg");
|
||||||
|
assertNotNull(resource);
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage(resource);
|
||||||
|
assertNotNull(source);
|
||||||
|
|
||||||
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
|
BufferedImage original = factory.getBufferedImage();
|
||||||
|
|
||||||
|
factory.setSourceSubsampling(2, 2);
|
||||||
|
BufferedImage image = factory.getBufferedImage(); // Accidentally also tests reuse...
|
||||||
|
|
||||||
|
// Values rounded up
|
||||||
|
assertEquals(94, image.getWidth());
|
||||||
|
assertEquals(142, image.getHeight());
|
||||||
|
|
||||||
|
for (int y = 0; y < image.getHeight(); y++) {
|
||||||
|
for (int x = 0; x < image.getWidth(); x++) {
|
||||||
|
assertEquals("RGB[" + x + ", " + y + "]", original.getRGB(x * 2, y * 2), image.getRGB(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetBufferedImageSourceRegion() {
|
||||||
|
URL resource = getClass().getResource("/sunflower.jpg");
|
||||||
|
assertNotNull(resource);
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage(resource);
|
||||||
|
assertNotNull(source);
|
||||||
|
|
||||||
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
|
BufferedImage original = factory.getBufferedImage();
|
||||||
|
|
||||||
|
factory.setSourceRegion(new Rectangle(40, 40, 40, 40));
|
||||||
|
BufferedImage image = factory.getBufferedImage(); // Accidentally also tests reuse...
|
||||||
|
|
||||||
|
assertEquals(40, image.getWidth());
|
||||||
|
assertEquals(40, image.getHeight());
|
||||||
|
|
||||||
|
for (int y = 0; y < image.getHeight(); y++) {
|
||||||
|
for (int x = 0; x < image.getWidth(); x++) {
|
||||||
|
assertEquals("RGB[" + x + ", " + y + "]", original.getRGB(40 + x, 40 + y), image.getRGB(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetBufferedImageSubsampledSourceRegion() throws Exception{
|
||||||
|
URL resource = getClass().getResource("/sunflower.jpg");
|
||||||
|
assertNotNull(resource);
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage(resource);
|
||||||
|
assertNotNull(source);
|
||||||
|
|
||||||
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
|
BufferedImage original = factory.getBufferedImage();
|
||||||
|
|
||||||
|
factory.setSourceRegion(new Rectangle(40, 40, 40, 40));
|
||||||
|
factory.setSourceSubsampling(2, 2);
|
||||||
|
BufferedImage image = factory.getBufferedImage(); // Accidentally also tests reuse...
|
||||||
|
|
||||||
|
assertEquals(20, image.getWidth());
|
||||||
|
assertEquals(20, image.getHeight());
|
||||||
|
|
||||||
|
for (int y = 0; y < image.getHeight(); y++) {
|
||||||
|
for (int x = 0; x < image.getWidth(); x++) {
|
||||||
|
assertEquals("RGB[" + x + ", " + y + "]", original.getRGB(40 + x * 2, 40 + y * 2), image.getRGB(x, y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAbort() throws Exception {
|
||||||
|
URL resource = getClass().getResource("/sunflower.jpg");
|
||||||
|
assertNotNull(resource);
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage(resource);
|
||||||
|
assertNotNull(source);
|
||||||
|
|
||||||
|
final BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
|
|
||||||
|
// Listener should abort ASAP
|
||||||
|
factory.addProgressListener(new BufferedImageFactory.ProgressListener() {
|
||||||
|
public void progress(BufferedImageFactory pFactory, float pPercentage) {
|
||||||
|
if (pPercentage > 5) {
|
||||||
|
pFactory.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
BufferedImage image = factory.getBufferedImage();
|
||||||
|
|
||||||
|
assertEquals(187, image.getWidth());
|
||||||
|
assertEquals(283, image.getHeight());
|
||||||
|
|
||||||
|
// Upper right should be loaded
|
||||||
|
assertEquals((image.getRGB(186, 0) & 0xFF0000) >> 16 , 0x68, 10);
|
||||||
|
assertEquals((image.getRGB(186, 0) & 0xFF00) >> 8, 0x91, 10);
|
||||||
|
assertEquals(image.getRGB(186, 0) & 0xFF, 0xE0, 10);
|
||||||
|
|
||||||
|
// Lower right should be blank
|
||||||
|
assertEquals(image.getRGB(186, 282) & 0xFFFFFF, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testListener() {
|
||||||
|
URL resource = getClass().getResource("/sunflower.jpg");
|
||||||
|
assertNotNull(resource);
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage(resource);
|
||||||
|
assertNotNull(source);
|
||||||
|
|
||||||
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
|
|
||||||
|
VerifyingListener listener = new VerifyingListener(factory);
|
||||||
|
factory.addProgressListener(listener);
|
||||||
|
factory.getBufferedImage();
|
||||||
|
|
||||||
|
listener.verify(100f);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveListener() {
|
||||||
|
URL resource = getClass().getResource("/sunflower.jpg");
|
||||||
|
assertNotNull(resource);
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage(resource);
|
||||||
|
assertNotNull(source);
|
||||||
|
|
||||||
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
|
|
||||||
|
VerifyingListener listener = new VerifyingListener(factory);
|
||||||
|
factory.addProgressListener(listener);
|
||||||
|
factory.removeProgressListener(listener);
|
||||||
|
factory.getBufferedImage();
|
||||||
|
|
||||||
|
listener.verify(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveNullListener() {
|
||||||
|
URL resource = getClass().getResource("/sunflower.jpg");
|
||||||
|
assertNotNull(resource);
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage(resource);
|
||||||
|
assertNotNull(source);
|
||||||
|
|
||||||
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
|
|
||||||
|
VerifyingListener listener = new VerifyingListener(factory);
|
||||||
|
factory.addProgressListener(listener);
|
||||||
|
factory.removeProgressListener(null);
|
||||||
|
factory.getBufferedImage();
|
||||||
|
|
||||||
|
listener.verify(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveNotAdddedListener() {
|
||||||
|
URL resource = getClass().getResource("/sunflower.jpg");
|
||||||
|
assertNotNull(resource);
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage(resource);
|
||||||
|
assertNotNull(source);
|
||||||
|
|
||||||
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
|
|
||||||
|
VerifyingListener listener = new VerifyingListener(factory);
|
||||||
|
factory.addProgressListener(listener);
|
||||||
|
factory.removeProgressListener(new BufferedImageFactory.ProgressListener() {
|
||||||
|
public void progress(BufferedImageFactory pFactory, float pPercentage) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
factory.getBufferedImage();
|
||||||
|
|
||||||
|
listener.verify(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRemoveAllListeners() {
|
||||||
|
URL resource = getClass().getResource("/sunflower.jpg");
|
||||||
|
assertNotNull(resource);
|
||||||
|
Image source = Toolkit.getDefaultToolkit().createImage(resource);
|
||||||
|
assertNotNull(source);
|
||||||
|
|
||||||
|
BufferedImageFactory factory = new BufferedImageFactory(source);
|
||||||
|
|
||||||
|
VerifyingListener listener = new VerifyingListener(factory);
|
||||||
|
VerifyingListener listener2 = new VerifyingListener(factory);
|
||||||
|
factory.addProgressListener(listener);
|
||||||
|
factory.addProgressListener(listener);
|
||||||
|
factory.addProgressListener(listener2);
|
||||||
|
factory.removeAllProgressListeners();
|
||||||
|
factory.getBufferedImage();
|
||||||
|
|
||||||
|
listener.verify(0);
|
||||||
|
listener2.verify(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class VerifyingListener implements BufferedImageFactory.ProgressListener {
|
||||||
|
private final BufferedImageFactory factory;
|
||||||
|
private float progress;
|
||||||
|
|
||||||
|
public VerifyingListener(BufferedImageFactory factory) {
|
||||||
|
this.factory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void progress(BufferedImageFactory pFactory, float pPercentage) {
|
||||||
|
assertEquals(factory, pFactory);
|
||||||
|
assertTrue(pPercentage >= progress && pPercentage <= 100f);
|
||||||
|
|
||||||
|
progress = pPercentage;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void verify(final float expectedProgress) {
|
||||||
|
assertEquals(expectedProgress, progress, .1f); // Sanity test that the listener was invoked
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,35 +24,35 @@ import java.lang.reflect.InvocationTargetException;
|
|||||||
public class ImageUtilTestCase extends TestCase {
|
public class ImageUtilTestCase extends TestCase {
|
||||||
|
|
||||||
private final static String IMAGE_NAME = "/sunflower.jpg";
|
private final static String IMAGE_NAME = "/sunflower.jpg";
|
||||||
private BufferedImage mOriginal;
|
private BufferedImage original;
|
||||||
private BufferedImage mImage;
|
private BufferedImage image;
|
||||||
private Image mScaled;
|
private Image scaled;
|
||||||
|
|
||||||
public ImageUtilTestCase() throws Exception {
|
public ImageUtilTestCase() throws Exception {
|
||||||
mImage = new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB);
|
image = new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB);
|
||||||
mScaled = mImage.getScaledInstance(5, 5, Image.SCALE_FAST);
|
scaled = image.getScaledInstance(5, 5, Image.SCALE_FAST);
|
||||||
|
|
||||||
// Read image from class path
|
// Read image from class path
|
||||||
InputStream is = getClass().getResourceAsStream(IMAGE_NAME);
|
InputStream is = getClass().getResourceAsStream(IMAGE_NAME);
|
||||||
mOriginal = ImageIO.read(is);
|
original = ImageIO.read(is);
|
||||||
|
|
||||||
assertNotNull(mOriginal);
|
assertNotNull(original);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
mImage = new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB);
|
image = new BufferedImage(10, 10, BufferedImage.TYPE_INT_ARGB);
|
||||||
mScaled = mImage.getScaledInstance(5, 5, Image.SCALE_FAST);
|
scaled = image.getScaledInstance(5, 5, Image.SCALE_FAST);
|
||||||
|
|
||||||
// Read image from class path
|
// Read image from class path
|
||||||
InputStream is = ClassLoader.getSystemResourceAsStream(IMAGE_NAME);
|
InputStream is = ClassLoader.getSystemResourceAsStream(IMAGE_NAME);
|
||||||
mOriginal = ImageIO.read(is);
|
original = ImageIO.read(is);
|
||||||
|
|
||||||
assertNotNull(mOriginal);
|
assertNotNull(original);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void tearDown() throws Exception {
|
protected void tearDown() throws Exception {
|
||||||
mOriginal = null;
|
original = null;
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -94,20 +94,20 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
// Should not be a buffered image
|
// Should not be a buffered image
|
||||||
assertFalse(
|
assertFalse(
|
||||||
"FOR SOME IMPLEMENTATIONS THIS MIGHT FAIL!\nIn that case, testToBufferedImage() will fail too.",
|
"FOR SOME IMPLEMENTATIONS THIS MIGHT FAIL!\nIn that case, testToBufferedImage() will fail too.",
|
||||||
mScaled instanceof BufferedImage
|
scaled instanceof BufferedImage
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testToBufferedImage() {
|
public void testToBufferedImage() {
|
||||||
BufferedImage sameAsImage = ImageUtil.toBuffered((RenderedImage) mImage);
|
BufferedImage sameAsImage = ImageUtil.toBuffered((RenderedImage) image);
|
||||||
BufferedImage bufferedScaled = ImageUtil.toBuffered(mScaled);
|
BufferedImage bufferedScaled = ImageUtil.toBuffered(scaled);
|
||||||
|
|
||||||
// Should be no need to convert
|
// Should be no need to convert
|
||||||
assertSame(mImage, sameAsImage);
|
assertSame(image, sameAsImage);
|
||||||
|
|
||||||
// Should have same dimensions
|
// Should have same dimensions
|
||||||
assertEquals(mScaled.getWidth(null), bufferedScaled.getWidth());
|
assertEquals(scaled.getWidth(null), bufferedScaled.getWidth());
|
||||||
assertEquals(mScaled.getHeight(null), bufferedScaled.getHeight());
|
assertEquals(scaled.getHeight(null), bufferedScaled.getHeight());
|
||||||
|
|
||||||
// Hmmm...
|
// Hmmm...
|
||||||
assertTrue(new Integer(42).equals(bufferedScaled.getProperty("lucky-number"))
|
assertTrue(new Integer(42).equals(bufferedScaled.getProperty("lucky-number"))
|
||||||
@@ -116,28 +116,28 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testToBufferedImageType() {
|
public void testToBufferedImageType() {
|
||||||
// Assumes mImage is TYPE_INT_ARGB
|
// Assumes image is TYPE_INT_ARGB
|
||||||
BufferedImage converted = ImageUtil.toBuffered(mImage, BufferedImage.TYPE_BYTE_INDEXED);
|
BufferedImage converted = ImageUtil.toBuffered(image, BufferedImage.TYPE_BYTE_INDEXED);
|
||||||
BufferedImage convertedToo = ImageUtil.toBuffered(mImage, BufferedImage.TYPE_BYTE_BINARY);
|
BufferedImage convertedToo = ImageUtil.toBuffered(image, BufferedImage.TYPE_BYTE_BINARY);
|
||||||
|
|
||||||
// Should not be the same
|
// Should not be the same
|
||||||
assertNotSame(mImage, converted);
|
assertNotSame(image, converted);
|
||||||
assertNotSame(mImage, convertedToo);
|
assertNotSame(image, convertedToo);
|
||||||
|
|
||||||
// Correct type
|
// Correct type
|
||||||
assertTrue(converted.getType() == BufferedImage.TYPE_BYTE_INDEXED);
|
assertTrue(converted.getType() == BufferedImage.TYPE_BYTE_INDEXED);
|
||||||
assertTrue(convertedToo.getType() == BufferedImage.TYPE_BYTE_BINARY);
|
assertTrue(convertedToo.getType() == BufferedImage.TYPE_BYTE_BINARY);
|
||||||
|
|
||||||
// Should have same dimensions
|
// Should have same dimensions
|
||||||
assertEquals(mImage.getWidth(), converted.getWidth());
|
assertEquals(image.getWidth(), converted.getWidth());
|
||||||
assertEquals(mImage.getHeight(), converted.getHeight());
|
assertEquals(image.getHeight(), converted.getHeight());
|
||||||
|
|
||||||
assertEquals(mImage.getWidth(), convertedToo.getWidth());
|
assertEquals(image.getWidth(), convertedToo.getWidth());
|
||||||
assertEquals(mImage.getHeight(), convertedToo.getHeight());
|
assertEquals(image.getHeight(), convertedToo.getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testBrightness() {
|
public void testBrightness() {
|
||||||
final BufferedImage original = mOriginal;
|
final BufferedImage original = this.original;
|
||||||
assertNotNull(original);
|
assertNotNull(original);
|
||||||
|
|
||||||
final BufferedImage notBrightened = ImageUtil.toBuffered(ImageUtil.brightness(original, 0f));
|
final BufferedImage notBrightened = ImageUtil.toBuffered(ImageUtil.brightness(original, 0f));
|
||||||
@@ -217,7 +217,7 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
|
|
||||||
|
|
||||||
public void testContrast() {
|
public void testContrast() {
|
||||||
final BufferedImage original = mOriginal;
|
final BufferedImage original = this.original;
|
||||||
|
|
||||||
assertNotNull(original);
|
assertNotNull(original);
|
||||||
|
|
||||||
@@ -370,7 +370,7 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testSharpen() {
|
public void testSharpen() {
|
||||||
final BufferedImage original = mOriginal;
|
final BufferedImage original = this.original;
|
||||||
|
|
||||||
assertNotNull(original);
|
assertNotNull(original);
|
||||||
|
|
||||||
@@ -495,7 +495,7 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testBlur() {
|
public void testBlur() {
|
||||||
final BufferedImage original = mOriginal;
|
final BufferedImage original = this.original;
|
||||||
|
|
||||||
assertNotNull(original);
|
assertNotNull(original);
|
||||||
|
|
||||||
@@ -563,7 +563,7 @@ public class ImageUtilTestCase extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testIndexImage() {
|
public void testIndexImage() {
|
||||||
BufferedImage sunflower = mOriginal;
|
BufferedImage sunflower = original;
|
||||||
|
|
||||||
assertNotNull(sunflower);
|
assertNotNull(sunflower);
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -0,0 +1,6 @@
|
|||||||
|
TODO:
|
||||||
|
Remove compile-time dependency on JMagick:
|
||||||
|
- Extract interface for MagickAccelerator
|
||||||
|
- Move implementation to separate module
|
||||||
|
- Instantiate impl via reflection
|
||||||
|
DONE:
|
||||||
+25
-24
@@ -2,30 +2,31 @@
|
|||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.twelvemonkeys.common</groupId>
|
<groupId>com.twelvemonkeys.common</groupId>
|
||||||
<artifactId>common</artifactId>
|
<artifactId>common</artifactId>
|
||||||
<version>3.0-SNAPSHOT</version>
|
<version>3.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>common-io</artifactId>
|
<artifactId>common-io</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>TwelveMonkeys :: Common :: IO</name>
|
<name>TwelveMonkeys :: Common :: IO</name>
|
||||||
<description>
|
<description>
|
||||||
The TwelveMonkeys IO support
|
The TwelveMonkeys Common IO support
|
||||||
</description>
|
</description>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>${project.groupId}</groupId>
|
<groupId>${project.groupId}</groupId>
|
||||||
<artifactId>common-lang</artifactId>
|
<artifactId>common-lang</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>${project.groupId}</groupId>
|
<dependency>
|
||||||
<artifactId>common-lang</artifactId>
|
<groupId>${project.groupId}</groupId>
|
||||||
<classifier>tests</classifier>
|
<artifactId>common-lang</artifactId>
|
||||||
<scope>test</scope>
|
<classifier>tests</classifier>
|
||||||
</dependency>
|
<scope>test</scope>
|
||||||
</dependencies>
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
+39
-39
@@ -14,28 +14,28 @@ import java.io.InputStream;
|
|||||||
*/
|
*/
|
||||||
abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
||||||
/** The backing stream */
|
/** The backing stream */
|
||||||
protected final InputStream mStream;
|
protected final InputStream stream;
|
||||||
|
|
||||||
/** The stream positon in the backing stream (mStream) */
|
/** The stream positon in the backing stream (stream) */
|
||||||
protected long mStreamPosition;
|
protected long streamPosition;
|
||||||
|
|
||||||
private StreamCache mCache;
|
private StreamCache cache;
|
||||||
|
|
||||||
protected AbstractCachedSeekableStream(final InputStream pStream, final StreamCache pCache) {
|
protected AbstractCachedSeekableStream(final InputStream pStream, final StreamCache pCache) {
|
||||||
Validate.notNull(pStream, "stream");
|
Validate.notNull(pStream, "stream");
|
||||||
Validate.notNull(pCache, "cache");
|
Validate.notNull(pCache, "cache");
|
||||||
|
|
||||||
mStream = pStream;
|
stream = pStream;
|
||||||
mCache = pCache;
|
cache = pCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final StreamCache getCache() {
|
protected final StreamCache getCache() {
|
||||||
return mCache;
|
return cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int available() throws IOException {
|
public int available() throws IOException {
|
||||||
long avail = mStreamPosition - mPosition + mStream.available();
|
long avail = streamPosition - position + stream.available();
|
||||||
return avail > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) avail;
|
return avail > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) avail;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,26 +43,26 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
|||||||
checkOpen();
|
checkOpen();
|
||||||
int read;
|
int read;
|
||||||
|
|
||||||
if (mPosition == mStreamPosition) {
|
if (position == streamPosition) {
|
||||||
// TODO: Read more bytes here!
|
// TODO: Read more bytes here!
|
||||||
// TODO: Use buffer if not in-memory cache? (See FileCacheSeekableStream overrides).
|
// TODO: Use buffer if not in-memory cache? (See FileCacheSeekableStream overrides).
|
||||||
// Read a byte from the stream
|
// Read a byte from the stream
|
||||||
read = mStream.read();
|
read = stream.read();
|
||||||
|
|
||||||
if (read >= 0) {
|
if (read >= 0) {
|
||||||
mStreamPosition++;
|
streamPosition++;
|
||||||
mCache.write(read);
|
cache.write(read);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// ..or read byte from the cache
|
// ..or read byte from the cache
|
||||||
syncPosition();
|
syncPosition();
|
||||||
read = mCache.read();
|
read = cache.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This field is not REALLY considered accessible.. :-P
|
// TODO: This field is not REALLY considered accessible.. :-P
|
||||||
if (read != -1) {
|
if (read != -1) {
|
||||||
mPosition++;
|
position++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return read;
|
return read;
|
||||||
@@ -73,32 +73,32 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
|||||||
checkOpen();
|
checkOpen();
|
||||||
int length;
|
int length;
|
||||||
|
|
||||||
if (mPosition == mStreamPosition) {
|
if (position == streamPosition) {
|
||||||
// Read bytes from the stream
|
// Read bytes from the stream
|
||||||
length = mStream.read(pBytes, pOffset, pLength);
|
length = stream.read(pBytes, pOffset, pLength);
|
||||||
|
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
mStreamPosition += length;
|
streamPosition += length;
|
||||||
mCache.write(pBytes, pOffset, length);
|
cache.write(pBytes, pOffset, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// ...or read bytes from the cache
|
// ...or read bytes from the cache
|
||||||
syncPosition();
|
syncPosition();
|
||||||
length = mCache.read(pBytes, pOffset, pLength);
|
length = cache.read(pBytes, pOffset, pLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This field is not REALLY considered accessible.. :-P
|
// TODO: This field is not REALLY considered accessible.. :-P
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
mPosition += length;
|
position += length;
|
||||||
}
|
}
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void syncPosition() throws IOException {
|
protected final void syncPosition() throws IOException {
|
||||||
if (mCache.getPosition() != mPosition) {
|
if (cache.getPosition() != position) {
|
||||||
mCache.seek(mPosition); // Assure EOF is correctly thrown
|
cache.seek(position); // Assure EOF is correctly thrown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,14 +111,14 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
|||||||
public abstract boolean isCachedFile();
|
public abstract boolean isCachedFile();
|
||||||
|
|
||||||
protected void seekImpl(long pPosition) throws IOException {
|
protected void seekImpl(long pPosition) throws IOException {
|
||||||
if (mStreamPosition < pPosition) {
|
if (streamPosition < pPosition) {
|
||||||
// Make sure we append at end of cache
|
// Make sure we append at end of cache
|
||||||
if (mCache.getPosition() != mStreamPosition) {
|
if (cache.getPosition() != streamPosition) {
|
||||||
mCache.seek(mStreamPosition);
|
cache.seek(streamPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read diff from stream into cache
|
// Read diff from stream into cache
|
||||||
long left = pPosition - mStreamPosition;
|
long left = pPosition - streamPosition;
|
||||||
|
|
||||||
// TODO: Use fixed buffer, instead of allocating here...
|
// TODO: Use fixed buffer, instead of allocating here...
|
||||||
int bufferLen = left > 1024 ? 1024 : (int) left;
|
int bufferLen = left > 1024 ? 1024 : (int) left;
|
||||||
@@ -126,11 +126,11 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
|||||||
|
|
||||||
while (left > 0) {
|
while (left > 0) {
|
||||||
int length = buffer.length < left ? buffer.length : (int) left;
|
int length = buffer.length < left ? buffer.length : (int) left;
|
||||||
int read = mStream.read(buffer, 0, length);
|
int read = stream.read(buffer, 0, length);
|
||||||
|
|
||||||
if (read > 0) {
|
if (read > 0) {
|
||||||
mCache.write(buffer, 0, read);
|
cache.write(buffer, 0, read);
|
||||||
mStreamPosition += read;
|
streamPosition += read;
|
||||||
left -= read;
|
left -= read;
|
||||||
}
|
}
|
||||||
else if (read < 0) {
|
else if (read < 0) {
|
||||||
@@ -138,27 +138,27 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (mStreamPosition >= pPosition) {
|
else /*if (streamPosition >= pPosition) */ {
|
||||||
// Seek backwards into the cache
|
// Seek backwards into the cache
|
||||||
mCache.seek(pPosition);
|
cache.seek(pPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
// System.out.println("pPosition: " + pPosition);
|
// System.out.println("pPosition: " + pPosition);
|
||||||
// System.out.println("mPosition: " + mPosition);
|
// System.out.println("position: " + position);
|
||||||
// System.out.println("mStreamPosition: " + mStreamPosition);
|
// System.out.println("streamPosition: " + streamPosition);
|
||||||
// System.out.println("mCache.mPosition: " + mCache.getPosition());
|
// System.out.println("cache.position: " + cache.getPosition());
|
||||||
|
|
||||||
// NOTE: If mPosition == pPosition then we're good to go
|
// NOTE: If position == pPosition then we're good to go
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void flushBeforeImpl(long pPosition) {
|
protected void flushBeforeImpl(long pPosition) {
|
||||||
mCache.flush(pPosition);
|
cache.flush(pPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void closeImpl() throws IOException {
|
protected void closeImpl() throws IOException {
|
||||||
mCache.flush(mPosition);
|
cache.flush(position);
|
||||||
mCache = null;
|
cache = null;
|
||||||
mStream.close();
|
stream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -46,15 +46,16 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class CompoundReader extends Reader {
|
public class CompoundReader extends Reader {
|
||||||
|
|
||||||
private Reader mCurrent;
|
private Reader current;
|
||||||
private List<Reader> mReaders;
|
private List<Reader> readers;
|
||||||
protected final Object mLock;
|
|
||||||
|
|
||||||
protected final boolean mMarkSupported;
|
protected final Object finalLock;
|
||||||
|
|
||||||
private int mCurrentReader;
|
protected final boolean markSupported;
|
||||||
private int mMarkedReader;
|
|
||||||
private int mMark;
|
private int currentReader;
|
||||||
|
private int markedReader;
|
||||||
|
private int mark;
|
||||||
private int mNext;
|
private int mNext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,10 +72,10 @@ public class CompoundReader extends Reader {
|
|||||||
public CompoundReader(final Iterator<Reader> pReaders) {
|
public CompoundReader(final Iterator<Reader> pReaders) {
|
||||||
super(Validate.notNull(pReaders, "readers"));
|
super(Validate.notNull(pReaders, "readers"));
|
||||||
|
|
||||||
mLock = pReaders; // NOTE: It's ok to sync on pReaders, as the
|
finalLock = pReaders; // NOTE: It's ok to sync on pReaders, as the
|
||||||
// reference can't change, only it's elements
|
// reference can't change, only it's elements
|
||||||
|
|
||||||
mReaders = new ArrayList<Reader>();
|
readers = new ArrayList<Reader>();
|
||||||
|
|
||||||
boolean markSupported = true;
|
boolean markSupported = true;
|
||||||
while (pReaders.hasNext()) {
|
while (pReaders.hasNext()) {
|
||||||
@@ -82,25 +83,25 @@ public class CompoundReader extends Reader {
|
|||||||
if (reader == null) {
|
if (reader == null) {
|
||||||
throw new NullPointerException("readers cannot contain null-elements");
|
throw new NullPointerException("readers cannot contain null-elements");
|
||||||
}
|
}
|
||||||
mReaders.add(reader);
|
readers.add(reader);
|
||||||
markSupported = markSupported && reader.markSupported();
|
markSupported = markSupported && reader.markSupported();
|
||||||
}
|
}
|
||||||
mMarkSupported = markSupported;
|
this.markSupported = markSupported;
|
||||||
|
|
||||||
mCurrent = nextReader();
|
current = nextReader();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final Reader nextReader() {
|
protected final Reader nextReader() {
|
||||||
if (mCurrentReader >= mReaders.size()) {
|
if (currentReader >= readers.size()) {
|
||||||
mCurrent = new EmptyReader();
|
current = new EmptyReader();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mCurrent = mReaders.get(mCurrentReader++);
|
current = readers.get(currentReader++);
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Reset mNext for every reader, and record marked reader in mark/reset methods!
|
// NOTE: Reset mNext for every reader, and record marked reader in mark/reset methods!
|
||||||
mNext = 0;
|
mNext = 0;
|
||||||
return mCurrent;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -109,17 +110,18 @@ public class CompoundReader extends Reader {
|
|||||||
* @throws IOException if the stream is closed
|
* @throws IOException if the stream is closed
|
||||||
*/
|
*/
|
||||||
protected final void ensureOpen() throws IOException {
|
protected final void ensureOpen() throws IOException {
|
||||||
if (mReaders == null) {
|
if (readers == null) {
|
||||||
throw new IOException("Stream closed");
|
throw new IOException("Stream closed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
// Close all readers
|
// Close all readers
|
||||||
for (Reader reader : mReaders) {
|
for (Reader reader : readers) {
|
||||||
reader.close();
|
reader.close();
|
||||||
}
|
}
|
||||||
mReaders = null;
|
|
||||||
|
readers = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -130,46 +132,46 @@ public class CompoundReader extends Reader {
|
|||||||
|
|
||||||
// TODO: It would be nice if we could actually close some readers now
|
// TODO: It would be nice if we could actually close some readers now
|
||||||
|
|
||||||
synchronized (mLock) {
|
synchronized (finalLock) {
|
||||||
ensureOpen();
|
ensureOpen();
|
||||||
mMark = mNext;
|
mark = mNext;
|
||||||
mMarkedReader = mCurrentReader;
|
markedReader = currentReader;
|
||||||
|
|
||||||
mCurrent.mark(pReadLimit);
|
current.mark(pReadLimit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() throws IOException {
|
public void reset() throws IOException {
|
||||||
synchronized (mLock) {
|
synchronized (finalLock) {
|
||||||
ensureOpen();
|
ensureOpen();
|
||||||
|
|
||||||
if (mCurrentReader != mMarkedReader) {
|
if (currentReader != markedReader) {
|
||||||
// Reset any reader before this
|
// Reset any reader before this
|
||||||
for (int i = mCurrentReader; i >= mMarkedReader; i--) {
|
for (int i = currentReader; i >= markedReader; i--) {
|
||||||
mReaders.get(i).reset();
|
readers.get(i).reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
mCurrentReader = mMarkedReader - 1;
|
currentReader = markedReader - 1;
|
||||||
nextReader();
|
nextReader();
|
||||||
}
|
}
|
||||||
mCurrent.reset();
|
current.reset();
|
||||||
|
|
||||||
mNext = mMark;
|
mNext = mark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean markSupported() {
|
public boolean markSupported() {
|
||||||
return mMarkSupported;
|
return markSupported;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
synchronized (mLock) {
|
synchronized (finalLock) {
|
||||||
int read = mCurrent.read();
|
int read = current.read();
|
||||||
|
|
||||||
if (read < 0 && mCurrentReader < mReaders.size()) {
|
if (read < 0 && currentReader < readers.size()) {
|
||||||
nextReader();
|
nextReader();
|
||||||
return read(); // In case of 0-length readers
|
return read(); // In case of 0-length readers
|
||||||
}
|
}
|
||||||
@@ -181,10 +183,10 @@ public class CompoundReader extends Reader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int read(char pBuffer[], int pOffset, int pLength) throws IOException {
|
public int read(char pBuffer[], int pOffset, int pLength) throws IOException {
|
||||||
synchronized (mLock) {
|
synchronized (finalLock) {
|
||||||
int read = mCurrent.read(pBuffer, pOffset, pLength);
|
int read = current.read(pBuffer, pOffset, pLength);
|
||||||
|
|
||||||
if (read < 0 && mCurrentReader < mReaders.size()) {
|
if (read < 0 && currentReader < readers.size()) {
|
||||||
nextReader();
|
nextReader();
|
||||||
return read(pBuffer, pOffset, pLength); // In case of 0-length readers
|
return read(pBuffer, pOffset, pLength); // In case of 0-length readers
|
||||||
}
|
}
|
||||||
@@ -197,15 +199,15 @@ public class CompoundReader extends Reader {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean ready() throws IOException {
|
public boolean ready() throws IOException {
|
||||||
return mCurrent.ready();
|
return current.ready();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long skip(long pChars) throws IOException {
|
public long skip(long pChars) throws IOException {
|
||||||
synchronized (mLock) {
|
synchronized (finalLock) {
|
||||||
long skipped = mCurrent.skip(pChars);
|
long skipped = current.skip(pChars);
|
||||||
|
|
||||||
if (skipped == 0 && mCurrentReader < mReaders.size()) {
|
if (skipped == 0 && currentReader < readers.size()) {
|
||||||
nextReader();
|
nextReader();
|
||||||
return skip(pChars); // In case of 0-length readers
|
return skip(pChars); // In case of 0-length readers
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-18
@@ -39,11 +39,12 @@ import java.io.ByteArrayInputStream;
|
|||||||
* <p/>
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/FastByteArrayOutputStream.java#2 $
|
* @version $Id: FastByteArrayOutputStream.java#2 $
|
||||||
*/
|
*/
|
||||||
|
// TODO: Performance test of a stream impl that uses list of fixed size blocks, rather than contiguous block
|
||||||
public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
||||||
/** Max grow size (unless if writing more than this ammount of bytes) */
|
/** Max grow size (unless if writing more than this amount of bytes) */
|
||||||
protected int mMaxGrowSize = 1024 * 1024; // 1 MB
|
protected int maxGrowSize = 1024 * 1024; // 1 MB
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code ByteArrayOutputStream} with the given initial buffer
|
* Creates a {@code ByteArrayOutputStream} with the given initial buffer
|
||||||
@@ -69,7 +70,7 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void write(byte pBytes[], int pOffset, int pLength) {
|
public void write(byte pBytes[], int pOffset, int pLength) {
|
||||||
if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
|
||||||
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
((pOffset + pLength) > pBytes.length) || ((pOffset + pLength) < 0)) {
|
||||||
throw new IndexOutOfBoundsException();
|
throw new IndexOutOfBoundsException();
|
||||||
@@ -77,23 +78,24 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
|||||||
else if (pLength == 0) {
|
else if (pLength == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int newcount = count + pLength;
|
|
||||||
growIfNeeded(newcount);
|
int newCount = count + pLength;
|
||||||
|
growIfNeeded(newCount);
|
||||||
System.arraycopy(pBytes, pOffset, buf, count, pLength);
|
System.arraycopy(pBytes, pOffset, buf, count, pLength);
|
||||||
count = newcount;
|
count = newCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void write(int pByte) {
|
public void write(int pByte) {
|
||||||
int newcount = count + 1;
|
int newCount = count + 1;
|
||||||
growIfNeeded(newcount);
|
growIfNeeded(newCount);
|
||||||
buf[count] = (byte) pByte;
|
buf[count] = (byte) pByte;
|
||||||
count = newcount;
|
count = newCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void growIfNeeded(int pNewcount) {
|
private void growIfNeeded(int pNewCount) {
|
||||||
if (pNewcount > buf.length) {
|
if (pNewCount > buf.length) {
|
||||||
int newSize = Math.max(Math.min(buf.length << 1, buf.length + mMaxGrowSize), pNewcount);
|
int newSize = Math.max(Math.min(buf.length << 1, buf.length + maxGrowSize), pNewCount);
|
||||||
byte newBuf[] = new byte[newSize];
|
byte newBuf[] = new byte[newSize];
|
||||||
System.arraycopy(buf, 0, newBuf, 0, count);
|
System.arraycopy(buf, 0, newBuf, 0, count);
|
||||||
buf = newBuf;
|
buf = newBuf;
|
||||||
@@ -109,9 +111,10 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
|||||||
// Non-synchronized version of toByteArray
|
// Non-synchronized version of toByteArray
|
||||||
@Override
|
@Override
|
||||||
public byte[] toByteArray() {
|
public byte[] toByteArray() {
|
||||||
byte newbuf[] = new byte[count];
|
byte newBuf[] = new byte[count];
|
||||||
System.arraycopy(buf, 0, newbuf, 0, count);
|
System.arraycopy(buf, 0, newBuf, 0, count);
|
||||||
return newbuf;
|
|
||||||
|
return newBuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,7 +124,7 @@ public final class FastByteArrayOutputStream extends ByteArrayOutputStream {
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Note that care needs to be taken to avoid writes to
|
* Note that care needs to be taken to avoid writes to
|
||||||
* this output stream after the input stream is created.
|
* this output stream after the input stream is created.
|
||||||
* Failing to do so, may result in unpredictable behviour.
|
* Failing to do so, may result in unpredictable behaviour.
|
||||||
*
|
*
|
||||||
* @return a new {@code ByteArrayInputStream}, reading from this stream's buffer.
|
* @return a new {@code ByteArrayInputStream}, reading from this stream's buffer.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -48,16 +48,7 @@ import java.io.*;
|
|||||||
*/
|
*/
|
||||||
public final class FileCacheSeekableStream extends AbstractCachedSeekableStream {
|
public final class FileCacheSeekableStream extends AbstractCachedSeekableStream {
|
||||||
|
|
||||||
// private final InputStream mStream;
|
private byte[] buffer;
|
||||||
// private final RandomAccessFile mCache;
|
|
||||||
private byte[] mBuffer;
|
|
||||||
|
|
||||||
/** The stream positon in the backing stream (mStream) */
|
|
||||||
// private long mStreamPosition;
|
|
||||||
|
|
||||||
// TODO: getStreamPosition() should always be the same as
|
|
||||||
// mCache.getFilePointer()
|
|
||||||
// otherwise there's some inconsistency here... Enforce this?
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code FileCacheSeekableStream} reading from the given
|
* Creates a {@code FileCacheSeekableStream} reading from the given
|
||||||
@@ -118,7 +109,7 @@ public final class FileCacheSeekableStream extends AbstractCachedSeekableStream
|
|||||||
super(pStream, new FileCache(pFile));
|
super(pStream, new FileCache(pFile));
|
||||||
|
|
||||||
// TODO: Allow for custom buffer sizes?
|
// TODO: Allow for custom buffer sizes?
|
||||||
mBuffer = new byte[1024];
|
buffer = new byte[1024];
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isCachedMemory() {
|
public final boolean isCachedMemory() {
|
||||||
@@ -132,39 +123,19 @@ public final class FileCacheSeekableStream extends AbstractCachedSeekableStream
|
|||||||
@Override
|
@Override
|
||||||
protected void closeImpl() throws IOException {
|
protected void closeImpl() throws IOException {
|
||||||
super.closeImpl();
|
super.closeImpl();
|
||||||
mBuffer = null;
|
buffer = null;
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
public final boolean isCached() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// InputStream overrides
|
|
||||||
@Override
|
|
||||||
public int available() throws IOException {
|
|
||||||
long avail = mStreamPosition - mPosition + mStream.available();
|
|
||||||
return avail > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) avail;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void closeImpl() throws IOException {
|
|
||||||
mStream.close();
|
|
||||||
mCache.close();
|
|
||||||
|
|
||||||
// TODO: Delete cache file here?
|
|
||||||
// ThreadPool.invokeLater(new DeleteFileAction(mCacheFile));
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
|
|
||||||
int read;
|
int read;
|
||||||
if (mPosition == mStreamPosition) {
|
if (position == streamPosition) {
|
||||||
// Read ahead into buffer, for performance
|
// Read ahead into buffer, for performance
|
||||||
read = readAhead(mBuffer, 0, mBuffer.length);
|
read = readAhead(buffer, 0, buffer.length);
|
||||||
if (read >= 0) {
|
if (read >= 0) {
|
||||||
read = mBuffer[0] & 0xff;
|
read = buffer[0] & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
//System.out.println("Read 1 byte from stream: " + Integer.toHexString(read & 0xff));
|
//System.out.println("Read 1 byte from stream: " + Integer.toHexString(read & 0xff));
|
||||||
@@ -179,7 +150,7 @@ public final class FileCacheSeekableStream extends AbstractCachedSeekableStream
|
|||||||
|
|
||||||
// TODO: This field is not REALLY considered accessible.. :-P
|
// TODO: This field is not REALLY considered accessible.. :-P
|
||||||
if (read != -1) {
|
if (read != -1) {
|
||||||
mPosition++;
|
position++;
|
||||||
}
|
}
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
@@ -189,7 +160,7 @@ public final class FileCacheSeekableStream extends AbstractCachedSeekableStream
|
|||||||
checkOpen();
|
checkOpen();
|
||||||
|
|
||||||
int length;
|
int length;
|
||||||
if (mPosition == mStreamPosition) {
|
if (position == streamPosition) {
|
||||||
// Read bytes from the stream
|
// Read bytes from the stream
|
||||||
length = readAhead(pBytes, pOffset, pLength);
|
length = readAhead(pBytes, pOffset, pLength);
|
||||||
|
|
||||||
@@ -198,83 +169,29 @@ public final class FileCacheSeekableStream extends AbstractCachedSeekableStream
|
|||||||
else {
|
else {
|
||||||
// ...or read bytes from the cache
|
// ...or read bytes from the cache
|
||||||
syncPosition();
|
syncPosition();
|
||||||
length = getCache().read(pBytes, pOffset, (int) Math.min(pLength, mStreamPosition - mPosition));
|
length = getCache().read(pBytes, pOffset, (int) Math.min(pLength, streamPosition - position));
|
||||||
|
|
||||||
//System.out.println("Read " + length + " byte from cache");
|
//System.out.println("Read " + length + " byte from cache");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This field is not REALLY considered accessible.. :-P
|
// TODO: This field is not REALLY considered accessible.. :-P
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
mPosition += length;
|
position += length;
|
||||||
}
|
}
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int readAhead(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
private int readAhead(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||||
int length;
|
int length;
|
||||||
length = mStream.read(pBytes, pOffset, pLength);
|
length = stream.read(pBytes, pOffset, pLength);
|
||||||
|
|
||||||
if (length > 0) {
|
if (length > 0) {
|
||||||
mStreamPosition += length;
|
streamPosition += length;
|
||||||
getCache().write(pBytes, pOffset, length);
|
getCache().write(pBytes, pOffset, length);
|
||||||
}
|
}
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
private void syncPosition() throws IOException {
|
|
||||||
if (mCache.getFilePointer() != mPosition) {
|
|
||||||
mCache.seek(mPosition); // Assure EOF is correctly thrown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Seekable overrides
|
|
||||||
|
|
||||||
protected void flushBeforeImpl(long pPosition) {
|
|
||||||
// TODO: Implement
|
|
||||||
// For now, it's probably okay to do nothing, this is just for
|
|
||||||
// performance (as long as people follow spec, not behaviour)
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void seekImpl(long pPosition) throws IOException {
|
|
||||||
if (mStreamPosition < pPosition) {
|
|
||||||
// Make sure we append at end of cache
|
|
||||||
if (mCache.getFilePointer() != mStreamPosition) {
|
|
||||||
mCache.seek(mStreamPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read diff from stream into cache
|
|
||||||
long left = pPosition - mStreamPosition;
|
|
||||||
int bufferLen = left > 1024 ? 1024 : (int) left;
|
|
||||||
byte[] buffer = new byte[bufferLen];
|
|
||||||
|
|
||||||
while (left > 0) {
|
|
||||||
int length = buffer.length < left ? buffer.length : (int) left;
|
|
||||||
int read = mStream.read(buffer, 0, length);
|
|
||||||
|
|
||||||
if (read > 0) {
|
|
||||||
mCache.write(buffer, 0, read);
|
|
||||||
mStreamPosition += read;
|
|
||||||
left -= read;
|
|
||||||
}
|
|
||||||
else if (read < 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (mStreamPosition >= pPosition) {
|
|
||||||
// Seek backwards into the cache
|
|
||||||
mCache.seek(pPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
// System.out.println("pPosition: " + pPosition);
|
|
||||||
// System.out.println("mStreamPosition: " + mStreamPosition);
|
|
||||||
// System.out.println("mCache.getFilePointer(): " + mCache.getFilePointer());
|
|
||||||
|
|
||||||
// NOTE: If mPosition == pPosition then we're good to go
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
final static class FileCache extends StreamCache {
|
final static class FileCache extends StreamCache {
|
||||||
private RandomAccessFile mCacheFile;
|
private RandomAccessFile mCacheFile;
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ public final class FileSeekableStream extends SeekableInputStream {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int available() throws IOException {
|
public int available() throws IOException {
|
||||||
long length = mRandomAccess.length() - mPosition;
|
long length = mRandomAccess.length() - position;
|
||||||
return length > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) length;
|
return length > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ public final class FileSeekableStream extends SeekableInputStream {
|
|||||||
|
|
||||||
int read = mRandomAccess.read();
|
int read = mRandomAccess.read();
|
||||||
if (read >= 0) {
|
if (read >= 0) {
|
||||||
mPosition++;
|
position++;
|
||||||
}
|
}
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
@@ -111,7 +111,7 @@ public final class FileSeekableStream extends SeekableInputStream {
|
|||||||
|
|
||||||
int read = mRandomAccess.read(pBytes, pOffset, pLength);
|
int read = mRandomAccess.read(pBytes, pOffset, pLength);
|
||||||
if (read > 0) {
|
if (read > 0) {
|
||||||
mPosition += read;
|
position += read;
|
||||||
}
|
}
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import java.io.InputStreamReader;
|
|||||||
* <p/>
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/FileSystem.java#1 $
|
* @version $Id: FileSystem.java#1 $
|
||||||
*/
|
*/
|
||||||
abstract class FileSystem {
|
abstract class FileSystem {
|
||||||
abstract long getFreeSpace(File pPath);
|
abstract long getFreeSpace(File pPath);
|
||||||
@@ -57,21 +57,21 @@ abstract class FileSystem {
|
|||||||
//System.out.println("os = " + os);
|
//System.out.println("os = " + os);
|
||||||
|
|
||||||
os = os.toLowerCase();
|
os = os.toLowerCase();
|
||||||
if (os.indexOf("windows") != -1) {
|
if (os.contains("windows")) {
|
||||||
return new Win32FileSystem();
|
return new Win32FileSystem();
|
||||||
}
|
}
|
||||||
else if (os.indexOf("linux") != -1 ||
|
else if (os.contains("linux") ||
|
||||||
os.indexOf("sun os") != -1 ||
|
os.contains("sun os") ||
|
||||||
os.indexOf("sunos") != -1 ||
|
os.contains("sunos") ||
|
||||||
os.indexOf("solaris") != -1 ||
|
os.contains("solaris") ||
|
||||||
os.indexOf("mpe/ix") != -1 ||
|
os.contains("mpe/ix") ||
|
||||||
os.indexOf("hp-ux") != -1 ||
|
os.contains("hp-ux") ||
|
||||||
os.indexOf("aix") != -1 ||
|
os.contains("aix") ||
|
||||||
os.indexOf("freebsd") != -1 ||
|
os.contains("freebsd") ||
|
||||||
os.indexOf("irix") != -1 ||
|
os.contains("irix") ||
|
||||||
os.indexOf("digital unix") != -1 ||
|
os.contains("digital unix") ||
|
||||||
os.indexOf("unix") != -1 ||
|
os.contains("unix") ||
|
||||||
os.indexOf("mac os x") != -1) {
|
os.contains("mac os x")) {
|
||||||
return new UnixFileSystem();
|
return new UnixFileSystem();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -80,10 +80,10 @@ abstract class FileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class UnknownFileSystem extends FileSystem {
|
private static class UnknownFileSystem extends FileSystem {
|
||||||
private final String mOSName;
|
private final String osName;
|
||||||
|
|
||||||
UnknownFileSystem(String pOSName) {
|
UnknownFileSystem(String pOSName) {
|
||||||
mOSName = pOSName;
|
osName = pOSName;
|
||||||
}
|
}
|
||||||
|
|
||||||
long getFreeSpace(File pPath) {
|
long getFreeSpace(File pPath) {
|
||||||
@@ -95,7 +95,7 @@ abstract class FileSystem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String getName() {
|
String getName() {
|
||||||
return "Unknown (" + mOSName + ")";
|
return "Unknown (" + osName + ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -186,6 +186,7 @@ public final class FileUtil {
|
|||||||
if (!pOverWrite && pToFile.exists()) {
|
if (!pOverWrite && pToFile.exists()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
OutputStream out = null;
|
OutputStream out = null;
|
||||||
|
|
||||||
@@ -202,6 +203,7 @@ public final class FileUtil {
|
|||||||
close(in);
|
close(in);
|
||||||
close(out);
|
close(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true; // If we got here, everything's probably okay.. ;-)
|
return true; // If we got here, everything's probably okay.. ;-)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,6 +309,8 @@ public final class FileUtil {
|
|||||||
Validate.notNull(pFrom, "from");
|
Validate.notNull(pFrom, "from");
|
||||||
Validate.notNull(pTo, "to");
|
Validate.notNull(pTo, "to");
|
||||||
|
|
||||||
|
// TODO: Consider using file channels for faster copy where possible
|
||||||
|
|
||||||
// Use buffer size two times byte array, to avoid i/o bottleneck
|
// Use buffer size two times byte array, to avoid i/o bottleneck
|
||||||
// TODO: Consider letting the client decide as this is sometimes not a good thing!
|
// TODO: Consider letting the client decide as this is sometimes not a good thing!
|
||||||
InputStream in = new BufferedInputStream(pFrom, BUF_SIZE * 2);
|
InputStream in = new BufferedInputStream(pFrom, BUF_SIZE * 2);
|
||||||
@@ -322,31 +326,9 @@ public final class FileUtil {
|
|||||||
// Flush out stream, to write any remaining buffered data
|
// Flush out stream, to write any remaining buffered data
|
||||||
out.flush();
|
out.flush();
|
||||||
|
|
||||||
return true; // If we got here, everything's probably okay.. ;-)
|
return true; // If we got here, everything is probably okay.. ;-)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
// Consider using the example from
|
|
||||||
// http://developer.java.sun.com/developer/Books/performance/ch04.pdf
|
|
||||||
// Test if this is really faster. And what about a lot of concurrence?
|
|
||||||
// Have a pool of buffers? :-)
|
|
||||||
|
|
||||||
static final int BUFF_SIZE = 100000;
|
|
||||||
static final byte[] buffer = new byte[BUFF_SIZE];
|
|
||||||
|
|
||||||
public static void copy(InputStream in, OutputStream out) throws IOException {
|
|
||||||
while (true) {
|
|
||||||
synchronized (buffer) {
|
|
||||||
int amountRead = in.read(buffer);
|
|
||||||
if (amountRead == -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
out.write(buffer, 0, amountRead);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the file (type) extension of the given file.
|
* Gets the file (type) extension of the given file.
|
||||||
* A file extension is the part of the filename, after the last occurence
|
* A file extension is the part of the filename, after the last occurence
|
||||||
@@ -568,6 +550,7 @@ public final class FileUtil {
|
|||||||
if (!pFile.exists()) {
|
if (!pFile.exists()) {
|
||||||
throw new FileNotFoundException(pFile.toString());
|
throw new FileNotFoundException(pFile.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] bytes = new byte[(int) pFile.length()];
|
byte[] bytes = new byte[(int) pFile.length()];
|
||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
|
|
||||||
@@ -586,6 +569,7 @@ public final class FileUtil {
|
|||||||
finally {
|
finally {
|
||||||
close(in);
|
close(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -685,28 +669,28 @@ public final class FileUtil {
|
|||||||
// a file array, which may throw OutOfMemoryExceptions for
|
// a file array, which may throw OutOfMemoryExceptions for
|
||||||
// large directories/in low memory situations
|
// large directories/in low memory situations
|
||||||
class DeleteFilesVisitor implements Visitor<File> {
|
class DeleteFilesVisitor implements Visitor<File> {
|
||||||
private int mFailedCount = 0;
|
private int failedCount = 0;
|
||||||
private IOException mException = null;
|
private IOException exception = null;
|
||||||
|
|
||||||
public void visit(final File pFile) {
|
public void visit(final File pFile) {
|
||||||
try {
|
try {
|
||||||
if (!delete(pFile, true)) {
|
if (!delete(pFile, true)) {
|
||||||
mFailedCount++;
|
failedCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
mFailedCount++;
|
failedCount++;
|
||||||
if (mException == null) {
|
if (exception == null) {
|
||||||
mException = e;
|
exception = e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean succeeded() throws IOException {
|
boolean succeeded() throws IOException {
|
||||||
if (mException != null) {
|
if (exception != null) {
|
||||||
throw mException;
|
throw exception;
|
||||||
}
|
}
|
||||||
return mFailedCount == 0;
|
return failedCount == 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DeleteFilesVisitor fileDeleter = new DeleteFilesVisitor();
|
DeleteFilesVisitor fileDeleter = new DeleteFilesVisitor();
|
||||||
@@ -886,6 +870,8 @@ public final class FileUtil {
|
|||||||
return folder.listFiles();
|
return folder.listFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Rewrite to use regexp
|
||||||
|
|
||||||
FilenameFilter filter = new FilenameMaskFilter(pFilenameMask);
|
FilenameFilter filter = new FilenameMaskFilter(pFilenameMask);
|
||||||
return folder.listFiles(filter);
|
return folder.listFiles(filter);
|
||||||
}
|
}
|
||||||
@@ -1029,6 +1015,7 @@ public final class FileUtil {
|
|||||||
* @return a human readable string representation
|
* @return a human readable string representation
|
||||||
*/
|
*/
|
||||||
public static String toHumanReadableSize(final long pSizeInBytes) {
|
public static String toHumanReadableSize(final long pSizeInBytes) {
|
||||||
|
// TODO: Rewrite to use String.format?
|
||||||
if (pSizeInBytes < 1024L) {
|
if (pSizeInBytes < 1024L) {
|
||||||
return pSizeInBytes + " Bytes";
|
return pSizeInBytes + " Bytes";
|
||||||
}
|
}
|
||||||
@@ -1053,7 +1040,7 @@ public final class FileUtil {
|
|||||||
private static ThreadLocal<NumberFormat> sNumberFormat = new ThreadLocal<NumberFormat>() {
|
private static ThreadLocal<NumberFormat> sNumberFormat = new ThreadLocal<NumberFormat>() {
|
||||||
protected NumberFormat initialValue() {
|
protected NumberFormat initialValue() {
|
||||||
NumberFormat format = NumberFormat.getNumberInstance();
|
NumberFormat format = NumberFormat.getNumberInstance();
|
||||||
// TODO: Consider making this locale/platfor specific, OR a method parameter...
|
// TODO: Consider making this locale/platform specific, OR a method parameter...
|
||||||
// format.setMaximumFractionDigits(2);
|
// format.setMaximumFractionDigits(2);
|
||||||
format.setMaximumFractionDigits(0);
|
format.setMaximumFractionDigits(0);
|
||||||
return format;
|
return format;
|
||||||
@@ -1075,6 +1062,7 @@ public final class FileUtil {
|
|||||||
*
|
*
|
||||||
* @see com.twelvemonkeys.util.Visitor
|
* @see com.twelvemonkeys.util.Visitor
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings({"ResultOfMethodCallIgnored"})
|
||||||
public static void visitFiles(final File pDirectory, final FileFilter pFilter, final Visitor<File> pVisitor) {
|
public static void visitFiles(final File pDirectory, final FileFilter pFilter, final Visitor<File> pVisitor) {
|
||||||
Validate.notNull(pDirectory, "directory");
|
Validate.notNull(pDirectory, "directory");
|
||||||
Validate.notNull(pVisitor, "visitor");
|
Validate.notNull(pVisitor, "visitor");
|
||||||
|
|||||||
@@ -56,13 +56,16 @@ import java.io.FilenameFilter;
|
|||||||
* @see File#list(java.io.FilenameFilter) java.io.File.list
|
* @see File#list(java.io.FilenameFilter) java.io.File.list
|
||||||
* @see FilenameFilter java.io.FilenameFilter
|
* @see FilenameFilter java.io.FilenameFilter
|
||||||
* @see WildcardStringParser
|
* @see WildcardStringParser
|
||||||
|
* @deprecated
|
||||||
*/
|
*/
|
||||||
public class FilenameMaskFilter implements FilenameFilter {
|
public class FilenameMaskFilter implements FilenameFilter {
|
||||||
|
|
||||||
|
// TODO: Rewrite to use regexp, or create new class
|
||||||
|
|
||||||
// Members
|
// Members
|
||||||
private String[] mFilenameMasksForInclusion;
|
private String[] filenameMasksForInclusion;
|
||||||
private String[] mFilenameMasksForExclusion;
|
private String[] filenameMasksForExclusion;
|
||||||
private boolean mInclusion = true;
|
private boolean inclusion = true;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -127,29 +130,29 @@ public class FilenameMaskFilter implements FilenameFilter {
|
|||||||
* @param pFilenameMasksForInclusion the filename masks to include
|
* @param pFilenameMasksForInclusion the filename masks to include
|
||||||
*/
|
*/
|
||||||
public void setFilenameMasksForInclusion(String[] pFilenameMasksForInclusion) {
|
public void setFilenameMasksForInclusion(String[] pFilenameMasksForInclusion) {
|
||||||
mFilenameMasksForInclusion = pFilenameMasksForInclusion;
|
filenameMasksForInclusion = pFilenameMasksForInclusion;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the current inclusion masks
|
* @return the current inclusion masks
|
||||||
*/
|
*/
|
||||||
public String[] getFilenameMasksForInclusion() {
|
public String[] getFilenameMasksForInclusion() {
|
||||||
return mFilenameMasksForInclusion.clone();
|
return filenameMasksForInclusion.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param pFilenameMasksForExclusion the filename masks to exclude
|
* @param pFilenameMasksForExclusion the filename masks to exclude
|
||||||
*/
|
*/
|
||||||
public void setFilenameMasksForExclusion(String[] pFilenameMasksForExclusion) {
|
public void setFilenameMasksForExclusion(String[] pFilenameMasksForExclusion) {
|
||||||
mFilenameMasksForExclusion = pFilenameMasksForExclusion;
|
filenameMasksForExclusion = pFilenameMasksForExclusion;
|
||||||
mInclusion = false;
|
inclusion = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the current exclusion masks
|
* @return the current exclusion masks
|
||||||
*/
|
*/
|
||||||
public String[] getFilenameMasksForExclusion() {
|
public String[] getFilenameMasksForExclusion() {
|
||||||
return mFilenameMasksForExclusion.clone();
|
return filenameMasksForExclusion.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -164,8 +167,8 @@ public class FilenameMaskFilter implements FilenameFilter {
|
|||||||
WildcardStringParser parser;
|
WildcardStringParser parser;
|
||||||
|
|
||||||
// Check each filename string mask whether the file is to be accepted
|
// Check each filename string mask whether the file is to be accepted
|
||||||
if (mInclusion) { // Inclusion
|
if (inclusion) { // Inclusion
|
||||||
for (String mask : mFilenameMasksForInclusion) {
|
for (String mask : filenameMasksForInclusion) {
|
||||||
parser = new WildcardStringParser(mask);
|
parser = new WildcardStringParser(mask);
|
||||||
if (parser.parseString(pName)) {
|
if (parser.parseString(pName)) {
|
||||||
|
|
||||||
@@ -181,7 +184,7 @@ public class FilenameMaskFilter implements FilenameFilter {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Exclusion
|
// Exclusion
|
||||||
for (String mask : mFilenameMasksForExclusion) {
|
for (String mask : filenameMasksForExclusion) {
|
||||||
parser = new WildcardStringParser(mask);
|
parser = new WildcardStringParser(mask);
|
||||||
if (parser.parseString(pName)) {
|
if (parser.parseString(pName)) {
|
||||||
|
|
||||||
@@ -204,32 +207,32 @@ public class FilenameMaskFilter implements FilenameFilter {
|
|||||||
StringBuilder retVal = new StringBuilder();
|
StringBuilder retVal = new StringBuilder();
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (mInclusion) {
|
if (inclusion) {
|
||||||
// Inclusion
|
// Inclusion
|
||||||
if (mFilenameMasksForInclusion == null) {
|
if (filenameMasksForInclusion == null) {
|
||||||
retVal.append("No filename masks set - property mFilenameMasksForInclusion is null!");
|
retVal.append("No filename masks set - property filenameMasksForInclusion is null!");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
retVal.append(mFilenameMasksForInclusion.length);
|
retVal.append(filenameMasksForInclusion.length);
|
||||||
retVal.append(" filename mask(s) - ");
|
retVal.append(" filename mask(s) - ");
|
||||||
for (i = 0; i < mFilenameMasksForInclusion.length; i++) {
|
for (i = 0; i < filenameMasksForInclusion.length; i++) {
|
||||||
retVal.append("\"");
|
retVal.append("\"");
|
||||||
retVal.append(mFilenameMasksForInclusion[i]);
|
retVal.append(filenameMasksForInclusion[i]);
|
||||||
retVal.append("\", \"");
|
retVal.append("\", \"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Exclusion
|
// Exclusion
|
||||||
if (mFilenameMasksForExclusion == null) {
|
if (filenameMasksForExclusion == null) {
|
||||||
retVal.append("No filename masks set - property mFilenameMasksForExclusion is null!");
|
retVal.append("No filename masks set - property filenameMasksForExclusion is null!");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
retVal.append(mFilenameMasksForExclusion.length);
|
retVal.append(filenameMasksForExclusion.length);
|
||||||
retVal.append(" exclusion filename mask(s) - ");
|
retVal.append(" exclusion filename mask(s) - ");
|
||||||
for (i = 0; i < mFilenameMasksForExclusion.length; i++) {
|
for (i = 0; i < filenameMasksForExclusion.length; i++) {
|
||||||
retVal.append("\"");
|
retVal.append("\"");
|
||||||
retVal.append(mFilenameMasksForExclusion[i]);
|
retVal.append(filenameMasksForExclusion[i]);
|
||||||
retVal.append("\", \"");
|
retVal.append("\", \"");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2008, Harald Kuhr
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* Redistribution and use in source and binary forms, with or without
|
|
||||||
* modification, are permitted provided that the following conditions are met:
|
|
||||||
* * Redistributions of source code must retain the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer.
|
|
||||||
* * Redistributions in binary form must reproduce the above copyright
|
|
||||||
* notice, this list of conditions and the following disclaimer in the
|
|
||||||
* documentation and/or other materials provided with the distribution.
|
|
||||||
* * Neither the name "TwelveMonkeys" nor the
|
|
||||||
* names of its contributors may be used to endorse or promote products
|
|
||||||
* derived from this software without specific prior written permission.
|
|
||||||
*
|
|
||||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
||||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
||||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
||||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
||||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
||||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
||||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.twelvemonkeys.io;
|
|
||||||
|
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.StringUtil;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FilenameFilter;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Java Bean used for approving file names which are to be included in a
|
|
||||||
* {@code java.io.File} listing. The file name suffixes are used as a
|
|
||||||
* filter input and is given to the class via the string array property:<br>
|
|
||||||
* <dd>{@code filenameSuffixesToExclude}
|
|
||||||
* <p>
|
|
||||||
* A recommended way of doing this is by referencing to the component which uses
|
|
||||||
* this class for file listing. In this way all properties are set in the same
|
|
||||||
* component and this utility component is kept in the background with only
|
|
||||||
* initial configuration necessary.
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a>
|
|
||||||
* @see File#list(java.io.FilenameFilter) java.io.File.list
|
|
||||||
* @see FilenameFilter java.io.FilenameFilter
|
|
||||||
*/
|
|
||||||
public class FilenameSuffixFilter implements FilenameFilter {
|
|
||||||
|
|
||||||
// Members
|
|
||||||
String[] mFilenameSuffixesToExclude;
|
|
||||||
|
|
||||||
/** Creates a {@code FileNameSuffixFilter} */
|
|
||||||
public FilenameSuffixFilter() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFilenameSuffixesToExclude(String[] pFilenameSuffixesToExclude) {
|
|
||||||
mFilenameSuffixesToExclude = pFilenameSuffixesToExclude;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] getFilenameSuffixesToExclude() {
|
|
||||||
return mFilenameSuffixesToExclude;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method implements the {@code java.io.FilenameFilter} interface.
|
|
||||||
* <p/>
|
|
||||||
*
|
|
||||||
* @param pDir the directory in which the file was found.
|
|
||||||
* @param pName the pName of the file.
|
|
||||||
* @return {@code true} if the pName should be included in the file list;
|
|
||||||
* {@code false} otherwise.
|
|
||||||
*/
|
|
||||||
public boolean accept(final File pDir, final String pName) {
|
|
||||||
if (StringUtil.isEmpty(mFilenameSuffixesToExclude)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String aMFilenameSuffixesToExclude : mFilenameSuffixesToExclude) {
|
|
||||||
// -- Edit by haraldK, to make interfaces more consistent
|
|
||||||
// if (StringUtil.filenameSuffixIs(pName, mFilenameSuffixesToExclude[i])) {
|
|
||||||
if (aMFilenameSuffixesToExclude.equals(FileUtil.getExtension(pName))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+32
-9
@@ -38,6 +38,8 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.lang.Validate;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -75,10 +77,7 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
|
|||||||
* @see java.io.FilterInputStream#in
|
* @see java.io.FilterInputStream#in
|
||||||
*/
|
*/
|
||||||
public LittleEndianDataInputStream(final InputStream pStream) {
|
public LittleEndianDataInputStream(final InputStream pStream) {
|
||||||
super(pStream);
|
super(Validate.notNull(pStream, "stream"));
|
||||||
if (pStream == null) {
|
|
||||||
throw new IllegalArgumentException("stream == null");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,9 +92,11 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
|
|||||||
*/
|
*/
|
||||||
public boolean readBoolean() throws IOException {
|
public boolean readBoolean() throws IOException {
|
||||||
int b = in.read();
|
int b = in.read();
|
||||||
|
|
||||||
if (b < 0) {
|
if (b < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return b != 0;
|
return b != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,9 +111,11 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
|
|||||||
*/
|
*/
|
||||||
public byte readByte() throws IOException {
|
public byte readByte() throws IOException {
|
||||||
int b = in.read();
|
int b = in.read();
|
||||||
|
|
||||||
if (b < 0) {
|
if (b < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (byte) b;
|
return (byte) b;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -128,9 +131,11 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
|
|||||||
*/
|
*/
|
||||||
public int readUnsignedByte() throws IOException {
|
public int readUnsignedByte() throws IOException {
|
||||||
int b = in.read();
|
int b = in.read();
|
||||||
|
|
||||||
if (b < 0) {
|
if (b < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,11 +151,13 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
|
|||||||
public short readShort() throws IOException {
|
public short readShort() throws IOException {
|
||||||
int byte1 = in.read();
|
int byte1 = in.read();
|
||||||
int byte2 = in.read();
|
int byte2 = in.read();
|
||||||
|
|
||||||
// only need to test last byte read
|
// only need to test last byte read
|
||||||
// if byte1 is -1 so is byte2
|
// if byte1 is -1 so is byte2
|
||||||
if (byte2 < 0) {
|
if (byte2 < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (short) (((byte2 << 24) >>> 16) + (byte1 << 24) >>> 24);
|
return (short) (((byte2 << 24) >>> 16) + (byte1 << 24) >>> 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,10 +173,11 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
|
|||||||
public int readUnsignedShort() throws IOException {
|
public int readUnsignedShort() throws IOException {
|
||||||
int byte1 = in.read();
|
int byte1 = in.read();
|
||||||
int byte2 = in.read();
|
int byte2 = in.read();
|
||||||
|
|
||||||
if (byte2 < 0) {
|
if (byte2 < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
//return ((byte2 << 24) >> 16) + ((byte1 << 24) >> 24);
|
|
||||||
return (byte2 << 8) + byte1;
|
return (byte2 << 8) + byte1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,9 +193,11 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
|
|||||||
public char readChar() throws IOException {
|
public char readChar() throws IOException {
|
||||||
int byte1 = in.read();
|
int byte1 = in.read();
|
||||||
int byte2 = in.read();
|
int byte2 = in.read();
|
||||||
|
|
||||||
if (byte2 < 0) {
|
if (byte2 < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (char) (((byte2 << 24) >>> 16) + ((byte1 << 24) >>> 24));
|
return (char) (((byte2 << 24) >>> 16) + ((byte1 << 24) >>> 24));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,6 +220,7 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
|
|||||||
if (byte4 < 0) {
|
if (byte4 < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (byte4 << 24) + ((byte3 << 24) >>> 8)
|
return (byte4 << 24) + ((byte3 << 24) >>> 8)
|
||||||
+ ((byte2 << 24) >>> 16) + ((byte1 << 24) >>> 24);
|
+ ((byte2 << 24) >>> 16) + ((byte1 << 24) >>> 24);
|
||||||
}
|
}
|
||||||
@@ -236,11 +247,11 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
|
|||||||
if (byte8 < 0) {
|
if (byte8 < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (byte8 << 56) + ((byte7 << 56) >>> 8)
|
return (byte8 << 56) + ((byte7 << 56) >>> 8)
|
||||||
+ ((byte6 << 56) >>> 16) + ((byte5 << 56) >>> 24)
|
+ ((byte6 << 56) >>> 16) + ((byte5 << 56) >>> 24)
|
||||||
+ ((byte4 << 56) >>> 32) + ((byte3 << 56) >>> 40)
|
+ ((byte4 << 56) >>> 32) + ((byte3 << 56) >>> 40)
|
||||||
+ ((byte2 << 56) >>> 48) + ((byte1 << 56) >>> 56);
|
+ ((byte2 << 56) >>> 48) + ((byte1 << 56) >>> 56);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -260,16 +271,17 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
|
|||||||
public String readUTF() throws IOException {
|
public String readUTF() throws IOException {
|
||||||
int byte1 = in.read();
|
int byte1 = in.read();
|
||||||
int byte2 = in.read();
|
int byte2 = in.read();
|
||||||
|
|
||||||
if (byte2 < 0) {
|
if (byte2 < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
|
||||||
int numbytes = (byte1 << 8) + byte2;
|
int numbytes = (byte1 << 8) + byte2;
|
||||||
char result[] = new char[numbytes];
|
char result[] = new char[numbytes];
|
||||||
int numread = 0;
|
int numread = 0;
|
||||||
int numchars = 0;
|
int numchars = 0;
|
||||||
|
|
||||||
while (numread < numbytes) {
|
while (numread < numbytes) {
|
||||||
|
|
||||||
int c1 = readUnsignedByte();
|
int c1 = readUnsignedByte();
|
||||||
int c2, c3;
|
int c2, c3;
|
||||||
|
|
||||||
@@ -281,27 +293,34 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
|
|||||||
}
|
}
|
||||||
else if (test == 12 || test == 13) { // two bytes
|
else if (test == 12 || test == 13) { // two bytes
|
||||||
numread += 2;
|
numread += 2;
|
||||||
|
|
||||||
if (numread > numbytes) {
|
if (numread > numbytes) {
|
||||||
throw new UTFDataFormatException();
|
throw new UTFDataFormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
c2 = readUnsignedByte();
|
c2 = readUnsignedByte();
|
||||||
|
|
||||||
if ((c2 & 0xC0) != 0x80) {
|
if ((c2 & 0xC0) != 0x80) {
|
||||||
throw new UTFDataFormatException();
|
throw new UTFDataFormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
result[numchars++] = (char) (((c1 & 0x1F) << 6) | (c2 & 0x3F));
|
result[numchars++] = (char) (((c1 & 0x1F) << 6) | (c2 & 0x3F));
|
||||||
}
|
}
|
||||||
else if (test == 14) { // three bytes
|
else if (test == 14) { // three bytes
|
||||||
numread += 3;
|
numread += 3;
|
||||||
|
|
||||||
if (numread > numbytes) {
|
if (numread > numbytes) {
|
||||||
throw new UTFDataFormatException();
|
throw new UTFDataFormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
c2 = readUnsignedByte();
|
c2 = readUnsignedByte();
|
||||||
c3 = readUnsignedByte();
|
c3 = readUnsignedByte();
|
||||||
|
|
||||||
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) {
|
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) {
|
||||||
throw new UTFDataFormatException();
|
throw new UTFDataFormatException();
|
||||||
}
|
}
|
||||||
result[numchars++] = (char)
|
|
||||||
(((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F));
|
result[numchars++] = (char) (((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F));
|
||||||
}
|
}
|
||||||
else { // malformed
|
else { // malformed
|
||||||
throw new UTFDataFormatException();
|
throw new UTFDataFormatException();
|
||||||
@@ -396,12 +415,16 @@ public class LittleEndianDataInputStream extends FilterInputStream implements Da
|
|||||||
if (pLength < 0) {
|
if (pLength < 0) {
|
||||||
throw new IndexOutOfBoundsException();
|
throw new IndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
while (count < pLength) {
|
while (count < pLength) {
|
||||||
int read = in.read(pBytes, pOffset + count, pLength - count);
|
int read = in.read(pBytes, pOffset + count, pLength - count);
|
||||||
|
|
||||||
if (read < 0) {
|
if (read < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
|
||||||
count += read;
|
count += read;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-21
@@ -38,6 +38,8 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.lang.Validate;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,7 +71,7 @@ public class LittleEndianDataOutputStream extends FilterOutputStream implements
|
|||||||
/**
|
/**
|
||||||
* The number of bytes written so far to the little endian output stream.
|
* The number of bytes written so far to the little endian output stream.
|
||||||
*/
|
*/
|
||||||
protected int mWritten;
|
protected int bytesWritten;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new little endian output stream and chains it to the
|
* Creates a new little endian output stream and chains it to the
|
||||||
@@ -79,10 +81,7 @@ public class LittleEndianDataOutputStream extends FilterOutputStream implements
|
|||||||
* @see java.io.FilterOutputStream#out
|
* @see java.io.FilterOutputStream#out
|
||||||
*/
|
*/
|
||||||
public LittleEndianDataOutputStream(OutputStream pStream) {
|
public LittleEndianDataOutputStream(OutputStream pStream) {
|
||||||
super(pStream);
|
super(Validate.notNull(pStream, "stream"));
|
||||||
if (pStream == null) {
|
|
||||||
throw new IllegalArgumentException("stream == null");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,7 +92,7 @@ public class LittleEndianDataOutputStream extends FilterOutputStream implements
|
|||||||
*/
|
*/
|
||||||
public synchronized void write(int pByte) throws IOException {
|
public synchronized void write(int pByte) throws IOException {
|
||||||
out.write(pByte);
|
out.write(pByte);
|
||||||
mWritten++;
|
bytesWritten++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,10 +104,9 @@ public class LittleEndianDataOutputStream extends FilterOutputStream implements
|
|||||||
* @param pLength the number of bytes to write.
|
* @param pLength the number of bytes to write.
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
*/
|
*/
|
||||||
public synchronized void write(byte[] pBytes, int pOffset, int pLength)
|
public synchronized void write(byte[] pBytes, int pOffset, int pLength) throws IOException {
|
||||||
throws IOException {
|
|
||||||
out.write(pBytes, pOffset, pLength);
|
out.write(pBytes, pOffset, pLength);
|
||||||
mWritten += pLength;
|
bytesWritten += pLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -137,7 +135,7 @@ public class LittleEndianDataOutputStream extends FilterOutputStream implements
|
|||||||
*/
|
*/
|
||||||
public void writeByte(int pByte) throws IOException {
|
public void writeByte(int pByte) throws IOException {
|
||||||
out.write(pByte);
|
out.write(pByte);
|
||||||
mWritten++;
|
bytesWritten++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -150,7 +148,7 @@ public class LittleEndianDataOutputStream extends FilterOutputStream implements
|
|||||||
public void writeShort(int pShort) throws IOException {
|
public void writeShort(int pShort) throws IOException {
|
||||||
out.write(pShort & 0xFF);
|
out.write(pShort & 0xFF);
|
||||||
out.write((pShort >>> 8) & 0xFF);
|
out.write((pShort >>> 8) & 0xFF);
|
||||||
mWritten += 2;
|
bytesWritten += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -163,7 +161,7 @@ public class LittleEndianDataOutputStream extends FilterOutputStream implements
|
|||||||
public void writeChar(int pChar) throws IOException {
|
public void writeChar(int pChar) throws IOException {
|
||||||
out.write(pChar & 0xFF);
|
out.write(pChar & 0xFF);
|
||||||
out.write((pChar >>> 8) & 0xFF);
|
out.write((pChar >>> 8) & 0xFF);
|
||||||
mWritten += 2;
|
bytesWritten += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -178,7 +176,7 @@ public class LittleEndianDataOutputStream extends FilterOutputStream implements
|
|||||||
out.write((pInt >>> 8) & 0xFF);
|
out.write((pInt >>> 8) & 0xFF);
|
||||||
out.write((pInt >>> 16) & 0xFF);
|
out.write((pInt >>> 16) & 0xFF);
|
||||||
out.write((pInt >>> 24) & 0xFF);
|
out.write((pInt >>> 24) & 0xFF);
|
||||||
mWritten += 4;
|
bytesWritten += 4;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,7 +196,7 @@ public class LittleEndianDataOutputStream extends FilterOutputStream implements
|
|||||||
out.write((int) (pLong >>> 40) & 0xFF);
|
out.write((int) (pLong >>> 40) & 0xFF);
|
||||||
out.write((int) (pLong >>> 48) & 0xFF);
|
out.write((int) (pLong >>> 48) & 0xFF);
|
||||||
out.write((int) (pLong >>> 56) & 0xFF);
|
out.write((int) (pLong >>> 56) & 0xFF);
|
||||||
mWritten += 8;
|
bytesWritten += 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -235,10 +233,12 @@ public class LittleEndianDataOutputStream extends FilterOutputStream implements
|
|||||||
*/
|
*/
|
||||||
public void writeBytes(String pString) throws IOException {
|
public void writeBytes(String pString) throws IOException {
|
||||||
int length = pString.length();
|
int length = pString.length();
|
||||||
|
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
out.write((byte) pString.charAt(i));
|
out.write((byte) pString.charAt(i));
|
||||||
}
|
}
|
||||||
mWritten += length;
|
|
||||||
|
bytesWritten += length;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -253,12 +253,14 @@ public class LittleEndianDataOutputStream extends FilterOutputStream implements
|
|||||||
*/
|
*/
|
||||||
public void writeChars(String pString) throws IOException {
|
public void writeChars(String pString) throws IOException {
|
||||||
int length = pString.length();
|
int length = pString.length();
|
||||||
|
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
int c = pString.charAt(i);
|
int c = pString.charAt(i);
|
||||||
out.write(c & 0xFF);
|
out.write(c & 0xFF);
|
||||||
out.write((c >>> 8) & 0xFF);
|
out.write((c >>> 8) & 0xFF);
|
||||||
}
|
}
|
||||||
mWritten += length * 2;
|
|
||||||
|
bytesWritten += length * 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -282,6 +284,7 @@ public class LittleEndianDataOutputStream extends FilterOutputStream implements
|
|||||||
|
|
||||||
for (int i = 0; i < numchars; i++) {
|
for (int i = 0; i < numchars; i++) {
|
||||||
int c = pString.charAt(i);
|
int c = pString.charAt(i);
|
||||||
|
|
||||||
if ((c >= 0x0001) && (c <= 0x007F)) {
|
if ((c >= 0x0001) && (c <= 0x007F)) {
|
||||||
numbytes++;
|
numbytes++;
|
||||||
}
|
}
|
||||||
@@ -299,8 +302,10 @@ public class LittleEndianDataOutputStream extends FilterOutputStream implements
|
|||||||
|
|
||||||
out.write((numbytes >>> 8) & 0xFF);
|
out.write((numbytes >>> 8) & 0xFF);
|
||||||
out.write(numbytes & 0xFF);
|
out.write(numbytes & 0xFF);
|
||||||
|
|
||||||
for (int i = 0; i < numchars; i++) {
|
for (int i = 0; i < numchars; i++) {
|
||||||
int c = pString.charAt(i);
|
int c = pString.charAt(i);
|
||||||
|
|
||||||
if ((c >= 0x0001) && (c <= 0x007F)) {
|
if ((c >= 0x0001) && (c <= 0x007F)) {
|
||||||
out.write(c);
|
out.write(c);
|
||||||
}
|
}
|
||||||
@@ -308,16 +313,16 @@ public class LittleEndianDataOutputStream extends FilterOutputStream implements
|
|||||||
out.write(0xE0 | ((c >> 12) & 0x0F));
|
out.write(0xE0 | ((c >> 12) & 0x0F));
|
||||||
out.write(0x80 | ((c >> 6) & 0x3F));
|
out.write(0x80 | ((c >> 6) & 0x3F));
|
||||||
out.write(0x80 | (c & 0x3F));
|
out.write(0x80 | (c & 0x3F));
|
||||||
mWritten += 2;
|
bytesWritten += 2;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
out.write(0xC0 | ((c >> 6) & 0x1F));
|
out.write(0xC0 | ((c >> 6) & 0x1F));
|
||||||
out.write(0x80 | (c & 0x3F));
|
out.write(0x80 | (c & 0x3F));
|
||||||
mWritten += 1;
|
bytesWritten += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mWritten += numchars + 2;
|
bytesWritten += numchars + 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -326,9 +331,9 @@ public class LittleEndianDataOutputStream extends FilterOutputStream implements
|
|||||||
* possible that this number is temporarily less than the actual
|
* possible that this number is temporarily less than the actual
|
||||||
* number of bytes written.)
|
* number of bytes written.)
|
||||||
* @return the value of the {@code written} field.
|
* @return the value of the {@code written} field.
|
||||||
* @see #mWritten
|
* @see #bytesWritten
|
||||||
*/
|
*/
|
||||||
public int size() {
|
public int size() {
|
||||||
return mWritten;
|
return bytesWritten;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+103
-77
@@ -56,58 +56,58 @@ import java.nio.channels.FileChannel;
|
|||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/LittleEndianRandomAccessFile.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/LittleEndianRandomAccessFile.java#1 $
|
||||||
*/
|
*/
|
||||||
public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
||||||
private RandomAccessFile mFile;
|
private RandomAccessFile file;
|
||||||
|
|
||||||
public LittleEndianRandomAccessFile(final String pName, final String pMode) throws FileNotFoundException {
|
public LittleEndianRandomAccessFile(final String pName, final String pMode) throws FileNotFoundException {
|
||||||
this(FileUtil.resolve(pName), pMode);
|
this(FileUtil.resolve(pName), pMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public LittleEndianRandomAccessFile(final File pFile, final String pMode) throws FileNotFoundException {
|
public LittleEndianRandomAccessFile(final File pFile, final String pMode) throws FileNotFoundException {
|
||||||
mFile = new RandomAccessFile(pFile, pMode);
|
file = new RandomAccessFile(pFile, pMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
mFile.close();
|
file.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileChannel getChannel() {
|
public FileChannel getChannel() {
|
||||||
return mFile.getChannel();
|
return file.getChannel();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FileDescriptor getFD() throws IOException {
|
public FileDescriptor getFD() throws IOException {
|
||||||
return mFile.getFD();
|
return file.getFD();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getFilePointer() throws IOException {
|
public long getFilePointer() throws IOException {
|
||||||
return mFile.getFilePointer();
|
return file.getFilePointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long length() throws IOException {
|
public long length() throws IOException {
|
||||||
return mFile.length();
|
return file.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
return mFile.read();
|
return file.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read(final byte[] b) throws IOException {
|
public int read(final byte[] b) throws IOException {
|
||||||
return mFile.read(b);
|
return file.read(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read(final byte[] b, final int off, final int len) throws IOException {
|
public int read(final byte[] b, final int off, final int len) throws IOException {
|
||||||
return mFile.read(b, off, len);
|
return file.read(b, off, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readFully(final byte[] b) throws IOException {
|
public void readFully(final byte[] b) throws IOException {
|
||||||
mFile.readFully(b);
|
file.readFully(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readFully(final byte[] b, final int off, final int len) throws IOException {
|
public void readFully(final byte[] b, final int off, final int len) throws IOException {
|
||||||
mFile.readFully(b, off, len);
|
file.readFully(b, off, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String readLine() throws IOException {
|
public String readLine() throws IOException {
|
||||||
return mFile.readLine();
|
return file.readLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -121,10 +121,12 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
*/
|
*/
|
||||||
public boolean readBoolean() throws IOException {
|
public boolean readBoolean() throws IOException {
|
||||||
int b = mFile.read();
|
int b = file.read();
|
||||||
|
|
||||||
if (b < 0) {
|
if (b < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return b != 0;
|
return b != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,10 +140,12 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
*/
|
*/
|
||||||
public byte readByte() throws IOException {
|
public byte readByte() throws IOException {
|
||||||
int b = mFile.read();
|
int b = file.read();
|
||||||
|
|
||||||
if (b < 0) {
|
if (b < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (byte) b;
|
return (byte) b;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -156,10 +160,12 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
*/
|
*/
|
||||||
public int readUnsignedByte() throws IOException {
|
public int readUnsignedByte() throws IOException {
|
||||||
int b = mFile.read();
|
int b = file.read();
|
||||||
|
|
||||||
if (b < 0) {
|
if (b < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,13 +179,15 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
*/
|
*/
|
||||||
public short readShort() throws IOException {
|
public short readShort() throws IOException {
|
||||||
int byte1 = mFile.read();
|
int byte1 = file.read();
|
||||||
int byte2 = mFile.read();
|
int byte2 = file.read();
|
||||||
|
|
||||||
// only need to test last byte read
|
// only need to test last byte read
|
||||||
// if byte1 is -1 so is byte2
|
// if byte1 is -1 so is byte2
|
||||||
if (byte2 < 0) {
|
if (byte2 < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (short) (((byte2 << 24) >>> 16) + (byte1 << 24) >>> 24);
|
return (short) (((byte2 << 24) >>> 16) + (byte1 << 24) >>> 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -193,11 +201,13 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
*/
|
*/
|
||||||
public int readUnsignedShort() throws IOException {
|
public int readUnsignedShort() throws IOException {
|
||||||
int byte1 = mFile.read();
|
int byte1 = file.read();
|
||||||
int byte2 = mFile.read();
|
int byte2 = file.read();
|
||||||
|
|
||||||
if (byte2 < 0) {
|
if (byte2 < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
|
||||||
//return ((byte2 << 24) >> 16) + ((byte1 << 24) >> 24);
|
//return ((byte2 << 24) >> 16) + ((byte1 << 24) >> 24);
|
||||||
return (byte2 << 8) + byte1;
|
return (byte2 << 8) + byte1;
|
||||||
}
|
}
|
||||||
@@ -212,11 +222,13 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
*/
|
*/
|
||||||
public char readChar() throws IOException {
|
public char readChar() throws IOException {
|
||||||
int byte1 = mFile.read();
|
int byte1 = file.read();
|
||||||
int byte2 = mFile.read();
|
int byte2 = file.read();
|
||||||
|
|
||||||
if (byte2 < 0) {
|
if (byte2 < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (char) (((byte2 << 24) >>> 16) + ((byte1 << 24) >>> 24));
|
return (char) (((byte2 << 24) >>> 16) + ((byte1 << 24) >>> 24));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,16 +243,16 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
*/
|
*/
|
||||||
public int readInt() throws IOException {
|
public int readInt() throws IOException {
|
||||||
int byte1 = mFile.read();
|
int byte1 = file.read();
|
||||||
int byte2 = mFile.read();
|
int byte2 = file.read();
|
||||||
int byte3 = mFile.read();
|
int byte3 = file.read();
|
||||||
int byte4 = mFile.read();
|
int byte4 = file.read();
|
||||||
|
|
||||||
if (byte4 < 0) {
|
if (byte4 < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
return (byte4 << 24) + ((byte3 << 24) >>> 8)
|
|
||||||
+ ((byte2 << 24) >>> 16) + ((byte1 << 24) >>> 24);
|
return (byte4 << 24) + ((byte3 << 24) >>> 8) + ((byte2 << 24) >>> 16) + ((byte1 << 24) >>> 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -253,18 +265,19 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
*/
|
*/
|
||||||
public long readLong() throws IOException {
|
public long readLong() throws IOException {
|
||||||
long byte1 = mFile.read();
|
long byte1 = file.read();
|
||||||
long byte2 = mFile.read();
|
long byte2 = file.read();
|
||||||
long byte3 = mFile.read();
|
long byte3 = file.read();
|
||||||
long byte4 = mFile.read();
|
long byte4 = file.read();
|
||||||
long byte5 = mFile.read();
|
long byte5 = file.read();
|
||||||
long byte6 = mFile.read();
|
long byte6 = file.read();
|
||||||
long byte7 = mFile.read();
|
long byte7 = file.read();
|
||||||
long byte8 = mFile.read();
|
long byte8 = file.read();
|
||||||
|
|
||||||
if (byte8 < 0) {
|
if (byte8 < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (byte8 << 56) + ((byte7 << 56) >>> 8)
|
return (byte8 << 56) + ((byte7 << 56) >>> 8)
|
||||||
+ ((byte6 << 56) >>> 16) + ((byte5 << 56) >>> 24)
|
+ ((byte6 << 56) >>> 16) + ((byte5 << 56) >>> 24)
|
||||||
+ ((byte4 << 56) >>> 32) + ((byte3 << 56) >>> 40)
|
+ ((byte4 << 56) >>> 32) + ((byte3 << 56) >>> 40)
|
||||||
@@ -287,11 +300,13 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
*/
|
*/
|
||||||
public String readUTF() throws IOException {
|
public String readUTF() throws IOException {
|
||||||
int byte1 = mFile.read();
|
int byte1 = file.read();
|
||||||
int byte2 = mFile.read();
|
int byte2 = file.read();
|
||||||
|
|
||||||
if (byte2 < 0) {
|
if (byte2 < 0) {
|
||||||
throw new EOFException();
|
throw new EOFException();
|
||||||
}
|
}
|
||||||
|
|
||||||
int numbytes = (byte1 << 8) + byte2;
|
int numbytes = (byte1 << 8) + byte2;
|
||||||
char result[] = new char[numbytes];
|
char result[] = new char[numbytes];
|
||||||
int numread = 0;
|
int numread = 0;
|
||||||
@@ -310,27 +325,34 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
}
|
}
|
||||||
else if (test == 12 || test == 13) { // two bytes
|
else if (test == 12 || test == 13) { // two bytes
|
||||||
numread += 2;
|
numread += 2;
|
||||||
|
|
||||||
if (numread > numbytes) {
|
if (numread > numbytes) {
|
||||||
throw new UTFDataFormatException();
|
throw new UTFDataFormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
c2 = readUnsignedByte();
|
c2 = readUnsignedByte();
|
||||||
|
|
||||||
if ((c2 & 0xC0) != 0x80) {
|
if ((c2 & 0xC0) != 0x80) {
|
||||||
throw new UTFDataFormatException();
|
throw new UTFDataFormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
result[numchars++] = (char) (((c1 & 0x1F) << 6) | (c2 & 0x3F));
|
result[numchars++] = (char) (((c1 & 0x1F) << 6) | (c2 & 0x3F));
|
||||||
}
|
}
|
||||||
else if (test == 14) { // three bytes
|
else if (test == 14) { // three bytes
|
||||||
numread += 3;
|
numread += 3;
|
||||||
|
|
||||||
if (numread > numbytes) {
|
if (numread > numbytes) {
|
||||||
throw new UTFDataFormatException();
|
throw new UTFDataFormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
c2 = readUnsignedByte();
|
c2 = readUnsignedByte();
|
||||||
c3 = readUnsignedByte();
|
c3 = readUnsignedByte();
|
||||||
|
|
||||||
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) {
|
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) {
|
||||||
throw new UTFDataFormatException();
|
throw new UTFDataFormatException();
|
||||||
}
|
}
|
||||||
result[numchars++] = (char)
|
|
||||||
(((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F));
|
result[numchars++] = (char) (((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F));
|
||||||
}
|
}
|
||||||
else { // malformed
|
else { // malformed
|
||||||
throw new UTFDataFormatException();
|
throw new UTFDataFormatException();
|
||||||
@@ -378,27 +400,27 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
* {@code 0} or if an I/O error occurs.
|
* {@code 0} or if an I/O error occurs.
|
||||||
*/
|
*/
|
||||||
public void seek(final long pos) throws IOException {
|
public void seek(final long pos) throws IOException {
|
||||||
mFile.seek(pos);
|
file.seek(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLength(final long newLength) throws IOException {
|
public void setLength(final long newLength) throws IOException {
|
||||||
mFile.setLength(newLength);
|
file.setLength(newLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int skipBytes(final int n) throws IOException {
|
public int skipBytes(final int n) throws IOException {
|
||||||
return mFile.skipBytes(n);
|
return file.skipBytes(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(final byte[] b) throws IOException {
|
public void write(final byte[] b) throws IOException {
|
||||||
mFile.write(b);
|
file.write(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(final byte[] b, final int off, final int len) throws IOException {
|
public void write(final byte[] b, final int off, final int len) throws IOException {
|
||||||
mFile.write(b, off, len);
|
file.write(b, off, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(final int b) throws IOException {
|
public void write(final int b) throws IOException {
|
||||||
mFile.write(b);
|
file.write(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -425,7 +447,7 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
*/
|
*/
|
||||||
public void writeByte(int pByte) throws IOException {
|
public void writeByte(int pByte) throws IOException {
|
||||||
mFile.write(pByte);
|
file.write(pByte);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -436,8 +458,8 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
*/
|
*/
|
||||||
public void writeShort(int pShort) throws IOException {
|
public void writeShort(int pShort) throws IOException {
|
||||||
mFile.write(pShort & 0xFF);
|
file.write(pShort & 0xFF);
|
||||||
mFile.write((pShort >>> 8) & 0xFF);
|
file.write((pShort >>> 8) & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -448,8 +470,8 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
*/
|
*/
|
||||||
public void writeChar(int pChar) throws IOException {
|
public void writeChar(int pChar) throws IOException {
|
||||||
mFile.write(pChar & 0xFF);
|
file.write(pChar & 0xFF);
|
||||||
mFile.write((pChar >>> 8) & 0xFF);
|
file.write((pChar >>> 8) & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -460,11 +482,10 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
*/
|
*/
|
||||||
public void writeInt(int pInt) throws IOException {
|
public void writeInt(int pInt) throws IOException {
|
||||||
mFile.write(pInt & 0xFF);
|
file.write(pInt & 0xFF);
|
||||||
mFile.write((pInt >>> 8) & 0xFF);
|
file.write((pInt >>> 8) & 0xFF);
|
||||||
mFile.write((pInt >>> 16) & 0xFF);
|
file.write((pInt >>> 16) & 0xFF);
|
||||||
mFile.write((pInt >>> 24) & 0xFF);
|
file.write((pInt >>> 24) & 0xFF);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -475,14 +496,14 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
*/
|
*/
|
||||||
public void writeLong(long pLong) throws IOException {
|
public void writeLong(long pLong) throws IOException {
|
||||||
mFile.write((int) pLong & 0xFF);
|
file.write((int) pLong & 0xFF);
|
||||||
mFile.write((int) (pLong >>> 8) & 0xFF);
|
file.write((int) (pLong >>> 8) & 0xFF);
|
||||||
mFile.write((int) (pLong >>> 16) & 0xFF);
|
file.write((int) (pLong >>> 16) & 0xFF);
|
||||||
mFile.write((int) (pLong >>> 24) & 0xFF);
|
file.write((int) (pLong >>> 24) & 0xFF);
|
||||||
mFile.write((int) (pLong >>> 32) & 0xFF);
|
file.write((int) (pLong >>> 32) & 0xFF);
|
||||||
mFile.write((int) (pLong >>> 40) & 0xFF);
|
file.write((int) (pLong >>> 40) & 0xFF);
|
||||||
mFile.write((int) (pLong >>> 48) & 0xFF);
|
file.write((int) (pLong >>> 48) & 0xFF);
|
||||||
mFile.write((int) (pLong >>> 56) & 0xFF);
|
file.write((int) (pLong >>> 56) & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -515,12 +536,13 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
* @param pString the {@code String} value to be written.
|
* @param pString the {@code String} value to be written.
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
* @see #writeByte(int)
|
* @see #writeByte(int)
|
||||||
* @see #mFile
|
* @see #file
|
||||||
*/
|
*/
|
||||||
public void writeBytes(String pString) throws IOException {
|
public void writeBytes(String pString) throws IOException {
|
||||||
int length = pString.length();
|
int length = pString.length();
|
||||||
|
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
mFile.write((byte) pString.charAt(i));
|
file.write((byte) pString.charAt(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -532,14 +554,15 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
* @param pString a {@code String} value to be written.
|
* @param pString a {@code String} value to be written.
|
||||||
* @throws IOException if the underlying stream throws an IOException.
|
* @throws IOException if the underlying stream throws an IOException.
|
||||||
* @see #writeChar(int)
|
* @see #writeChar(int)
|
||||||
* @see #mFile
|
* @see #file
|
||||||
*/
|
*/
|
||||||
public void writeChars(String pString) throws IOException {
|
public void writeChars(String pString) throws IOException {
|
||||||
int length = pString.length();
|
int length = pString.length();
|
||||||
|
|
||||||
for (int i = 0; i < length; i++) {
|
for (int i = 0; i < length; i++) {
|
||||||
int c = pString.charAt(i);
|
int c = pString.charAt(i);
|
||||||
mFile.write(c & 0xFF);
|
file.write(c & 0xFF);
|
||||||
mFile.write((c >>> 8) & 0xFF);
|
file.write((c >>> 8) & 0xFF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -564,6 +587,7 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
|
|
||||||
for (int i = 0; i < numchars; i++) {
|
for (int i = 0; i < numchars; i++) {
|
||||||
int c = pString.charAt(i);
|
int c = pString.charAt(i);
|
||||||
|
|
||||||
if ((c >= 0x0001) && (c <= 0x007F)) {
|
if ((c >= 0x0001) && (c <= 0x007F)) {
|
||||||
numbytes++;
|
numbytes++;
|
||||||
}
|
}
|
||||||
@@ -579,21 +603,23 @@ public class LittleEndianRandomAccessFile implements DataInput, DataOutput {
|
|||||||
throw new UTFDataFormatException();
|
throw new UTFDataFormatException();
|
||||||
}
|
}
|
||||||
|
|
||||||
mFile.write((numbytes >>> 8) & 0xFF);
|
file.write((numbytes >>> 8) & 0xFF);
|
||||||
mFile.write(numbytes & 0xFF);
|
file.write(numbytes & 0xFF);
|
||||||
|
|
||||||
for (int i = 0; i < numchars; i++) {
|
for (int i = 0; i < numchars; i++) {
|
||||||
int c = pString.charAt(i);
|
int c = pString.charAt(i);
|
||||||
|
|
||||||
if ((c >= 0x0001) && (c <= 0x007F)) {
|
if ((c >= 0x0001) && (c <= 0x007F)) {
|
||||||
mFile.write(c);
|
file.write(c);
|
||||||
}
|
}
|
||||||
else if (c > 0x07FF) {
|
else if (c > 0x07FF) {
|
||||||
mFile.write(0xE0 | ((c >> 12) & 0x0F));
|
file.write(0xE0 | ((c >> 12) & 0x0F));
|
||||||
mFile.write(0x80 | ((c >> 6) & 0x3F));
|
file.write(0x80 | ((c >> 6) & 0x3F));
|
||||||
mFile.write(0x80 | (c & 0x3F));
|
file.write(0x80 | (c & 0x3F));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mFile.write(0xC0 | ((c >> 6) & 0x1F));
|
file.write(0xC0 | ((c >> 6) & 0x1F));
|
||||||
mFile.write(0x80 | (c & 0x3F));
|
file.write(0x80 | (c & 0x3F));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-31
@@ -65,13 +65,13 @@ public final class MemoryCacheSeekableStream extends AbstractCachedSeekableStrea
|
|||||||
final static class MemoryCache extends StreamCache {
|
final static class MemoryCache extends StreamCache {
|
||||||
final static int BLOCK_SIZE = 1 << 13;
|
final static int BLOCK_SIZE = 1 << 13;
|
||||||
|
|
||||||
private final List<byte[]> mCache = new ArrayList<byte[]>();
|
private final List<byte[]> cache = new ArrayList<byte[]>();
|
||||||
private long mLength;
|
private long length;
|
||||||
private long mPosition;
|
private long position;
|
||||||
private long mStart;
|
private long start;
|
||||||
|
|
||||||
private byte[] getBlock() throws IOException {
|
private byte[] getBlock() throws IOException {
|
||||||
final long currPos = mPosition - mStart;
|
final long currPos = position - start;
|
||||||
if (currPos < 0) {
|
if (currPos < 0) {
|
||||||
throw new IOException("StreamCache flushed before read position");
|
throw new IOException("StreamCache flushed before read position");
|
||||||
}
|
}
|
||||||
@@ -82,31 +82,31 @@ public final class MemoryCacheSeekableStream extends AbstractCachedSeekableStrea
|
|||||||
throw new IOException("Memory cache max size exceeded");
|
throw new IOException("Memory cache max size exceeded");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index >= mCache.size()) {
|
if (index >= cache.size()) {
|
||||||
try {
|
try {
|
||||||
mCache.add(new byte[BLOCK_SIZE]);
|
cache.add(new byte[BLOCK_SIZE]);
|
||||||
// System.out.println("Allocating new block, size: " + BLOCK_SIZE);
|
// System.out.println("Allocating new block, size: " + BLOCK_SIZE);
|
||||||
// System.out.println("New total size: " + mCache.size() * BLOCK_SIZE + " (" + mCache.size() + " blocks)");
|
// System.out.println("New total size: " + cache.size() * BLOCK_SIZE + " (" + cache.size() + " blocks)");
|
||||||
}
|
}
|
||||||
catch (OutOfMemoryError e) {
|
catch (OutOfMemoryError e) {
|
||||||
throw new IOException("No more memory for cache: " + mCache.size() * BLOCK_SIZE);
|
throw new IOException("No more memory for cache: " + cache.size() * BLOCK_SIZE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//System.out.println("index: " + index);
|
//System.out.println("index: " + index);
|
||||||
|
|
||||||
return mCache.get((int) index);
|
return cache.get((int) index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(final int pByte) throws IOException {
|
public void write(final int pByte) throws IOException {
|
||||||
byte[] buffer = getBlock();
|
byte[] buffer = getBlock();
|
||||||
|
|
||||||
int idx = (int) (mPosition % BLOCK_SIZE);
|
int idx = (int) (position % BLOCK_SIZE);
|
||||||
buffer[idx] = (byte) pByte;
|
buffer[idx] = (byte) pByte;
|
||||||
mPosition++;
|
position++;
|
||||||
|
|
||||||
if (mPosition > mLength) {
|
if (position > length) {
|
||||||
mLength = mPosition;
|
length = position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,28 +115,28 @@ public final class MemoryCacheSeekableStream extends AbstractCachedSeekableStrea
|
|||||||
public void write(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
public void write(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
||||||
byte[] buffer = getBlock();
|
byte[] buffer = getBlock();
|
||||||
for (int i = 0; i < pLength; i++) {
|
for (int i = 0; i < pLength; i++) {
|
||||||
int index = (int) mPosition % BLOCK_SIZE;
|
int index = (int) position % BLOCK_SIZE;
|
||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
buffer = getBlock();
|
buffer = getBlock();
|
||||||
}
|
}
|
||||||
buffer[index] = pBuffer[pOffset + i];
|
buffer[index] = pBuffer[pOffset + i];
|
||||||
|
|
||||||
mPosition++;
|
position++;
|
||||||
}
|
}
|
||||||
if (mPosition > mLength) {
|
if (position > length) {
|
||||||
mLength = mPosition;
|
length = position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
if (mPosition >= mLength) {
|
if (position >= length) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] buffer = getBlock();
|
byte[] buffer = getBlock();
|
||||||
|
|
||||||
int idx = (int) (mPosition % BLOCK_SIZE);
|
int idx = (int) (position % BLOCK_SIZE);
|
||||||
mPosition++;
|
position++;
|
||||||
|
|
||||||
return buffer[idx] & 0xff;
|
return buffer[idx] & 0xff;
|
||||||
}
|
}
|
||||||
@@ -144,33 +144,33 @@ public final class MemoryCacheSeekableStream extends AbstractCachedSeekableStrea
|
|||||||
// TODO: OptimizeMe!!!
|
// TODO: OptimizeMe!!!
|
||||||
@Override
|
@Override
|
||||||
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||||
if (mPosition >= mLength) {
|
if (position >= length) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] buffer = getBlock();
|
byte[] buffer = getBlock();
|
||||||
|
|
||||||
int bufferPos = (int) (mPosition % BLOCK_SIZE);
|
int bufferPos = (int) (position % BLOCK_SIZE);
|
||||||
|
|
||||||
// Find maxIdx and simplify test in for-loop
|
// Find maxIdx and simplify test in for-loop
|
||||||
int maxLen = (int) Math.min(Math.min(pLength, buffer.length - bufferPos), mLength - mPosition);
|
int maxLen = (int) Math.min(Math.min(pLength, buffer.length - bufferPos), length - position);
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
//for (i = 0; i < pLength && i < buffer.length - idx && i < mLength - mPosition; i++) {
|
//for (i = 0; i < pLength && i < buffer.length - idx && i < length - position; i++) {
|
||||||
for (i = 0; i < maxLen; i++) {
|
for (i = 0; i < maxLen; i++) {
|
||||||
pBytes[pOffset + i] = buffer[bufferPos + i];
|
pBytes[pOffset + i] = buffer[bufferPos + i];
|
||||||
}
|
}
|
||||||
|
|
||||||
mPosition += i;
|
position += i;
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void seek(final long pPosition) throws IOException {
|
public void seek(final long pPosition) throws IOException {
|
||||||
if (pPosition < mStart) {
|
if (pPosition < start) {
|
||||||
throw new IOException("Seek before flush position");
|
throw new IOException("Seek before flush position");
|
||||||
}
|
}
|
||||||
mPosition = pPosition;
|
position = pPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -178,14 +178,14 @@ public final class MemoryCacheSeekableStream extends AbstractCachedSeekableStrea
|
|||||||
int firstPos = (int) (pPosition / BLOCK_SIZE) - 1;
|
int firstPos = (int) (pPosition / BLOCK_SIZE) - 1;
|
||||||
|
|
||||||
for (int i = 0; i < firstPos; i++) {
|
for (int i = 0; i < firstPos; i++) {
|
||||||
mCache.remove(0);
|
cache.remove(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
mStart = pPosition;
|
start = pPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getPosition() {
|
public long getPosition() {
|
||||||
return mPosition;
|
return position;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,13 +50,12 @@ public abstract class RandomAccessStream implements Seekable, DataInput, DataOut
|
|||||||
// TODO: Package private SeekableDelegate?
|
// TODO: Package private SeekableDelegate?
|
||||||
|
|
||||||
// TODO: Both read and write must update stream position
|
// TODO: Both read and write must update stream position
|
||||||
//private int mPosition = -1;
|
//private int position = -1;
|
||||||
|
|
||||||
/** This random access stream, wrapped in an {@code InputStream} */
|
/** This random access stream, wrapped in an {@code InputStream} */
|
||||||
SeekableInputStream mInputView = null;
|
SeekableInputStream inputView = null;
|
||||||
/** This random access stream, wrapped in an {@code OutputStream} */
|
/** This random access stream, wrapped in an {@code OutputStream} */
|
||||||
SeekableOutputStream mOutputView = null;
|
SeekableOutputStream outputView = null;
|
||||||
|
|
||||||
|
|
||||||
// TODO: Create an Input and an Output interface matching InputStream and OutputStream?
|
// TODO: Create an Input and an Output interface matching InputStream and OutputStream?
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
@@ -119,10 +118,10 @@ public abstract class RandomAccessStream implements Seekable, DataInput, DataOut
|
|||||||
* @return a {@code SeekableInputStream} reading from this stream
|
* @return a {@code SeekableInputStream} reading from this stream
|
||||||
*/
|
*/
|
||||||
public final SeekableInputStream asInputStream() {
|
public final SeekableInputStream asInputStream() {
|
||||||
if (mInputView == null) {
|
if (inputView == null) {
|
||||||
mInputView = new InputStreamView(this);
|
inputView = new InputStreamView(this);
|
||||||
}
|
}
|
||||||
return mInputView;
|
return inputView;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -134,15 +133,15 @@ public abstract class RandomAccessStream implements Seekable, DataInput, DataOut
|
|||||||
* @return a {@code SeekableOutputStream} writing to this stream
|
* @return a {@code SeekableOutputStream} writing to this stream
|
||||||
*/
|
*/
|
||||||
public final SeekableOutputStream asOutputStream() {
|
public final SeekableOutputStream asOutputStream() {
|
||||||
if (mOutputView == null) {
|
if (outputView == null) {
|
||||||
mOutputView = new OutputStreamView(this);
|
outputView = new OutputStreamView(this);
|
||||||
}
|
}
|
||||||
return mOutputView;
|
return outputView;
|
||||||
}
|
}
|
||||||
|
|
||||||
static final class InputStreamView extends SeekableInputStream {
|
static final class InputStreamView extends SeekableInputStream {
|
||||||
// TODO: Consider adding synchonization (on mStream) for all operations
|
// TODO: Consider adding synchonization (on stream) for all operations
|
||||||
// TODO: Is is a good thing that close/flush etc works on mStream?
|
// TODO: Is is a good thing that close/flush etc works on stream?
|
||||||
// - Or should it rather just work on the views?
|
// - Or should it rather just work on the views?
|
||||||
// - Allow multiple views?
|
// - Allow multiple views?
|
||||||
|
|
||||||
@@ -190,8 +189,8 @@ public abstract class RandomAccessStream implements Seekable, DataInput, DataOut
|
|||||||
}
|
}
|
||||||
|
|
||||||
static final class OutputStreamView extends SeekableOutputStream {
|
static final class OutputStreamView extends SeekableOutputStream {
|
||||||
// TODO: Consider adding synchonization (on mStream) for all operations
|
// TODO: Consider adding synchonization (on stream) for all operations
|
||||||
// TODO: Is is a good thing that close/flush etc works on mStream?
|
// TODO: Is is a good thing that close/flush etc works on stream?
|
||||||
// - Or should it rather just work on the views?
|
// - Or should it rather just work on the views?
|
||||||
// - Allow multiple views?
|
// - Allow multiple views?
|
||||||
|
|
||||||
|
|||||||
@@ -43,15 +43,15 @@ import java.util.Stack;
|
|||||||
public abstract class SeekableInputStream extends InputStream implements Seekable {
|
public abstract class SeekableInputStream extends InputStream implements Seekable {
|
||||||
|
|
||||||
// TODO: It's at the moment not possible to create subclasses outside this
|
// TODO: It's at the moment not possible to create subclasses outside this
|
||||||
// package, as there's no access to mPosition. mPosition needs to be
|
// package, as there's no access to position. position needs to be
|
||||||
// updated from the read/read/read methods...
|
// updated from the read/read/read methods...
|
||||||
|
|
||||||
/** The stream position in this stream */
|
/** The stream position in this stream */
|
||||||
long mPosition;
|
long position;
|
||||||
long mFlushedPosition;
|
long flushedPosition;
|
||||||
boolean mClosed;
|
boolean closed;
|
||||||
|
|
||||||
protected Stack<Long> mMarkedPositions = new Stack<Long>();
|
protected Stack<Long> markedPositions = new Stack<Long>();
|
||||||
|
|
||||||
/// InputStream overrides
|
/// InputStream overrides
|
||||||
@Override
|
@Override
|
||||||
@@ -69,17 +69,29 @@ public abstract class SeekableInputStream extends InputStream implements Seekabl
|
|||||||
* @throws IOException if an I/O exception occurs during skip
|
* @throws IOException if an I/O exception occurs during skip
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public final long skip(long pLength) throws IOException {
|
public final long skip(final long pLength) throws IOException {
|
||||||
long pos = mPosition;
|
long pos = position;
|
||||||
if (pos + pLength < mFlushedPosition) {
|
long wantedPosition = pos + pLength;
|
||||||
|
if (wantedPosition < flushedPosition) {
|
||||||
throw new IOException("position < flushedPosition");
|
throw new IOException("position < flushedPosition");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop at stream length for compatibility, even though it's allowed
|
// Stop at stream length for compatibility, even though it might be allowed
|
||||||
// to seek past end of stream
|
// to seek past end of stream
|
||||||
seek(Math.min(pos + pLength, pos + available()));
|
int available = available();
|
||||||
|
if (available > 0) {
|
||||||
|
seek(Math.min(wantedPosition, pos + available));
|
||||||
|
}
|
||||||
|
// TODO: Add optimization for streams with known length!
|
||||||
|
else {
|
||||||
|
// Slow mode...
|
||||||
|
int toSkip = (int) Math.max(Math.min(pLength, 512), -512);
|
||||||
|
while (toSkip > 0 && read() >= 0) {
|
||||||
|
toSkip--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return mPosition - pos;
|
return position - pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -88,7 +100,7 @@ public abstract class SeekableInputStream extends InputStream implements Seekabl
|
|||||||
|
|
||||||
// TODO: We don't really need to do this.. Is it a good idea?
|
// TODO: We don't really need to do this.. Is it a good idea?
|
||||||
try {
|
try {
|
||||||
flushBefore(Math.max(mPosition - pLimit, mFlushedPosition));
|
flushBefore(Math.max(position - pLimit, flushedPosition));
|
||||||
}
|
}
|
||||||
catch (IOException ignore) {
|
catch (IOException ignore) {
|
||||||
// Ignore, as it's not really critical
|
// Ignore, as it's not really critical
|
||||||
@@ -111,29 +123,29 @@ public abstract class SeekableInputStream extends InputStream implements Seekabl
|
|||||||
|
|
||||||
// NOTE: This is correct according to javax.imageio (IndexOutOfBoundsException),
|
// NOTE: This is correct according to javax.imageio (IndexOutOfBoundsException),
|
||||||
// but it's kind of inconsistent with reset that throws IOException...
|
// but it's kind of inconsistent with reset that throws IOException...
|
||||||
if (pPosition < mFlushedPosition) {
|
if (pPosition < flushedPosition) {
|
||||||
throw new IndexOutOfBoundsException("position < flushedPosition");
|
throw new IndexOutOfBoundsException("position < flushedPosition");
|
||||||
}
|
}
|
||||||
|
|
||||||
seekImpl(pPosition);
|
seekImpl(pPosition);
|
||||||
mPosition = pPosition;
|
position = pPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void seekImpl(long pPosition) throws IOException;
|
protected abstract void seekImpl(long pPosition) throws IOException;
|
||||||
|
|
||||||
public final void mark() {
|
public final void mark() {
|
||||||
mMarkedPositions.push(mPosition);
|
markedPositions.push(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void reset() throws IOException {
|
public final void reset() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
if (!mMarkedPositions.isEmpty()) {
|
if (!markedPositions.isEmpty()) {
|
||||||
long newPos = mMarkedPositions.pop();
|
long newPos = markedPositions.pop();
|
||||||
|
|
||||||
// NOTE: This is correct according to javax.imageio (IOException),
|
// NOTE: This is correct according to javax.imageio (IOException),
|
||||||
// but it's kind of inconsistent with seek that throws IndexOutOfBoundsException...
|
// but it's kind of inconsistent with seek that throws IndexOutOfBoundsException...
|
||||||
if (newPos < mFlushedPosition) {
|
if (newPos < flushedPosition) {
|
||||||
throw new IOException("Previous marked position has been discarded");
|
throw new IOException("Previous marked position has been discarded");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +162,7 @@ public abstract class SeekableInputStream extends InputStream implements Seekabl
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final void flushBefore(long pPosition) throws IOException {
|
public final void flushBefore(long pPosition) throws IOException {
|
||||||
if (pPosition < mFlushedPosition) {
|
if (pPosition < flushedPosition) {
|
||||||
throw new IndexOutOfBoundsException("position < flushedPosition");
|
throw new IndexOutOfBoundsException("position < flushedPosition");
|
||||||
}
|
}
|
||||||
if (pPosition > getStreamPosition()) {
|
if (pPosition > getStreamPosition()) {
|
||||||
@@ -158,7 +170,7 @@ public abstract class SeekableInputStream extends InputStream implements Seekabl
|
|||||||
}
|
}
|
||||||
checkOpen();
|
checkOpen();
|
||||||
flushBeforeImpl(pPosition);
|
flushBeforeImpl(pPosition);
|
||||||
mFlushedPosition = pPosition;
|
flushedPosition = pPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -172,21 +184,21 @@ public abstract class SeekableInputStream extends InputStream implements Seekabl
|
|||||||
protected abstract void flushBeforeImpl(long pPosition) throws IOException;
|
protected abstract void flushBeforeImpl(long pPosition) throws IOException;
|
||||||
|
|
||||||
public final void flush() throws IOException {
|
public final void flush() throws IOException {
|
||||||
flushBefore(mFlushedPosition);
|
flushBefore(flushedPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final long getFlushedPosition() throws IOException {
|
public final long getFlushedPosition() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
return mFlushedPosition;
|
return flushedPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final long getStreamPosition() throws IOException {
|
public final long getStreamPosition() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
return mPosition;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void checkOpen() throws IOException {
|
protected final void checkOpen() throws IOException {
|
||||||
if (mClosed) {
|
if (closed) {
|
||||||
throw new IOException("closed");
|
throw new IOException("closed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,7 +206,7 @@ public abstract class SeekableInputStream extends InputStream implements Seekabl
|
|||||||
@Override
|
@Override
|
||||||
public final void close() throws IOException {
|
public final void close() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
mClosed = true;
|
closed = true;
|
||||||
closeImpl();
|
closeImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,7 +223,7 @@ public abstract class SeekableInputStream extends InputStream implements Seekabl
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void finalize() throws Throwable {
|
protected void finalize() throws Throwable {
|
||||||
if (!mClosed) {
|
if (!closed) {
|
||||||
try {
|
try {
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,11 +43,11 @@ import java.util.Stack;
|
|||||||
*/
|
*/
|
||||||
public abstract class SeekableOutputStream extends OutputStream implements Seekable {
|
public abstract class SeekableOutputStream extends OutputStream implements Seekable {
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
long mPosition;
|
long position;
|
||||||
long mFlushedPosition;
|
long flushedPosition;
|
||||||
boolean mClosed;
|
boolean closed;
|
||||||
|
|
||||||
protected Stack<Long> mMarkedPositions = new Stack<Long>();
|
protected Stack<Long> markedPositions = new Stack<Long>();
|
||||||
|
|
||||||
/// Outputstream overrides
|
/// Outputstream overrides
|
||||||
@Override
|
@Override
|
||||||
@@ -63,28 +63,28 @@ public abstract class SeekableOutputStream extends OutputStream implements Seeka
|
|||||||
|
|
||||||
// TODO: This is correct according to javax.imageio (IndexOutOfBoundsException),
|
// TODO: This is correct according to javax.imageio (IndexOutOfBoundsException),
|
||||||
// but it's inconsistent with reset that throws IOException...
|
// but it's inconsistent with reset that throws IOException...
|
||||||
if (pPosition < mFlushedPosition) {
|
if (pPosition < flushedPosition) {
|
||||||
throw new IndexOutOfBoundsException("position < flushedPosition!");
|
throw new IndexOutOfBoundsException("position < flushedPosition!");
|
||||||
}
|
}
|
||||||
|
|
||||||
seekImpl(pPosition);
|
seekImpl(pPosition);
|
||||||
mPosition = pPosition;
|
position = pPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void seekImpl(long pPosition) throws IOException;
|
protected abstract void seekImpl(long pPosition) throws IOException;
|
||||||
|
|
||||||
public final void mark() {
|
public final void mark() {
|
||||||
mMarkedPositions.push(mPosition);
|
markedPositions.push(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void reset() throws IOException {
|
public final void reset() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
if (!mMarkedPositions.isEmpty()) {
|
if (!markedPositions.isEmpty()) {
|
||||||
long newPos = mMarkedPositions.pop();
|
long newPos = markedPositions.pop();
|
||||||
|
|
||||||
// TODO: This is correct according to javax.imageio (IOException),
|
// TODO: This is correct according to javax.imageio (IOException),
|
||||||
// but it's inconsistent with seek that throws IndexOutOfBoundsException...
|
// but it's inconsistent with seek that throws IndexOutOfBoundsException...
|
||||||
if (newPos < mFlushedPosition) {
|
if (newPos < flushedPosition) {
|
||||||
throw new IOException("Previous marked position has been discarded!");
|
throw new IOException("Previous marked position has been discarded!");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,7 +93,7 @@ public abstract class SeekableOutputStream extends OutputStream implements Seeka
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final void flushBefore(long pPosition) throws IOException {
|
public final void flushBefore(long pPosition) throws IOException {
|
||||||
if (pPosition < mFlushedPosition) {
|
if (pPosition < flushedPosition) {
|
||||||
throw new IndexOutOfBoundsException("position < flushedPosition!");
|
throw new IndexOutOfBoundsException("position < flushedPosition!");
|
||||||
}
|
}
|
||||||
if (pPosition > getStreamPosition()) {
|
if (pPosition > getStreamPosition()) {
|
||||||
@@ -101,28 +101,28 @@ public abstract class SeekableOutputStream extends OutputStream implements Seeka
|
|||||||
}
|
}
|
||||||
checkOpen();
|
checkOpen();
|
||||||
flushBeforeImpl(pPosition);
|
flushBeforeImpl(pPosition);
|
||||||
mFlushedPosition = pPosition;
|
flushedPosition = pPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void flushBeforeImpl(long pPosition) throws IOException;
|
protected abstract void flushBeforeImpl(long pPosition) throws IOException;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void flush() throws IOException {
|
public final void flush() throws IOException {
|
||||||
flushBefore(mFlushedPosition);
|
flushBefore(flushedPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final long getFlushedPosition() throws IOException {
|
public final long getFlushedPosition() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
return mFlushedPosition;
|
return flushedPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final long getStreamPosition() throws IOException {
|
public final long getStreamPosition() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
return mPosition;
|
return position;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final void checkOpen() throws IOException {
|
protected final void checkOpen() throws IOException {
|
||||||
if (mClosed) {
|
if (closed) {
|
||||||
throw new IOException("closed");
|
throw new IOException("closed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,7 +130,7 @@ public abstract class SeekableOutputStream extends OutputStream implements Seeka
|
|||||||
@Override
|
@Override
|
||||||
public final void close() throws IOException {
|
public final void close() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
mClosed = true;
|
closed = true;
|
||||||
closeImpl();
|
closeImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,8 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.lang.Validate;
|
||||||
|
|
||||||
import java.io.StringReader;
|
import java.io.StringReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
@@ -42,13 +44,13 @@ import java.io.Reader;
|
|||||||
*/
|
*/
|
||||||
public class StringArrayReader extends StringReader {
|
public class StringArrayReader extends StringReader {
|
||||||
|
|
||||||
private StringReader mCurrent;
|
private StringReader current;
|
||||||
private String[] mStrings;
|
private String[] strings;
|
||||||
protected final Object mLock;
|
protected final Object finalLock;
|
||||||
private int mCurrentSting;
|
private int currentSting;
|
||||||
private int mMarkedString;
|
private int markedString;
|
||||||
private int mMark;
|
private int mark;
|
||||||
private int mNext;
|
private int next;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new string array reader.
|
* Create a new string array reader.
|
||||||
@@ -57,28 +59,28 @@ public class StringArrayReader extends StringReader {
|
|||||||
*/
|
*/
|
||||||
public StringArrayReader(final String[] pStrings) {
|
public StringArrayReader(final String[] pStrings) {
|
||||||
super("");
|
super("");
|
||||||
if (pStrings == null) {
|
|
||||||
throw new NullPointerException("strings == null");
|
|
||||||
}
|
|
||||||
|
|
||||||
mLock = lock = pStrings; // NOTE: It's ok to sync on pStrings, as the
|
Validate.notNull(pStrings, "strings");
|
||||||
|
|
||||||
|
finalLock = lock = pStrings; // NOTE: It's ok to sync on pStrings, as the
|
||||||
// reference can't change, only it's elements
|
// reference can't change, only it's elements
|
||||||
|
|
||||||
mStrings = pStrings.clone(); // Defensive copy for content
|
strings = pStrings.clone(); // Defensive copy for content
|
||||||
nextReader();
|
nextReader();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final Reader nextReader() {
|
protected final Reader nextReader() {
|
||||||
if (mCurrentSting >= mStrings.length) {
|
if (currentSting >= strings.length) {
|
||||||
mCurrent = new EmptyReader();
|
current = new EmptyReader();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mCurrent = new StringReader(mStrings[mCurrentSting++]);
|
current = new StringReader(strings[currentSting++]);
|
||||||
}
|
}
|
||||||
// NOTE: Reset mNext for every reader, and record marked reader in mark/reset methods!
|
|
||||||
mNext = 0;
|
// NOTE: Reset next for every reader, and record marked reader in mark/reset methods!
|
||||||
|
next = 0;
|
||||||
|
|
||||||
return mCurrent;
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -87,15 +89,15 @@ public class StringArrayReader extends StringReader {
|
|||||||
* @throws IOException if the stream is closed
|
* @throws IOException if the stream is closed
|
||||||
*/
|
*/
|
||||||
protected final void ensureOpen() throws IOException {
|
protected final void ensureOpen() throws IOException {
|
||||||
if (mStrings == null) {
|
if (strings == null) {
|
||||||
throw new IOException("Stream closed");
|
throw new IOException("Stream closed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() {
|
public void close() {
|
||||||
super.close();
|
super.close();
|
||||||
mStrings = null;
|
strings = null;
|
||||||
mCurrent.close();
|
current.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void mark(int pReadLimit) throws IOException {
|
public void mark(int pReadLimit) throws IOException {
|
||||||
@@ -103,29 +105,29 @@ public class StringArrayReader extends StringReader {
|
|||||||
throw new IllegalArgumentException("Read limit < 0");
|
throw new IllegalArgumentException("Read limit < 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (mLock) {
|
synchronized (finalLock) {
|
||||||
ensureOpen();
|
ensureOpen();
|
||||||
mMark = mNext;
|
mark = next;
|
||||||
mMarkedString = mCurrentSting;
|
markedString = currentSting;
|
||||||
|
|
||||||
mCurrent.mark(pReadLimit);
|
current.mark(pReadLimit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset() throws IOException {
|
public void reset() throws IOException {
|
||||||
synchronized (mLock) {
|
synchronized (finalLock) {
|
||||||
ensureOpen();
|
ensureOpen();
|
||||||
|
|
||||||
if (mCurrentSting != mMarkedString) {
|
if (currentSting != markedString) {
|
||||||
mCurrentSting = mMarkedString - 1;
|
currentSting = markedString - 1;
|
||||||
nextReader();
|
nextReader();
|
||||||
mCurrent.skip(mMark);
|
current.skip(mark);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mCurrent.reset();
|
current.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
mNext = mMark;
|
next = mark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,49 +136,49 @@ public class StringArrayReader extends StringReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
synchronized (mLock) {
|
synchronized (finalLock) {
|
||||||
int read = mCurrent.read();
|
int read = current.read();
|
||||||
|
|
||||||
if (read < 0 && mCurrentSting < mStrings.length) {
|
if (read < 0 && currentSting < strings.length) {
|
||||||
nextReader();
|
nextReader();
|
||||||
return read(); // In case of empty strings
|
return read(); // In case of empty strings
|
||||||
}
|
}
|
||||||
|
|
||||||
mNext++;
|
next++;
|
||||||
|
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read(char pBuffer[], int pOffset, int pLength) throws IOException {
|
public int read(char pBuffer[], int pOffset, int pLength) throws IOException {
|
||||||
synchronized (mLock) {
|
synchronized (finalLock) {
|
||||||
int read = mCurrent.read(pBuffer, pOffset, pLength);
|
int read = current.read(pBuffer, pOffset, pLength);
|
||||||
|
|
||||||
if (read < 0 && mCurrentSting < mStrings.length) {
|
if (read < 0 && currentSting < strings.length) {
|
||||||
nextReader();
|
nextReader();
|
||||||
return read(pBuffer, pOffset, pLength); // In case of empty strings
|
return read(pBuffer, pOffset, pLength); // In case of empty strings
|
||||||
}
|
}
|
||||||
|
|
||||||
mNext += read;
|
next += read;
|
||||||
|
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean ready() throws IOException {
|
public boolean ready() throws IOException {
|
||||||
return mCurrent.ready();
|
return current.ready();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long skip(long pChars) throws IOException {
|
public long skip(long pChars) throws IOException {
|
||||||
synchronized (mLock) {
|
synchronized (finalLock) {
|
||||||
long skipped = mCurrent.skip(pChars);
|
long skipped = current.skip(pChars);
|
||||||
|
|
||||||
if (skipped == 0 && mCurrentSting < mStrings.length) {
|
if (skipped == 0 && currentSting < strings.length) {
|
||||||
nextReader();
|
nextReader();
|
||||||
return skip(pChars);
|
return skip(pChars);
|
||||||
}
|
}
|
||||||
|
|
||||||
mNext += skipped;
|
next += skipped;
|
||||||
|
|
||||||
return skipped;
|
return skipped;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,8 +43,8 @@ import java.io.InputStream;
|
|||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/SubStream.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/SubStream.java#2 $
|
||||||
*/
|
*/
|
||||||
public final class SubStream extends FilterInputStream {
|
public final class SubStream extends FilterInputStream {
|
||||||
private long mLeft;
|
private long bytesLeft;
|
||||||
private int mMarkLimit;
|
private int markLimit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code SubStream} of the given {@code pStream}.
|
* Creates a {@code SubStream} of the given {@code pStream}.
|
||||||
@@ -54,7 +54,7 @@ public final class SubStream extends FilterInputStream {
|
|||||||
*/
|
*/
|
||||||
public SubStream(final InputStream pStream, final long pLength) {
|
public SubStream(final InputStream pStream, final long pLength) {
|
||||||
super(Validate.notNull(pStream, "stream"));
|
super(Validate.notNull(pStream, "stream"));
|
||||||
mLeft = pLength;
|
bytesLeft = pLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,32 +64,32 @@ public final class SubStream extends FilterInputStream {
|
|||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
// NOTE: Do not close the underlying stream
|
// NOTE: Do not close the underlying stream
|
||||||
while (mLeft > 0) {
|
while (bytesLeft > 0) {
|
||||||
//noinspection ResultOfMethodCallIgnored
|
//noinspection ResultOfMethodCallIgnored
|
||||||
skip(mLeft);
|
skip(bytesLeft);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int available() throws IOException {
|
public int available() throws IOException {
|
||||||
return (int) Math.min(super.available(), mLeft);
|
return (int) Math.min(super.available(), bytesLeft);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mark(int pReadLimit) {
|
public void mark(int pReadLimit) {
|
||||||
super.mark(pReadLimit);// This either succeeds or does nothing...
|
super.mark(pReadLimit);// This either succeeds or does nothing...
|
||||||
mMarkLimit = pReadLimit;
|
markLimit = pReadLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() throws IOException {
|
public void reset() throws IOException {
|
||||||
super.reset();// This either succeeds or throws IOException
|
super.reset();// This either succeeds or throws IOException
|
||||||
mLeft += mMarkLimit;
|
bytesLeft += markLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
if (mLeft-- <= 0) {
|
if (bytesLeft-- <= 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return super.read();
|
return super.read();
|
||||||
@@ -102,12 +102,12 @@ public final class SubStream extends FilterInputStream {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
public int read(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||||
if (mLeft <= 0) {
|
if (bytesLeft <= 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int read = super.read(pBytes, pOffset, (int) findMaxLen(pLength));
|
int read = super.read(pBytes, pOffset, (int) findMaxLen(pLength));
|
||||||
mLeft = read < 0 ? 0 : mLeft - read;
|
bytesLeft = read < 0 ? 0 : bytesLeft - read;
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,8 +118,8 @@ public final class SubStream extends FilterInputStream {
|
|||||||
* @return the maximum number of bytes to read
|
* @return the maximum number of bytes to read
|
||||||
*/
|
*/
|
||||||
private long findMaxLen(long pLength) {
|
private long findMaxLen(long pLength) {
|
||||||
if (mLeft < pLength) {
|
if (bytesLeft < pLength) {
|
||||||
return (int) Math.max(mLeft, 0);
|
return (int) Math.max(bytesLeft, 0);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return pLength;
|
return pLength;
|
||||||
@@ -129,7 +129,7 @@ public final class SubStream extends FilterInputStream {
|
|||||||
@Override
|
@Override
|
||||||
public long skip(long pLength) throws IOException {
|
public long skip(long pLength) throws IOException {
|
||||||
long skipped = super.skip(findMaxLen(pLength));// Skips 0 or more, never -1
|
long skipped = super.skip(findMaxLen(pLength));// Skips 0 or more, never -1
|
||||||
mLeft -= skipped;
|
bytesLeft -= skipped;
|
||||||
return skipped;
|
return skipped;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ final class Win32Lnk extends File {
|
|||||||
(byte) 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'F'
|
(byte) 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'F'
|
||||||
};
|
};
|
||||||
|
|
||||||
private final File mTarget;
|
private final File target;
|
||||||
|
|
||||||
private static final int FLAG_ITEM_ID_LIST = 0x01;
|
private static final int FLAG_ITEM_ID_LIST = 0x01;
|
||||||
private static final int FLAG_FILE_LOC_INFO = 0x02;
|
private static final int FLAG_FILE_LOC_INFO = 0x02;
|
||||||
private static final int FLAG_DESC_STRING = 0x04;
|
private static final int FLAG_DESC_STRING = 0x04;
|
||||||
@@ -65,10 +66,10 @@ final class Win32Lnk extends File {
|
|||||||
File target = parse(this);
|
File target = parse(this);
|
||||||
if (target == this) {
|
if (target == this) {
|
||||||
// NOTE: This is a workaround
|
// NOTE: This is a workaround
|
||||||
// mTarget = this causes infinite loops in some methods
|
// target = this causes infinite loops in some methods
|
||||||
target = new File(pPath);
|
target = new File(pPath);
|
||||||
}
|
}
|
||||||
mTarget = target;
|
this.target = target;
|
||||||
}
|
}
|
||||||
|
|
||||||
Win32Lnk(final File pPath) throws IOException {
|
Win32Lnk(final File pPath) throws IOException {
|
||||||
@@ -336,24 +337,24 @@ final class Win32Lnk extends File {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
public File getTarget() {
|
public File getTarget() {
|
||||||
return mTarget;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
// java.io.File overrides below
|
// java.io.File overrides below
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isDirectory() {
|
public boolean isDirectory() {
|
||||||
return mTarget.isDirectory();
|
return target.isDirectory();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canRead() {
|
public boolean canRead() {
|
||||||
return mTarget.canRead();
|
return target.canRead();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canWrite() {
|
public boolean canWrite() {
|
||||||
return mTarget.canWrite();
|
return target.canWrite();
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: equals is implemented using compareto == 0
|
// NOTE: equals is implemented using compareto == 0
|
||||||
@@ -362,7 +363,7 @@ final class Win32Lnk extends File {
|
|||||||
// TODO: Verify this
|
// TODO: Verify this
|
||||||
// Probably not a good idea, as it IS NOT THE SAME file
|
// Probably not a good idea, as it IS NOT THE SAME file
|
||||||
// It's probably better to not override
|
// It's probably better to not override
|
||||||
return mTarget.compareTo(pathname);
|
return target.compareTo(pathname);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -375,7 +376,7 @@ final class Win32Lnk extends File {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean exists() {
|
public boolean exists() {
|
||||||
return mTarget.exists();
|
return target.exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
// A .lnk may be absolute
|
// A .lnk may be absolute
|
||||||
@@ -385,12 +386,12 @@ final class Win32Lnk extends File {
|
|||||||
// Theses should be resolved according to the API (for Unix).
|
// Theses should be resolved according to the API (for Unix).
|
||||||
@Override
|
@Override
|
||||||
public File getCanonicalFile() throws IOException {
|
public File getCanonicalFile() throws IOException {
|
||||||
return mTarget.getCanonicalFile();
|
return target.getCanonicalFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getCanonicalPath() throws IOException {
|
public String getCanonicalPath() throws IOException {
|
||||||
return mTarget.getCanonicalPath();
|
return target.getCanonicalPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
//public String getName() {
|
//public String getName() {
|
||||||
@@ -402,47 +403,47 @@ final class Win32Lnk extends File {
|
|||||||
// public boolean isAbsolute() {
|
// public boolean isAbsolute() {
|
||||||
@Override
|
@Override
|
||||||
public boolean isFile() {
|
public boolean isFile() {
|
||||||
return mTarget.isFile();
|
return target.isFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isHidden() {
|
public boolean isHidden() {
|
||||||
return mTarget.isHidden();
|
return target.isHidden();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long lastModified() {
|
public long lastModified() {
|
||||||
return mTarget.lastModified();
|
return target.lastModified();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long length() {
|
public long length() {
|
||||||
return mTarget.length();
|
return target.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] list() {
|
public String[] list() {
|
||||||
return mTarget.list();
|
return target.list();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] list(final FilenameFilter filter) {
|
public String[] list(final FilenameFilter filter) {
|
||||||
return mTarget.list(filter);
|
return target.list(filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public File[] listFiles() {
|
public File[] listFiles() {
|
||||||
return Win32File.wrap(mTarget.listFiles());
|
return Win32File.wrap(target.listFiles());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public File[] listFiles(final FileFilter filter) {
|
public File[] listFiles(final FileFilter filter) {
|
||||||
return Win32File.wrap(mTarget.listFiles(filter));
|
return Win32File.wrap(target.listFiles(filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public File[] listFiles(final FilenameFilter filter) {
|
public File[] listFiles(final FilenameFilter filter) {
|
||||||
return Win32File.wrap(mTarget.listFiles(filter));
|
return Win32File.wrap(target.listFiles(filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Makes no sense, does it?
|
// Makes no sense, does it?
|
||||||
@@ -454,19 +455,19 @@ final class Win32Lnk extends File {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean setLastModified(long time) {
|
public boolean setLastModified(long time) {
|
||||||
return mTarget.setLastModified(time);
|
return target.setLastModified(time);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean setReadOnly() {
|
public boolean setReadOnly() {
|
||||||
return mTarget.setReadOnly();
|
return target.setReadOnly();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
if (mTarget.equals(this)) {
|
if (target.equals(this)) {
|
||||||
return super.toString();
|
return super.toString();
|
||||||
}
|
}
|
||||||
return super.toString() + " -> " + mTarget.toString();
|
return super.toString() + " -> " + target.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,10 +51,11 @@ import java.nio.CharBuffer;
|
|||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/WriterOutputStream.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/WriterOutputStream.java#2 $
|
||||||
*/
|
*/
|
||||||
public class WriterOutputStream extends OutputStream {
|
public class WriterOutputStream extends OutputStream {
|
||||||
protected Writer mWriter;
|
protected Writer writer;
|
||||||
final protected Decoder mDecoder;
|
final protected Decoder decoder;
|
||||||
final ByteArrayOutputStream mBufferStream = new FastByteArrayOutputStream(1024);
|
final ByteArrayOutputStream bufferStream = new FastByteArrayOutputStream(1024);
|
||||||
private volatile boolean mIsFlushing = false; // Ugly but critical...
|
|
||||||
|
private volatile boolean isFlushing = false; // Ugly but critical...
|
||||||
|
|
||||||
private static final boolean NIO_AVAILABLE = isNIOAvailable();
|
private static final boolean NIO_AVAILABLE = isNIOAvailable();
|
||||||
|
|
||||||
@@ -71,8 +72,8 @@ public class WriterOutputStream extends OutputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public WriterOutputStream(final Writer pWriter, final String pCharset) {
|
public WriterOutputStream(final Writer pWriter, final String pCharset) {
|
||||||
mWriter = pWriter;
|
writer = pWriter;
|
||||||
mDecoder = getDecoder(pCharset);
|
decoder = getDecoder(pCharset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WriterOutputStream(final Writer pWriter) {
|
public WriterOutputStream(final Writer pWriter) {
|
||||||
@@ -94,14 +95,14 @@ public class WriterOutputStream extends OutputStream {
|
|||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
flush();
|
flush();
|
||||||
mWriter.close();
|
writer.close();
|
||||||
mWriter = null;
|
writer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void flush() throws IOException {
|
public void flush() throws IOException {
|
||||||
flushBuffer();
|
flushBuffer();
|
||||||
mWriter.flush();
|
writer.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -115,22 +116,22 @@ public class WriterOutputStream extends OutputStream {
|
|||||||
@Override
|
@Override
|
||||||
public final void write(byte[] pBytes, int pOffset, int pLength) throws IOException {
|
public final void write(byte[] pBytes, int pOffset, int pLength) throws IOException {
|
||||||
flushBuffer();
|
flushBuffer();
|
||||||
mDecoder.decodeTo(mWriter, pBytes, pOffset, pLength);
|
decoder.decodeTo(writer, pBytes, pOffset, pLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void write(int pByte) {
|
public final void write(int pByte) {
|
||||||
// TODO: Is it possible to know if this is a good place in the stream to
|
// TODO: Is it possible to know if this is a good place in the stream to
|
||||||
// flush? It might be in the middle of a multi-byte encoded character..
|
// flush? It might be in the middle of a multi-byte encoded character..
|
||||||
mBufferStream.write(pByte);
|
bufferStream.write(pByte);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flushBuffer() throws IOException {
|
private void flushBuffer() throws IOException {
|
||||||
if (!mIsFlushing && mBufferStream.size() > 0) {
|
if (!isFlushing && bufferStream.size() > 0) {
|
||||||
mIsFlushing = true;
|
isFlushing = true;
|
||||||
mBufferStream.writeTo(this); // NOTE: Avoids cloning buffer array
|
bufferStream.writeTo(this); // NOTE: Avoids cloning buffer array
|
||||||
mBufferStream.reset();
|
bufferStream.reset();
|
||||||
mIsFlushing = false;
|
isFlushing = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,7 +139,7 @@ public class WriterOutputStream extends OutputStream {
|
|||||||
public static void main(String[] pArgs) throws IOException {
|
public static void main(String[] pArgs) throws IOException {
|
||||||
int iterations = 1000000;
|
int iterations = 1000000;
|
||||||
|
|
||||||
byte[] bytes = "åøæÅØÆ klashf lkash ljah lhaaklhghdfgu ksd".getBytes("UTF-8");
|
byte[] bytes = "������ klashf lkash ljah lhaaklhghdfgu ksd".getBytes("UTF-8");
|
||||||
|
|
||||||
Decoder d;
|
Decoder d;
|
||||||
long start;
|
long start;
|
||||||
|
|||||||
@@ -41,12 +41,12 @@ import java.io.InputStream;
|
|||||||
*/
|
*/
|
||||||
// TODO: Move to other package or make public
|
// TODO: Move to other package or make public
|
||||||
abstract class AbstractRLEDecoder implements Decoder {
|
abstract class AbstractRLEDecoder implements Decoder {
|
||||||
protected final byte[] mRow;
|
protected final byte[] row;
|
||||||
protected final int mWidth;
|
protected final int width;
|
||||||
protected int mSrcX;
|
protected int srcX;
|
||||||
protected int mSrcY;
|
protected int srcY;
|
||||||
protected int mDstX;
|
protected int dstX;
|
||||||
protected int mDstY;
|
protected int dstY;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an RLEDecoder. As RLE encoded BMP's may contain x and y deltas,
|
* Creates an RLEDecoder. As RLE encoded BMP's may contain x and y deltas,
|
||||||
@@ -56,21 +56,21 @@ abstract class AbstractRLEDecoder implements Decoder {
|
|||||||
* @param pHeight heigth of the image
|
* @param pHeight heigth of the image
|
||||||
*/
|
*/
|
||||||
AbstractRLEDecoder(int pWidth, int pHeight) {
|
AbstractRLEDecoder(int pWidth, int pHeight) {
|
||||||
mWidth = pWidth;
|
width = pWidth;
|
||||||
int bytesPerRow = mWidth;
|
int bytesPerRow = width;
|
||||||
int mod = bytesPerRow % 4;
|
int mod = bytesPerRow % 4;
|
||||||
|
|
||||||
if (mod != 0) {
|
if (mod != 0) {
|
||||||
bytesPerRow += 4 - mod;
|
bytesPerRow += 4 - mod;
|
||||||
}
|
}
|
||||||
|
|
||||||
mRow = new byte[bytesPerRow];
|
row = new byte[bytesPerRow];
|
||||||
|
|
||||||
mSrcX = 0;
|
srcX = 0;
|
||||||
mSrcY = pHeight - 1;
|
srcY = pHeight - 1;
|
||||||
|
|
||||||
mDstX = mSrcX;
|
dstX = srcX;
|
||||||
mDstY = mSrcY;
|
dstY = srcY;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,26 +95,26 @@ abstract class AbstractRLEDecoder implements Decoder {
|
|||||||
public final int decode(InputStream pStream, byte[] pBuffer) throws IOException {
|
public final int decode(InputStream pStream, byte[] pBuffer) throws IOException {
|
||||||
int decoded = 0;
|
int decoded = 0;
|
||||||
|
|
||||||
while (decoded < pBuffer.length && mDstY >= 0) {
|
while (decoded < pBuffer.length && dstY >= 0) {
|
||||||
// NOTE: Decode only full rows, don't decode if y delta
|
// NOTE: Decode only full rows, don't decode if y delta
|
||||||
if (mDstX == 0 && mSrcY == mDstY) {
|
if (dstX == 0 && srcY == dstY) {
|
||||||
decodeRow(pStream);
|
decodeRow(pStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
int length = Math.min(mRow.length - mDstX, pBuffer.length - decoded);
|
int length = Math.min(row.length - dstX, pBuffer.length - decoded);
|
||||||
System.arraycopy(mRow, mDstX, pBuffer, decoded, length);
|
System.arraycopy(row, dstX, pBuffer, decoded, length);
|
||||||
mDstX += length;
|
dstX += length;
|
||||||
decoded += length;
|
decoded += length;
|
||||||
|
|
||||||
if (mDstX == mRow.length) {
|
if (dstX == row.length) {
|
||||||
mDstX = 0;
|
dstX = 0;
|
||||||
mDstY--;
|
dstY--;
|
||||||
|
|
||||||
// NOTE: If src Y is < dst Y, we have a delta, and have to fill the
|
// NOTE: If src Y is < dst Y, we have a delta, and have to fill the
|
||||||
// gap with zero-bytes
|
// gap with zero-bytes
|
||||||
if (mDstY > mSrcY) {
|
if (dstY > srcY) {
|
||||||
for (int i = 0; i < mRow.length; i++) {
|
for (int i = 0; i < row.length; i++) {
|
||||||
mRow[i] = 0x00;
|
row[i] = 0x00;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,9 +61,9 @@ public final class Base64Decoder implements Decoder {
|
|||||||
|
|
||||||
final static byte[] PEM_CONVERT_ARRAY;
|
final static byte[] PEM_CONVERT_ARRAY;
|
||||||
|
|
||||||
private byte[] mDecodeBuffer = new byte[4];
|
private byte[] decodeBuffer = new byte[4];
|
||||||
private ByteArrayOutputStream mWrapped;
|
private ByteArrayOutputStream wrapped;
|
||||||
private Object mWrappedObject;
|
private Object wrappedObject;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
PEM_CONVERT_ARRAY = new byte[256];
|
PEM_CONVERT_ARRAY = new byte[256];
|
||||||
@@ -116,8 +116,8 @@ public final class Base64Decoder implements Decoder {
|
|||||||
}
|
}
|
||||||
} while (read == 10 || read == 13);
|
} while (read == 10 || read == 13);
|
||||||
|
|
||||||
mDecodeBuffer[0] = (byte) read;
|
decodeBuffer[0] = (byte) read;
|
||||||
read = readFully(pInput, mDecodeBuffer, 1, pLength - 1);
|
read = readFully(pInput, decodeBuffer, 1, pLength - 1);
|
||||||
|
|
||||||
if (read == -1) {
|
if (read == -1) {
|
||||||
return false;
|
return false;
|
||||||
@@ -125,24 +125,24 @@ public final class Base64Decoder implements Decoder {
|
|||||||
|
|
||||||
int length = pLength;
|
int length = pLength;
|
||||||
|
|
||||||
if (length > 3 && mDecodeBuffer[3] == 61) {
|
if (length > 3 && decodeBuffer[3] == 61) {
|
||||||
length = 3;
|
length = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length > 2 && mDecodeBuffer[2] == 61) {
|
if (length > 2 && decodeBuffer[2] == 61) {
|
||||||
length = 2;
|
length = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (length) {
|
switch (length) {
|
||||||
case 4:
|
case 4:
|
||||||
byte3 = PEM_CONVERT_ARRAY[mDecodeBuffer[3] & 255];
|
byte3 = PEM_CONVERT_ARRAY[decodeBuffer[3] & 255];
|
||||||
// fall through
|
// fall through
|
||||||
case 3:
|
case 3:
|
||||||
byte2 = PEM_CONVERT_ARRAY[mDecodeBuffer[2] & 255];
|
byte2 = PEM_CONVERT_ARRAY[decodeBuffer[2] & 255];
|
||||||
// fall through
|
// fall through
|
||||||
case 2:
|
case 2:
|
||||||
byte1 = PEM_CONVERT_ARRAY[mDecodeBuffer[1] & 255];
|
byte1 = PEM_CONVERT_ARRAY[decodeBuffer[1] & 255];
|
||||||
byte0 = PEM_CONVERT_ARRAY[mDecodeBuffer[0] & 255];
|
byte0 = PEM_CONVERT_ARRAY[decodeBuffer[0] & 255];
|
||||||
// fall through
|
// fall through
|
||||||
default:
|
default:
|
||||||
switch (length) {
|
switch (length) {
|
||||||
@@ -185,15 +185,15 @@ public final class Base64Decoder implements Decoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int decode(final InputStream pStream, final byte[] pBuffer) throws IOException {
|
public int decode(final InputStream pStream, final byte[] pBuffer) throws IOException {
|
||||||
if (mWrappedObject != pBuffer) {
|
if (wrappedObject != pBuffer) {
|
||||||
// NOTE: Array not cloned in FastByteArrayOutputStream
|
// NOTE: Array not cloned in FastByteArrayOutputStream
|
||||||
mWrapped = new FastByteArrayOutputStream(pBuffer);
|
wrapped = new FastByteArrayOutputStream(pBuffer);
|
||||||
mWrappedObject = pBuffer;
|
wrappedObject = pBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
mWrapped.reset(); // NOTE: This only resets count to 0
|
wrapped.reset(); // NOTE: This only resets count to 0
|
||||||
decodeBuffer(pStream, mWrapped, pBuffer.length);
|
decodeBuffer(pStream, wrapped, pBuffer.length);
|
||||||
|
|
||||||
return mWrapped.size();
|
return wrapped.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ package com.twelvemonkeys.io.enc;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thrown by {@code Decoder}s when encoded data can not be decocded.
|
* Thrown by {@code Decoder}s when encoded data can not be decoded.
|
||||||
* <p/>
|
* <p/>
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
|||||||
@@ -47,13 +47,13 @@ import java.io.IOException;
|
|||||||
public interface Decoder {
|
public interface Decoder {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes up to {@code pBuffer.length} bytes from the given inputstream,
|
* Decodes up to {@code pBuffer.length} bytes from the given input stream,
|
||||||
* into the given buffer.
|
* into the given buffer.
|
||||||
*
|
*
|
||||||
* @param pStream the inputstream to decode data from
|
* @param pStream the input stream to decode data from
|
||||||
* @param pBuffer buffer to store the read data
|
* @param pBuffer buffer to store the read data
|
||||||
*
|
*
|
||||||
* @return the total number of bytes read into the buffer, or {@code -1}
|
* @return the total number of bytes read into the buffer, or {@code 0}
|
||||||
* if there is no more data because the end of the stream has been reached.
|
* if there is no more data because the end of the stream has been reached.
|
||||||
*
|
*
|
||||||
* @throws DecodeException if encoded data is corrupt
|
* @throws DecodeException if encoded data is corrupt
|
||||||
|
|||||||
@@ -44,10 +44,10 @@ import java.io.FilterInputStream;
|
|||||||
*/
|
*/
|
||||||
public final class DecoderStream extends FilterInputStream {
|
public final class DecoderStream extends FilterInputStream {
|
||||||
|
|
||||||
protected int mBufferPos;
|
protected int bufferPos;
|
||||||
protected int mBufferLimit;
|
protected int bufferLimit;
|
||||||
protected final byte[] mBuffer;
|
protected final byte[] buffer;
|
||||||
protected final Decoder mDecoder;
|
protected final Decoder decoder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new decoder stream and chains it to the
|
* Creates a new decoder stream and chains it to the
|
||||||
@@ -76,26 +76,26 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
*/
|
*/
|
||||||
public DecoderStream(final InputStream pStream, final Decoder pDecoder, final int pBufferSize) {
|
public DecoderStream(final InputStream pStream, final Decoder pDecoder, final int pBufferSize) {
|
||||||
super(pStream);
|
super(pStream);
|
||||||
mDecoder = pDecoder;
|
decoder = pDecoder;
|
||||||
mBuffer = new byte[pBufferSize];
|
buffer = new byte[pBufferSize];
|
||||||
mBufferPos = 0;
|
bufferPos = 0;
|
||||||
mBufferLimit = 0;
|
bufferLimit = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int available() throws IOException {
|
public int available() throws IOException {
|
||||||
return mBufferLimit - mBufferPos + super.available();
|
return bufferLimit - bufferPos + super.available();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
if (mBufferPos == mBufferLimit) {
|
if (bufferPos == bufferLimit) {
|
||||||
mBufferLimit = fill();
|
bufferLimit = fill();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mBufferLimit < 0) {
|
if (bufferLimit < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mBuffer[mBufferPos++] & 0xff;
|
return buffer[bufferPos++] & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read(final byte pBytes[]) throws IOException {
|
public int read(final byte pBytes[]) throws IOException {
|
||||||
@@ -115,7 +115,7 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// End of file?
|
// End of file?
|
||||||
if ((mBufferLimit - mBufferPos) < 0) {
|
if ((bufferLimit - bufferPos) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,26 +124,26 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
int off = pOffset;
|
int off = pOffset;
|
||||||
|
|
||||||
while (pLength > count) {
|
while (pLength > count) {
|
||||||
int avail = mBufferLimit - mBufferPos;
|
int avail = bufferLimit - bufferPos;
|
||||||
|
|
||||||
if (avail <= 0) {
|
if (avail <= 0) {
|
||||||
mBufferLimit = fill();
|
bufferLimit = fill();
|
||||||
|
|
||||||
if (mBufferLimit < 0) {
|
if (bufferLimit < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy as many bytes as possible
|
// Copy as many bytes as possible
|
||||||
int dstLen = Math.min(pLength - count, avail);
|
int dstLen = Math.min(pLength - count, avail);
|
||||||
System.arraycopy(mBuffer, mBufferPos, pBytes, off, dstLen);
|
System.arraycopy(buffer, bufferPos, pBytes, off, dstLen);
|
||||||
|
|
||||||
mBufferPos += dstLen;
|
bufferPos += dstLen;
|
||||||
|
|
||||||
// Update offset (rest)
|
// Update offset (rest)
|
||||||
off += dstLen;
|
off += dstLen;
|
||||||
|
|
||||||
// Inrease count
|
// Increase count
|
||||||
count += dstLen;
|
count += dstLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
|
|
||||||
public long skip(final long pLength) throws IOException {
|
public long skip(final long pLength) throws IOException {
|
||||||
// End of file?
|
// End of file?
|
||||||
if (mBufferLimit - mBufferPos < 0) {
|
if (bufferLimit - bufferPos < 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,12 +160,12 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
long total = 0;
|
long total = 0;
|
||||||
|
|
||||||
while (total < pLength) {
|
while (total < pLength) {
|
||||||
int avail = mBufferLimit - mBufferPos;
|
int avail = bufferLimit - bufferPos;
|
||||||
|
|
||||||
if (avail == 0) {
|
if (avail == 0) {
|
||||||
mBufferLimit = fill();
|
bufferLimit = fill();
|
||||||
|
|
||||||
if (mBufferLimit < 0) {
|
if (bufferLimit < 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,7 +174,7 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
// an int, so the cast is safe
|
// an int, so the cast is safe
|
||||||
int skipped = (int) Math.min(pLength - total, avail);
|
int skipped = (int) Math.min(pLength - total, avail);
|
||||||
|
|
||||||
mBufferPos += skipped; // Just skip these bytes
|
bufferPos += skipped; // Just skip these bytes
|
||||||
total += skipped;
|
total += skipped;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,19 +190,19 @@ public final class DecoderStream extends FilterInputStream {
|
|||||||
* @throws IOException if an I/O error occurs
|
* @throws IOException if an I/O error occurs
|
||||||
*/
|
*/
|
||||||
protected int fill() throws IOException {
|
protected int fill() throws IOException {
|
||||||
int read = mDecoder.decode(in, mBuffer);
|
int read = decoder.decode(in, buffer);
|
||||||
|
|
||||||
// TODO: Enforce this in test case, leave here to aid debugging
|
// TODO: Enforce this in test case, leave here to aid debugging
|
||||||
if (read > mBuffer.length) {
|
if (read > buffer.length) {
|
||||||
throw new AssertionError(
|
throw new AssertionError(
|
||||||
String.format(
|
String.format(
|
||||||
"Decode beyond buffer (%d): %d (using %s decoder)",
|
"Decode beyond buffer (%d): %d (using %s decoder)",
|
||||||
mBuffer.length, read, mDecoder.getClass().getName()
|
buffer.length, read, decoder.getClass().getName()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
mBufferPos = 0;
|
bufferPos = 0;
|
||||||
|
|
||||||
if (read == 0) {
|
if (read == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
|
|||||||
@@ -57,8 +57,7 @@ public interface Encoder {
|
|||||||
*
|
*
|
||||||
* @throws java.io.IOException if an I/O error occurs
|
* @throws java.io.IOException if an I/O error occurs
|
||||||
*/
|
*/
|
||||||
void encode(OutputStream pStream, byte[] pBuffer, int pOffset, int pLength)
|
void encode(OutputStream pStream, byte[] pBuffer, int pOffset, int pLength) throws IOException;
|
||||||
throws IOException;
|
|
||||||
|
|
||||||
//TODO: int requiredBufferSize(): -1 == any, otherwise, use this buffer size
|
//TODO: int requiredBufferSize(): -1 == any, otherwise, use this buffer size
|
||||||
// void flush()?
|
// void flush()?
|
||||||
|
|||||||
@@ -44,11 +44,11 @@ import java.io.IOException;
|
|||||||
*/
|
*/
|
||||||
public final class EncoderStream extends FilterOutputStream {
|
public final class EncoderStream extends FilterOutputStream {
|
||||||
|
|
||||||
protected final Encoder mEncoder;
|
protected final Encoder encoder;
|
||||||
private final boolean mFlushOnWrite;
|
private final boolean flushOnWrite;
|
||||||
|
|
||||||
protected int mBufferPos;
|
protected int bufferPos;
|
||||||
protected final byte[] mBuffer;
|
protected final byte[] buffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an output stream filter built on top of the specified
|
* Creates an output stream filter built on top of the specified
|
||||||
@@ -73,11 +73,11 @@ public final class EncoderStream extends FilterOutputStream {
|
|||||||
public EncoderStream(final OutputStream pStream, final Encoder pEncoder, final boolean pFlushOnWrite) {
|
public EncoderStream(final OutputStream pStream, final Encoder pEncoder, final boolean pFlushOnWrite) {
|
||||||
super(pStream);
|
super(pStream);
|
||||||
|
|
||||||
mEncoder = pEncoder;
|
encoder = pEncoder;
|
||||||
mFlushOnWrite = pFlushOnWrite;
|
flushOnWrite = pFlushOnWrite;
|
||||||
|
|
||||||
mBuffer = new byte[1024];
|
buffer = new byte[1024];
|
||||||
mBufferPos = 0;
|
bufferPos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
@@ -91,12 +91,12 @@ public final class EncoderStream extends FilterOutputStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void encodeBuffer() throws IOException {
|
private void encodeBuffer() throws IOException {
|
||||||
if (mBufferPos != 0) {
|
if (bufferPos != 0) {
|
||||||
// Make sure all remaining data in buffer is written to the stream
|
// Make sure all remaining data in buffer is written to the stream
|
||||||
mEncoder.encode(out, mBuffer, 0, mBufferPos);
|
encoder.encode(out, buffer, 0, bufferPos);
|
||||||
|
|
||||||
// Reset buffer
|
// Reset buffer
|
||||||
mBufferPos = 0;
|
bufferPos = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,27 +109,25 @@ public final class EncoderStream extends FilterOutputStream {
|
|||||||
// that the encoder can't buffer. In that case, the encoder should probably
|
// that the encoder can't buffer. In that case, the encoder should probably
|
||||||
// tell the EncoderStream how large buffer it prefers...
|
// tell the EncoderStream how large buffer it prefers...
|
||||||
public void write(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
public void write(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
|
||||||
if (!mFlushOnWrite && mBufferPos + pLength < mBuffer.length) {
|
if (!flushOnWrite && bufferPos + pLength < buffer.length) {
|
||||||
// Buffer data
|
// Buffer data
|
||||||
System.arraycopy(pBytes, pOffset, mBuffer, mBufferPos, pLength);
|
System.arraycopy(pBytes, pOffset, buffer, bufferPos, pLength);
|
||||||
mBufferPos += pLength;
|
bufferPos += pLength;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Encode data already in the buffer
|
// Encode data already in the buffer
|
||||||
if (mBufferPos != 0) {
|
encodeBuffer();
|
||||||
encodeBuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode rest without buffering
|
// Encode rest without buffering
|
||||||
mEncoder.encode(out, pBytes, pOffset, pLength);
|
encoder.encode(out, pBytes, pOffset, pLength);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(final int pByte) throws IOException {
|
public void write(final int pByte) throws IOException {
|
||||||
if (mBufferPos >= mBuffer.length - 1) {
|
if (bufferPos >= buffer.length - 1) {
|
||||||
encodeBuffer(); // Resets mBufferPos to 0
|
encodeBuffer(); // Resets bufferPos to 0
|
||||||
}
|
}
|
||||||
|
|
||||||
mBuffer[mBufferPos++] = (byte) pByte;
|
buffer[bufferPos++] = (byte) pByte;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,11 +46,11 @@ import java.io.EOFException;
|
|||||||
*/
|
*/
|
||||||
public final class PackBits16Decoder implements Decoder {
|
public final class PackBits16Decoder implements Decoder {
|
||||||
// TODO: Refactor this into an option for the PackBitsDecoder?
|
// TODO: Refactor this into an option for the PackBitsDecoder?
|
||||||
private final boolean mDisableNoop;
|
private final boolean disableNoop;
|
||||||
|
|
||||||
private int mLeftOfRun;
|
private int leftOfRun;
|
||||||
private boolean mSplitRun;
|
private boolean splitRun;
|
||||||
private boolean mEOF;
|
private boolean reachedEOF;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code PackBitsDecoder}.
|
* Creates a {@code PackBitsDecoder}.
|
||||||
@@ -71,7 +71,7 @@ public final class PackBits16Decoder implements Decoder {
|
|||||||
* @param pDisableNoop {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
|
* @param pDisableNoop {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
|
||||||
*/
|
*/
|
||||||
public PackBits16Decoder(final boolean pDisableNoop) {
|
public PackBits16Decoder(final boolean pDisableNoop) {
|
||||||
mDisableNoop = pDisableNoop;
|
disableNoop = pDisableNoop;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -85,7 +85,7 @@ public final class PackBits16Decoder implements Decoder {
|
|||||||
* @throws java.io.IOException
|
* @throws java.io.IOException
|
||||||
*/
|
*/
|
||||||
public int decode(final InputStream pStream, final byte[] pBuffer) throws IOException {
|
public int decode(final InputStream pStream, final byte[] pBuffer) throws IOException {
|
||||||
if (mEOF) {
|
if (reachedEOF) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,16 +95,16 @@ public final class PackBits16Decoder implements Decoder {
|
|||||||
while (read < max) {
|
while (read < max) {
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
if (mSplitRun) {
|
if (splitRun) {
|
||||||
// Continue run
|
// Continue run
|
||||||
n = mLeftOfRun;
|
n = leftOfRun;
|
||||||
mSplitRun = false;
|
splitRun = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Start new run
|
// Start new run
|
||||||
int b = pStream.read();
|
int b = pStream.read();
|
||||||
if (b < 0) {
|
if (b < 0) {
|
||||||
mEOF = true;
|
reachedEOF = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
n = (byte) b;
|
n = (byte) b;
|
||||||
@@ -112,13 +112,13 @@ public final class PackBits16Decoder implements Decoder {
|
|||||||
|
|
||||||
// Split run at or before max
|
// Split run at or before max
|
||||||
if (n >= 0 && 2 * (n + 1) + read > max) {
|
if (n >= 0 && 2 * (n + 1) + read > max) {
|
||||||
mLeftOfRun = n;
|
leftOfRun = n;
|
||||||
mSplitRun = true;
|
splitRun = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (n < 0 && 2 * (-n + 1) + read > max) {
|
else if (n < 0 && 2 * (-n + 1) + read > max) {
|
||||||
mLeftOfRun = n;
|
leftOfRun = n;
|
||||||
mSplitRun = true;
|
splitRun = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,7 +130,7 @@ public final class PackBits16Decoder implements Decoder {
|
|||||||
read += len;
|
read += len;
|
||||||
}
|
}
|
||||||
// Allow -128 for compatibility, see above
|
// Allow -128 for compatibility, see above
|
||||||
else if (mDisableNoop || n != -128) {
|
else if (disableNoop || n != -128) {
|
||||||
// Replicate the next short -n + 1 times
|
// Replicate the next short -n + 1 times
|
||||||
byte value1 = readByte(pStream);
|
byte value1 = readByte(pStream);
|
||||||
byte value2 = readByte(pStream);
|
byte value2 = readByte(pStream);
|
||||||
|
|||||||
@@ -28,9 +28,9 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.EOFException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decoder implementation for Apple PackBits run-length encoding.
|
* Decoder implementation for Apple PackBits run-length encoding.
|
||||||
@@ -63,11 +63,13 @@ import java.io.EOFException;
|
|||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/PackBitsDecoder.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/PackBitsDecoder.java#1 $
|
||||||
*/
|
*/
|
||||||
public final class PackBitsDecoder implements Decoder {
|
public final class PackBitsDecoder implements Decoder {
|
||||||
private final boolean mDisableNoop;
|
// TODO: Look at ICNSImageReader#unpackbits... What is this weirdness?
|
||||||
|
|
||||||
private int mLeftOfRun;
|
private final boolean disableNoop;
|
||||||
private boolean mSplitRun;
|
|
||||||
private boolean mEOF;
|
private int leftOfRun;
|
||||||
|
private boolean splitRun;
|
||||||
|
private boolean reachedEOF;
|
||||||
|
|
||||||
/** Creates a {@code PackBitsDecoder}. */
|
/** Creates a {@code PackBitsDecoder}. */
|
||||||
public PackBitsDecoder() {
|
public PackBitsDecoder() {
|
||||||
@@ -78,29 +80,26 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
* Creates a {@code PackBitsDecoder}, with optional compatibility mode.
|
* Creates a {@code PackBitsDecoder}, with optional compatibility mode.
|
||||||
* <p/>
|
* <p/>
|
||||||
* As some implementations of PackBits-like encoders treat {@code -128} as length of
|
* As some implementations of PackBits-like encoders treat {@code -128} as length of
|
||||||
* a compressed run, instead of a no-op, it's possible to disable no-ops
|
* a compressed run, instead of a no-op, it's possible to disable no-ops for compatibility.
|
||||||
* for compatibility.
|
* Should be used with caution, even though, most known encoders never write no-ops in the compressed streams.
|
||||||
* Should be used with caution, even though, most known encoders never write
|
|
||||||
* no-ops in the compressed streams.
|
|
||||||
*
|
*
|
||||||
* @param pDisableNoop {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
|
* @param pDisableNoop {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
|
||||||
*/
|
*/
|
||||||
public PackBitsDecoder(final boolean pDisableNoop) {
|
public PackBitsDecoder(final boolean pDisableNoop) {
|
||||||
mDisableNoop = pDisableNoop;
|
disableNoop = pDisableNoop;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes bytes from the given input stream, to the given buffer.
|
* Decodes bytes from the given input stream, to the given buffer.
|
||||||
*
|
*
|
||||||
* @param pStream the stream to decode from
|
* @param pStream the stream to decode from
|
||||||
* @param pBuffer a byte array, minimum 128 (or 129 if no-op is disabled)
|
* @param pBuffer a byte array, minimum 128 (or 129 if no-op is disabled) bytes long
|
||||||
* bytes long
|
|
||||||
* @return The number of bytes decoded
|
* @return The number of bytes decoded
|
||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws java.io.IOException
|
||||||
*/
|
*/
|
||||||
public int decode(final InputStream pStream, final byte[] pBuffer) throws IOException {
|
public int decode(final InputStream pStream, final byte[] pBuffer) throws IOException {
|
||||||
if (mEOF) {
|
if (reachedEOF) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,16 +110,16 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
while (read < max) {
|
while (read < max) {
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
if (mSplitRun) {
|
if (splitRun) {
|
||||||
// Continue run
|
// Continue run
|
||||||
n = mLeftOfRun;
|
n = leftOfRun;
|
||||||
mSplitRun = false;
|
splitRun = false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Start new run
|
// Start new run
|
||||||
int b = pStream.read();
|
int b = pStream.read();
|
||||||
if (b < 0) {
|
if (b < 0) {
|
||||||
mEOF = true;
|
reachedEOF = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
n = (byte) b;
|
n = (byte) b;
|
||||||
@@ -128,13 +127,13 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
|
|
||||||
// Split run at or before max
|
// Split run at or before max
|
||||||
if (n >= 0 && n + 1 + read > max) {
|
if (n >= 0 && n + 1 + read > max) {
|
||||||
mLeftOfRun = n;
|
leftOfRun = n;
|
||||||
mSplitRun = true;
|
splitRun = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if (n < 0 && -n + 1 + read > max) {
|
else if (n < 0 && -n + 1 + read > max) {
|
||||||
mLeftOfRun = n;
|
leftOfRun = n;
|
||||||
mSplitRun = true;
|
splitRun = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -146,7 +145,7 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
read += n + 1;
|
read += n + 1;
|
||||||
}
|
}
|
||||||
// Allow -128 for compatibility, see above
|
// Allow -128 for compatibility, see above
|
||||||
else if (mDisableNoop || n != -128) {
|
else if (disableNoop || n != -128) {
|
||||||
// Replicate the next byte -n + 1 times
|
// Replicate the next byte -n + 1 times
|
||||||
byte value = readByte(pStream);
|
byte value = readByte(pStream);
|
||||||
|
|
||||||
@@ -164,7 +163,7 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte readByte(final InputStream pStream) throws IOException {
|
static byte readByte(final InputStream pStream) throws IOException {
|
||||||
int read = pStream.read();
|
int read = pStream.read();
|
||||||
|
|
||||||
if (read < 0) {
|
if (read < 0) {
|
||||||
@@ -174,21 +173,21 @@ public final class PackBitsDecoder implements Decoder {
|
|||||||
return (byte) read;
|
return (byte) read;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void readFully(final InputStream pStream, final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
static void readFully(final InputStream pStream, final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
|
||||||
if (pLength < 0) {
|
if (pLength < 0) {
|
||||||
throw new IndexOutOfBoundsException();
|
throw new IndexOutOfBoundsException(String.format("Negative length: %d", pLength));
|
||||||
}
|
}
|
||||||
|
|
||||||
int read = 0;
|
int total = 0;
|
||||||
|
|
||||||
while (read < pLength) {
|
while (total < pLength) {
|
||||||
int count = pStream.read(pBuffer, pOffset + read, pLength - read);
|
int count = pStream.read(pBuffer, pOffset + total, pLength - total);
|
||||||
|
|
||||||
if (count < 0) {
|
if (count < 0) {
|
||||||
throw new EOFException("Unexpected end of PackBits stream");
|
throw new EOFException("Unexpected end of PackBits stream");
|
||||||
}
|
}
|
||||||
|
|
||||||
read += count;
|
total += count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ import java.io.IOException;
|
|||||||
*/
|
*/
|
||||||
public final class PackBitsEncoder implements Encoder {
|
public final class PackBitsEncoder implements Encoder {
|
||||||
|
|
||||||
final private byte[] mBuffer = new byte[128];
|
final private byte[] buffer = new byte[128];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code PackBitsEncoder}.
|
* Creates a {@code PackBitsEncoder}.
|
||||||
@@ -101,17 +101,17 @@ public final class PackBitsEncoder implements Encoder {
|
|||||||
run = 0;
|
run = 0;
|
||||||
while ((run < 128 && ((offset < max && pBuffer[offset] != pBuffer[offset + 1])
|
while ((run < 128 && ((offset < max && pBuffer[offset] != pBuffer[offset + 1])
|
||||||
|| (offset < maxMinus1 && pBuffer[offset] != pBuffer[offset + 2])))) {
|
|| (offset < maxMinus1 && pBuffer[offset] != pBuffer[offset + 2])))) {
|
||||||
mBuffer[run++] = pBuffer[offset++];
|
buffer[run++] = pBuffer[offset++];
|
||||||
}
|
}
|
||||||
|
|
||||||
// If last byte, include it in literal run, if space
|
// If last byte, include it in literal run, if space
|
||||||
if (offset == max && run > 0 && run < 128) {
|
if (offset == max && run > 0 && run < 128) {
|
||||||
mBuffer[run++] = pBuffer[offset++];
|
buffer[run++] = pBuffer[offset++];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (run > 0) {
|
if (run > 0) {
|
||||||
pStream.write(run - 1);
|
pStream.write(run - 1);
|
||||||
pStream.write(mBuffer, 0, run);
|
pStream.write(buffer, 0, run);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If last byte, and not space, start new literal run
|
// If last byte, and not space, start new literal run
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ final class RLE4Decoder extends AbstractRLEDecoder {
|
|||||||
int deltaX = 0;
|
int deltaX = 0;
|
||||||
int deltaY = 0;
|
int deltaY = 0;
|
||||||
|
|
||||||
while (mSrcY >= 0) {
|
while (srcY >= 0) {
|
||||||
int byte1 = pInput.read();
|
int byte1 = pInput.read();
|
||||||
int byte2 = checkEOF(pInput.read());
|
int byte2 = checkEOF(pInput.read());
|
||||||
|
|
||||||
@@ -58,20 +58,20 @@ final class RLE4Decoder extends AbstractRLEDecoder {
|
|||||||
case 0x00:
|
case 0x00:
|
||||||
// End of line
|
// End of line
|
||||||
// NOTE: Some BMPs have double EOLs..
|
// NOTE: Some BMPs have double EOLs..
|
||||||
if (mSrcX != 0) {
|
if (srcX != 0) {
|
||||||
mSrcX = mRow.length;
|
srcX = row.length;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x01:
|
case 0x01:
|
||||||
// End of bitmap
|
// End of bitmap
|
||||||
mSrcX = mRow.length;
|
srcX = row.length;
|
||||||
mSrcY = 0;
|
srcY = 0;
|
||||||
break;
|
break;
|
||||||
case 0x02:
|
case 0x02:
|
||||||
// Delta
|
// Delta
|
||||||
deltaX = mSrcX + pInput.read();
|
deltaX = srcX + pInput.read();
|
||||||
deltaY = mSrcY - checkEOF(pInput.read());
|
deltaY = srcY - checkEOF(pInput.read());
|
||||||
mSrcX = mRow.length;
|
srcX = row.length;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Absolute mode
|
// Absolute mode
|
||||||
@@ -82,13 +82,13 @@ final class RLE4Decoder extends AbstractRLEDecoder {
|
|||||||
boolean paddingByte = (((byte2 + 1) / 2) % 2) != 0;
|
boolean paddingByte = (((byte2 + 1) / 2) % 2) != 0;
|
||||||
while (byte2 > 1) {
|
while (byte2 > 1) {
|
||||||
int packed = checkEOF(pInput.read());
|
int packed = checkEOF(pInput.read());
|
||||||
mRow[mSrcX++] = (byte) packed;
|
row[srcX++] = (byte) packed;
|
||||||
byte2 -= 2;
|
byte2 -= 2;
|
||||||
}
|
}
|
||||||
if (byte2 == 1) {
|
if (byte2 == 1) {
|
||||||
// TODO: Half byte alignment? Seems to be ok...
|
// TODO: Half byte alignment? Seems to be ok...
|
||||||
int packed = checkEOF(pInput.read());
|
int packed = checkEOF(pInput.read());
|
||||||
mRow[mSrcX++] = (byte) (packed & 0xf0);
|
row[srcX++] = (byte) (packed & 0xf0);
|
||||||
}
|
}
|
||||||
if (paddingByte) {
|
if (paddingByte) {
|
||||||
checkEOF(pInput.read());
|
checkEOF(pInput.read());
|
||||||
@@ -100,24 +100,24 @@ final class RLE4Decoder extends AbstractRLEDecoder {
|
|||||||
// Encoded mode
|
// Encoded mode
|
||||||
// Replicate the two samples in byte2 as many times as byte1 says
|
// Replicate the two samples in byte2 as many times as byte1 says
|
||||||
while (byte1 > 1) {
|
while (byte1 > 1) {
|
||||||
mRow[mSrcX++] = (byte) byte2;
|
row[srcX++] = (byte) byte2;
|
||||||
byte1 -= 2;
|
byte1 -= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (byte1 == 1) {
|
if (byte1 == 1) {
|
||||||
// TODO: Half byte alignment? Seems to be ok...
|
// TODO: Half byte alignment? Seems to be ok...
|
||||||
mRow[mSrcX++] = (byte) (byte2 & 0xf0);
|
row[srcX++] = (byte) (byte2 & 0xf0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're done with a complete row, copy the data
|
// If we're done with a complete row, copy the data
|
||||||
if (mSrcX == mRow.length) {
|
if (srcX == row.length) {
|
||||||
// Move to new position, either absolute (delta) or next line
|
// Move to new position, either absolute (delta) or next line
|
||||||
if (deltaX != 0 || deltaY != 0) {
|
if (deltaX != 0 || deltaY != 0) {
|
||||||
mSrcX = (deltaX + 1) / 2;
|
srcX = (deltaX + 1) / 2;
|
||||||
|
|
||||||
if (deltaY > mSrcY) {
|
if (deltaY > srcY) {
|
||||||
mSrcY = deltaY;
|
srcY = deltaY;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,8 +125,8 @@ final class RLE4Decoder extends AbstractRLEDecoder {
|
|||||||
deltaY = 0;
|
deltaY = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mSrcX = 0;
|
srcX = 0;
|
||||||
mSrcY--;
|
srcY--;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ final class RLE8Decoder extends AbstractRLEDecoder {
|
|||||||
int deltaX = 0;
|
int deltaX = 0;
|
||||||
int deltaY = 0;
|
int deltaY = 0;
|
||||||
|
|
||||||
while (mSrcY >= 0) {
|
while (srcY >= 0) {
|
||||||
int byte1 = pInput.read();
|
int byte1 = pInput.read();
|
||||||
int byte2 = checkEOF(pInput.read());
|
int byte2 = checkEOF(pInput.read());
|
||||||
|
|
||||||
@@ -58,27 +58,27 @@ final class RLE8Decoder extends AbstractRLEDecoder {
|
|||||||
case 0x00:
|
case 0x00:
|
||||||
// End of line
|
// End of line
|
||||||
// NOTE: Some BMPs have double EOLs..
|
// NOTE: Some BMPs have double EOLs..
|
||||||
if (mSrcX != 0) {
|
if (srcX != 0) {
|
||||||
mSrcX = mRow.length;
|
srcX = row.length;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x01:
|
case 0x01:
|
||||||
// End of bitmap
|
// End of bitmap
|
||||||
mSrcX = mRow.length;
|
srcX = row.length;
|
||||||
mSrcY = 0;
|
srcY = 0;
|
||||||
break;
|
break;
|
||||||
case 0x02:
|
case 0x02:
|
||||||
// Delta
|
// Delta
|
||||||
deltaX = mSrcX + pInput.read();
|
deltaX = srcX + pInput.read();
|
||||||
deltaY = mSrcY - checkEOF(pInput.read());
|
deltaY = srcY - checkEOF(pInput.read());
|
||||||
mSrcX = mRow.length;
|
srcX = row.length;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Absolute mode
|
// Absolute mode
|
||||||
// Copy the next byte2 (3..255) bytes from file to output
|
// Copy the next byte2 (3..255) bytes from file to output
|
||||||
boolean paddingByte = (byte2 % 2) != 0;
|
boolean paddingByte = (byte2 % 2) != 0;
|
||||||
while (byte2-- > 0) {
|
while (byte2-- > 0) {
|
||||||
mRow[mSrcX++] = (byte) checkEOF(pInput.read());
|
row[srcX++] = (byte) checkEOF(pInput.read());
|
||||||
}
|
}
|
||||||
if (paddingByte) {
|
if (paddingByte) {
|
||||||
checkEOF(pInput.read());
|
checkEOF(pInput.read());
|
||||||
@@ -90,26 +90,26 @@ final class RLE8Decoder extends AbstractRLEDecoder {
|
|||||||
// Replicate byte2 as many times as byte1 says
|
// Replicate byte2 as many times as byte1 says
|
||||||
byte value = (byte) byte2;
|
byte value = (byte) byte2;
|
||||||
while (byte1-- > 0) {
|
while (byte1-- > 0) {
|
||||||
mRow[mSrcX++] = value;
|
row[srcX++] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're done with a complete row, copy the data
|
// If we're done with a complete row, copy the data
|
||||||
if (mSrcX == mRow.length) {
|
if (srcX == row.length) {
|
||||||
|
|
||||||
// Move to new position, either absolute (delta) or next line
|
// Move to new position, either absolute (delta) or next line
|
||||||
if (deltaX != 0 || deltaY != 0) {
|
if (deltaX != 0 || deltaY != 0) {
|
||||||
mSrcX = deltaX;
|
srcX = deltaX;
|
||||||
if (deltaY != mSrcY) {
|
if (deltaY != srcY) {
|
||||||
mSrcY = deltaY;
|
srcY = deltaY;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
deltaX = 0;
|
deltaX = 0;
|
||||||
deltaY = 0;
|
deltaY = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mSrcX = 0;
|
srcX = 0;
|
||||||
mSrcY--;
|
srcY--;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,41 +54,44 @@ public final class CompoundDocument {
|
|||||||
// TODO: Write support...
|
// TODO: Write support...
|
||||||
// TODO: Properties: http://support.microsoft.com/kb/186898
|
// TODO: Properties: http://support.microsoft.com/kb/186898
|
||||||
|
|
||||||
private static final byte[] MAGIC = new byte[]{
|
static final byte[] MAGIC = new byte[]{
|
||||||
(byte) 0xD0, (byte) 0xCF, (byte) 0x11, (byte) 0xE0,
|
(byte) 0xD0, (byte) 0xCF, (byte) 0x11, (byte) 0xE0,
|
||||||
(byte) 0xA1, (byte) 0xB1, (byte) 0x1A, (byte) 0xE1,
|
(byte) 0xA1, (byte) 0xB1, (byte) 0x1A, (byte) 0xE1,
|
||||||
};
|
};
|
||||||
public static final int HEADER_SIZE = 512;
|
|
||||||
|
|
||||||
private final DataInput mInput;
|
|
||||||
|
|
||||||
private UUID mUID;
|
|
||||||
|
|
||||||
private int mSectorSize;
|
|
||||||
private int mShortSectorSize;
|
|
||||||
|
|
||||||
private int mDirectorySId;
|
|
||||||
|
|
||||||
private int mMinStreamSize;
|
|
||||||
|
|
||||||
private int mShortSATSID;
|
|
||||||
private int mShortSATSize;
|
|
||||||
|
|
||||||
// Master Sector Allocation Table
|
|
||||||
private int[] mMasterSAT;
|
|
||||||
private int[] mSAT;
|
|
||||||
private int[] mShortSAT;
|
|
||||||
|
|
||||||
private Entry mRootEntry;
|
|
||||||
private SIdChain mShortStreamSIdChain;
|
|
||||||
private SIdChain mDirectorySIdChain;
|
|
||||||
|
|
||||||
private static final int END_OF_CHAIN_SID = -2;
|
|
||||||
private static final int FREE_SID = -1;
|
private static final int FREE_SID = -1;
|
||||||
|
private static final int END_OF_CHAIN_SID = -2;
|
||||||
|
private static final int SAT_SECTOR_SID = -3; // Sector used by SAT
|
||||||
|
private static final int MSAT_SECTOR_SID = -4; // Sector used my Master SAT
|
||||||
|
|
||||||
|
public static final int HEADER_SIZE = 512;
|
||||||
|
|
||||||
/** The epoch offset of CompoundDocument time stamps */
|
/** The epoch offset of CompoundDocument time stamps */
|
||||||
public final static long EPOCH_OFFSET = -11644477200000L;
|
public final static long EPOCH_OFFSET = -11644477200000L;
|
||||||
|
|
||||||
|
private final DataInput input;
|
||||||
|
|
||||||
|
private UUID uUID;
|
||||||
|
|
||||||
|
private int sectorSize;
|
||||||
|
private int shortSectorSize;
|
||||||
|
|
||||||
|
private int directorySId;
|
||||||
|
|
||||||
|
private int minStreamSize;
|
||||||
|
|
||||||
|
private int shortSATSId;
|
||||||
|
private int shortSATSize;
|
||||||
|
|
||||||
|
// Master Sector Allocation Table
|
||||||
|
private int[] masterSAT;
|
||||||
|
private int[] SAT;
|
||||||
|
private int[] shortSAT;
|
||||||
|
|
||||||
|
private Entry rootEntry;
|
||||||
|
private SIdChain shortStreamSIdChain;
|
||||||
|
private SIdChain directorySIdChain;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a (for now) read only {@code CompoundDocument}.
|
* Creates a (for now) read only {@code CompoundDocument}.
|
||||||
*
|
*
|
||||||
@@ -97,7 +100,7 @@ public final class CompoundDocument {
|
|||||||
* @throws IOException if an I/O exception occurs while reading the header
|
* @throws IOException if an I/O exception occurs while reading the header
|
||||||
*/
|
*/
|
||||||
public CompoundDocument(final File pFile) throws IOException {
|
public CompoundDocument(final File pFile) throws IOException {
|
||||||
mInput = new LittleEndianRandomAccessFile(FileUtil.resolve(pFile), "r");
|
input = new LittleEndianRandomAccessFile(FileUtil.resolve(pFile), "r");
|
||||||
|
|
||||||
// TODO: Might be better to read header on first read operation?!
|
// TODO: Might be better to read header on first read operation?!
|
||||||
// OTOH: It's also good to be fail-fast, so at least we should make
|
// OTOH: It's also good to be fail-fast, so at least we should make
|
||||||
@@ -118,7 +121,7 @@ public final class CompoundDocument {
|
|||||||
|
|
||||||
// For testing only, consider exposing later
|
// For testing only, consider exposing later
|
||||||
CompoundDocument(final SeekableInputStream pInput) throws IOException {
|
CompoundDocument(final SeekableInputStream pInput) throws IOException {
|
||||||
mInput = new SeekableLittleEndianDataInputStream(pInput);
|
input = new SeekableLittleEndianDataInputStream(pInput);
|
||||||
|
|
||||||
// TODO: Might be better to read header on first read operation?!
|
// TODO: Might be better to read header on first read operation?!
|
||||||
// OTOH: It's also good to be fail-fast, so at least we should make
|
// OTOH: It's also good to be fail-fast, so at least we should make
|
||||||
@@ -134,7 +137,7 @@ public final class CompoundDocument {
|
|||||||
* @throws IOException if an I/O exception occurs while reading the header
|
* @throws IOException if an I/O exception occurs while reading the header
|
||||||
*/
|
*/
|
||||||
public CompoundDocument(final ImageInputStream pInput) throws IOException {
|
public CompoundDocument(final ImageInputStream pInput) throws IOException {
|
||||||
mInput = pInput;
|
input = pInput;
|
||||||
|
|
||||||
// TODO: Might be better to read header on first read operation?!
|
// TODO: Might be better to read header on first read operation?!
|
||||||
// OTOH: It's also good to be fail-fast, so at least we should make
|
// OTOH: It's also good to be fail-fast, so at least we should make
|
||||||
@@ -210,74 +213,81 @@ public final class CompoundDocument {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void readHeader() throws IOException {
|
private void readHeader() throws IOException {
|
||||||
if (mMasterSAT != null) {
|
if (masterSAT != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!canRead(mInput, false)) {
|
if (!canRead(input, false)) {
|
||||||
throw new CorruptDocumentException("Not an OLE 2 Compound Document");
|
throw new CorruptDocumentException("Not an OLE 2 Compound Document");
|
||||||
}
|
}
|
||||||
|
|
||||||
// UID (seems to be all 0s)
|
// UID (seems to be all 0s)
|
||||||
mUID = new UUID(mInput.readLong(), mInput.readLong());
|
uUID = new UUID(input.readLong(), input.readLong());
|
||||||
|
// System.out.println("uUID: " + uUID);
|
||||||
|
|
||||||
/*int version = */mInput.readUnsignedShort();
|
// int version =
|
||||||
//System.out.println("version: " + version);
|
input.readUnsignedShort();
|
||||||
/*int revision = */mInput.readUnsignedShort();
|
// System.out.println("version: " + version);
|
||||||
//System.out.println("revision: " + revision);
|
// int revision =
|
||||||
|
input.readUnsignedShort();
|
||||||
|
// System.out.println("revision: " + revision);
|
||||||
|
|
||||||
int byteOrder = mInput.readUnsignedShort();
|
int byteOrder = input.readUnsignedShort();
|
||||||
if (byteOrder != 0xfffe) {
|
// System.out.printf("byteOrder: 0x%04x\n", byteOrder);
|
||||||
// Reversed, as I'm allready reading little-endian
|
if (byteOrder == 0xffff) {
|
||||||
throw new CorruptDocumentException("Cannot read big endian OLE 2 Compound Documents");
|
throw new CorruptDocumentException("Cannot read big endian OLE 2 Compound Documents");
|
||||||
}
|
}
|
||||||
|
else if (byteOrder != 0xfffe) {
|
||||||
|
// Reversed, as I'm already reading little-endian
|
||||||
|
throw new CorruptDocumentException(String.format("Unknown byte order marker: 0x%04x, expected 0xfffe or 0xffff", byteOrder));
|
||||||
|
}
|
||||||
|
|
||||||
mSectorSize = 1 << mInput.readUnsignedShort();
|
sectorSize = 1 << input.readUnsignedShort();
|
||||||
//System.out.println("sectorSize: " + mSectorSize + " bytes");
|
// System.out.println("sectorSize: " + sectorSize + " bytes");
|
||||||
mShortSectorSize = 1 << mInput.readUnsignedShort();
|
shortSectorSize = 1 << input.readUnsignedShort();
|
||||||
//System.out.println("shortSectorSize: " + mShortSectorSize + " bytes");
|
// System.out.println("shortSectorSize: " + shortSectorSize + " bytes");
|
||||||
|
|
||||||
// Reserved
|
// Reserved
|
||||||
if (mInput.skipBytes(10) != 10) {
|
if (skipBytesFully(10) != 10) {
|
||||||
throw new CorruptDocumentException();
|
throw new CorruptDocumentException();
|
||||||
}
|
}
|
||||||
|
|
||||||
int SATSize = mInput.readInt();
|
int SATSize = input.readInt();
|
||||||
//System.out.println("normalSATSize: " + mSATSize);
|
// System.out.println("normalSATSize: " + SATSize);
|
||||||
|
|
||||||
mDirectorySId = mInput.readInt();
|
directorySId = input.readInt();
|
||||||
//System.out.println("directorySId: " + mDirectorySId);
|
// System.out.println("directorySId: " + directorySId);
|
||||||
|
|
||||||
// Reserved
|
// Reserved
|
||||||
if (mInput.skipBytes(4) != 4) {
|
if (skipBytesFully(4) != 4) {
|
||||||
throw new CorruptDocumentException();
|
throw new CorruptDocumentException();
|
||||||
}
|
}
|
||||||
|
|
||||||
mMinStreamSize = mInput.readInt();
|
minStreamSize = input.readInt();
|
||||||
//System.out.println("minStreamSize: " + mMinStreamSize + " bytes");
|
// System.out.println("minStreamSize: " + minStreamSize + " bytes");
|
||||||
|
|
||||||
mShortSATSID = mInput.readInt();
|
shortSATSId = input.readInt();
|
||||||
//System.out.println("shortSATSID: " + mShortSATSID);
|
// System.out.println("shortSATSId: " + shortSATSId);
|
||||||
mShortSATSize = mInput.readInt();
|
shortSATSize = input.readInt();
|
||||||
//System.out.println("shortSATSize: " + mShortSATSize);
|
// System.out.println("shortSATSize: " + shortSATSize);
|
||||||
int masterSATSId = mInput.readInt();
|
int masterSATSId = input.readInt();
|
||||||
//System.out.println("masterSATSId: " + mMasterSATSID);
|
// System.out.println("masterSATSId: " + masterSATSId);
|
||||||
int masterSATSize = mInput.readInt();
|
int masterSATSize = input.readInt();
|
||||||
//System.out.println("masterSATSize: " + mMasterSATSize);
|
// System.out.println("masterSATSize: " + masterSATSize);
|
||||||
|
|
||||||
// Read masterSAT: 436 bytes, containing up to 109 SIDs
|
// Read masterSAT: 436 bytes, containing up to 109 SIDs
|
||||||
//System.out.println("MSAT:");
|
//System.out.println("MSAT:");
|
||||||
mMasterSAT = new int[SATSize];
|
masterSAT = new int[SATSize];
|
||||||
final int headerSIds = Math.min(SATSize, 109);
|
final int headerSIds = Math.min(SATSize, 109);
|
||||||
for (int i = 0; i < headerSIds; i++) {
|
for (int i = 0; i < headerSIds; i++) {
|
||||||
mMasterSAT[i] = mInput.readInt();
|
masterSAT[i] = input.readInt();
|
||||||
//System.out.println("\tSID(" + i + "): " + mMasterSAT[i]);
|
//System.out.println("\tSID(" + i + "): " + masterSAT[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (masterSATSId == END_OF_CHAIN_SID) {
|
if (masterSATSId == END_OF_CHAIN_SID) {
|
||||||
// End of chain
|
// End of chain
|
||||||
int freeSIdLength = 436 - (SATSize * 4);
|
int freeSIdLength = 436 - (SATSize * 4);
|
||||||
if (mInput.skipBytes(freeSIdLength) != freeSIdLength) {
|
if (skipBytesFully(freeSIdLength) != freeSIdLength) {
|
||||||
throw new CorruptDocumentException();
|
throw new CorruptDocumentException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -288,17 +298,17 @@ public final class CompoundDocument {
|
|||||||
int index = headerSIds;
|
int index = headerSIds;
|
||||||
for (int i = 0; i < masterSATSize; i++) {
|
for (int i = 0; i < masterSATSize; i++) {
|
||||||
for (int j = 0; j < 127; j++) {
|
for (int j = 0; j < 127; j++) {
|
||||||
int sid = mInput.readInt();
|
int sid = input.readInt();
|
||||||
switch (sid) {
|
switch (sid) {
|
||||||
case FREE_SID:// Free
|
case FREE_SID:// Free
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mMasterSAT[index++] = sid;
|
masterSAT[index++] = sid;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int next = mInput.readInt();
|
int next = input.readInt();
|
||||||
if (next == END_OF_CHAIN_SID) {// End of chain
|
if (next == END_OF_CHAIN_SID) {// End of chain
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -308,38 +318,53 @@ public final class CompoundDocument {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int skipBytesFully(final int n) throws IOException {
|
||||||
|
int toSkip = n;
|
||||||
|
|
||||||
|
while (toSkip > 0) {
|
||||||
|
int skipped = input.skipBytes(n);
|
||||||
|
if (skipped <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
toSkip -= skipped;
|
||||||
|
}
|
||||||
|
|
||||||
|
return n - toSkip;
|
||||||
|
}
|
||||||
|
|
||||||
private void readSAT() throws IOException {
|
private void readSAT() throws IOException {
|
||||||
if (mSAT != null) {
|
if (SAT != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int intsPerSector = mSectorSize / 4;
|
final int intsPerSector = sectorSize / 4;
|
||||||
|
|
||||||
// Read the Sector Allocation Table
|
// Read the Sector Allocation Table
|
||||||
mSAT = new int[mMasterSAT.length * intsPerSector];
|
SAT = new int[masterSAT.length * intsPerSector];
|
||||||
|
|
||||||
for (int i = 0; i < mMasterSAT.length; i++) {
|
for (int i = 0; i < masterSAT.length; i++) {
|
||||||
seekToSId(mMasterSAT[i], FREE_SID);
|
seekToSId(masterSAT[i], FREE_SID);
|
||||||
|
|
||||||
for (int j = 0; j < intsPerSector; j++) {
|
for (int j = 0; j < intsPerSector; j++) {
|
||||||
int nextSID = mInput.readInt();
|
int nextSID = input.readInt();
|
||||||
int index = (j + (i * intsPerSector));
|
int index = (j + (i * intsPerSector));
|
||||||
|
|
||||||
mSAT[index] = nextSID;
|
SAT[index] = nextSID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the short-stream Sector Allocation Table
|
// Read the short-stream Sector Allocation Table
|
||||||
SIdChain chain = getSIdChain(mShortSATSID, FREE_SID);
|
SIdChain chain = getSIdChain(shortSATSId, FREE_SID);
|
||||||
mShortSAT = new int[mShortSATSize * intsPerSector];
|
shortSAT = new int[shortSATSize * intsPerSector];
|
||||||
for (int i = 0; i < mShortSATSize; i++) {
|
for (int i = 0; i < shortSATSize; i++) {
|
||||||
seekToSId(chain.get(i), FREE_SID);
|
seekToSId(chain.get(i), FREE_SID);
|
||||||
|
|
||||||
for (int j = 0; j < intsPerSector; j++) {
|
for (int j = 0; j < intsPerSector; j++) {
|
||||||
int nextSID = mInput.readInt();
|
int nextSID = input.readInt();
|
||||||
int index = (j + (i * intsPerSector));
|
int index = (j + (i * intsPerSector));
|
||||||
|
|
||||||
mShortSAT[index] = nextSID;
|
shortSAT[index] = nextSID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -355,7 +380,7 @@ public final class CompoundDocument {
|
|||||||
private SIdChain getSIdChain(final int pSId, final long pStreamSize) throws IOException {
|
private SIdChain getSIdChain(final int pSId, final long pStreamSize) throws IOException {
|
||||||
SIdChain chain = new SIdChain();
|
SIdChain chain = new SIdChain();
|
||||||
|
|
||||||
int[] sat = isShortStream(pStreamSize) ? mShortSAT : mSAT;
|
int[] sat = isShortStream(pStreamSize) ? shortSAT : SAT;
|
||||||
|
|
||||||
int sid = pSId;
|
int sid = pSId;
|
||||||
while (sid != END_OF_CHAIN_SID && sid != FREE_SID) {
|
while (sid != END_OF_CHAIN_SID && sid != FREE_SID) {
|
||||||
@@ -367,7 +392,7 @@ public final class CompoundDocument {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isShortStream(final long pStreamSize) {
|
private boolean isShortStream(final long pStreamSize) {
|
||||||
return pStreamSize != FREE_SID && pStreamSize < mMinStreamSize;
|
return pStreamSize != FREE_SID && pStreamSize < minStreamSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -381,70 +406,76 @@ public final class CompoundDocument {
|
|||||||
long pos;
|
long pos;
|
||||||
|
|
||||||
if (isShortStream(pStreamSize)) {
|
if (isShortStream(pStreamSize)) {
|
||||||
// The short-stream is not continouos...
|
// The short stream is not continuous...
|
||||||
Entry root = getRootEntry();
|
Entry root = getRootEntry();
|
||||||
if (mShortStreamSIdChain == null) {
|
if (shortStreamSIdChain == null) {
|
||||||
mShortStreamSIdChain = getSIdChain(root.startSId, root.streamSize);
|
shortStreamSIdChain = getSIdChain(root.startSId, root.streamSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
int shortPerStd = mSectorSize / mShortSectorSize;
|
// System.err.println("pSId: " + pSId);
|
||||||
int offset = pSId / shortPerStd;
|
int shortPerSId = sectorSize / shortSectorSize;
|
||||||
int shortOffset = pSId - (offset * shortPerStd);
|
// System.err.println("shortPerSId: " + shortPerSId);
|
||||||
|
int offset = pSId / shortPerSId;
|
||||||
|
// System.err.println("offset: " + offset);
|
||||||
|
int shortOffset = pSId - (offset * shortPerSId);
|
||||||
|
// System.err.println("shortOffset: " + shortOffset);
|
||||||
|
// System.err.println("shortStreamSIdChain.offset: " + shortStreamSIdChain.get(offset));
|
||||||
|
|
||||||
pos = HEADER_SIZE
|
pos = HEADER_SIZE
|
||||||
+ (mShortStreamSIdChain.get(offset) * (long) mSectorSize)
|
+ (shortStreamSIdChain.get(offset) * (long) sectorSize)
|
||||||
+ (shortOffset * (long) mShortSectorSize);
|
+ (shortOffset * (long) shortSectorSize);
|
||||||
|
// System.err.println("pos: " + pos);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pos = HEADER_SIZE + pSId * (long) mSectorSize;
|
pos = HEADER_SIZE + pSId * (long) sectorSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mInput instanceof LittleEndianRandomAccessFile) {
|
if (input instanceof LittleEndianRandomAccessFile) {
|
||||||
((LittleEndianRandomAccessFile) mInput).seek(pos);
|
((LittleEndianRandomAccessFile) input).seek(pos);
|
||||||
}
|
}
|
||||||
else if (mInput instanceof ImageInputStream) {
|
else if (input instanceof ImageInputStream) {
|
||||||
((ImageInputStream) mInput).seek(pos);
|
((ImageInputStream) input).seek(pos);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
((SeekableLittleEndianDataInputStream) mInput).seek(pos);
|
((SeekableLittleEndianDataInputStream) input).seek(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void seekToDId(final int pDId) throws IOException {
|
private void seekToDId(final int pDId) throws IOException {
|
||||||
if (mDirectorySIdChain == null) {
|
if (directorySIdChain == null) {
|
||||||
mDirectorySIdChain = getSIdChain(mDirectorySId, FREE_SID);
|
directorySIdChain = getSIdChain(directorySId, FREE_SID);
|
||||||
}
|
}
|
||||||
|
|
||||||
int dIdsPerSId = mSectorSize / Entry.LENGTH;
|
int dIdsPerSId = sectorSize / Entry.LENGTH;
|
||||||
|
|
||||||
int sIdOffset = pDId / dIdsPerSId;
|
int sIdOffset = pDId / dIdsPerSId;
|
||||||
int dIdOffset = pDId - (sIdOffset * dIdsPerSId);
|
int dIdOffset = pDId - (sIdOffset * dIdsPerSId);
|
||||||
|
|
||||||
int sId = mDirectorySIdChain.get(sIdOffset);
|
int sId = directorySIdChain.get(sIdOffset);
|
||||||
|
|
||||||
seekToSId(sId, FREE_SID);
|
seekToSId(sId, FREE_SID);
|
||||||
if (mInput instanceof LittleEndianRandomAccessFile) {
|
if (input instanceof LittleEndianRandomAccessFile) {
|
||||||
LittleEndianRandomAccessFile input = (LittleEndianRandomAccessFile) mInput;
|
LittleEndianRandomAccessFile input = (LittleEndianRandomAccessFile) this.input;
|
||||||
input.seek(input.getFilePointer() + dIdOffset * Entry.LENGTH);
|
input.seek(input.getFilePointer() + dIdOffset * Entry.LENGTH);
|
||||||
}
|
}
|
||||||
else if (mInput instanceof ImageInputStream) {
|
else if (input instanceof ImageInputStream) {
|
||||||
ImageInputStream input = (ImageInputStream) mInput;
|
ImageInputStream input = (ImageInputStream) this.input;
|
||||||
input.seek(input.getStreamPosition() + dIdOffset * Entry.LENGTH);
|
input.seek(input.getStreamPosition() + dIdOffset * Entry.LENGTH);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SeekableLittleEndianDataInputStream input = (SeekableLittleEndianDataInputStream) mInput;
|
SeekableLittleEndianDataInputStream input = (SeekableLittleEndianDataInputStream) this.input;
|
||||||
input.seek(input.getStreamPosition() + dIdOffset * Entry.LENGTH);
|
input.seek(input.getStreamPosition() + dIdOffset * Entry.LENGTH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SeekableInputStream getInputStreamForSId(final int pStreamId, final int pStreamSize) throws IOException {
|
SeekableInputStream getInputStreamForSId(final int pStreamId, final int pStreamSize) throws IOException {
|
||||||
SIdChain chain = getSIdChain(pStreamId, pStreamSize);
|
SIdChain chain = getSIdChain(pStreamId, pStreamSize);
|
||||||
|
|
||||||
// TODO: Detach? Means, we have to copy to a byte buffer, or keep track of
|
// TODO: Detach? Means, we have to copy to a byte buffer, or keep track of
|
||||||
// positions, and seek back and forth (would be cool, but difficult)..
|
// positions, and seek back and forth (would be cool, but difficult)..
|
||||||
int sectorSize = pStreamSize < mMinStreamSize ? mShortSectorSize : mSectorSize;
|
int sectorSize = pStreamSize < minStreamSize ? shortSectorSize : this.sectorSize;
|
||||||
|
|
||||||
return new Stream(chain, pStreamSize, sectorSize, this);
|
return new MemoryCacheSeekableStream(new Stream(chain, pStreamSize, sectorSize, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
private InputStream getDirectoryStreamForDId(final int pDirectoryId) throws IOException {
|
private InputStream getDirectoryStreamForDId(final int pDirectoryId) throws IOException {
|
||||||
@@ -453,7 +484,7 @@ public final class CompoundDocument {
|
|||||||
byte[] bytes = new byte[Entry.LENGTH];
|
byte[] bytes = new byte[Entry.LENGTH];
|
||||||
|
|
||||||
seekToDId(pDirectoryId);
|
seekToDId(pDirectoryId);
|
||||||
mInput.readFully(bytes);
|
input.readFully(bytes);
|
||||||
|
|
||||||
return new ByteArrayInputStream(bytes);
|
return new ByteArrayInputStream(bytes);
|
||||||
}
|
}
|
||||||
@@ -462,8 +493,8 @@ public final class CompoundDocument {
|
|||||||
Entry entry = Entry.readEntry(new LittleEndianDataInputStream(
|
Entry entry = Entry.readEntry(new LittleEndianDataInputStream(
|
||||||
getDirectoryStreamForDId(pDirectoryId)
|
getDirectoryStreamForDId(pDirectoryId)
|
||||||
));
|
));
|
||||||
entry.mParent = pParent;
|
entry.parent = pParent;
|
||||||
entry.mDocument = this;
|
entry.document = this;
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,21 +558,23 @@ public final class CompoundDocument {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Entry getRootEntry() throws IOException {
|
public Entry getRootEntry() throws IOException {
|
||||||
if (mRootEntry == null) {
|
if (rootEntry == null) {
|
||||||
readSAT();
|
readSAT();
|
||||||
|
|
||||||
mRootEntry = getEntry(0, null);
|
rootEntry = getEntry(0, null);
|
||||||
|
|
||||||
if (mRootEntry.type != Entry.ROOT_STORAGE) {
|
if (rootEntry.type != Entry.ROOT_STORAGE) {
|
||||||
throw new CorruptDocumentException("Invalid root storage type: " + mRootEntry.type);
|
throw new CorruptDocumentException("Invalid root storage type: " + rootEntry.type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mRootEntry;
|
|
||||||
|
return rootEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is useless, as most documents on file have all-zero UUIDs...
|
||||||
// @Override
|
// @Override
|
||||||
// public int hashCode() {
|
// public int hashCode() {
|
||||||
// return mUID.hashCode();
|
// return uUID.hashCode();
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// @Override
|
// @Override
|
||||||
@@ -555,7 +588,7 @@ public final class CompoundDocument {
|
|||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// if (pOther.getClass() == getClass()) {
|
// if (pOther.getClass() == getClass()) {
|
||||||
// return mUID.equals(((CompoundDocument) pOther).mUID);
|
// return uUID.equals(((CompoundDocument) pOther).uUID);
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
// return false;
|
// return false;
|
||||||
@@ -565,7 +598,7 @@ public final class CompoundDocument {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format(
|
return String.format(
|
||||||
"%s[uuid: %s, sector size: %d/%d bytes, directory SID: %d, master SAT: %s entries]",
|
"%s[uuid: %s, sector size: %d/%d bytes, directory SID: %d, master SAT: %s entries]",
|
||||||
getClass().getSimpleName(), mUID, mSectorSize, mShortSectorSize, mDirectorySId, mMasterSAT.length
|
getClass().getSimpleName(), uUID, sectorSize, shortSectorSize, directorySId, masterSAT.length
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,29 +634,29 @@ public final class CompoundDocument {
|
|||||||
return ((pMSTime >> 1) / 5000) + EPOCH_OFFSET;
|
return ((pMSTime >> 1) / 5000) + EPOCH_OFFSET;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Enforce stream length!
|
static class Stream extends InputStream {
|
||||||
static class Stream extends SeekableInputStream {
|
private final SIdChain chain;
|
||||||
private SIdChain mChain;
|
private final CompoundDocument document;
|
||||||
int mNextSectorPos;
|
private final long length;
|
||||||
byte[] mBuffer;
|
|
||||||
int mBufferPos;
|
|
||||||
|
|
||||||
private final CompoundDocument mDocument;
|
private long streamPos;
|
||||||
private final long mLength;
|
private int nextSectorPos;
|
||||||
|
private byte[] buffer;
|
||||||
|
private int bufferPos;
|
||||||
|
|
||||||
public Stream(final SIdChain pChain, final long pLength, final int pSectorSize, final CompoundDocument pDocument) {
|
public Stream(SIdChain chain, int streamSize, int sectorSize, CompoundDocument document) {
|
||||||
mChain = pChain;
|
this.chain = chain;
|
||||||
mLength = pLength;
|
this.length = streamSize;
|
||||||
|
|
||||||
mBuffer = new byte[pSectorSize];
|
this.buffer = new byte[sectorSize];
|
||||||
mBufferPos = mBuffer.length;
|
this.bufferPos = buffer.length;
|
||||||
|
|
||||||
mDocument = pDocument;
|
this.document = document;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int available() throws IOException {
|
public int available() throws IOException {
|
||||||
return (int) Math.min(mBuffer.length - mBufferPos, mLength - getStreamPosition());
|
return (int) Math.min(buffer.length - bufferPos, length - streamPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int read() throws IOException {
|
public int read() throws IOException {
|
||||||
@@ -633,20 +666,23 @@ public final class CompoundDocument {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mBuffer[mBufferPos++] & 0xff;
|
streamPos++;
|
||||||
|
|
||||||
|
return buffer[bufferPos++] & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean fillBuffer() throws IOException {
|
private boolean fillBuffer() throws IOException {
|
||||||
if (mNextSectorPos < mChain.length()) {
|
if (streamPos < length && nextSectorPos < chain.length()) {
|
||||||
// TODO: Sync on mDocument.mInput here, and we are completely detached... :-)
|
// TODO: Sync on document.input here, and we are completely detached... :-)
|
||||||
// TODO: We also need to sync other places...
|
// TODO: Update: We also need to sync other places... :-P
|
||||||
synchronized (mDocument) {
|
synchronized (document) {
|
||||||
mDocument.seekToSId(mChain.get(mNextSectorPos), mLength);
|
document.seekToSId(chain.get(nextSectorPos), length);
|
||||||
mDocument.mInput.readFully(mBuffer);
|
document.input.readFully(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
mNextSectorPos++;
|
nextSectorPos++;
|
||||||
mBufferPos = 0;
|
bufferPos = 0;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,99 +699,66 @@ public final class CompoundDocument {
|
|||||||
|
|
||||||
int toRead = Math.min(len, available());
|
int toRead = Math.min(len, available());
|
||||||
|
|
||||||
System.arraycopy(mBuffer, mBufferPos, b, off, toRead);
|
System.arraycopy(buffer, bufferPos, b, off, toRead);
|
||||||
mBufferPos += toRead;
|
bufferPos += toRead;
|
||||||
|
streamPos += toRead;
|
||||||
|
|
||||||
return toRead;
|
return toRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCached() {
|
@Override
|
||||||
return true;
|
public void close() throws IOException {
|
||||||
}
|
buffer = null;
|
||||||
|
|
||||||
public boolean isCachedMemory() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCachedFile() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void closeImpl() throws IOException {
|
|
||||||
mBuffer = null;
|
|
||||||
mChain = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void seekImpl(final long pPosition) throws IOException {
|
|
||||||
long pos = getStreamPosition();
|
|
||||||
|
|
||||||
if (pos - mBufferPos >= pPosition && pPosition <= pos + available()) {
|
|
||||||
// Skip inside buffer only
|
|
||||||
mBufferPos += (pPosition - pos);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Skip outside buffer
|
|
||||||
mNextSectorPos = (int) (pPosition / mBuffer.length);
|
|
||||||
if (!fillBuffer()) {
|
|
||||||
throw new EOFException();
|
|
||||||
}
|
|
||||||
mBufferPos = (int) (pPosition % mBuffer.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void flushBeforeImpl(long pPosition) throws IOException {
|
|
||||||
// No need to do anything here
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add test case for this class!!!
|
|
||||||
static class SeekableLittleEndianDataInputStream extends LittleEndianDataInputStream implements Seekable {
|
static class SeekableLittleEndianDataInputStream extends LittleEndianDataInputStream implements Seekable {
|
||||||
private final SeekableInputStream mSeekable;
|
private final SeekableInputStream seekable;
|
||||||
|
|
||||||
public SeekableLittleEndianDataInputStream(final SeekableInputStream pInput) {
|
public SeekableLittleEndianDataInputStream(final SeekableInputStream pInput) {
|
||||||
super(pInput);
|
super(pInput);
|
||||||
mSeekable = pInput;
|
seekable = pInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void seek(final long pPosition) throws IOException {
|
public void seek(final long pPosition) throws IOException {
|
||||||
mSeekable.seek(pPosition);
|
seekable.seek(pPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCachedFile() {
|
public boolean isCachedFile() {
|
||||||
return mSeekable.isCachedFile();
|
return seekable.isCachedFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCachedMemory() {
|
public boolean isCachedMemory() {
|
||||||
return mSeekable.isCachedMemory();
|
return seekable.isCachedMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCached() {
|
public boolean isCached() {
|
||||||
return mSeekable.isCached();
|
return seekable.isCached();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getStreamPosition() throws IOException {
|
public long getStreamPosition() throws IOException {
|
||||||
return mSeekable.getStreamPosition();
|
return seekable.getStreamPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getFlushedPosition() throws IOException {
|
public long getFlushedPosition() throws IOException {
|
||||||
return mSeekable.getFlushedPosition();
|
return seekable.getFlushedPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flushBefore(final long pPosition) throws IOException {
|
public void flushBefore(final long pPosition) throws IOException {
|
||||||
mSeekable.flushBefore(pPosition);
|
seekable.flushBefore(pPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flush() throws IOException {
|
public void flush() throws IOException {
|
||||||
mSeekable.flush();
|
seekable.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reset() throws IOException {
|
public void reset() throws IOException {
|
||||||
mSeekable.reset();
|
seekable.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void mark() {
|
public void mark() {
|
||||||
mSeekable.mark();
|
seekable.mark();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import com.twelvemonkeys.io.SeekableInputStream;
|
|||||||
|
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
@@ -61,9 +62,9 @@ public final class Entry implements Comparable<Entry> {
|
|||||||
int startSId;
|
int startSId;
|
||||||
int streamSize;
|
int streamSize;
|
||||||
|
|
||||||
CompoundDocument mDocument;
|
CompoundDocument document;
|
||||||
Entry mParent;
|
Entry parent;
|
||||||
SortedSet<Entry> mChildren;
|
SortedSet<Entry> children;
|
||||||
|
|
||||||
public final static int LENGTH = 128;
|
public final static int LENGTH = 128;
|
||||||
|
|
||||||
@@ -99,28 +100,26 @@ public final class Entry implements Comparable<Entry> {
|
|||||||
* @throws IOException if an i/o exception occurs during reading
|
* @throws IOException if an i/o exception occurs during reading
|
||||||
*/
|
*/
|
||||||
private void read(final DataInput pInput) throws IOException {
|
private void read(final DataInput pInput) throws IOException {
|
||||||
char[] chars = new char[32];
|
byte[] bytes = new byte[64];
|
||||||
for (int i = 0; i < chars.length; i++) {
|
pInput.readFully(bytes);
|
||||||
chars[i] = pInput.readChar();
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: Length is in bytes, including the null-terminator...
|
// NOTE: Length is in bytes, including the null-terminator...
|
||||||
int nameLength = pInput.readShort();
|
int nameLength = pInput.readShort();
|
||||||
name = new String(chars, 0, (nameLength - 1) / 2);
|
name = new String(bytes, 0, nameLength - 2, Charset.forName("UTF-16LE"));
|
||||||
//System.out.println("name: " + name);
|
// System.out.println("name: " + name);
|
||||||
|
|
||||||
type = pInput.readByte();
|
type = pInput.readByte();
|
||||||
//System.out.println("type: " + type);
|
// System.out.println("type: " + type);
|
||||||
|
|
||||||
nodeColor = pInput.readByte();
|
nodeColor = pInput.readByte();
|
||||||
//System.out.println("nodeColor: " + nodeColor);
|
// System.out.println("nodeColor: " + nodeColor);
|
||||||
|
|
||||||
prevDId = pInput.readInt();
|
prevDId = pInput.readInt();
|
||||||
//System.out.println("prevDID: " + prevDID);
|
// System.out.println("prevDId: " + prevDId);
|
||||||
nextDId = pInput.readInt();
|
nextDId = pInput.readInt();
|
||||||
//System.out.println("nextDID: " + nextDID);
|
// System.out.println("nextDId: " + nextDId);
|
||||||
rootNodeDId = pInput.readInt();
|
rootNodeDId = pInput.readInt();
|
||||||
//System.out.println("rootNodeDID: " + rootNodeDID);
|
// System.out.println("rootNodeDId: " + rootNodeDId);
|
||||||
|
|
||||||
// UID (16) + user flags (4), ignored
|
// UID (16) + user flags (4), ignored
|
||||||
if (pInput.skipBytes(20) != 20) {
|
if (pInput.skipBytes(20) != 20) {
|
||||||
@@ -131,9 +130,9 @@ public final class Entry implements Comparable<Entry> {
|
|||||||
modifiedTimestamp = CompoundDocument.toJavaTimeInMillis(pInput.readLong());
|
modifiedTimestamp = CompoundDocument.toJavaTimeInMillis(pInput.readLong());
|
||||||
|
|
||||||
startSId = pInput.readInt();
|
startSId = pInput.readInt();
|
||||||
//System.out.println("startSID: " + startSID);
|
// System.out.println("startSId: " + startSId);
|
||||||
streamSize = pInput.readInt();
|
streamSize = pInput.readInt();
|
||||||
//System.out.println("streamSize: " + streamSize);
|
// System.out.println("streamSize: " + streamSize);
|
||||||
|
|
||||||
// Reserved
|
// Reserved
|
||||||
pInput.readInt();
|
pInput.readInt();
|
||||||
@@ -186,11 +185,11 @@ public final class Entry implements Comparable<Entry> {
|
|||||||
* @see #length()
|
* @see #length()
|
||||||
*/
|
*/
|
||||||
public SeekableInputStream getInputStream() throws IOException {
|
public SeekableInputStream getInputStream() throws IOException {
|
||||||
if (isDirectory()) {
|
if (!isFile()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mDocument.getInputStreamForSId(startSId, streamSize);
|
return document.getInputStreamForSId(startSId, streamSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -201,9 +200,10 @@ public final class Entry implements Comparable<Entry> {
|
|||||||
* @see #getInputStream()
|
* @see #getInputStream()
|
||||||
*/
|
*/
|
||||||
public long length() {
|
public long length() {
|
||||||
if (isDirectory()) {
|
if (!isFile()) {
|
||||||
return 0L;
|
return 0L;
|
||||||
}
|
}
|
||||||
|
|
||||||
return streamSize;
|
return streamSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,7 +248,7 @@ public final class Entry implements Comparable<Entry> {
|
|||||||
* the root {@code Entry}
|
* the root {@code Entry}
|
||||||
*/
|
*/
|
||||||
public Entry getParentEntry() {
|
public Entry getParentEntry() {
|
||||||
return mParent;
|
return parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -266,7 +266,7 @@ public final class Entry implements Comparable<Entry> {
|
|||||||
|
|
||||||
Entry dummy = new Entry();
|
Entry dummy = new Entry();
|
||||||
dummy.name = pName;
|
dummy.name = pName;
|
||||||
dummy.mParent = this;
|
dummy.parent = this;
|
||||||
|
|
||||||
SortedSet child = getChildEntries().tailSet(dummy);
|
SortedSet child = getChildEntries().tailSet(dummy);
|
||||||
return (Entry) child.first();
|
return (Entry) child.first();
|
||||||
@@ -279,26 +279,26 @@ public final class Entry implements Comparable<Entry> {
|
|||||||
* @throws java.io.IOException if an I/O exception occurs
|
* @throws java.io.IOException if an I/O exception occurs
|
||||||
*/
|
*/
|
||||||
public SortedSet<Entry> getChildEntries() throws IOException {
|
public SortedSet<Entry> getChildEntries() throws IOException {
|
||||||
if (mChildren == null) {
|
if (children == null) {
|
||||||
if (isFile() || rootNodeDId == -1) {
|
if (isFile() || rootNodeDId == -1) {
|
||||||
mChildren = NO_CHILDREN;
|
children = NO_CHILDREN;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Start at root node in R/B tree, and raed to the left and right,
|
// Start at root node in R/B tree, and read to the left and right,
|
||||||
// re-build tree, according to the docs
|
// re-build tree, according to the docs
|
||||||
mChildren = mDocument.getEntries(rootNodeDId, this);
|
children = Collections.unmodifiableSortedSet(document.getEntries(rootNodeDId, this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mChildren;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "\"" + name + "\""
|
return "\"" + name + "\""
|
||||||
+ " (" + (isFile() ? "Document" : (isDirectory() ? "Directory" : "Root"))
|
+ " (" + (isFile() ? "Document" : (isDirectory() ? "Directory" : "Root"))
|
||||||
+ (mParent != null ? ", parent: \"" + mParent.getName() + "\"" : "")
|
+ (parent != null ? ", parent: \"" + parent.getName() + "\"" : "")
|
||||||
+ (isFile() ? "" : ", children: " + (mChildren != null ? String.valueOf(mChildren.size()) : "(unknown)"))
|
+ (isFile() ? "" : ", children: " + (children != null ? String.valueOf(children.size()) : "(unknown)"))
|
||||||
+ ", SId=" + startSId + ", length=" + streamSize + ")";
|
+ ", SId=" + startSId + ", length=" + streamSize + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,8 +312,8 @@ public final class Entry implements Comparable<Entry> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Entry other = (Entry) pOther;
|
Entry other = (Entry) pOther;
|
||||||
return name.equals(other.name) && (mParent == other.mParent
|
return name.equals(other.name) && (parent == other.parent
|
||||||
|| (mParent != null && mParent.equals(other.mParent)));
|
|| (parent != null && parent.equals(other.parent)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import java.util.NoSuchElementException;
|
|||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/ole2/SIdChain.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/ole2/SIdChain.java#1 $
|
||||||
*/
|
*/
|
||||||
class SIdChain {
|
final class SIdChain {
|
||||||
int[] chain;
|
int[] chain;
|
||||||
int size = 0;
|
int size = 0;
|
||||||
int next = 0;
|
int next = 0;
|
||||||
|
|||||||
@@ -37,11 +37,10 @@ import java.net.*;
|
|||||||
* @see SimpleAuthenticator
|
* @see SimpleAuthenticator
|
||||||
* @see java.net.Authenticator
|
* @see java.net.Authenticator
|
||||||
*
|
*
|
||||||
* @author Harald Kuhr (haraldk@iconmedialab.no),
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version 1.0
|
* @version 1.0
|
||||||
*/
|
*/
|
||||||
public interface AuthenticatorFilter {
|
public interface AuthenticatorFilter {
|
||||||
public boolean accept(InetAddress pAddress, int pPort, String pProtocol,
|
public boolean accept(InetAddress pAddress, int pPort, String pProtocol, String pPrompt, String pScheme);
|
||||||
String pPrompt, String pScheme);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -26,7 +26,7 @@
|
|||||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.twelvemonkeys.util;
|
package com.twelvemonkeys.net;
|
||||||
|
|
||||||
import com.twelvemonkeys.io.*;
|
import com.twelvemonkeys.io.*;
|
||||||
import com.twelvemonkeys.io.enc.Base64Decoder;
|
import com.twelvemonkeys.io.enc.Base64Decoder;
|
||||||
@@ -41,8 +41,9 @@ import java.io.*;
|
|||||||
* @author unascribed
|
* @author unascribed
|
||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/BASE64.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/BASE64.java#1 $
|
||||||
|
* @deprecated Use {@link com.twelvemonkeys.io.enc.Base64Encoder}/{@link Base64Decoder} instead
|
||||||
*/
|
*/
|
||||||
public class BASE64 {
|
class BASE64 {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This array maps the characters to their 6 bit values
|
* This array maps the characters to their 6 bit values
|
||||||
@@ -29,7 +29,6 @@
|
|||||||
package com.twelvemonkeys.net;
|
package com.twelvemonkeys.net;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.StringUtil;
|
import com.twelvemonkeys.lang.StringUtil;
|
||||||
import com.twelvemonkeys.util.BASE64;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
@@ -65,18 +64,18 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
|||||||
private final static String HTTP_HEADER_END = "\r\n\r\n";
|
private final static String HTTP_HEADER_END = "\r\n\r\n";
|
||||||
private static final String HEADER_WWW_AUTH = "WWW-Authenticate";
|
private static final String HEADER_WWW_AUTH = "WWW-Authenticate";
|
||||||
private final static int BUF_SIZE = 8192;
|
private final static int BUF_SIZE = 8192;
|
||||||
private int mMaxRedirects = (System.getProperty("http.maxRedirects") != null)
|
private int maxRedirects = (System.getProperty("http.maxRedirects") != null)
|
||||||
? Integer.parseInt(System.getProperty("http.maxRedirects"))
|
? Integer.parseInt(System.getProperty("http.maxRedirects"))
|
||||||
: 20;
|
: 20;
|
||||||
protected int mTimeout = -1;
|
protected int timeout = -1;
|
||||||
protected int mConnectTimeout = -1;
|
protected int connectTimeout = -1;
|
||||||
private Socket mSocket = null;
|
private Socket socket = null;
|
||||||
protected InputStream mErrorStream = null;
|
protected InputStream errorStream = null;
|
||||||
protected InputStream mInputStream = null;
|
protected InputStream inputStream = null;
|
||||||
protected OutputStream mOutputStream = null;
|
protected OutputStream outputStream = null;
|
||||||
private String[] mResponseHeaders = null;
|
private String[] responseHeaders = null;
|
||||||
protected Properties mResponseHeaderFields = null;
|
protected Properties responseHeaderFields = null;
|
||||||
protected Properties mRequestProperties = new Properties();
|
protected Properties requestProperties = new Properties();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a HttpURLConnection.
|
* Creates a HttpURLConnection.
|
||||||
@@ -114,7 +113,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
|||||||
protected HttpURLConnection(URL pURL, int pTimeout, int pConnectTimeout) {
|
protected HttpURLConnection(URL pURL, int pTimeout, int pConnectTimeout) {
|
||||||
super(pURL);
|
super(pURL);
|
||||||
setTimeout(pTimeout);
|
setTimeout(pTimeout);
|
||||||
mConnectTimeout = pConnectTimeout;
|
connectTimeout = pConnectTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -135,13 +134,13 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
|||||||
if (connected) {
|
if (connected) {
|
||||||
throw new IllegalAccessError("Already connected");
|
throw new IllegalAccessError("Already connected");
|
||||||
}
|
}
|
||||||
String oldValue = mRequestProperties.getProperty(pKey);
|
String oldValue = requestProperties.getProperty(pKey);
|
||||||
|
|
||||||
if (oldValue == null) {
|
if (oldValue == null) {
|
||||||
mRequestProperties.setProperty(pKey, pValue);
|
requestProperties.setProperty(pKey, pValue);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mRequestProperties.setProperty(pKey, oldValue + ", " + pValue);
|
requestProperties.setProperty(pKey, oldValue + ", " + pValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +157,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
|||||||
if (connected) {
|
if (connected) {
|
||||||
throw new IllegalAccessError("Already connected");
|
throw new IllegalAccessError("Already connected");
|
||||||
}
|
}
|
||||||
return mRequestProperties.getProperty(pKey);
|
return requestProperties.getProperty(pKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -212,7 +211,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
|||||||
* if there is no such field in the header.
|
* if there is no such field in the header.
|
||||||
*/
|
*/
|
||||||
public String getHeaderField(String pName) {
|
public String getHeaderField(String pName) {
|
||||||
return mResponseHeaderFields.getProperty(StringUtil.toLowerCase(pName));
|
return responseHeaderFields.getProperty(StringUtil.toLowerCase(pName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -230,10 +229,10 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
|||||||
*/
|
*/
|
||||||
public String getHeaderField(int pIndex) {
|
public String getHeaderField(int pIndex) {
|
||||||
// TODO: getInputStream() first, to make sure we have header fields
|
// TODO: getInputStream() first, to make sure we have header fields
|
||||||
if (pIndex >= mResponseHeaders.length) {
|
if (pIndex >= responseHeaders.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String field = mResponseHeaders[pIndex];
|
String field = responseHeaders[pIndex];
|
||||||
|
|
||||||
// pIndex == 0, means the response code etc (i.e. "HTTP/1.1 200 OK").
|
// pIndex == 0, means the response code etc (i.e. "HTTP/1.1 200 OK").
|
||||||
if ((pIndex == 0) || (field == null)) {
|
if ((pIndex == 0) || (field == null)) {
|
||||||
@@ -256,10 +255,10 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
|||||||
*/
|
*/
|
||||||
public String getHeaderFieldKey(int pIndex) {
|
public String getHeaderFieldKey(int pIndex) {
|
||||||
// TODO: getInputStream() first, to make sure we have header fields
|
// TODO: getInputStream() first, to make sure we have header fields
|
||||||
if (pIndex >= mResponseHeaders.length) {
|
if (pIndex >= responseHeaders.length) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String field = mResponseHeaders[pIndex];
|
String field = responseHeaders[pIndex];
|
||||||
|
|
||||||
if (StringUtil.isEmpty(field)) {
|
if (StringUtil.isEmpty(field)) {
|
||||||
return null;
|
return null;
|
||||||
@@ -283,10 +282,10 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
|||||||
if (pTimeout < 0) { // Must be positive
|
if (pTimeout < 0) { // Must be positive
|
||||||
throw new IllegalArgumentException("Timeout must be positive.");
|
throw new IllegalArgumentException("Timeout must be positive.");
|
||||||
}
|
}
|
||||||
mTimeout = pTimeout;
|
timeout = pTimeout;
|
||||||
if (mSocket != null) {
|
if (socket != null) {
|
||||||
try {
|
try {
|
||||||
mSocket.setSoTimeout(pTimeout);
|
socket.setSoTimeout(pTimeout);
|
||||||
}
|
}
|
||||||
catch (SocketException se) {
|
catch (SocketException se) {
|
||||||
// Not much to do about that...
|
// Not much to do about that...
|
||||||
@@ -305,12 +304,12 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
|||||||
public int getTimeout() {
|
public int getTimeout() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return ((mSocket != null)
|
return ((socket != null)
|
||||||
? mSocket.getSoTimeout()
|
? socket.getSoTimeout()
|
||||||
: mTimeout);
|
: timeout);
|
||||||
}
|
}
|
||||||
catch (SocketException se) {
|
catch (SocketException se) {
|
||||||
return mTimeout;
|
return timeout;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,24 +331,24 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
|||||||
}
|
}
|
||||||
int length;
|
int length;
|
||||||
|
|
||||||
if (mInputStream == null) {
|
if (inputStream == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// "De-chunk" the output stream
|
// "De-chunk" the output stream
|
||||||
else if ("chunked".equalsIgnoreCase(getHeaderField("Transfer-Encoding"))) {
|
else if ("chunked".equalsIgnoreCase(getHeaderField("Transfer-Encoding"))) {
|
||||||
if (!(mInputStream instanceof ChunkedInputStream)) {
|
if (!(inputStream instanceof ChunkedInputStream)) {
|
||||||
mInputStream = new ChunkedInputStream(mInputStream);
|
inputStream = new ChunkedInputStream(inputStream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we don't wait forever, if the content-length is known
|
// Make sure we don't wait forever, if the content-length is known
|
||||||
else if ((length = getHeaderFieldInt("Content-Length", -1)) >= 0) {
|
else if ((length = getHeaderFieldInt("Content-Length", -1)) >= 0) {
|
||||||
if (!(mInputStream instanceof FixedLengthInputStream)) {
|
if (!(inputStream instanceof FixedLengthInputStream)) {
|
||||||
mInputStream = new FixedLengthInputStream(mInputStream, length);
|
inputStream = new FixedLengthInputStream(inputStream, length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return mInputStream;
|
return inputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -364,7 +363,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
|||||||
if (!connected) {
|
if (!connected) {
|
||||||
connect();
|
connect();
|
||||||
}
|
}
|
||||||
return mOutputStream;
|
return outputStream;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -374,15 +373,15 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
|||||||
* instance can be reused for other requests.
|
* instance can be reused for other requests.
|
||||||
*/
|
*/
|
||||||
public void disconnect() {
|
public void disconnect() {
|
||||||
if (mSocket != null) {
|
if (socket != null) {
|
||||||
try {
|
try {
|
||||||
mSocket.close();
|
socket.close();
|
||||||
}
|
}
|
||||||
catch (IOException ioe) {
|
catch (IOException ioe) {
|
||||||
|
|
||||||
// Does not matter, I guess.
|
// Does not matter, I guess.
|
||||||
}
|
}
|
||||||
mSocket = null;
|
socket = null;
|
||||||
}
|
}
|
||||||
connected = false;
|
connected = false;
|
||||||
}
|
}
|
||||||
@@ -397,37 +396,37 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
|||||||
: HTTP_DEFAULT_PORT;
|
: HTTP_DEFAULT_PORT;
|
||||||
|
|
||||||
// Create socket if we don't have one
|
// Create socket if we don't have one
|
||||||
if (mSocket == null) {
|
if (socket == null) {
|
||||||
//mSocket = new Socket(pURL.getHost(), port); // Blocks...
|
//socket = new Socket(pURL.getHost(), port); // Blocks...
|
||||||
mSocket = createSocket(pURL, port, mConnectTimeout);
|
socket = createSocket(pURL, port, connectTimeout);
|
||||||
mSocket.setSoTimeout(mTimeout);
|
socket.setSoTimeout(timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get Socket output stream
|
// Get Socket output stream
|
||||||
OutputStream os = mSocket.getOutputStream();
|
OutputStream os = socket.getOutputStream();
|
||||||
|
|
||||||
// Connect using HTTP
|
// Connect using HTTP
|
||||||
writeRequestHeaders(os, pURL, method, mRequestProperties, usingProxy(), pAuth, pAuthType);
|
writeRequestHeaders(os, pURL, method, requestProperties, usingProxy(), pAuth, pAuthType);
|
||||||
|
|
||||||
// Get response input stream
|
// Get response input stream
|
||||||
InputStream sis = mSocket.getInputStream();
|
InputStream sis = socket.getInputStream();
|
||||||
BufferedInputStream is = new BufferedInputStream(sis);
|
BufferedInputStream is = new BufferedInputStream(sis);
|
||||||
|
|
||||||
// Detatch reponse headers from reponse input stream
|
// Detatch reponse headers from reponse input stream
|
||||||
InputStream header = detatchResponseHeader(is);
|
InputStream header = detatchResponseHeader(is);
|
||||||
|
|
||||||
// Parse headers and set response code/message
|
// Parse headers and set response code/message
|
||||||
mResponseHeaders = parseResponseHeader(header);
|
responseHeaders = parseResponseHeader(header);
|
||||||
mResponseHeaderFields = parseHeaderFields(mResponseHeaders);
|
responseHeaderFields = parseHeaderFields(responseHeaders);
|
||||||
|
|
||||||
//System.err.println("Headers fields:");
|
//System.err.println("Headers fields:");
|
||||||
//mResponseHeaderFields.list(System.err);
|
//responseHeaderFields.list(System.err);
|
||||||
// Test HTTP response code, to see if further action is needed
|
// Test HTTP response code, to see if further action is needed
|
||||||
switch (getResponseCode()) {
|
switch (getResponseCode()) {
|
||||||
case HTTP_OK:
|
case HTTP_OK:
|
||||||
// 200 OK
|
// 200 OK
|
||||||
mInputStream = is;
|
inputStream = is;
|
||||||
mErrorStream = null;
|
errorStream = null;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -472,7 +471,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
|||||||
|
|
||||||
// Avoid infinite loop
|
// Avoid infinite loop
|
||||||
if (pRetries++ <= 0) {
|
if (pRetries++ <= 0) {
|
||||||
throw new ProtocolException("Server redirected too many times (" + mMaxRedirects + ") (Authentication required: " + auth + ")"); // This is what sun.net.www.protocol.http.HttpURLConnection does
|
throw new ProtocolException("Server redirected too many times (" + maxRedirects + ") (Authentication required: " + auth + ")"); // This is what sun.net.www.protocol.http.HttpURLConnection does
|
||||||
}
|
}
|
||||||
else if (pa != null) {
|
else if (pa != null) {
|
||||||
connect(pURL, pa, method, pRetries);
|
connect(pURL, pa, method, pRetries);
|
||||||
@@ -506,8 +505,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
|||||||
|
|
||||||
// Test if we can reuse the Socket
|
// Test if we can reuse the Socket
|
||||||
if (!(newLoc.getAuthority().equals(pURL.getAuthority()) && (newLoc.getPort() == pURL.getPort()))) {
|
if (!(newLoc.getAuthority().equals(pURL.getAuthority()) && (newLoc.getPort() == pURL.getPort()))) {
|
||||||
mSocket.close(); // Close the socket, won't need it anymore
|
socket.close(); // Close the socket, won't need it anymore
|
||||||
mSocket = null;
|
socket = null;
|
||||||
}
|
}
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
//System.err.println("Redirecting to " + location);
|
//System.err.println("Redirecting to " + location);
|
||||||
@@ -526,22 +525,22 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
|||||||
default :
|
default :
|
||||||
// Not 200 OK, or any of the redirect responses
|
// Not 200 OK, or any of the redirect responses
|
||||||
// Probably an error...
|
// Probably an error...
|
||||||
mErrorStream = is;
|
errorStream = is;
|
||||||
mInputStream = null;
|
inputStream = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Need rethinking...
|
// --- Need rethinking...
|
||||||
// No further questions, let the Socket wait forever (until the server
|
// No further questions, let the Socket wait forever (until the server
|
||||||
// closes the connection)
|
// closes the connection)
|
||||||
//mSocket.setSoTimeout(0);
|
//socket.setSoTimeout(0);
|
||||||
// Probably not... The timeout should only kick if the read BLOCKS.
|
// Probably not... The timeout should only kick if the read BLOCKS.
|
||||||
// Shutdown output, meaning any writes to the outputstream below will
|
// Shutdown output, meaning any writes to the outputstream below will
|
||||||
// probably fail...
|
// probably fail...
|
||||||
//mSocket.shutdownOutput();
|
//socket.shutdownOutput();
|
||||||
// Not a good idea at all... POSTs need the outputstream to send the
|
// Not a good idea at all... POSTs need the outputstream to send the
|
||||||
// form-data.
|
// form-data.
|
||||||
// --- /Need rethinking.
|
// --- /Need rethinking.
|
||||||
mOutputStream = os;
|
outputStream = os;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static interface SocketConnector extends Runnable {
|
private static interface SocketConnector extends Runnable {
|
||||||
@@ -663,7 +662,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection {
|
|||||||
return; // Ignore
|
return; // Ignore
|
||||||
}
|
}
|
||||||
connected = true;
|
connected = true;
|
||||||
connect(url, null, null, mMaxRedirects);
|
connect(url, null, null, maxRedirects);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package com.twelvemonkeys.net;
|
|||||||
import com.twelvemonkeys.io.FileUtil;
|
import com.twelvemonkeys.io.FileUtil;
|
||||||
import com.twelvemonkeys.lang.StringUtil;
|
import com.twelvemonkeys.lang.StringUtil;
|
||||||
import com.twelvemonkeys.lang.DateUtil;
|
import com.twelvemonkeys.lang.DateUtil;
|
||||||
import com.twelvemonkeys.util.BASE64;
|
|
||||||
import com.twelvemonkeys.util.CollectionUtil;
|
import com.twelvemonkeys.util.CollectionUtil;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|||||||
@@ -28,29 +28,31 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.net;
|
package com.twelvemonkeys.net;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.lang.Validate;
|
||||||
|
|
||||||
import java.net.Authenticator;
|
import java.net.Authenticator;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.PasswordAuthentication;
|
import java.net.PasswordAuthentication;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Hashtable;
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A simple Authenticator implementation.
|
* A simple Authenticator implementation.
|
||||||
* Singleton class, obtain reference through the static
|
* Singleton class, obtain reference through the static
|
||||||
* {@code getInstance} method.
|
* {@code getInstance} method.
|
||||||
* <P>
|
* <p/>
|
||||||
* <EM>After swearing, sweating, pulling my hair, banging my head repeatedly
|
* <EM>After swearing, sweating, pulling my hair, banging my head repeatedly
|
||||||
* into the walls and reading the java.net.Authenticator API documentation
|
* into the walls and reading the java.net.Authenticator API documentation
|
||||||
* once more, an idea came to my mind. This is the result. I hope you find it
|
* once more, an idea came to my mind. This is the result. I hope you find it
|
||||||
* useful. -- Harald K.</EM>
|
* useful. -- Harald K.</EM>
|
||||||
*
|
*
|
||||||
* @see java.net.Authenticator
|
|
||||||
*
|
|
||||||
* @author Harald Kuhr (haraldk@iconmedialab.no)
|
* @author Harald Kuhr (haraldk@iconmedialab.no)
|
||||||
* @version 1.0
|
* @version 1.0
|
||||||
|
* @see java.net.Authenticator
|
||||||
*/
|
*/
|
||||||
public class SimpleAuthenticator extends Authenticator {
|
public class SimpleAuthenticator extends Authenticator {
|
||||||
|
|
||||||
/** The reference to the single instance of this class. */
|
/** The reference to the single instance of this class. */
|
||||||
private static SimpleAuthenticator sInstance = null;
|
private static SimpleAuthenticator sInstance = null;
|
||||||
/** Keeps track of the state of this class. */
|
/** Keeps track of the state of this class. */
|
||||||
@@ -63,237 +65,179 @@ public class SimpleAuthenticator extends Authenticator {
|
|||||||
/** Basic authentication scheme. */
|
/** Basic authentication scheme. */
|
||||||
public final static String BASIC = "Basic";
|
public final static String BASIC = "Basic";
|
||||||
|
|
||||||
/**
|
/** The hastable that keeps track of the PasswordAuthentications. */
|
||||||
* The hastable that keeps track of the PasswordAuthentications.
|
protected Map<AuthKey, PasswordAuthentication> passwordAuthentications = null;
|
||||||
*/
|
|
||||||
|
|
||||||
protected Hashtable mPasswordAuthentications = null;
|
/** The hastable that keeps track of the Authenticators. */
|
||||||
|
protected Map<PasswordAuthenticator, AuthenticatorFilter> authenticators = null;
|
||||||
/**
|
|
||||||
* The hastable that keeps track of the Authenticators.
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected Hashtable mAuthenticators = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a SimpleAuthenticator.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
/** Creates a SimpleAuthenticator. */
|
||||||
private SimpleAuthenticator() {
|
private SimpleAuthenticator() {
|
||||||
mPasswordAuthentications = new Hashtable();
|
passwordAuthentications = new HashMap<AuthKey, PasswordAuthentication>();
|
||||||
mAuthenticators = new Hashtable();
|
authenticators = new HashMap<PasswordAuthenticator, AuthenticatorFilter>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the SimpleAuthenticator instance and registers it through the
|
* Gets the SimpleAuthenticator instance and registers it through the
|
||||||
* Authenticator.setDefault(). If there is no current instance
|
* Authenticator.setDefault(). If there is no current instance
|
||||||
* of the SimpleAuthenticator in the VM, one is created. This method will
|
* of the SimpleAuthenticator in the VM, one is created. This method will
|
||||||
* try to figure out if the setDefault() succeeded (a hack), and will
|
* try to figure out if the setDefault() succeeded (a hack), and will
|
||||||
* return null if it was not able to register the instance as default.
|
* return null if it was not able to register the instance as default.
|
||||||
*
|
*
|
||||||
* @return The single instance of this class, or null, if another
|
* @return The single instance of this class, or null, if another
|
||||||
* Authenticator is allready registered as default.
|
* Authenticator is allready registered as default.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public static synchronized SimpleAuthenticator getInstance() {
|
public static synchronized SimpleAuthenticator getInstance() {
|
||||||
if (!sInitialized) {
|
if (!sInitialized) {
|
||||||
// Create an instance
|
// Create an instance
|
||||||
sInstance = new SimpleAuthenticator();
|
sInstance = new SimpleAuthenticator();
|
||||||
|
|
||||||
// Try to set default (this may quietly fail...)
|
// Try to set default (this may quietly fail...)
|
||||||
Authenticator.setDefault(sInstance);
|
Authenticator.setDefault(sInstance);
|
||||||
|
|
||||||
// A hack to figure out if we really did set the authenticator
|
// A hack to figure out if we really did set the authenticator
|
||||||
PasswordAuthentication pa =
|
PasswordAuthentication pa = Authenticator.requestPasswordAuthentication(null, FOURTYTWO, null, null, MAGIC);
|
||||||
Authenticator.requestPasswordAuthentication(null, FOURTYTWO,
|
|
||||||
null, null, MAGIC);
|
|
||||||
|
|
||||||
// If this test returns false, we didn't succeed, so we set the
|
// If this test returns false, we didn't succeed, so we set the
|
||||||
// instance back to null.
|
// instance back to null.
|
||||||
if (pa == null || !MAGIC.equals(pa.getUserName()) ||
|
if (pa == null || !MAGIC.equals(pa.getUserName()) || !("" + FOURTYTWO).equals(new String(pa.getPassword()))) {
|
||||||
!("" + FOURTYTWO).equals(new String(pa.getPassword())))
|
sInstance = null;
|
||||||
sInstance = null;
|
}
|
||||||
|
|
||||||
// Done
|
// Done
|
||||||
sInitialized = true;
|
sInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sInstance;
|
return sInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the PasswordAuthentication for the request. Called when password
|
* Gets the PasswordAuthentication for the request. Called when password
|
||||||
* authorization is needed.
|
* authorization is needed.
|
||||||
*
|
*
|
||||||
* @return The PasswordAuthentication collected from the user, or null if
|
* @return The PasswordAuthentication collected from the user, or null if
|
||||||
* none is provided.
|
* none is provided.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
protected PasswordAuthentication getPasswordAuthentication() {
|
protected PasswordAuthentication getPasswordAuthentication() {
|
||||||
// Don't worry, this is just a hack to figure out if we were able
|
// Don't worry, this is just a hack to figure out if we were able
|
||||||
// to set this Authenticator through the setDefault method.
|
// to set this Authenticator through the setDefault method.
|
||||||
if (!sInitialized && MAGIC.equals(getRequestingScheme())
|
if (!sInitialized && MAGIC.equals(getRequestingScheme()) && getRequestingPort() == FOURTYTWO) {
|
||||||
&& getRequestingPort() == FOURTYTWO)
|
return new PasswordAuthentication(MAGIC, ("" + FOURTYTWO).toCharArray());
|
||||||
return new PasswordAuthentication(MAGIC, ("" + FOURTYTWO)
|
}
|
||||||
.toCharArray());
|
/*
|
||||||
/*
|
System.err.println("getPasswordAuthentication");
|
||||||
System.err.println("getPasswordAuthentication");
|
System.err.println(getRequestingSite());
|
||||||
System.err.println(getRequestingSite());
|
System.err.println(getRequestingPort());
|
||||||
System.err.println(getRequestingPort());
|
System.err.println(getRequestingProtocol());
|
||||||
System.err.println(getRequestingProtocol());
|
System.err.println(getRequestingPrompt());
|
||||||
System.err.println(getRequestingPrompt());
|
System.err.println(getRequestingScheme());
|
||||||
System.err.println(getRequestingScheme());
|
*/
|
||||||
*/
|
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// Look for a more specific PasswordAuthenticatior before using
|
// Look for a more specific PasswordAuthenticatior before using
|
||||||
// Default:
|
// Default:
|
||||||
//
|
//
|
||||||
// if (...)
|
// if (...)
|
||||||
// return pa.requestPasswordAuthentication(getRequestingSite(),
|
// return pa.requestPasswordAuthentication(getRequestingSite(),
|
||||||
// getRequestingPort(),
|
// getRequestingPort(),
|
||||||
// getRequestingProtocol(),
|
// getRequestingProtocol(),
|
||||||
// getRequestingPrompt(),
|
// getRequestingPrompt(),
|
||||||
// getRequestingScheme());
|
// getRequestingScheme());
|
||||||
|
|
||||||
return (PasswordAuthentication)
|
return passwordAuthentications.get(new AuthKey(getRequestingSite(),
|
||||||
mPasswordAuthentications.get(new AuthKey(getRequestingSite(),
|
getRequestingPort(),
|
||||||
getRequestingPort(),
|
getRequestingProtocol(),
|
||||||
getRequestingProtocol(),
|
getRequestingPrompt(),
|
||||||
getRequestingPrompt(),
|
getRequestingScheme()));
|
||||||
getRequestingScheme()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Registers a PasswordAuthentication with a given URL address. */
|
||||||
* Registers a PasswordAuthentication with a given URL address.
|
public PasswordAuthentication registerPasswordAuthentication(URL pURL, PasswordAuthentication pPA) {
|
||||||
*
|
return registerPasswordAuthentication(NetUtil.createInetAddressFromURL(pURL),
|
||||||
*/
|
pURL.getPort(),
|
||||||
|
pURL.getProtocol(),
|
||||||
public PasswordAuthentication registerPasswordAuthentication(URL pURL,
|
null, // Prompt/Realm
|
||||||
PasswordAuthentication pPA) {
|
BASIC,
|
||||||
return registerPasswordAuthentication(NetUtil.createInetAddressFromURL(pURL),
|
pPA);
|
||||||
pURL.getPort(),
|
|
||||||
pURL.getProtocol(),
|
|
||||||
null, // Prompt/Realm
|
|
||||||
BASIC,
|
|
||||||
pPA);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Registers a PasswordAuthentication with a given net address. */
|
||||||
* Registers a PasswordAuthentication with a given net address.
|
public PasswordAuthentication registerPasswordAuthentication(InetAddress pAddress, int pPort, String pProtocol, String pPrompt, String pScheme, PasswordAuthentication pPA) {
|
||||||
*
|
/*
|
||||||
*/
|
System.err.println("registerPasswordAuthentication");
|
||||||
|
System.err.println(pAddress);
|
||||||
|
System.err.println(pPort);
|
||||||
|
System.err.println(pProtocol);
|
||||||
|
System.err.println(pPrompt);
|
||||||
|
System.err.println(pScheme);
|
||||||
|
*/
|
||||||
|
|
||||||
public PasswordAuthentication registerPasswordAuthentication(
|
return passwordAuthentications.put(new AuthKey(pAddress, pPort, pProtocol, pPrompt, pScheme), pPA);
|
||||||
InetAddress pAddress, int pPort, String pProtocol,
|
|
||||||
String pPrompt, String pScheme, PasswordAuthentication pPA)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
System.err.println("registerPasswordAuthentication");
|
|
||||||
System.err.println(pAddress);
|
|
||||||
System.err.println(pPort);
|
|
||||||
System.err.println(pProtocol);
|
|
||||||
System.err.println(pPrompt);
|
|
||||||
System.err.println(pScheme);
|
|
||||||
*/
|
|
||||||
|
|
||||||
return (PasswordAuthentication)
|
|
||||||
mPasswordAuthentications.put(new AuthKey(pAddress, pPort,
|
|
||||||
pProtocol, pPrompt,
|
|
||||||
pScheme),
|
|
||||||
pPA);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Unregisters a PasswordAuthentication with a given URL address. */
|
||||||
* Unregisters a PasswordAuthentication with a given URL address.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
public PasswordAuthentication unregisterPasswordAuthentication(URL pURL) {
|
public PasswordAuthentication unregisterPasswordAuthentication(URL pURL) {
|
||||||
return unregisterPasswordAuthentication(NetUtil.createInetAddressFromURL(pURL),
|
return unregisterPasswordAuthentication(NetUtil.createInetAddressFromURL(pURL), pURL.getPort(), pURL.getProtocol(), null, BASIC);
|
||||||
pURL.getPort(),
|
|
||||||
pURL.getProtocol(),
|
|
||||||
null,
|
|
||||||
BASIC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Unregisters a PasswordAuthentication with a given net address. */
|
||||||
* Unregisters a PasswordAuthentication with a given net address.
|
public PasswordAuthentication unregisterPasswordAuthentication(InetAddress pAddress, int pPort, String pProtocol, String pPrompt, String pScheme) {
|
||||||
*
|
return passwordAuthentications.remove(new AuthKey(pAddress, pPort, pProtocol, pPrompt, pScheme));
|
||||||
*/
|
|
||||||
|
|
||||||
public PasswordAuthentication unregisterPasswordAuthentication(
|
|
||||||
InetAddress pAddress, int pPort, String pProtocol,
|
|
||||||
String pPrompt, String pScheme)
|
|
||||||
{
|
|
||||||
return (PasswordAuthentication)
|
|
||||||
mPasswordAuthentications.remove(new AuthKey(pAddress, pPort,
|
|
||||||
pProtocol, pPrompt,
|
|
||||||
pScheme));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Registers a PasswordAuthenticator that can answer authentication
|
* TODO: Registers a PasswordAuthenticator that can answer authentication
|
||||||
* requests.
|
* requests.
|
||||||
*
|
*
|
||||||
* @see PasswordAuthenticator
|
* @see PasswordAuthenticator
|
||||||
*/
|
*/
|
||||||
|
public void registerPasswordAuthenticator(PasswordAuthenticator pPA, AuthenticatorFilter pFilter) {
|
||||||
public void registerPasswordAuthenticator(PasswordAuthenticator pPA,
|
authenticators.put(pPA, pFilter);
|
||||||
AuthenticatorFilter pFilter) {
|
|
||||||
mAuthenticators.put(pPA, pFilter);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Unregisters a PasswordAuthenticator that can answer authentication
|
* TODO: Unregisters a PasswordAuthenticator that can answer authentication
|
||||||
* requests.
|
* requests.
|
||||||
*
|
*
|
||||||
* @see PasswordAuthenticator
|
* @see PasswordAuthenticator
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void unregisterPasswordAuthenticator(PasswordAuthenticator pPA) {
|
public void unregisterPasswordAuthenticator(PasswordAuthenticator pPA) {
|
||||||
mAuthenticators.remove(pPA);
|
authenticators.remove(pPA);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class, used for caching the PasswordAuthentication objects.
|
* Utility class, used for caching the PasswordAuthentication objects.
|
||||||
* Everything but address may be null
|
* Everything but address may be null
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class AuthKey {
|
class AuthKey {
|
||||||
|
|
||||||
InetAddress mAddress = null;
|
|
||||||
int mPort = -1;
|
|
||||||
String mProtocol = null;
|
|
||||||
String mPrompt = null;
|
|
||||||
String mScheme = null;
|
|
||||||
|
|
||||||
AuthKey(InetAddress pAddress, int pPort, String pProtocol,
|
InetAddress address = null;
|
||||||
String pPrompt, String pScheme) {
|
int port = -1;
|
||||||
if (pAddress == null)
|
String protocol = null;
|
||||||
throw new IllegalArgumentException("Address argument can't be null!");
|
String prompt = null;
|
||||||
|
String scheme = null;
|
||||||
|
|
||||||
mAddress = pAddress;
|
AuthKey(InetAddress pAddress, int pPort, String pProtocol, String pPrompt, String pScheme) {
|
||||||
mPort = pPort;
|
Validate.notNull(pAddress, "address");
|
||||||
mProtocol = pProtocol;
|
|
||||||
mPrompt = pPrompt;
|
|
||||||
mScheme = pScheme;
|
|
||||||
|
|
||||||
// System.out.println("Created: " + this);
|
address = pAddress;
|
||||||
|
port = pPort;
|
||||||
|
protocol = pProtocol;
|
||||||
|
prompt = pPrompt;
|
||||||
|
scheme = pScheme;
|
||||||
|
|
||||||
|
// System.out.println("Created: " + this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Creates a string representation of this object. */
|
||||||
* Creates a string representation of this object.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "AuthKey[" + mAddress + ":" + mPort + "/" + mProtocol + " \"" + mPrompt + "\" (" + mScheme + ")]";
|
return "AuthKey[" + address + ":" + port + "/" + protocol + " \"" + prompt + "\" (" + scheme + ")]";
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean equals(Object pObj) {
|
public boolean equals(Object pObj) {
|
||||||
return (pObj instanceof AuthKey ? equals((AuthKey) pObj) : false);
|
return (pObj instanceof AuthKey && equals((AuthKey) pObj));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ahem.. Breaks the rule from Object.equals(Object):
|
// Ahem.. Breaks the rule from Object.equals(Object):
|
||||||
@@ -302,25 +246,25 @@ class AuthKey {
|
|||||||
// should return true.
|
// should return true.
|
||||||
|
|
||||||
public boolean equals(AuthKey pKey) {
|
public boolean equals(AuthKey pKey) {
|
||||||
// Maybe allow nulls, and still be equal?
|
// Maybe allow nulls, and still be equal?
|
||||||
return (mAddress.equals(pKey.mAddress)
|
return (address.equals(pKey.address)
|
||||||
&& (mPort == -1
|
&& (port == -1
|
||||||
|| pKey.mPort == -1
|
|| pKey.port == -1
|
||||||
|| mPort == pKey.mPort)
|
|| port == pKey.port)
|
||||||
&& (mProtocol == null
|
&& (protocol == null
|
||||||
|| pKey.mProtocol == null
|
|| pKey.protocol == null
|
||||||
|| mProtocol.equals(pKey.mProtocol))
|
|| protocol.equals(pKey.protocol))
|
||||||
&& (mPrompt == null
|
&& (prompt == null
|
||||||
|| pKey.mPrompt == null
|
|| pKey.prompt == null
|
||||||
|| mPrompt.equals(pKey.mPrompt))
|
|| prompt.equals(pKey.prompt))
|
||||||
&& (mScheme == null
|
&& (scheme == null
|
||||||
|| pKey.mScheme == null
|
|| pKey.scheme == null
|
||||||
|| mScheme.equalsIgnoreCase(pKey.mScheme)));
|
|| scheme.equalsIgnoreCase(pKey.scheme)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
// There won't be too many pr address, will it? ;-)
|
// There won't be too many pr address, will it? ;-)
|
||||||
return mAddress.hashCode();
|
return address.hashCode();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,13 +52,13 @@ public final class DOMSerializer {
|
|||||||
private static final String PARAM_PRETTY_PRINT = "format-pretty-print";
|
private static final String PARAM_PRETTY_PRINT = "format-pretty-print";
|
||||||
private static final String PARAM_XML_DECLARATION = "xml-declaration";
|
private static final String PARAM_XML_DECLARATION = "xml-declaration";
|
||||||
|
|
||||||
private final LSSerializer mSerializer;
|
private final LSSerializer serializer;
|
||||||
private final LSOutput mOutput;
|
private final LSOutput output;
|
||||||
|
|
||||||
private DOMSerializer() {
|
private DOMSerializer() {
|
||||||
DOMImplementationLS domImpl = Support.getImplementation();
|
DOMImplementationLS domImpl = Support.getImplementation();
|
||||||
mSerializer = domImpl.createLSSerializer();
|
serializer = domImpl.createLSSerializer();
|
||||||
mOutput = domImpl.createLSOutput();
|
output = domImpl.createLSOutput();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,8 +71,8 @@ public final class DOMSerializer {
|
|||||||
public DOMSerializer(final OutputStream pStream, final String pEncoding) {
|
public DOMSerializer(final OutputStream pStream, final String pEncoding) {
|
||||||
this();
|
this();
|
||||||
|
|
||||||
mOutput.setByteStream(pStream);
|
output.setByteStream(pStream);
|
||||||
mOutput.setEncoding(pEncoding);
|
output.setEncoding(pEncoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,17 +84,17 @@ public final class DOMSerializer {
|
|||||||
public DOMSerializer(final Writer pStream) {
|
public DOMSerializer(final Writer pStream) {
|
||||||
this();
|
this();
|
||||||
|
|
||||||
mOutput.setCharacterStream(pStream);
|
output.setCharacterStream(pStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// TODO: Is it useful?
|
// TODO: Is it useful?
|
||||||
public void setNewLine(final String pNewLine) {
|
public void setNewLine(final String pNewLine) {
|
||||||
mSerializer.setNewLine(pNewLine);
|
serializer.setNewLine(pNewLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getNewLine() {
|
public String getNewLine() {
|
||||||
return mSerializer.getNewLine();
|
return serializer.getNewLine();
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -107,18 +107,18 @@ public final class DOMSerializer {
|
|||||||
* @param pPrettyPrint {@code true} to enable pretty printing
|
* @param pPrettyPrint {@code true} to enable pretty printing
|
||||||
*/
|
*/
|
||||||
public void setPrettyPrint(final boolean pPrettyPrint) {
|
public void setPrettyPrint(final boolean pPrettyPrint) {
|
||||||
DOMConfiguration configuration = mSerializer.getDomConfig();
|
DOMConfiguration configuration = serializer.getDomConfig();
|
||||||
if (configuration.canSetParameter(PARAM_PRETTY_PRINT, pPrettyPrint)) {
|
if (configuration.canSetParameter(PARAM_PRETTY_PRINT, pPrettyPrint)) {
|
||||||
configuration.setParameter(PARAM_PRETTY_PRINT, pPrettyPrint);
|
configuration.setParameter(PARAM_PRETTY_PRINT, pPrettyPrint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getPrettyPrint() {
|
public boolean getPrettyPrint() {
|
||||||
return Boolean.TRUE.equals(mSerializer.getDomConfig().getParameter(PARAM_PRETTY_PRINT));
|
return Boolean.TRUE.equals(serializer.getDomConfig().getParameter(PARAM_PRETTY_PRINT));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setXMLDeclaration(boolean pXMLDeclaration) {
|
private void setXMLDeclaration(boolean pXMLDeclaration) {
|
||||||
mSerializer.getDomConfig().setParameter(PARAM_XML_DECLARATION, pXMLDeclaration);
|
serializer.getDomConfig().setParameter(PARAM_XML_DECLARATION, pXMLDeclaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -142,7 +142,7 @@ public final class DOMSerializer {
|
|||||||
|
|
||||||
private void serializeImpl(final Node pNode, final boolean pOmitDecl) {
|
private void serializeImpl(final Node pNode, final boolean pOmitDecl) {
|
||||||
setXMLDeclaration(pOmitDecl);
|
setXMLDeclaration(pOmitDecl);
|
||||||
mSerializer.write(pNode, mOutput);
|
serializer.write(pNode, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Support {
|
private static class Support {
|
||||||
|
|||||||
@@ -59,28 +59,22 @@ public class XMLSerializer {
|
|||||||
// TODO: Consider using IOException to communicate trouble, rather than RTE,
|
// TODO: Consider using IOException to communicate trouble, rather than RTE,
|
||||||
// to be more compatible...
|
// to be more compatible...
|
||||||
|
|
||||||
// TODO: Idea: Create a SerializationContext that stores attributes on
|
private final OutputStream output;
|
||||||
// serialization, to keep the serialization thread-safe
|
private final Charset encoding;
|
||||||
// Store preserveSpace attribute in this context, to avoid costly traversals
|
private final SerializationContext context;
|
||||||
// Store user options here too
|
|
||||||
// TODO: Push/pop?
|
|
||||||
|
|
||||||
private final OutputStream mOutput;
|
|
||||||
private final Charset mEncoding;
|
|
||||||
private final SerializationContext mContext;
|
|
||||||
|
|
||||||
public XMLSerializer(final OutputStream pOutput, final String pEncoding) {
|
public XMLSerializer(final OutputStream pOutput, final String pEncoding) {
|
||||||
mOutput = pOutput;
|
output = pOutput;
|
||||||
mEncoding = Charset.forName(pEncoding);
|
encoding = Charset.forName(pEncoding);
|
||||||
mContext = new SerializationContext();
|
context = new SerializationContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setIndentation(String pIndent) {
|
public final void setIndentation(String pIndent) {
|
||||||
mContext.indent = pIndent != null ? pIndent : " ";
|
context.indent = pIndent != null ? pIndent : "\t";
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void setStripComments(boolean pStrip) {
|
public final void setStripComments(boolean pStrip) {
|
||||||
mContext.stripComments = pStrip;
|
context.stripComments = pStrip;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -101,12 +95,12 @@ public class XMLSerializer {
|
|||||||
* @param pWriteXMLDeclaration {@code true} if the XML declaration should be included, otherwise {@code false}.
|
* @param pWriteXMLDeclaration {@code true} if the XML declaration should be included, otherwise {@code false}.
|
||||||
*/
|
*/
|
||||||
public void serialize(final Node pRootNode, final boolean pWriteXMLDeclaration) {
|
public void serialize(final Node pRootNode, final boolean pWriteXMLDeclaration) {
|
||||||
PrintWriter out = new PrintWriter(new OutputStreamWriter(mOutput, mEncoding));
|
PrintWriter out = new PrintWriter(new OutputStreamWriter(output, encoding));
|
||||||
try {
|
try {
|
||||||
if (pWriteXMLDeclaration) {
|
if (pWriteXMLDeclaration) {
|
||||||
writeXMLDeclaration(out);
|
writeXMLDeclaration(out);
|
||||||
}
|
}
|
||||||
writeXML(out, pRootNode, mContext.copy());
|
writeXML(out, pRootNode, context.copy());
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
out.flush();
|
out.flush();
|
||||||
@@ -115,7 +109,7 @@ public class XMLSerializer {
|
|||||||
|
|
||||||
private void writeXMLDeclaration(final PrintWriter pOut) {
|
private void writeXMLDeclaration(final PrintWriter pOut) {
|
||||||
pOut.print("<?xml version=\"1.0\" encoding=\"");
|
pOut.print("<?xml version=\"1.0\" encoding=\"");
|
||||||
pOut.print(mEncoding.name());
|
pOut.print(encoding.name());
|
||||||
pOut.println("\"?>");
|
pOut.println("\"?>");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,11 +273,7 @@ public class XMLSerializer {
|
|||||||
pos = appendAndEscape(pValue, pos, i, builder, ">");
|
pos = appendAndEscape(pValue, pos, i, builder, ">");
|
||||||
break;
|
break;
|
||||||
//case '\'':
|
//case '\'':
|
||||||
// pos = appendAndEscape(pString, pos, i, builder, "'");
|
|
||||||
// break;
|
|
||||||
//case '"':
|
//case '"':
|
||||||
// pos = appendAndEscape(pString, pos, i, builder, """);
|
|
||||||
// break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -347,17 +337,6 @@ public class XMLSerializer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//StringBuilder builder = new StringBuilder(pValue.length() + 30);
|
|
||||||
//
|
|
||||||
//int start = 0;
|
|
||||||
//while (end >= 0) {
|
|
||||||
// builder.append(pValue.substring(start, end));
|
|
||||||
// builder.append(""");
|
|
||||||
// start = end + 1;
|
|
||||||
// end = pValue.indexOf('"', start);
|
|
||||||
//}
|
|
||||||
//builder.append(pValue.substring(start));
|
|
||||||
|
|
||||||
builder.append(pValue.substring(pos));
|
builder.append(pValue.substring(pos));
|
||||||
|
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
@@ -389,14 +368,14 @@ public class XMLSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static String validateCDataValue(final String pValue) {
|
private static String validateCDataValue(final String pValue) {
|
||||||
if (pValue.indexOf("]]>") >= 0) {
|
if (pValue.contains("]]>")) {
|
||||||
throw new IllegalArgumentException("Malformed input document: CDATA block may not contain the string ']]>'");
|
throw new IllegalArgumentException("Malformed input document: CDATA block may not contain the string ']]>'");
|
||||||
}
|
}
|
||||||
return pValue;
|
return pValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String validateCommentValue(final String pValue) {
|
private static String validateCommentValue(final String pValue) {
|
||||||
if (pValue.indexOf("--") >= 0) {
|
if (pValue.contains("--")) {
|
||||||
throw new IllegalArgumentException("Malformed input document: Comment may not contain the string '--'");
|
throw new IllegalArgumentException("Malformed input document: Comment may not contain the string '--'");
|
||||||
}
|
}
|
||||||
return pValue;
|
return pValue;
|
||||||
@@ -420,8 +399,6 @@ public class XMLSerializer {
|
|||||||
// even if the document was created using attributes instead of namespaces...
|
// even if the document was created using attributes instead of namespaces...
|
||||||
// In that case, prefix will be null...
|
// In that case, prefix will be null...
|
||||||
|
|
||||||
// TODO: Don't insert duplicate/unnecessary namesspace declarations
|
|
||||||
|
|
||||||
// Handle namespace
|
// Handle namespace
|
||||||
String namespace = pNode.getNamespaceURI();
|
String namespace = pNode.getNamespaceURI();
|
||||||
if (namespace != null && !namespace.equals(pContext.defaultNamespace)) {
|
if (namespace != null && !namespace.equals(pContext.defaultNamespace)) {
|
||||||
@@ -570,6 +547,11 @@ public class XMLSerializer {
|
|||||||
pre.appendChild(document.createTextNode(" \t \n\r some text & white ' ' \n "));
|
pre.appendChild(document.createTextNode(" \t \n\r some text & white ' ' \n "));
|
||||||
test.appendChild(pre);
|
test.appendChild(pre);
|
||||||
|
|
||||||
|
Element pre2 = document.createElementNS("http://www.twelvemonkeys.com/xml/test", "tight");
|
||||||
|
pre2.setAttributeNS("http://www.w3.org/XML/1998/namespace", "xml:space", "preserve");
|
||||||
|
pre2.appendChild(document.createTextNode("no-space-around-me"));
|
||||||
|
test.appendChild(pre2);
|
||||||
|
|
||||||
// Create serializer and output document
|
// Create serializer and output document
|
||||||
//XMLSerializer serializer = new XMLSerializer(pOutput, new OutputFormat(document, UTF_8_ENCODING, true));
|
//XMLSerializer serializer = new XMLSerializer(pOutput, new OutputFormat(document, UTF_8_ENCODING, true));
|
||||||
System.out.println("XMLSerializer:");
|
System.out.println("XMLSerializer:");
|
||||||
@@ -612,7 +594,7 @@ public class XMLSerializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static class SerializationContext implements Cloneable {
|
static class SerializationContext implements Cloneable {
|
||||||
String indent = " ";
|
String indent = "\t";
|
||||||
int level = 0;
|
int level = 0;
|
||||||
boolean preserveSpace = false;
|
boolean preserveSpace = false;
|
||||||
boolean stripComments = false;
|
boolean stripComments = false;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.twelvemonkeys.io;
|
|||||||
|
|
||||||
import com.twelvemonkeys.lang.StringUtil;
|
import com.twelvemonkeys.lang.StringUtil;
|
||||||
import com.twelvemonkeys.util.CollectionUtil;
|
import com.twelvemonkeys.util.CollectionUtil;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -9,6 +10,8 @@ import java.io.StringReader;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CompoundReaderTestCase
|
* CompoundReaderTestCase
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -18,7 +21,6 @@ import java.util.ArrayList;
|
|||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/CompoundReaderTestCase.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/CompoundReaderTestCase.java#2 $
|
||||||
*/
|
*/
|
||||||
public class CompoundReaderTestCase extends ReaderAbstractTestCase {
|
public class CompoundReaderTestCase extends ReaderAbstractTestCase {
|
||||||
|
|
||||||
protected Reader makeReader(String pInput) {
|
protected Reader makeReader(String pInput) {
|
||||||
// Split
|
// Split
|
||||||
String[] input = StringUtil.toStringArray(pInput, " ");
|
String[] input = StringUtil.toStringArray(pInput, " ");
|
||||||
@@ -36,6 +38,7 @@ public class CompoundReaderTestCase extends ReaderAbstractTestCase {
|
|||||||
return new CompoundReader(readers.iterator());
|
return new CompoundReader(readers.iterator());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testNullConstructor() {
|
public void testNullConstructor() {
|
||||||
try {
|
try {
|
||||||
new CompoundReader(null);
|
new CompoundReader(null);
|
||||||
@@ -46,11 +49,13 @@ public class CompoundReaderTestCase extends ReaderAbstractTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testEmptyIteratorConstructor() throws IOException {
|
public void testEmptyIteratorConstructor() throws IOException {
|
||||||
Reader reader = new CompoundReader(CollectionUtil.iterator(new Reader[0]));
|
Reader reader = new CompoundReader(CollectionUtil.iterator(new Reader[0]));
|
||||||
assertEquals(-1, reader.read());
|
assertEquals(-1, reader.read());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testIteratorWithNullConstructor() throws IOException {
|
public void testIteratorWithNullConstructor() throws IOException {
|
||||||
try {
|
try {
|
||||||
new CompoundReader(CollectionUtil.iterator(new Reader[] {null}));
|
new CompoundReader(CollectionUtil.iterator(new Reader[] {null}));
|
||||||
|
|||||||
+5
@@ -1,8 +1,12 @@
|
|||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FastByteArrayOutputStreamTestCase
|
* FastByteArrayOutputStreamTestCase
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -16,6 +20,7 @@ public class FastByteArrayOutputStreamTestCase extends OutputStreamAbstractTestC
|
|||||||
return new FastByteArrayOutputStream(256);
|
return new FastByteArrayOutputStream(256);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testCreateInputStream() throws IOException {
|
public void testCreateInputStream() throws IOException {
|
||||||
FastByteArrayOutputStream out = makeObject();
|
FastByteArrayOutputStream out = makeObject();
|
||||||
|
|
||||||
|
|||||||
-4
@@ -11,10 +11,6 @@ import java.io.InputStream;
|
|||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/FileCacheSeekableStreamTestCase.java#3 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/FileCacheSeekableStreamTestCase.java#3 $
|
||||||
*/
|
*/
|
||||||
public class FileCacheSeekableStreamTestCase extends SeekableInputStreamAbstractTestCase {
|
public class FileCacheSeekableStreamTestCase extends SeekableInputStreamAbstractTestCase {
|
||||||
public FileCacheSeekableStreamTestCase(String name) {
|
|
||||||
super(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected SeekableInputStream makeInputStream(final InputStream pStream) {
|
protected SeekableInputStream makeInputStream(final InputStream pStream) {
|
||||||
try {
|
try {
|
||||||
return new FileCacheSeekableStream(pStream);
|
return new FileCacheSeekableStream(pStream);
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MemoryCacheSeekableStreamTestCase
|
* MemoryCacheSeekableStreamTestCase
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -10,10 +14,6 @@ import java.io.*;
|
|||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/FileSeekableStreamTestCase.java#3 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/FileSeekableStreamTestCase.java#3 $
|
||||||
*/
|
*/
|
||||||
public class FileSeekableStreamTestCase extends SeekableInputStreamAbstractTestCase {
|
public class FileSeekableStreamTestCase extends SeekableInputStreamAbstractTestCase {
|
||||||
public FileSeekableStreamTestCase(String name) {
|
|
||||||
super(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected SeekableInputStream makeInputStream(final InputStream pStream) {
|
protected SeekableInputStream makeInputStream(final InputStream pStream) {
|
||||||
try {
|
try {
|
||||||
return new FileSeekableStream(createFileWithContent(pStream));
|
return new FileSeekableStream(createFileWithContent(pStream));
|
||||||
@@ -37,11 +37,13 @@ public class FileSeekableStreamTestCase extends SeekableInputStreamAbstractTestC
|
|||||||
return temp;
|
return temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
@Override
|
@Override
|
||||||
public void testCloseUnderlyingStream() throws IOException {
|
public void testCloseUnderlyingStream() throws IOException {
|
||||||
// There is no underlying stream here...
|
// There is no underlying stream here...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testCloseUnderlyingFile() throws IOException {
|
public void testCloseUnderlyingFile() throws IOException {
|
||||||
final boolean[] closed = new boolean[1];
|
final boolean[] closed = new boolean[1];
|
||||||
|
|
||||||
|
|||||||
+30
-10
@@ -17,12 +17,15 @@
|
|||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.ObjectAbstractTestCase;
|
import com.twelvemonkeys.lang.ObjectAbstractTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* InputStreamAbstractTestCase
|
* InputStreamAbstractTestCase
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -38,10 +41,6 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
final static private long SEED = 29487982745l;
|
final static private long SEED = 29487982745l;
|
||||||
final static Random sRandom = new Random(SEED);
|
final static Random sRandom = new Random(SEED);
|
||||||
|
|
||||||
public InputStreamAbstractTestCase(String name) {
|
|
||||||
super(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final Object makeObject() {
|
protected final Object makeObject() {
|
||||||
return makeInputStream();
|
return makeInputStream();
|
||||||
}
|
}
|
||||||
@@ -71,11 +70,12 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testRead() throws Exception {
|
public void testRead() throws Exception {
|
||||||
int size = 5;
|
int size = 5;
|
||||||
InputStream input = makeInputStream(makeOrderedArray(size));
|
InputStream input = makeInputStream(makeOrderedArray(size));
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
assertEquals("Check Size [" + i + "]", (size - i), input.available());
|
assertTrue("Check Size [" + i + "]", (size - i) >= input.available());
|
||||||
assertEquals("Check Value [" + i + "]", i, input.read());
|
assertEquals("Check Value [" + i + "]", i, input.read());
|
||||||
}
|
}
|
||||||
assertEquals("Available after contents all read", 0, input.available());
|
assertEquals("Available after contents all read", 0, input.available());
|
||||||
@@ -90,6 +90,7 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testAvailable() throws Exception {
|
public void testAvailable() throws Exception {
|
||||||
InputStream input = makeInputStream(1);
|
InputStream input = makeInputStream(1);
|
||||||
assertFalse("Unexpected EOF", input.read() < 0);
|
assertFalse("Unexpected EOF", input.read() < 0);
|
||||||
@@ -100,6 +101,7 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
assertEquals("Available after End of File", 0, input.available());
|
assertEquals("Available after End of File", 0, input.available());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testReadByteArray() throws Exception {
|
public void testReadByteArray() throws Exception {
|
||||||
byte[] bytes = new byte[10];
|
byte[] bytes = new byte[10];
|
||||||
byte[] data = makeOrderedArray(15);
|
byte[] data = makeOrderedArray(15);
|
||||||
@@ -145,6 +147,7 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testEOF() throws Exception {
|
public void testEOF() throws Exception {
|
||||||
InputStream input = makeInputStream(makeOrderedArray(2));
|
InputStream input = makeInputStream(makeOrderedArray(2));
|
||||||
assertEquals("Read 1", 0, input.read());
|
assertEquals("Read 1", 0, input.read());
|
||||||
@@ -154,6 +157,7 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
assertEquals("Read 5", -1, input.read());
|
assertEquals("Read 5", -1, input.read());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testMarkResetUnsupported() throws IOException {
|
public void testMarkResetUnsupported() throws IOException {
|
||||||
InputStream input = makeInputStream(10);
|
InputStream input = makeInputStream(10);
|
||||||
if (input.markSupported()) {
|
if (input.markSupported()) {
|
||||||
@@ -176,6 +180,7 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResetNoMark() throws Exception {
|
public void testResetNoMark() throws Exception {
|
||||||
InputStream input = makeInputStream(makeOrderedArray(10));
|
InputStream input = makeInputStream(makeOrderedArray(10));
|
||||||
|
|
||||||
@@ -196,6 +201,7 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testMarkReset() throws Exception {
|
public void testMarkReset() throws Exception {
|
||||||
InputStream input = makeInputStream(makeOrderedArray(25));
|
InputStream input = makeInputStream(makeOrderedArray(25));
|
||||||
|
|
||||||
@@ -226,6 +232,7 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResetAfterReadLimit() throws Exception {
|
public void testResetAfterReadLimit() throws Exception {
|
||||||
InputStream input = makeInputStream(makeOrderedArray(25));
|
InputStream input = makeInputStream(makeOrderedArray(25));
|
||||||
|
|
||||||
@@ -257,6 +264,7 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResetAfterReset() throws Exception {
|
public void testResetAfterReset() throws Exception {
|
||||||
InputStream input = makeInputStream(makeOrderedArray(25));
|
InputStream input = makeInputStream(makeOrderedArray(25));
|
||||||
|
|
||||||
@@ -264,7 +272,8 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
return; // Not supported, skip test
|
return; // Not supported, skip test
|
||||||
}
|
}
|
||||||
|
|
||||||
assertTrue("Expected to read positive value", input.read() >= 0);
|
int first = input.read();
|
||||||
|
assertTrue("Expected to read positive value", first >= 0);
|
||||||
|
|
||||||
int readlimit = 5;
|
int readlimit = 5;
|
||||||
|
|
||||||
@@ -273,19 +282,24 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
int read = input.read();
|
int read = input.read();
|
||||||
assertTrue("Expected to read positive value", read >= 0);
|
assertTrue("Expected to read positive value", read >= 0);
|
||||||
|
|
||||||
input.reset();
|
assertTrue(input.read() >= 0);
|
||||||
assertEquals("Expected value read differes from actual", read, input.read());
|
assertTrue(input.read() >= 0);
|
||||||
|
|
||||||
// Reset after read limit passed, may either throw exception, or reset to last mark
|
input.reset();
|
||||||
|
assertEquals("Expected value read differs from actual", read, input.read());
|
||||||
|
|
||||||
|
// Reset after read limit passed, may either throw exception, or reset to last good mark
|
||||||
try {
|
try {
|
||||||
input.reset();
|
input.reset();
|
||||||
assertEquals("Re-read of reset data should be same", read, input.read());
|
int reRead = input.read();
|
||||||
|
assertTrue("Re-read of reset data should be same as initially marked or first", reRead == read || reRead == first);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
|
assertTrue("Wrong read-limit IOException message", e.getMessage().contains("mark"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSkip() throws Exception {
|
public void testSkip() throws Exception {
|
||||||
InputStream input = makeInputStream(makeOrderedArray(10));
|
InputStream input = makeInputStream(makeOrderedArray(10));
|
||||||
|
|
||||||
@@ -302,6 +316,7 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
assertEquals("Unexpected value read after EOF", -1, input.read());
|
assertEquals("Unexpected value read after EOF", -1, input.read());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSanityOrdered() throws IOException {
|
public void testSanityOrdered() throws IOException {
|
||||||
// This is to sanity check that the test itself is correct...
|
// This is to sanity check that the test itself is correct...
|
||||||
byte[] bytes = makeOrderedArray(25);
|
byte[] bytes = makeOrderedArray(25);
|
||||||
@@ -314,6 +329,7 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSanityOrdered2() throws IOException {
|
public void testSanityOrdered2() throws IOException {
|
||||||
// This is to sanity check that the test itself is correct...
|
// This is to sanity check that the test itself is correct...
|
||||||
byte[] bytes = makeOrderedArray(25);
|
byte[] bytes = makeOrderedArray(25);
|
||||||
@@ -332,6 +348,7 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSanityNegative() throws IOException {
|
public void testSanityNegative() throws IOException {
|
||||||
// This is to sanity check that the test itself is correct...
|
// This is to sanity check that the test itself is correct...
|
||||||
byte[] bytes = new byte[25];
|
byte[] bytes = new byte[25];
|
||||||
@@ -347,6 +364,7 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSanityNegative2() throws IOException {
|
public void testSanityNegative2() throws IOException {
|
||||||
// This is to sanity check that the test itself is correct...
|
// This is to sanity check that the test itself is correct...
|
||||||
byte[] bytes = new byte[25];
|
byte[] bytes = new byte[25];
|
||||||
@@ -368,6 +386,7 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSanityRandom() throws IOException {
|
public void testSanityRandom() throws IOException {
|
||||||
// This is to sanity check that the test itself is correct...
|
// This is to sanity check that the test itself is correct...
|
||||||
byte[] bytes = makeRandomArray(25);
|
byte[] bytes = makeRandomArray(25);
|
||||||
@@ -380,6 +399,7 @@ public abstract class InputStreamAbstractTestCase extends ObjectAbstractTestCase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSanityRandom2() throws IOException {
|
public void testSanityRandom2() throws IOException {
|
||||||
// This is to sanity check that the test itself is correct...
|
// This is to sanity check that the test itself is correct...
|
||||||
byte[] bytes = makeRandomArray(25);
|
byte[] bytes = makeRandomArray(25);
|
||||||
|
|||||||
-4
@@ -10,10 +10,6 @@ import java.io.InputStream;
|
|||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/MemoryCacheSeekableStreamTestCase.java#2 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/MemoryCacheSeekableStreamTestCase.java#2 $
|
||||||
*/
|
*/
|
||||||
public class MemoryCacheSeekableStreamTestCase extends SeekableInputStreamAbstractTestCase {
|
public class MemoryCacheSeekableStreamTestCase extends SeekableInputStreamAbstractTestCase {
|
||||||
public MemoryCacheSeekableStreamTestCase(String name) {
|
|
||||||
super(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected SeekableInputStream makeInputStream(final InputStream pStream) {
|
protected SeekableInputStream makeInputStream(final InputStream pStream) {
|
||||||
return new MemoryCacheSeekableStream(pStream);
|
return new MemoryCacheSeekableStream(pStream);
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-3
@@ -1,10 +1,13 @@
|
|||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.ObjectAbstractTestCase;
|
import com.twelvemonkeys.lang.ObjectAbstractTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* InputStreamAbstractTestCase
|
* InputStreamAbstractTestCase
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -15,6 +18,7 @@ import java.io.IOException;
|
|||||||
public abstract class OutputStreamAbstractTestCase extends ObjectAbstractTestCase {
|
public abstract class OutputStreamAbstractTestCase extends ObjectAbstractTestCase {
|
||||||
protected abstract OutputStream makeObject();
|
protected abstract OutputStream makeObject();
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testWrite() throws IOException {
|
public void testWrite() throws IOException {
|
||||||
OutputStream os = makeObject();
|
OutputStream os = makeObject();
|
||||||
|
|
||||||
@@ -23,12 +27,14 @@ public abstract class OutputStreamAbstractTestCase extends ObjectAbstractTestCas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testWriteByteArray() throws IOException {
|
public void testWriteByteArray() throws IOException {
|
||||||
OutputStream os = makeObject();
|
OutputStream os = makeObject();
|
||||||
|
|
||||||
os.write(new byte[256]);
|
os.write(new byte[256]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testWriteByteArrayNull() {
|
public void testWriteByteArrayNull() {
|
||||||
OutputStream os = makeObject();
|
OutputStream os = makeObject();
|
||||||
try {
|
try {
|
||||||
@@ -46,7 +52,8 @@ public abstract class OutputStreamAbstractTestCase extends ObjectAbstractTestCas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testWriteByteArrayOffsetLenght() throws IOException {
|
@Test
|
||||||
|
public void testWriteByteArrayOffsetLength() throws IOException {
|
||||||
byte[] input = new byte[256];
|
byte[] input = new byte[256];
|
||||||
|
|
||||||
OutputStream os = makeObject();
|
OutputStream os = makeObject();
|
||||||
@@ -65,7 +72,8 @@ public abstract class OutputStreamAbstractTestCase extends ObjectAbstractTestCas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testWriteByteArrayZeroLenght() {
|
@Test
|
||||||
|
public void testWriteByteArrayZeroLength() {
|
||||||
OutputStream os = makeObject();
|
OutputStream os = makeObject();
|
||||||
try {
|
try {
|
||||||
os.write(new byte[1], 0, 0);
|
os.write(new byte[1], 0, 0);
|
||||||
@@ -75,7 +83,8 @@ public abstract class OutputStreamAbstractTestCase extends ObjectAbstractTestCas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testWriteByteArrayOffsetLenghtNull() {
|
@Test
|
||||||
|
public void testWriteByteArrayOffsetLengthNull() {
|
||||||
OutputStream os = makeObject();
|
OutputStream os = makeObject();
|
||||||
try {
|
try {
|
||||||
os.write(null, 5, 10);
|
os.write(null, 5, 10);
|
||||||
@@ -92,6 +101,7 @@ public abstract class OutputStreamAbstractTestCase extends ObjectAbstractTestCas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testWriteByteArrayNegativeOffset() {
|
public void testWriteByteArrayNegativeOffset() {
|
||||||
OutputStream os = makeObject();
|
OutputStream os = makeObject();
|
||||||
try {
|
try {
|
||||||
@@ -109,6 +119,7 @@ public abstract class OutputStreamAbstractTestCase extends ObjectAbstractTestCas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testWriteByteArrayNegativeLength() {
|
public void testWriteByteArrayNegativeLength() {
|
||||||
OutputStream os = makeObject();
|
OutputStream os = makeObject();
|
||||||
try {
|
try {
|
||||||
@@ -126,6 +137,7 @@ public abstract class OutputStreamAbstractTestCase extends ObjectAbstractTestCas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testWriteByteArrayOffsetOutOfBounds() {
|
public void testWriteByteArrayOffsetOutOfBounds() {
|
||||||
OutputStream os = makeObject();
|
OutputStream os = makeObject();
|
||||||
try {
|
try {
|
||||||
@@ -143,6 +155,7 @@ public abstract class OutputStreamAbstractTestCase extends ObjectAbstractTestCas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testWriteByteArrayLengthOutOfBounds() {
|
public void testWriteByteArrayLengthOutOfBounds() {
|
||||||
OutputStream os = makeObject();
|
OutputStream os = makeObject();
|
||||||
try {
|
try {
|
||||||
@@ -160,14 +173,17 @@ public abstract class OutputStreamAbstractTestCase extends ObjectAbstractTestCas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testFlush() {
|
public void testFlush() {
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testClose() {
|
public void testClose() {
|
||||||
// TODO: Implement
|
// TODO: Implement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testWriteAfterClose() throws IOException {
|
public void testWriteAfterClose() throws IOException {
|
||||||
OutputStream os = makeObject();
|
OutputStream os = makeObject();
|
||||||
|
|
||||||
@@ -200,6 +216,7 @@ public abstract class OutputStreamAbstractTestCase extends ObjectAbstractTestCas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testFlushAfterClose() throws IOException {
|
public void testFlushAfterClose() throws IOException {
|
||||||
OutputStream os = makeObject();
|
OutputStream os = makeObject();
|
||||||
|
|
||||||
@@ -221,6 +238,7 @@ public abstract class OutputStreamAbstractTestCase extends ObjectAbstractTestCas
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testCloseAfterClose() throws IOException {
|
public void testCloseAfterClose() throws IOException {
|
||||||
OutputStream os = makeObject();
|
OutputStream os = makeObject();
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.ObjectAbstractTestCase;
|
import com.twelvemonkeys.lang.ObjectAbstractTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ReaderAbstractTestCase
|
* ReaderAbstractTestCase
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -36,6 +39,7 @@ public abstract class ReaderAbstractTestCase extends ObjectAbstractTestCase {
|
|||||||
|
|
||||||
protected abstract Reader makeReader(String pInput);
|
protected abstract Reader makeReader(String pInput);
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testRead() throws IOException {
|
public void testRead() throws IOException {
|
||||||
Reader reader = makeReader();
|
Reader reader = makeReader();
|
||||||
|
|
||||||
@@ -51,6 +55,7 @@ public abstract class ReaderAbstractTestCase extends ObjectAbstractTestCase {
|
|||||||
assertEquals(mInput, buffer.toString());
|
assertEquals(mInput, buffer.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testReadBuffer() throws IOException {
|
public void testReadBuffer() throws IOException {
|
||||||
Reader reader = makeReader();
|
Reader reader = makeReader();
|
||||||
|
|
||||||
@@ -70,6 +75,7 @@ public abstract class ReaderAbstractTestCase extends ObjectAbstractTestCase {
|
|||||||
assertEquals(mInput, new String(chars));
|
assertEquals(mInput, new String(chars));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSkipToEnd() throws IOException {
|
public void testSkipToEnd() throws IOException {
|
||||||
Reader reader = makeReader();
|
Reader reader = makeReader();
|
||||||
|
|
||||||
@@ -83,6 +89,7 @@ public abstract class ReaderAbstractTestCase extends ObjectAbstractTestCase {
|
|||||||
assertEquals(0, toSkip);
|
assertEquals(0, toSkip);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSkipToEndAndRead() throws IOException {
|
public void testSkipToEndAndRead() throws IOException {
|
||||||
Reader reader = makeReader();
|
Reader reader = makeReader();
|
||||||
|
|
||||||
@@ -95,6 +102,7 @@ public abstract class ReaderAbstractTestCase extends ObjectAbstractTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: It's possible to support reset and not mark (resets to beginning of stream, for example)
|
// TODO: It's possible to support reset and not mark (resets to beginning of stream, for example)
|
||||||
|
@Test
|
||||||
public void testResetMarkSupported() throws IOException {
|
public void testResetMarkSupported() throws IOException {
|
||||||
Reader reader = makeReader();
|
Reader reader = makeReader();
|
||||||
|
|
||||||
@@ -154,6 +162,7 @@ public abstract class ReaderAbstractTestCase extends ObjectAbstractTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testResetMarkNotSupported() throws IOException {
|
public void testResetMarkNotSupported() throws IOException {
|
||||||
Reader reader = makeReader();
|
Reader reader = makeReader();
|
||||||
|
|
||||||
@@ -198,7 +207,7 @@ public abstract class ReaderAbstractTestCase extends ObjectAbstractTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testReadAfterClose() throws IOException {
|
public void testReadAfterClose() throws IOException {
|
||||||
Reader reader = makeReader("foo bar");
|
Reader reader = makeReader("foo bar");
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SeekableAbstractTestCase
|
* SeekableAbstractTestCase
|
||||||
@@ -9,14 +11,16 @@ import junit.framework.TestCase;
|
|||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/SeekableAbstractTestCase.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/SeekableAbstractTestCase.java#1 $
|
||||||
*/
|
*/
|
||||||
public abstract class SeekableAbstractTestCase extends TestCase implements SeekableInterfaceTest {
|
public abstract class SeekableAbstractTestCase implements SeekableInterfaceTest {
|
||||||
|
|
||||||
protected abstract Seekable createSeekable();
|
protected abstract Seekable createSeekable();
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testFail() {
|
public void testFail() {
|
||||||
fail();
|
fail("Do not create stand-alone test classes based on this class. Instead, create an inner class and delegate to it.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSeekable() {
|
public void testSeekable() {
|
||||||
assertTrue(createSeekable() instanceof Seekable);
|
assertTrue(createSeekable() instanceof Seekable);
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-11
@@ -1,10 +1,14 @@
|
|||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.EOFException;
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SeekableInputStreamAbstractTestCase
|
* SeekableInputStreamAbstractTestCase
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -13,13 +17,8 @@ import java.io.InputStream;
|
|||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/SeekableInputStreamAbstractTestCase.java#4 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/SeekableInputStreamAbstractTestCase.java#4 $
|
||||||
*/
|
*/
|
||||||
public abstract class SeekableInputStreamAbstractTestCase extends InputStreamAbstractTestCase implements SeekableInterfaceTest {
|
public abstract class SeekableInputStreamAbstractTestCase extends InputStreamAbstractTestCase implements SeekableInterfaceTest {
|
||||||
|
|
||||||
public SeekableInputStreamAbstractTestCase(String name) {
|
|
||||||
super(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
//// TODO: Figure out a better way of creating interface tests without duplicating code
|
//// TODO: Figure out a better way of creating interface tests without duplicating code
|
||||||
final SeekableAbstractTestCase mSeekableTestCase = new SeekableAbstractTestCase() {
|
final SeekableAbstractTestCase seekableTestCase = new SeekableAbstractTestCase() {
|
||||||
protected Seekable createSeekable() {
|
protected Seekable createSeekable() {
|
||||||
return makeInputStream();
|
return makeInputStream();
|
||||||
}
|
}
|
||||||
@@ -41,6 +40,7 @@ public abstract class SeekableInputStreamAbstractTestCase extends InputStreamAbs
|
|||||||
|
|
||||||
protected abstract SeekableInputStream makeInputStream(InputStream pStream);
|
protected abstract SeekableInputStream makeInputStream(InputStream pStream);
|
||||||
|
|
||||||
|
@Test
|
||||||
@Override
|
@Override
|
||||||
public void testResetAfterReset() throws Exception {
|
public void testResetAfterReset() throws Exception {
|
||||||
InputStream input = makeInputStream(makeOrderedArray(25));
|
InputStream input = makeInputStream(makeOrderedArray(25));
|
||||||
@@ -59,9 +59,9 @@ public abstract class SeekableInputStreamAbstractTestCase extends InputStreamAbs
|
|||||||
assertTrue("Expected to read positive value", read >= 0);
|
assertTrue("Expected to read positive value", read >= 0);
|
||||||
|
|
||||||
input.reset();
|
input.reset();
|
||||||
assertEquals("Expected value read differes from actual", read, input.read());
|
assertEquals("Expected value read differs from actual", read, input.read());
|
||||||
|
|
||||||
// Reset after read limit passed, may either throw exception, or reset to last mark
|
// Reset after read limit passed, may either throw exception, or reset to last good mark
|
||||||
try {
|
try {
|
||||||
input.reset();
|
input.reset();
|
||||||
assertEquals("Re-read of reset data should be first", 0, input.read());
|
assertEquals("Re-read of reset data should be first", 0, input.read());
|
||||||
@@ -71,10 +71,12 @@ public abstract class SeekableInputStreamAbstractTestCase extends InputStreamAbs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSeekable() {
|
public void testSeekable() {
|
||||||
mSeekableTestCase.testSeekable();
|
seekableTestCase.testSeekable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testFlushBeyondCurrentPos() throws Exception {
|
public void testFlushBeyondCurrentPos() throws Exception {
|
||||||
SeekableInputStream seekable = makeInputStream(20);
|
SeekableInputStream seekable = makeInputStream(20);
|
||||||
|
|
||||||
@@ -88,6 +90,7 @@ public abstract class SeekableInputStreamAbstractTestCase extends InputStreamAbs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSeek() throws Exception {
|
public void testSeek() throws Exception {
|
||||||
SeekableInputStream seekable = makeInputStream(55);
|
SeekableInputStream seekable = makeInputStream(55);
|
||||||
int pos = 37;
|
int pos = 37;
|
||||||
@@ -97,6 +100,7 @@ public abstract class SeekableInputStreamAbstractTestCase extends InputStreamAbs
|
|||||||
assertEquals("Stream positon should match seeked position", pos, streamPos);
|
assertEquals("Stream positon should match seeked position", pos, streamPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSeekFlush() throws Exception {
|
public void testSeekFlush() throws Exception {
|
||||||
SeekableInputStream seekable = makeInputStream(133);
|
SeekableInputStream seekable = makeInputStream(133);
|
||||||
int pos = 45;
|
int pos = 45;
|
||||||
@@ -114,6 +118,7 @@ public abstract class SeekableInputStreamAbstractTestCase extends InputStreamAbs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testMarkFlushReset() throws Exception {
|
public void testMarkFlushReset() throws Exception {
|
||||||
SeekableInputStream seekable = makeInputStream(77);
|
SeekableInputStream seekable = makeInputStream(77);
|
||||||
|
|
||||||
@@ -134,6 +139,7 @@ public abstract class SeekableInputStreamAbstractTestCase extends InputStreamAbs
|
|||||||
assertEquals(position, seekable.getStreamPosition());
|
assertEquals(position, seekable.getStreamPosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testSeekSkipRead() throws Exception {
|
public void testSeekSkipRead() throws Exception {
|
||||||
SeekableInputStream seekable = makeInputStream(133);
|
SeekableInputStream seekable = makeInputStream(133);
|
||||||
int pos = 45;
|
int pos = 45;
|
||||||
@@ -147,7 +153,7 @@ public abstract class SeekableInputStreamAbstractTestCase extends InputStreamAbs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSeekSkip(SeekableInputStream pSeekable, String pStr) throws IOException {
|
protected void testSeekSkip(SeekableInputStream pSeekable, String pStr) throws IOException {
|
||||||
System.out.println();
|
System.out.println();
|
||||||
pSeekable.seek(pStr.length());
|
pSeekable.seek(pStr.length());
|
||||||
FileUtil.read(pSeekable);
|
FileUtil.read(pSeekable);
|
||||||
@@ -330,6 +336,7 @@ public abstract class SeekableInputStreamAbstractTestCase extends InputStreamAbs
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testReadResetReadDirectBufferBug() throws IOException {
|
public void testReadResetReadDirectBufferBug() throws IOException {
|
||||||
// Make sure we use the exact size of the buffer
|
// Make sure we use the exact size of the buffer
|
||||||
final int size = 1024;
|
final int size = 1024;
|
||||||
@@ -365,6 +372,7 @@ public abstract class SeekableInputStreamAbstractTestCase extends InputStreamAbs
|
|||||||
assertTrue(rangeEquals(bytes, size, result, 0, size));
|
assertTrue(rangeEquals(bytes, size, result, 0, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testReadAllByteValuesRegression() throws IOException {
|
public void testReadAllByteValuesRegression() throws IOException {
|
||||||
final int size = 128;
|
final int size = 128;
|
||||||
|
|
||||||
@@ -401,6 +409,7 @@ public abstract class SeekableInputStreamAbstractTestCase extends InputStreamAbs
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testCloseUnderlyingStream() throws IOException {
|
public void testCloseUnderlyingStream() throws IOException {
|
||||||
final boolean[] closed = new boolean[1];
|
final boolean[] closed = new boolean[1];
|
||||||
|
|
||||||
@@ -476,5 +485,4 @@ public abstract class SeekableInputStreamAbstractTestCase extends InputStreamAbs
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package com.twelvemonkeys.io;
|
package com.twelvemonkeys.io;
|
||||||
|
|
||||||
import com.twelvemonkeys.lang.StringUtil;
|
import com.twelvemonkeys.lang.StringUtil;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* StringArrayReaderTestCase
|
* StringArrayReaderTestCase
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -28,6 +31,7 @@ public class StringArrayReaderTestCase extends ReaderAbstractTestCase {
|
|||||||
return new StringArrayReader(input);
|
return new StringArrayReader(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testNullConstructor() {
|
public void testNullConstructor() {
|
||||||
try {
|
try {
|
||||||
new StringArrayReader(null);
|
new StringArrayReader(null);
|
||||||
@@ -38,15 +42,15 @@ public class StringArrayReaderTestCase extends ReaderAbstractTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testEmptyArrayConstructor() throws IOException {
|
public void testEmptyArrayConstructor() throws IOException {
|
||||||
Reader reader = new StringArrayReader(new String[0]);
|
Reader reader = new StringArrayReader(new String[0]);
|
||||||
assertEquals(-1, reader.read());
|
assertEquals(-1, reader.read());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testEmptyStringConstructor() throws IOException {
|
public void testEmptyStringConstructor() throws IOException {
|
||||||
Reader reader = new StringArrayReader(new String[] {""});
|
Reader reader = new StringArrayReader(new String[] {""});
|
||||||
assertEquals(-1, reader.read());
|
assertEquals(-1, reader.read());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ package com.twelvemonkeys.io.enc;
|
|||||||
|
|
||||||
|
|
||||||
import com.twelvemonkeys.io.FileUtil;
|
import com.twelvemonkeys.io.FileUtil;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base64DecoderTest
|
* Base64DecoderTest
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -22,6 +25,7 @@ public class Base64DecoderTestCase extends DecoderAbstractTestCase {
|
|||||||
return new Base64Encoder();
|
return new Base64Encoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testEmptyDecode2() throws IOException {
|
public void testEmptyDecode2() throws IOException {
|
||||||
String data = "";
|
String data = "";
|
||||||
|
|
||||||
@@ -33,6 +37,7 @@ public class Base64DecoderTestCase extends DecoderAbstractTestCase {
|
|||||||
assertEquals("Strings does not match", "", new String(bytes.toByteArray()));
|
assertEquals("Strings does not match", "", new String(bytes.toByteArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testShortDecode() throws IOException {
|
public void testShortDecode() throws IOException {
|
||||||
String data = "dGVzdA==";
|
String data = "dGVzdA==";
|
||||||
|
|
||||||
@@ -44,6 +49,7 @@ public class Base64DecoderTestCase extends DecoderAbstractTestCase {
|
|||||||
assertEquals("Strings does not match", "test", new String(bytes.toByteArray()));
|
assertEquals("Strings does not match", "test", new String(bytes.toByteArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testLongDecode() throws IOException {
|
public void testLongDecode() throws IOException {
|
||||||
String data = "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVlciBhZGlwaXNjaW5nIGVsaXQuIEZ1" +
|
String data = "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVlciBhZGlwaXNjaW5nIGVsaXQuIEZ1" +
|
||||||
"c2NlIGVzdC4gTW9yYmkgbHVjdHVzIGNvbnNlY3RldHVlciBqdXN0by4gVml2YW11cyBkYXBpYnVzIGxh" +
|
"c2NlIGVzdC4gTW9yYmkgbHVjdHVzIGNvbnNlY3RldHVlciBqdXN0by4gVml2YW11cyBkYXBpYnVzIGxh" +
|
||||||
@@ -62,4 +68,4 @@ public class Base64DecoderTestCase extends DecoderAbstractTestCase {
|
|||||||
"ullamcorper, nisi in dictum amet.",
|
"ullamcorper, nisi in dictum amet.",
|
||||||
new String(bytes.toByteArray()));
|
new String(bytes.toByteArray()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base64EncoderTest
|
* Base64EncoderTest
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -19,6 +23,7 @@ public class Base64EncoderTestCase extends EncoderAbstractTestCase {
|
|||||||
return new Base64Decoder();
|
return new Base64Decoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testNegativeEncode() throws IOException {
|
public void testNegativeEncode() throws IOException {
|
||||||
Encoder encoder = createEncoder();
|
Encoder encoder = createEncoder();
|
||||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
@@ -31,6 +36,7 @@ public class Base64EncoderTestCase extends EncoderAbstractTestCase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testEmptyEncode() throws IOException {
|
public void testEmptyEncode() throws IOException {
|
||||||
String data = "";
|
String data = "";
|
||||||
|
|
||||||
@@ -41,6 +47,7 @@ public class Base64EncoderTestCase extends EncoderAbstractTestCase {
|
|||||||
assertEquals("Strings does not match", "", new String(bytes.toByteArray()));
|
assertEquals("Strings does not match", "", new String(bytes.toByteArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testShortEncode() throws IOException {
|
public void testShortEncode() throws IOException {
|
||||||
String data = "test";
|
String data = "test";
|
||||||
|
|
||||||
@@ -51,6 +58,7 @@ public class Base64EncoderTestCase extends EncoderAbstractTestCase {
|
|||||||
assertEquals("Strings does not match", "dGVzdA==", new String(bytes.toByteArray()));
|
assertEquals("Strings does not match", "dGVzdA==", new String(bytes.toByteArray()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testLongEncode() throws IOException {
|
public void testLongEncode() throws IOException {
|
||||||
String data = "Lorem ipsum dolor sit amet, consectetuer adipiscing " +
|
String data = "Lorem ipsum dolor sit amet, consectetuer adipiscing " +
|
||||||
"elit. Fusce est. Morbi luctus consectetuer justo. Vivamus " +
|
"elit. Fusce est. Morbi luctus consectetuer justo. Vivamus " +
|
||||||
|
|||||||
+17
-18
@@ -2,9 +2,11 @@ package com.twelvemonkeys.io.enc;
|
|||||||
|
|
||||||
import com.twelvemonkeys.io.FileUtil;
|
import com.twelvemonkeys.io.FileUtil;
|
||||||
import com.twelvemonkeys.lang.ObjectAbstractTestCase;
|
import com.twelvemonkeys.lang.ObjectAbstractTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.Arrays;
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AbstractDecoderTest
|
* AbstractDecoderTest
|
||||||
@@ -22,24 +24,22 @@ public abstract class DecoderAbstractTestCase extends ObjectAbstractTestCase {
|
|||||||
return createDecoder();
|
return createDecoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = NullPointerException.class)
|
||||||
public final void testNullDecode() throws IOException {
|
public final void testNullDecode() throws IOException {
|
||||||
Decoder decoder = createDecoder();
|
Decoder decoder = createDecoder();
|
||||||
ByteArrayInputStream bytes = new ByteArrayInputStream(new byte[20]);
|
ByteArrayInputStream bytes = new ByteArrayInputStream(new byte[20]);
|
||||||
|
|
||||||
try {
|
decoder.decode(bytes, null);
|
||||||
decoder.decode(bytes, null);
|
fail("null should throw NullPointerException");
|
||||||
fail("null should throw NullPointerException");
|
|
||||||
}
|
|
||||||
catch (NullPointerException e) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public final void testEmptyDecode() throws IOException {
|
public final void testEmptyDecode() throws IOException {
|
||||||
Decoder decoder = createDecoder();
|
Decoder decoder = createDecoder();
|
||||||
ByteArrayInputStream bytes = new ByteArrayInputStream(new byte[0]);
|
ByteArrayInputStream bytes = new ByteArrayInputStream(new byte[0]);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
int count = decoder.decode(bytes, new byte[2]);
|
int count = decoder.decode(bytes, new byte[128]);
|
||||||
assertEquals("Should not be able to read any bytes", 0, count);
|
assertEquals("Should not be able to read any bytes", 0, count);
|
||||||
}
|
}
|
||||||
catch (EOFException allowed) {
|
catch (EOFException allowed) {
|
||||||
@@ -63,26 +63,25 @@ public abstract class DecoderAbstractTestCase extends ObjectAbstractTestCase {
|
|||||||
byte[] encoded = outBytes.toByteArray();
|
byte[] encoded = outBytes.toByteArray();
|
||||||
|
|
||||||
byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createDecoder()));
|
byte[] decoded = FileUtil.read(new DecoderStream(new ByteArrayInputStream(encoded), createDecoder()));
|
||||||
assertTrue(Arrays.equals(data, decoded));
|
assertArrayEquals(String.format("Data %d", pLength), data, decoded);
|
||||||
|
|
||||||
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createDecoder());
|
InputStream in = new DecoderStream(new ByteArrayInputStream(encoded), createDecoder());
|
||||||
outBytes = new ByteArrayOutputStream();
|
outBytes = new ByteArrayOutputStream();
|
||||||
/*
|
|
||||||
byte[] buffer = new byte[3];
|
|
||||||
for (int n = in.read(buffer); n > 0; n = in.read(buffer)) {
|
|
||||||
outBytes.write(buffer, 0, n);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
FileUtil.copy(in, outBytes);
|
FileUtil.copy(in, outBytes);
|
||||||
|
|
||||||
outBytes.close();
|
outBytes.close();
|
||||||
in.close();
|
in.close();
|
||||||
|
|
||||||
decoded = outBytes.toByteArray();
|
decoded = outBytes.toByteArray();
|
||||||
assertTrue(Arrays.equals(data, decoded));
|
assertArrayEquals(String.format("Data %d", pLength), data, decoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public final void testStreams() throws Exception {
|
public final void testStreams() throws Exception {
|
||||||
for (int i = 0; i < 100; i++) {
|
if (createCompatibleEncoder() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < 100; i++) {
|
||||||
try {
|
try {
|
||||||
runStreamTest(i);
|
runStreamTest(i);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,14 @@ package com.twelvemonkeys.io.enc;
|
|||||||
|
|
||||||
import com.twelvemonkeys.io.FileUtil;
|
import com.twelvemonkeys.io.FileUtil;
|
||||||
import com.twelvemonkeys.lang.ObjectAbstractTestCase;
|
import com.twelvemonkeys.lang.ObjectAbstractTestCase;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AbstractEncoderTest
|
* AbstractEncoderTest
|
||||||
* <p/>
|
* <p/>
|
||||||
@@ -26,6 +29,7 @@ public abstract class EncoderAbstractTestCase extends ObjectAbstractTestCase {
|
|||||||
return createEncoder();
|
return createEncoder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public final void testNullEncode() throws IOException {
|
public final void testNullEncode() throws IOException {
|
||||||
Encoder encoder = createEncoder();
|
Encoder encoder = createEncoder();
|
||||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||||
@@ -79,6 +83,7 @@ public abstract class EncoderAbstractTestCase extends ObjectAbstractTestCase {
|
|||||||
assertTrue(Arrays.equals(data, decoded));
|
assertTrue(Arrays.equals(data, decoded));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public final void testStreams() throws Exception {
|
public final void testStreams() throws Exception {
|
||||||
for (int i = 0; i < 100; i++) {
|
for (int i = 0; i < 100; i++) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
package com.twelvemonkeys.io.enc;
|
package com.twelvemonkeys.io.enc;
|
||||||
|
|
||||||
import com.twelvemonkeys.io.enc.Decoder;
|
|
||||||
import com.twelvemonkeys.io.enc.Encoder;
|
|
||||||
import com.twelvemonkeys.io.enc.PackBitsDecoder;
|
|
||||||
import com.twelvemonkeys.io.enc.PackBitsEncoder;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PackBitsDecoderTest
|
* PackBitsDecoderTest
|
||||||
* <p/>
|
* <p/>
|
||||||
|
|||||||
+106
-25
@@ -1,6 +1,7 @@
|
|||||||
package com.twelvemonkeys.io.ole2;
|
package com.twelvemonkeys.io.ole2;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import com.twelvemonkeys.io.MemoryCacheSeekableStream;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
import javax.imageio.stream.MemoryCacheImageInputStream;
|
import javax.imageio.stream.MemoryCacheImageInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -9,6 +10,10 @@ import java.io.InputStream;
|
|||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
|
import java.util.SortedSet;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CompoundDocumentTestCase
|
* CompoundDocumentTestCase
|
||||||
@@ -17,9 +22,89 @@ import java.nio.ByteOrder;
|
|||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocumentTestCase.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/io/ole2/CompoundDocumentTestCase.java#1 $
|
||||||
*/
|
*/
|
||||||
public class CompoundDocumentTestCase extends TestCase {
|
public class CompoundDocumentTestCase {
|
||||||
|
|
||||||
|
private static final String SAMPLE_DATA = "/Thumbs-camera.db";
|
||||||
|
|
||||||
|
protected final CompoundDocument createTestDocument() throws IOException {
|
||||||
|
URL input = getClass().getResource(SAMPLE_DATA);
|
||||||
|
|
||||||
|
assertNotNull("Missing test resource!", input);
|
||||||
|
assertEquals("Test resource not a file:// resource", "file", input.getProtocol());
|
||||||
|
|
||||||
|
try {
|
||||||
|
return new CompoundDocument(new File(input.toURI()));
|
||||||
|
}
|
||||||
|
catch (URISyntaxException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRoot() throws IOException {
|
||||||
|
CompoundDocument document = createTestDocument();
|
||||||
|
|
||||||
|
Entry root = document.getRootEntry();
|
||||||
|
|
||||||
|
assertNotNull(root);
|
||||||
|
assertEquals("Root Entry", root.getName());
|
||||||
|
assertTrue(root.isRoot());
|
||||||
|
assertFalse(root.isFile());
|
||||||
|
assertFalse(root.isDirectory());
|
||||||
|
assertEquals(0, root.length());
|
||||||
|
assertNull(root.getInputStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testContents() throws IOException {
|
||||||
|
CompoundDocument document = createTestDocument();
|
||||||
|
|
||||||
|
Entry root = document.getRootEntry();
|
||||||
|
|
||||||
|
assertNotNull(root);
|
||||||
|
|
||||||
|
SortedSet<Entry> children = new TreeSet<Entry>(root.getChildEntries());
|
||||||
|
assertEquals(25, children.size());
|
||||||
|
|
||||||
|
// Weirdness in the file format, name is *written backwards* 1-24 + Catalog
|
||||||
|
for (String name : "1,2,3,4,5,6,7,8,9,01,02,11,12,21,22,31,32,41,42,51,61,71,81,91,Catalog".split(",")) {
|
||||||
|
assertEquals(name, children.first().getName());
|
||||||
|
children.remove(children.first());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = UnsupportedOperationException.class)
|
||||||
|
public void testChildEntriesUnmodifiable() throws IOException {
|
||||||
|
CompoundDocument document = createTestDocument();
|
||||||
|
|
||||||
|
Entry root = document.getRootEntry();
|
||||||
|
|
||||||
|
assertNotNull(root);
|
||||||
|
|
||||||
|
SortedSet<Entry> children = root.getChildEntries();
|
||||||
|
|
||||||
|
// Should not be allowed, as it modifies the internal structure
|
||||||
|
children.remove(children.first());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadThumbsCatalogFile() throws IOException {
|
||||||
|
CompoundDocument document = createTestDocument();
|
||||||
|
|
||||||
|
Entry root = document.getRootEntry();
|
||||||
|
|
||||||
|
assertNotNull(root);
|
||||||
|
assertEquals(25, root.getChildEntries().size());
|
||||||
|
|
||||||
|
Entry catalog = root.getChildEntry("Catalog");
|
||||||
|
|
||||||
|
assertNotNull(catalog);
|
||||||
|
assertNotNull("Input stream may not be null", catalog.getInputStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testReadCatalogInputStream() throws IOException {
|
public void testReadCatalogInputStream() throws IOException {
|
||||||
InputStream input = getClass().getResourceAsStream("/Thumbs-camera.db");
|
InputStream input = getClass().getResourceAsStream(SAMPLE_DATA);
|
||||||
|
|
||||||
assertNotNull("Missing test resource!", input);
|
assertNotNull("Missing test resource!", input);
|
||||||
|
|
||||||
@@ -33,8 +118,25 @@ public class CompoundDocumentTestCase extends TestCase {
|
|||||||
assertNotNull("Input stream may not be null", catalog.getInputStream());
|
assertNotNull("Input stream may not be null", catalog.getInputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadCatalogSeekableStream() throws IOException {
|
||||||
|
InputStream input = getClass().getResourceAsStream(SAMPLE_DATA);
|
||||||
|
|
||||||
|
assertNotNull("Missing test resource!", input);
|
||||||
|
|
||||||
|
CompoundDocument document = new CompoundDocument(new MemoryCacheSeekableStream(input));
|
||||||
|
Entry root = document.getRootEntry();
|
||||||
|
assertNotNull(root);
|
||||||
|
assertEquals(25, root.getChildEntries().size());
|
||||||
|
|
||||||
|
Entry catalog = root.getChildEntry("Catalog");
|
||||||
|
assertNotNull(catalog);
|
||||||
|
assertNotNull("Input stream may not be null", catalog.getInputStream());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void testReadCatalogImageInputStream() throws IOException {
|
public void testReadCatalogImageInputStream() throws IOException {
|
||||||
InputStream input = getClass().getResourceAsStream("/Thumbs-camera.db");
|
InputStream input = getClass().getResourceAsStream(SAMPLE_DATA);
|
||||||
|
|
||||||
assertNotNull("Missing test resource!", input);
|
assertNotNull("Missing test resource!", input);
|
||||||
|
|
||||||
@@ -53,25 +155,4 @@ public class CompoundDocumentTestCase extends TestCase {
|
|||||||
assertNotNull(catalog);
|
assertNotNull(catalog);
|
||||||
assertNotNull("Input stream may not be null", catalog.getInputStream());
|
assertNotNull("Input stream may not be null", catalog.getInputStream());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadThumbsCatalogFile() throws IOException, URISyntaxException {
|
|
||||||
URL input = getClass().getResource("/Thumbs-camera.db");
|
|
||||||
|
|
||||||
assertNotNull("Missing test resource!", input);
|
|
||||||
assertEquals("Test resource not a file:// resource", "file", input.getProtocol());
|
|
||||||
|
|
||||||
File file = new File(input.toURI());
|
|
||||||
|
|
||||||
CompoundDocument document = new CompoundDocument(file);
|
|
||||||
|
|
||||||
Entry root = document.getRootEntry();
|
|
||||||
|
|
||||||
assertNotNull(root);
|
|
||||||
assertEquals(25, root.getChildEntries().size());
|
|
||||||
|
|
||||||
Entry catalog = root.getChildEntry("Catalog");
|
|
||||||
|
|
||||||
assertNotNull(catalog);
|
|
||||||
assertNotNull("Input stream may not be null", catalog.getInputStream());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+60
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011, Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name "TwelveMonkeys" nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.twelvemonkeys.io.ole2;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.io.*;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CompoundDocument_SeekableLittleEndianDataInputStreamTestCase
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haraldk$
|
||||||
|
* @version $Id: CompoundDocument_SeekableLittleEndianDataInputStreamTestCase.java,v 1.0 18.10.11 16:35 haraldk Exp$
|
||||||
|
*/
|
||||||
|
public class CompoundDocument_SeekableLittleEndianDataInputStreamTestCase extends InputStreamAbstractTestCase implements SeekableInterfaceTest {
|
||||||
|
private final SeekableInterfaceTest seekableTest = new SeekableAbstractTestCase() {
|
||||||
|
@Override
|
||||||
|
protected Seekable createSeekable() {
|
||||||
|
return (Seekable) makeInputStream();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected CompoundDocument.SeekableLittleEndianDataInputStream makeInputStream(byte[] pBytes) {
|
||||||
|
return new CompoundDocument.SeekableLittleEndianDataInputStream(new MemoryCacheSeekableStream(new ByteArrayInputStream(pBytes)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSeekable() {
|
||||||
|
seekableTest.testSeekable();
|
||||||
|
}
|
||||||
|
}
|
||||||
+207
@@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2011, Harald Kuhr
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name "TwelveMonkeys" nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.twelvemonkeys.io.ole2;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.io.InputStreamAbstractTestCase;
|
||||||
|
import com.twelvemonkeys.io.LittleEndianDataOutputStream;
|
||||||
|
import com.twelvemonkeys.io.MemoryCacheSeekableStream;
|
||||||
|
import com.twelvemonkeys.io.SeekableInputStream;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CompoundDocument_StreamTestCase
|
||||||
|
*
|
||||||
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
|
* @author last modified by $Author: haraldk$
|
||||||
|
* @version $Id: CompoundDocument_StreamTestCase.java,v 1.0 13.10.11 12:01 haraldk Exp$
|
||||||
|
*/
|
||||||
|
//@Ignore("Need proper in-memory creation of CompoundDocuments")
|
||||||
|
public class CompoundDocument_StreamTestCase extends InputStreamAbstractTestCase {
|
||||||
|
private static final String SAMPLE_DATA = "/Thumbs-camera.db";
|
||||||
|
|
||||||
|
protected final CompoundDocument createTestDocument() throws IOException {
|
||||||
|
URL input = getClass().getResource(SAMPLE_DATA);
|
||||||
|
|
||||||
|
assertNotNull("Missing test resource!", input);
|
||||||
|
assertEquals("Test resource not a file:// resource", "file", input.getProtocol());
|
||||||
|
|
||||||
|
try {
|
||||||
|
return new CompoundDocument(new File(input.toURI()));
|
||||||
|
}
|
||||||
|
catch (URISyntaxException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SeekableInputStream createRealInputStream() {
|
||||||
|
try {
|
||||||
|
Entry first = createTestDocument().getRootEntry().getChildEntries().first();
|
||||||
|
assertNotNull(first);
|
||||||
|
return first.getInputStream();
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected InputStream makeInputStream(byte[] data) {
|
||||||
|
try {
|
||||||
|
// Set up fake document
|
||||||
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
|
LittleEndianDataOutputStream dataStream = new LittleEndianDataOutputStream(stream);
|
||||||
|
|
||||||
|
dataStream.write(CompoundDocument.MAGIC); // 8 bytes magic
|
||||||
|
dataStream.write(new byte[16]); // UUID 16 bytes, all zero
|
||||||
|
dataStream.write(new byte[]{0x3E, 0, 3, 0}); // version (62), rev (3)
|
||||||
|
// 28
|
||||||
|
dataStream.write(new byte[]{(byte) 0xfe, (byte) 0xff}); // Byte order
|
||||||
|
dataStream.write(new byte[]{9, 0, 6, 0}); // Sector size (1 << x), short sector size
|
||||||
|
dataStream.write(new byte[10]); // Reserved 10 bytes
|
||||||
|
// 44
|
||||||
|
dataStream.writeInt(1); // SAT size (1)
|
||||||
|
dataStream.writeInt(1); // Directory SId
|
||||||
|
dataStream.write(new byte[4]); // Reserved 4 bytes
|
||||||
|
// 56
|
||||||
|
dataStream.writeInt(4096); // Min stream size (4096)
|
||||||
|
dataStream.writeInt(3); // Short SAT SId
|
||||||
|
dataStream.writeInt(1); // Short SAT size
|
||||||
|
dataStream.writeInt(-2); // Master SAT SId (-2, end of chain)
|
||||||
|
// 72
|
||||||
|
dataStream.writeInt(0); // Master SAT size
|
||||||
|
dataStream.writeInt(0); // Master SAT entry 0 (0)
|
||||||
|
dataStream.writeInt(128); // Master SAT entry 1 (128)
|
||||||
|
// 84
|
||||||
|
dataStream.write(createPad(428, (byte) -1)); // Pad (until 512 bytes)
|
||||||
|
// 512 -- end header
|
||||||
|
|
||||||
|
// SId 0
|
||||||
|
// SAT
|
||||||
|
dataStream.writeInt(-3); // SAT entry 0 (SAT)
|
||||||
|
dataStream.writeInt(-2); // SAT entry 1 (EOS)
|
||||||
|
dataStream.write(createPad(512 - 8, (byte) -1)); // Pad (until 512 bytes)
|
||||||
|
// 1024 -- end SAT
|
||||||
|
|
||||||
|
// SId 1
|
||||||
|
// Directory
|
||||||
|
// 64 bytes UTF16LE ("Root Entry" + null-termination)
|
||||||
|
byte[] name = "Root Entry".getBytes(Charset.forName("UTF-16LE"));
|
||||||
|
dataStream.write(name); // Name
|
||||||
|
dataStream.write(createPad(64 - name.length, (byte) 0)); // Pad name to 64 bytes
|
||||||
|
dataStream.writeShort((short) (name.length + 2)); // 2 byte length (incl null-term)
|
||||||
|
dataStream.write(new byte[]{5, 0}); // type (root), node color
|
||||||
|
dataStream.writeInt(-1); // prevDId, -1
|
||||||
|
dataStream.writeInt(-1); // nextDId, -1
|
||||||
|
dataStream.writeInt(1); // rootNodeDId
|
||||||
|
dataStream.write(createPad(36, (byte) 0)); // UID + flags + 2 x long timestamps
|
||||||
|
dataStream.writeInt(2); // Start SId
|
||||||
|
dataStream.writeInt(8); // Stream size
|
||||||
|
dataStream.writeInt(0); // Reserved
|
||||||
|
|
||||||
|
name = "data".getBytes(Charset.forName("UTF-16LE"));
|
||||||
|
dataStream.write(name); // Name
|
||||||
|
dataStream.write(createPad(64 - name.length, (byte) 0)); // Pad name to 64 bytes
|
||||||
|
dataStream.writeShort((short) (name.length + 2)); // 2 byte length (incl null-term)
|
||||||
|
dataStream.write(new byte[]{2, 0}); // type (user stream), node color
|
||||||
|
dataStream.writeInt(-1); // prevDId, -1
|
||||||
|
dataStream.writeInt(-1); // nextDId, -1
|
||||||
|
dataStream.writeInt(-1); // rootNodeDId
|
||||||
|
dataStream.write(createPad(36, (byte) 0)); // UID + flags + 2 x long timestamps
|
||||||
|
dataStream.writeInt(0); // Start SId
|
||||||
|
dataStream.writeInt(data.length); // Stream size
|
||||||
|
dataStream.writeInt(0); // Reserved
|
||||||
|
|
||||||
|
dataStream.write(createPad(512 - 256, (byte) -1)); // Pad to full sector (512 bytes)
|
||||||
|
// 1536 -- end Directory
|
||||||
|
|
||||||
|
// SId 2
|
||||||
|
// Data
|
||||||
|
dataStream.write(data); // The data
|
||||||
|
dataStream.write(createPad(512 - data.length, (byte) -1)); // Pad to full sector (512 bytes)
|
||||||
|
// 2048 -- end Data
|
||||||
|
|
||||||
|
// SId 3
|
||||||
|
// Short SAT
|
||||||
|
dataStream.writeInt(2); // Short SAT entry 0
|
||||||
|
dataStream.writeInt(-2); // Short SAT entry 1 (EOS)
|
||||||
|
dataStream.write(createPad(512 - 8, (byte) -1)); // Pad to full sector (512 bytes)
|
||||||
|
// 2560 -- end Short SAT
|
||||||
|
|
||||||
|
|
||||||
|
InputStream input = new ByteArrayInputStream(stream.toByteArray());
|
||||||
|
CompoundDocument document = new CompoundDocument(new MemoryCacheSeekableStream(input));
|
||||||
|
|
||||||
|
Entry entry = document.getRootEntry().getChildEntries().first();
|
||||||
|
|
||||||
|
return entry.getInputStream();
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] createPad(final int length, final byte val) {
|
||||||
|
byte[] pad = new byte[length];
|
||||||
|
Arrays.fill(pad, val);
|
||||||
|
return pad;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Ignore
|
||||||
|
@Test
|
||||||
|
public void testDev() throws IOException {
|
||||||
|
InputStream stream = makeInputStream(makeOrderedArray(32));
|
||||||
|
|
||||||
|
int read;
|
||||||
|
int count = 0;
|
||||||
|
while ((read = stream.read()) >= 0) {
|
||||||
|
// System.out.printf("read %02d: 0x%02x%n", count, read & 0xFF);
|
||||||
|
assertEquals(count, read);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertFalse("Short stream", count < 32);
|
||||||
|
assertFalse("Stream overrun", count > 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInputStreamSkip() throws IOException {
|
||||||
|
InputStream stream = makeInputStream();
|
||||||
|
|
||||||
|
// BUGFIX: Would skip and return 0 for first skip
|
||||||
|
assertTrue(stream.skip(10) > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -276,19 +276,19 @@ public final class BeanUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// If this does not throw an excption, it works
|
// If this does not throw an exception, it works
|
||||||
method = pObject.getClass().getMethod(pName, pParams);
|
method = pObject.getClass().getMethod(pName, pParams);
|
||||||
}
|
}
|
||||||
catch (Throwable t) {
|
catch (Throwable t) {
|
||||||
// Ignore
|
// Ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2: Try any supertypes of paramType, to see if we have a match
|
// 2: Try any super-types of paramType, to see if we have a match
|
||||||
if (method == null) {
|
if (method == null) {
|
||||||
while ((paramType = paramType.getSuperclass()) != null) {
|
while ((paramType = paramType.getSuperclass()) != null) {
|
||||||
pParams[0] = paramType;
|
pParams[0] = paramType;
|
||||||
try {
|
try {
|
||||||
// If this does not throw an excption, it works
|
// If this does not throw an exception, it works
|
||||||
method = pObject.getClass().getMethod(pName, pParams);
|
method = pObject.getClass().getMethod(pName, pParams);
|
||||||
}
|
}
|
||||||
catch (Throwable t) {
|
catch (Throwable t) {
|
||||||
@@ -365,6 +365,9 @@ public final class BeanUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Convert value to single-value array if needed
|
||||||
|
// TODO: Convert CSV String to string array (or potentially any type of array)
|
||||||
|
|
||||||
// TODO: Convert other types
|
// TODO: Convert other types
|
||||||
if (pValue instanceof String) {
|
if (pValue instanceof String) {
|
||||||
Converter converter = Converter.getInstance();
|
Converter converter = Converter.getInstance();
|
||||||
@@ -596,8 +599,7 @@ public final class BeanUtil {
|
|||||||
catch (NoSuchMethodException ignore) {
|
catch (NoSuchMethodException ignore) {
|
||||||
// If invocation failed, convert lisp-style and try again
|
// If invocation failed, convert lisp-style and try again
|
||||||
if (pLispToCamel && property.indexOf('-') > 0) {
|
if (pLispToCamel && property.indexOf('-') > 0) {
|
||||||
setPropertyValue(pBean, StringUtil.lispToCamel(property, false),
|
setPropertyValue(pBean, StringUtil.lispToCamel(property, false), entry.getValue());
|
||||||
entry.getValue());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,9 +30,6 @@ package com.twelvemonkeys.lang;
|
|||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.text.ParseException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A utility class with useful date manipulation methods and constants.
|
* A utility class with useful date manipulation methods and constants.
|
||||||
@@ -191,20 +188,4 @@ public final class DateUtil {
|
|||||||
int offset = pTimeZone.getOffset(pTime);
|
int offset = pTimeZone.getOffset(pTime);
|
||||||
return (((pTime + offset) / DAY) * DAY) - offset;
|
return (((pTime + offset) / DAY) * DAY) - offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] pArgs) throws ParseException {
|
|
||||||
DateFormat format = new SimpleDateFormat("yyyy.MM.dd HH.mm.ss S");
|
|
||||||
|
|
||||||
long time = pArgs.length > 0 ? format.parse(pArgs[0]).getTime() : System.currentTimeMillis();
|
|
||||||
|
|
||||||
System.out.println(time + ": " + format.format(new Date(time)));
|
|
||||||
time = roundToSecond(time);
|
|
||||||
System.out.println(time + ": " + format.format(new Date(time)));
|
|
||||||
time = roundToMinute(time);
|
|
||||||
System.out.println(time + ": " + format.format(new Date(time)));
|
|
||||||
time = roundToHour(time);
|
|
||||||
System.out.println(time + ": " + format.format(new Date(time)));
|
|
||||||
time = roundToDay(time);
|
|
||||||
System.out.println(time + ": " + format.format(new Date(time)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
package com.twelvemonkeys.lang;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.UndeclaredThrowableException;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ExceptionUtil
|
|
||||||
*
|
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
|
||||||
* @author last modified by $Author: haku $
|
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/ExceptionUtil.java#2 $
|
|
||||||
*/
|
|
||||||
public final class ExceptionUtil {
|
|
||||||
|
|
||||||
/*public*/ static void launder(final Throwable pThrowable, Class<? extends Throwable>... pExpectedTypes) {
|
|
||||||
if (pThrowable instanceof Error) {
|
|
||||||
throw (Error) pThrowable;
|
|
||||||
}
|
|
||||||
if (pThrowable instanceof RuntimeException) {
|
|
||||||
throw (RuntimeException) pThrowable;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Class<? extends Throwable> expectedType : pExpectedTypes) {
|
|
||||||
if (expectedType.isInstance(pThrowable)) {
|
|
||||||
throw new RuntimeException(pThrowable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new UndeclaredThrowableException(pThrowable);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked", "UnusedDeclaration"})
|
|
||||||
static <T extends Throwable> void throwAs(final Class<T> pType, final Throwable pThrowable) throws T {
|
|
||||||
throw (T) pThrowable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void throwUnchecked(final Throwable pThrowable) {
|
|
||||||
throwAs(RuntimeException.class, pThrowable);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*public*/ static void handle(final Throwable pThrowable, final ThrowableHandler<? extends Throwable>... pHandler) {
|
|
||||||
handleImpl(pThrowable, pHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({"unchecked"})
|
|
||||||
private static <T extends Throwable> void handleImpl(final Throwable pThrowable, final ThrowableHandler<T>... pHandler) {
|
|
||||||
// TODO: Sort more specific throwable handlers before less specific?
|
|
||||||
for (ThrowableHandler<T> handler : pHandler) {
|
|
||||||
if (handler.handles(pThrowable)) {
|
|
||||||
handler.handle((T) pThrowable);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throwUnchecked(pThrowable);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static abstract class ThrowableHandler<T extends Throwable> {
|
|
||||||
private Class<? extends T>[] mThrowables;
|
|
||||||
|
|
||||||
protected ThrowableHandler(final Class<? extends T>... pThrowables) {
|
|
||||||
// TODO: Assert not null
|
|
||||||
mThrowables = pThrowables.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
final public boolean handles(final Throwable pThrowable) {
|
|
||||||
for (Class<? extends T> throwable : mThrowables) {
|
|
||||||
if (throwable.isAssignableFrom(pThrowable.getClass())) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void handle(T pThrowable);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({"InfiniteLoopStatement"})
|
|
||||||
public static void main(String[] pArgs) {
|
|
||||||
while (true) {
|
|
||||||
foo();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void foo() {
|
|
||||||
try {
|
|
||||||
bar();
|
|
||||||
}
|
|
||||||
catch (Throwable t) {
|
|
||||||
handle(t,
|
|
||||||
new ThrowableHandler<IOException>(IOException.class) {
|
|
||||||
public void handle(final IOException pThrowable) {
|
|
||||||
System.out.println("IOException: " + pThrowable + " handled");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new ThrowableHandler<Exception>(SQLException.class, NumberFormatException.class) {
|
|
||||||
public void handle(final Exception pThrowable) {
|
|
||||||
System.out.println("Exception: " + pThrowable + " handled");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new ThrowableHandler<Throwable>(Throwable.class) {
|
|
||||||
public void handle(final Throwable pThrowable) {
|
|
||||||
System.err.println("Generic throwable: " + pThrowable + " NOT handled");
|
|
||||||
throwUnchecked(pThrowable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void bar() {
|
|
||||||
baz();
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({"ThrowableInstanceNeverThrown"})
|
|
||||||
private static void baz() {
|
|
||||||
double random = Math.random();
|
|
||||||
if (random < (1.0 / 3.0)) {
|
|
||||||
throwUnchecked(new FileNotFoundException("FNF Boo"));
|
|
||||||
}
|
|
||||||
if (random < (2.0 / 3.0)) {
|
|
||||||
throwUnchecked(new SQLException("SQL Boo"));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throwUnchecked(new Exception("Some Boo"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -43,36 +43,6 @@ public final class MathUtil {
|
|||||||
/** */
|
/** */
|
||||||
private MathUtil() {
|
private MathUtil() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts an angle measured in degrees to the equivalent angle measured
|
|
||||||
* in radians.
|
|
||||||
* This method is a replacement for the Math.toRadians() method in
|
|
||||||
* Java versions < 1.2 (typically for Applets).
|
|
||||||
*
|
|
||||||
* @param pAngDeg an angle, in degrees
|
|
||||||
* @return the measurement of the angle {@code angdeg} in radians.
|
|
||||||
*
|
|
||||||
* @see java.lang.Math#toRadians(double)
|
|
||||||
*/
|
|
||||||
public static double toRadians(final double pAngDeg) {
|
|
||||||
return pAngDeg * Math.PI / 180.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts an angle measured in radians to the equivalent angle measured
|
|
||||||
* in degrees.
|
|
||||||
* This method is a replacement for the Math.toDegrees() method in
|
|
||||||
* Java versions < 1.2 (typically for Applets).
|
|
||||||
*
|
|
||||||
* @param pAngRad an angle, in radians
|
|
||||||
* @return the measurement of the angle {@code angrad} in degrees.
|
|
||||||
*
|
|
||||||
* @see java.lang.Math#toDegrees(double)
|
|
||||||
*/
|
|
||||||
public static double toDegrees(final double pAngRad) {
|
|
||||||
return pAngRad * 180.0 / Math.PI;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the natural logarithm (base <I>e</I>) of a double value.
|
* Returns the natural logarithm (base <I>e</I>) of a double value.
|
||||||
@@ -135,7 +105,7 @@ public final class MathUtil {
|
|||||||
* @see Math#abs(long)
|
* @see Math#abs(long)
|
||||||
* @see Long#MIN_VALUE
|
* @see Long#MIN_VALUE
|
||||||
*
|
*
|
||||||
* @param pNumber
|
* @param pNumber a number
|
||||||
* @return the absolute value of {@code pNumber}
|
* @return the absolute value of {@code pNumber}
|
||||||
*
|
*
|
||||||
* @throws ArithmeticException if {@code pNumber == Long.MIN_VALUE}
|
* @throws ArithmeticException if {@code pNumber == Long.MIN_VALUE}
|
||||||
@@ -144,6 +114,7 @@ public final class MathUtil {
|
|||||||
if (pNumber == Long.MIN_VALUE) {
|
if (pNumber == Long.MIN_VALUE) {
|
||||||
throw new ArithmeticException("long overflow: 9223372036854775808");
|
throw new ArithmeticException("long overflow: 9223372036854775808");
|
||||||
}
|
}
|
||||||
|
|
||||||
return (pNumber < 0) ? -pNumber : pNumber;
|
return (pNumber < 0) ? -pNumber : pNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,7 +125,7 @@ public final class MathUtil {
|
|||||||
* @see Math#abs(int)
|
* @see Math#abs(int)
|
||||||
* @see Integer#MIN_VALUE
|
* @see Integer#MIN_VALUE
|
||||||
*
|
*
|
||||||
* @param pNumber
|
* @param pNumber a number
|
||||||
* @return the absolute value of {@code pNumber}
|
* @return the absolute value of {@code pNumber}
|
||||||
*
|
*
|
||||||
* @throws ArithmeticException if {@code pNumber == Integer.MIN_VALUE}
|
* @throws ArithmeticException if {@code pNumber == Integer.MIN_VALUE}
|
||||||
@@ -163,6 +134,7 @@ public final class MathUtil {
|
|||||||
if (pNumber == Integer.MIN_VALUE) {
|
if (pNumber == Integer.MIN_VALUE) {
|
||||||
throw new ArithmeticException("int overflow: 2147483648");
|
throw new ArithmeticException("int overflow: 2147483648");
|
||||||
}
|
}
|
||||||
|
|
||||||
return (pNumber < 0) ? -pNumber : pNumber;
|
return (pNumber < 0) ? -pNumber : pNumber;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,8 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.lang;
|
package com.twelvemonkeys.lang;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Platform
|
* Platform
|
||||||
*
|
*
|
||||||
@@ -39,40 +41,46 @@ public final class Platform {
|
|||||||
/**
|
/**
|
||||||
* Normalized operating system constant
|
* Normalized operating system constant
|
||||||
*/
|
*/
|
||||||
final OperatingSystem mOS;
|
final OperatingSystem os;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unormalized operating system version constant (for completeness)
|
* Unnormalized operating system version constant (for completeness)
|
||||||
*/
|
*/
|
||||||
final String mVersion;
|
final String version;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalized system architecture constant
|
* Normalized system architecture constant
|
||||||
*/
|
*/
|
||||||
final Architecture mArchitecture;
|
final Architecture architecture;
|
||||||
|
|
||||||
static final private Platform INSTANCE = new Platform();
|
static final private Platform INSTANCE = new Platform();
|
||||||
|
|
||||||
private Platform() {
|
private Platform() {
|
||||||
mOS = normalizeOperatingSystem();
|
this(System.getProperties());
|
||||||
mVersion = System.getProperty("os.version");
|
|
||||||
mArchitecture = normalizeArchitecture(mOS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static OperatingSystem normalizeOperatingSystem() {
|
Platform(final Properties properties) {
|
||||||
String os = System.getProperty("os.name");
|
os = normalizeOperatingSystem(properties.getProperty("os.name"));
|
||||||
|
version = properties.getProperty("os.version");
|
||||||
|
architecture = normalizeArchitecture(os, properties.getProperty("os.arch"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static OperatingSystem normalizeOperatingSystem(final String osName) {
|
||||||
|
String os = osName;
|
||||||
|
|
||||||
if (os == null) {
|
if (os == null) {
|
||||||
throw new IllegalStateException("System property \"os.name\" == null");
|
throw new IllegalStateException("System property \"os.name\" == null");
|
||||||
}
|
}
|
||||||
|
|
||||||
os = os.toLowerCase();
|
os = os.toLowerCase();
|
||||||
|
|
||||||
if (os.startsWith("windows")) {
|
if (os.startsWith("windows")) {
|
||||||
return OperatingSystem.Windows;
|
return OperatingSystem.Windows;
|
||||||
}
|
}
|
||||||
else if (os.startsWith("linux")) {
|
else if (os.startsWith("linux")) {
|
||||||
return OperatingSystem.Linux;
|
return OperatingSystem.Linux;
|
||||||
}
|
}
|
||||||
else if (os.startsWith("mac os")) {
|
else if (os.startsWith("mac os") || os.startsWith("darwin")) {
|
||||||
return OperatingSystem.MacOS;
|
return OperatingSystem.MacOS;
|
||||||
}
|
}
|
||||||
else if (os.startsWith("solaris") || os.startsWith("sunos")) {
|
else if (os.startsWith("solaris") || os.startsWith("sunos")) {
|
||||||
@@ -82,15 +90,16 @@ public final class Platform {
|
|||||||
return OperatingSystem.Unknown;
|
return OperatingSystem.Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Architecture normalizeArchitecture(final OperatingSystem pOsName) {
|
static Architecture normalizeArchitecture(final OperatingSystem pOsName, final String osArch) {
|
||||||
String arch = System.getProperty("os.arch");
|
String arch = osArch;
|
||||||
|
|
||||||
if (arch == null) {
|
if (arch == null) {
|
||||||
throw new IllegalStateException("System property \"os.arch\" == null");
|
throw new IllegalStateException("System property \"os.arch\" == null");
|
||||||
}
|
}
|
||||||
|
|
||||||
arch = arch.toLowerCase();
|
arch = arch.toLowerCase();
|
||||||
if (pOsName == OperatingSystem.Windows
|
|
||||||
&& (arch.startsWith("x86") || arch.startsWith("i386"))) {
|
if (pOsName == OperatingSystem.Windows && (arch.startsWith("x86") || arch.startsWith("i386"))) {
|
||||||
return Architecture.X86;
|
return Architecture.X86;
|
||||||
// TODO: 64 bit
|
// TODO: 64 bit
|
||||||
}
|
}
|
||||||
@@ -101,6 +110,9 @@ public final class Platform {
|
|||||||
else if (arch.startsWith("i686")) {
|
else if (arch.startsWith("i686")) {
|
||||||
return Architecture.I686;
|
return Architecture.I686;
|
||||||
}
|
}
|
||||||
|
else if (arch.startsWith("power") || arch.startsWith("ppc")) {
|
||||||
|
return Architecture.PPC;
|
||||||
|
}
|
||||||
// TODO: More Linux options?
|
// TODO: More Linux options?
|
||||||
// TODO: 64 bit
|
// TODO: 64 bit
|
||||||
}
|
}
|
||||||
@@ -108,9 +120,13 @@ public final class Platform {
|
|||||||
if (arch.startsWith("power") || arch.startsWith("ppc")) {
|
if (arch.startsWith("power") || arch.startsWith("ppc")) {
|
||||||
return Architecture.PPC;
|
return Architecture.PPC;
|
||||||
}
|
}
|
||||||
else if (arch.startsWith("i386")) {
|
else if (arch.startsWith("x86")) {
|
||||||
return Architecture.I386;
|
return Architecture.X86;
|
||||||
}
|
}
|
||||||
|
else if (arch.startsWith("i386")) {
|
||||||
|
return Architecture.X86;
|
||||||
|
}
|
||||||
|
// TODO: 64 bit
|
||||||
}
|
}
|
||||||
else if (pOsName == OperatingSystem.Solaris) {
|
else if (pOsName == OperatingSystem.Solaris) {
|
||||||
if (arch.startsWith("sparc")) {
|
if (arch.startsWith("sparc")) {
|
||||||
@@ -138,21 +154,21 @@ public final class Platform {
|
|||||||
* @return this platform's OS.
|
* @return this platform's OS.
|
||||||
*/
|
*/
|
||||||
public OperatingSystem getOS() {
|
public OperatingSystem getOS() {
|
||||||
return mOS;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return this platform's OS version.
|
* @return this platform's OS version.
|
||||||
*/
|
*/
|
||||||
public String getVersion() {
|
public String getVersion() {
|
||||||
return mVersion;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return this platform's architecture.
|
* @return this platform's architecture.
|
||||||
*/
|
*/
|
||||||
public Architecture getArchitecture() {
|
public Architecture getArchitecture() {
|
||||||
return mArchitecture;
|
return architecture;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -160,7 +176,7 @@ public final class Platform {
|
|||||||
* @return the current {@code OperatingSystem}.
|
* @return the current {@code OperatingSystem}.
|
||||||
*/
|
*/
|
||||||
public static OperatingSystem os() {
|
public static OperatingSystem os() {
|
||||||
return INSTANCE.mOS;
|
return INSTANCE.os;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -168,7 +184,7 @@ public final class Platform {
|
|||||||
* @return the current OS version.
|
* @return the current OS version.
|
||||||
*/
|
*/
|
||||||
public static String version() {
|
public static String version() {
|
||||||
return INSTANCE.mVersion;
|
return INSTANCE.version;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -176,7 +192,7 @@ public final class Platform {
|
|||||||
* @return the current {@code Architecture}.
|
* @return the current {@code Architecture}.
|
||||||
*/
|
*/
|
||||||
public static Architecture arch() {
|
public static Architecture arch() {
|
||||||
return INSTANCE.mArchitecture;
|
return INSTANCE.architecture;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -197,14 +213,14 @@ public final class Platform {
|
|||||||
|
|
||||||
Unknown(System.getProperty("os.arch"));
|
Unknown(System.getProperty("os.arch"));
|
||||||
|
|
||||||
final String mName;// for debug only
|
final String name;// for debug only
|
||||||
|
|
||||||
private Architecture(String pName) {
|
private Architecture(String pName) {
|
||||||
mName = pName;
|
name = pName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return mName;
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,22 +239,26 @@ public final class Platform {
|
|||||||
Solaris("Solaris", "sun"),
|
Solaris("Solaris", "sun"),
|
||||||
MacOS("Mac OS", "osx"),
|
MacOS("Mac OS", "osx"),
|
||||||
|
|
||||||
Unknown(System.getProperty("os.name"), "");
|
Unknown(System.getProperty("os.name"), null);
|
||||||
|
|
||||||
final String mId;
|
final String id;
|
||||||
final String mName;// for debug only
|
final String name;// for debug only
|
||||||
|
|
||||||
private OperatingSystem(String pName, String pId) {
|
private OperatingSystem(String pName, String pId) {
|
||||||
mName = pName;
|
name = pName;
|
||||||
mId = pId;
|
id = pId != null ? pId : pName.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return mName;
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String id() {
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return mId;
|
return String.format("%s (%s)", id, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public final class StringUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a new {@link String} by decoding the specified subarray of bytes using the specified charset.
|
* Constructs a new {@link String} by decoding the specified sub array of bytes using the specified charset.
|
||||||
* Replacement for {@link String#String(byte[], int, int, String) new String(byte[], int, int, String)}, that does
|
* Replacement for {@link String#String(byte[], int, int, String) new String(byte[], int, int, String)}, that does
|
||||||
* not throw the checked {@link UnsupportedEncodingException},
|
* not throw the checked {@link UnsupportedEncodingException},
|
||||||
* but instead the unchecked {@link UnsupportedCharsetException} if the character set is not supported.
|
* but instead the unchecked {@link UnsupportedCharsetException} if the character set is not supported.
|
||||||
@@ -1580,7 +1580,7 @@ public final class StringUtil {
|
|||||||
* Converts a string array to a string separated by the given delimiter.
|
* Converts a string array to a string separated by the given delimiter.
|
||||||
*
|
*
|
||||||
* @param pStringArray the string array
|
* @param pStringArray the string array
|
||||||
* @param pDelimiterString the delimter string
|
* @param pDelimiterString the delimiter string
|
||||||
* @return string of delimiter separated values
|
* @return string of delimiter separated values
|
||||||
* @throws IllegalArgumentException if {@code pDelimiterString == null}
|
* @throws IllegalArgumentException if {@code pDelimiterString == null}
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -81,8 +81,7 @@ public final class SystemUtil {
|
|||||||
*
|
*
|
||||||
* @return an input stream reading from the resource
|
* @return an input stream reading from the resource
|
||||||
*/
|
*/
|
||||||
private static InputStream getResourceAsStream(ClassLoader pClassLoader, String pName,
|
private static InputStream getResourceAsStream(ClassLoader pClassLoader, String pName, boolean pGuessSuffix) {
|
||||||
boolean pGuessSuffix) {
|
|
||||||
InputStream is;
|
InputStream is;
|
||||||
|
|
||||||
if (!pGuessSuffix) {
|
if (!pGuessSuffix) {
|
||||||
@@ -122,8 +121,7 @@ public final class SystemUtil {
|
|||||||
*
|
*
|
||||||
* @return an input stream reading from the resource
|
* @return an input stream reading from the resource
|
||||||
*/
|
*/
|
||||||
private static InputStream getFileAsStream(String pName,
|
private static InputStream getFileAsStream(String pName, boolean pGuessSuffix) {
|
||||||
boolean pGuessSuffix) {
|
|
||||||
InputStream is = null;
|
InputStream is = null;
|
||||||
File propertiesFile;
|
File propertiesFile;
|
||||||
|
|
||||||
@@ -206,8 +204,7 @@ public final class SystemUtil {
|
|||||||
* @todo Reconsider ever using the System ClassLoader: http://www.javaworld.com/javaworld/javaqa/2003-06/01-qa-0606-load.html
|
* @todo Reconsider ever using the System ClassLoader: http://www.javaworld.com/javaworld/javaqa/2003-06/01-qa-0606-load.html
|
||||||
* @todo Consider using Context Classloader instead?
|
* @todo Consider using Context Classloader instead?
|
||||||
*/
|
*/
|
||||||
public static Properties loadProperties(Class pClass, String pName)
|
public static Properties loadProperties(Class pClass, String pName) throws IOException
|
||||||
throws IOException
|
|
||||||
{
|
{
|
||||||
// Convert to name the classloader understands
|
// Convert to name the classloader understands
|
||||||
String name = !StringUtil.isEmpty(pName) ? pName : pClass.getName().replace('.', '/');
|
String name = !StringUtil.isEmpty(pName) ? pName : pClass.getName().replace('.', '/');
|
||||||
@@ -219,8 +216,7 @@ public final class SystemUtil {
|
|||||||
|
|
||||||
// TODO: WHAT IF MULTIPLE RESOURCES EXISTS?!
|
// TODO: WHAT IF MULTIPLE RESOURCES EXISTS?!
|
||||||
// Try loading resource through the current class' classloader
|
// Try loading resource through the current class' classloader
|
||||||
if (pClass != null
|
if (pClass != null && (is = getResourceAsStream(pClass.getClassLoader(), name, guessSuffix)) != null) {
|
||||||
&& (is = getResourceAsStream(pClass.getClassLoader(), name, guessSuffix)) != null) {
|
|
||||||
//&& (is = getResourceAsStream(pClass, name, guessSuffix)) != null) {
|
//&& (is = getResourceAsStream(pClass, name, guessSuffix)) != null) {
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
//System.out.println(((is instanceof XMLPropertiesInputStream) ?
|
//System.out.println(((is instanceof XMLPropertiesInputStream) ?
|
||||||
@@ -228,9 +224,8 @@ public final class SystemUtil {
|
|||||||
// + " from Class' ClassLoader");
|
// + " from Class' ClassLoader");
|
||||||
}
|
}
|
||||||
// If that fails, try the system classloader
|
// If that fails, try the system classloader
|
||||||
else if ((is = getResourceAsStream(ClassLoader.getSystemClassLoader(),
|
else if ((is = getResourceAsStream(ClassLoader.getSystemClassLoader(), name, guessSuffix)) != null) {
|
||||||
name, guessSuffix)) != null) {
|
//else if ((is = getSystemResourceAsStream(name, guessSuffix)) != null) {
|
||||||
//else if ((is = getSystemResourceAsStream(name, guessSuffix)) != null) {
|
|
||||||
// Nothing to do
|
// Nothing to do
|
||||||
//System.out.println(((is instanceof XMLPropertiesInputStream) ?
|
//System.out.println(((is instanceof XMLPropertiesInputStream) ?
|
||||||
// "XML-properties" : "Normal .properties")
|
// "XML-properties" : "Normal .properties")
|
||||||
@@ -682,8 +677,8 @@ public final class SystemUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Class getClass(String pClassName, boolean pInitialize, ClassLoader pLoader) throws ClassNotFoundException {
|
private static Class getClass(String pClassName, boolean pInitialize, ClassLoader pLoader) throws ClassNotFoundException {
|
||||||
// NOTE: We need the context classloader, as SystemUtil's
|
// NOTE: We need the context class loader, as SystemUtil's
|
||||||
// classloader may have a totally different classloader than
|
// class loader may have a totally different class loader than
|
||||||
// the original caller class (as in Class.forName(cn, false, null)).
|
// the original caller class (as in Class.forName(cn, false, null)).
|
||||||
ClassLoader loader = pLoader != null ? pLoader :
|
ClassLoader loader = pLoader != null ? pLoader :
|
||||||
Thread.currentThread().getContextClassLoader();
|
Thread.currentThread().getContextClassLoader();
|
||||||
|
|||||||
@@ -9,13 +9,15 @@ import java.util.Map;
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Uses type parameterized return values, thus making it possible to check
|
* Uses type parameterized return values, thus making it possible to check
|
||||||
* constructor arguments before
|
* constructor arguments before
|
||||||
* they are passed on to {@code super} or {@code this} type constructors.
|
* they are passed on to {@code super} or {@code this} type constructors.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/Validate.java#1 $
|
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/Validate.java#1 $
|
||||||
*/
|
*/
|
||||||
public final class Validate {
|
public final class Validate {
|
||||||
|
// TODO: Make it possible to throw IllegalStateException instead of IllegalArgumentException?
|
||||||
|
|
||||||
private static final String UNSPECIFIED_PARAM_NAME = "method parameter";
|
private static final String UNSPECIFIED_PARAM_NAME = "method parameter";
|
||||||
|
|
||||||
private Validate() {}
|
private Validate() {}
|
||||||
@@ -30,23 +32,34 @@ public final class Validate {
|
|||||||
if (pParameter == null) {
|
if (pParameter == null) {
|
||||||
throw new IllegalArgumentException(String.format("%s may not be null", pParamName == null ? UNSPECIFIED_PARAM_NAME : pParamName));
|
throw new IllegalArgumentException(String.format("%s may not be null", pParamName == null ? UNSPECIFIED_PARAM_NAME : pParamName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return pParameter;
|
return pParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not empty...
|
// Not empty
|
||||||
|
|
||||||
public static <T extends CharSequence> T notEmpty(final T pParameter) {
|
public static <T extends CharSequence> T notEmpty(final T pParameter) {
|
||||||
return notEmpty(pParameter, null);
|
return notEmpty(pParameter, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T extends CharSequence> T notEmpty(final T pParameter, final String pParamName) {
|
public static <T extends CharSequence> T notEmpty(final T pParameter, final String pParamName) {
|
||||||
if (pParameter == null || pParameter.length() == 0) {
|
if (pParameter == null || pParameter.length() == 0 || isOnlyWhiteSpace(pParameter)) {
|
||||||
throw new IllegalArgumentException(String.format("%s may not be empty", pParamName == null ? UNSPECIFIED_PARAM_NAME : pParamName));
|
throw new IllegalArgumentException(String.format("%s may not be blank", pParamName == null ? UNSPECIFIED_PARAM_NAME : pParamName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return pParameter;
|
return pParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <T extends CharSequence> boolean isOnlyWhiteSpace(T pParameter) {
|
||||||
|
for (int i = 0; i < pParameter.length(); i++) {
|
||||||
|
if (!Character.isWhitespace(pParameter.charAt(i))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public static <T> T[] notEmpty(final T[] pParameter) {
|
public static <T> T[] notEmpty(final T[] pParameter) {
|
||||||
return notEmpty(pParameter, null);
|
return notEmpty(pParameter, null);
|
||||||
}
|
}
|
||||||
@@ -90,7 +103,7 @@ public final class Validate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T[] noNullElements(final T[] pParameter, final String pParamName) {
|
public static <T> T[] noNullElements(final T[] pParameter, final String pParamName) {
|
||||||
noNullElements(Arrays.asList(pParameter), pParamName);
|
noNullElements(pParameter == null ? null : Arrays.asList(pParameter), pParamName);
|
||||||
return pParameter;
|
return pParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,6 +112,8 @@ public final class Validate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static <T> Collection<T> noNullElements(final Collection<T> pParameter, final String pParamName) {
|
public static <T> Collection<T> noNullElements(final Collection<T> pParameter, final String pParamName) {
|
||||||
|
notNull(pParameter, pParamName);
|
||||||
|
|
||||||
for (T element : pParameter) {
|
for (T element : pParameter) {
|
||||||
if (element == null) {
|
if (element == null) {
|
||||||
throw new IllegalArgumentException(String.format("%s may not contain null elements", pParamName == null ? UNSPECIFIED_PARAM_NAME : pParamName));
|
throw new IllegalArgumentException(String.format("%s may not contain null elements", pParamName == null ? UNSPECIFIED_PARAM_NAME : pParamName));
|
||||||
@@ -108,17 +123,49 @@ public final class Validate {
|
|||||||
return pParameter;
|
return pParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <K, V> Map<K, V> noNullElements(final Map<K, V> pParameter) {
|
public static <K, V> Map<K, V> noNullValues(final Map<K, V> pParameter) {
|
||||||
return noNullElements(pParameter, null);
|
return noNullValues(pParameter, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <K, V> Map<K, V> noNullElements(final Map<K, V> pParameter, final String pParamName) {
|
public static <K, V> Map<K, V> noNullValues(final Map<K, V> pParameter, final String pParamName) {
|
||||||
for (V element : pParameter.values()) {
|
notNull(pParameter, pParamName);
|
||||||
if (element == null) {
|
|
||||||
throw new IllegalArgumentException(String.format("%s may not contain null elements", pParamName == null ? UNSPECIFIED_PARAM_NAME : pParamName));
|
for (V value : pParameter.values()) {
|
||||||
|
if (value == null) {
|
||||||
|
throw new IllegalArgumentException(String.format("%s may not contain null values", pParamName == null ? UNSPECIFIED_PARAM_NAME : pParamName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pParameter;
|
return pParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <K, V> Map<K, V> noNullKeys(final Map<K, V> pParameter) {
|
||||||
|
return noNullKeys(pParameter, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <K, V> Map<K, V> noNullKeys(final Map<K, V> pParameter, final String pParamName) {
|
||||||
|
notNull(pParameter, pParamName);
|
||||||
|
|
||||||
|
for (K key : pParameter.keySet()) {
|
||||||
|
if (key == null) {
|
||||||
|
throw new IllegalArgumentException(String.format("%s may not contain null keys", pParamName == null ? UNSPECIFIED_PARAM_NAME : pParamName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is true
|
||||||
|
|
||||||
|
public static boolean isTrue(final boolean pExpression, final String pMessage) {
|
||||||
|
return isTrue(pExpression, pExpression, pMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T isTrue(final boolean condition, final T value, final String message) {
|
||||||
|
if (!condition) {
|
||||||
|
throw new IllegalArgumentException(String.format(message == null ? "expression may not be %s" : message, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,12 +40,12 @@ import java.io.Serializable;
|
|||||||
*/
|
*/
|
||||||
// TODO: The generics in this class looks suspicious..
|
// TODO: The generics in this class looks suspicious..
|
||||||
abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Serializable, Cloneable {
|
abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Serializable, Cloneable {
|
||||||
protected Map<K, Entry<K, V>> mEntries;
|
protected Map<K, Entry<K, V>> entries;
|
||||||
protected transient volatile int mModCount;
|
protected transient volatile int modCount;
|
||||||
|
|
||||||
private transient volatile Set<Entry<K, V>> mEntrySet = null;
|
private transient volatile Set<Entry<K, V>> entrySet = null;
|
||||||
private transient volatile Set<K> mKeySet = null;
|
private transient volatile Set<K> keySet = null;
|
||||||
private transient volatile Collection<V> mValues = null;
|
private transient volatile Collection<V> values = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code Map} backed by a {@code HashMap}.
|
* Creates a {@code Map} backed by a {@code HashMap}.
|
||||||
@@ -104,7 +104,7 @@ abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements M
|
|||||||
throw new IllegalArgumentException("backing must be empty");
|
throw new IllegalArgumentException("backing must be empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
mEntries = pBacking;
|
this.entries = pBacking;
|
||||||
init();
|
init();
|
||||||
|
|
||||||
if (pContents != null) {
|
if (pContents != null) {
|
||||||
@@ -125,21 +125,21 @@ abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements M
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
return mEntries.size();
|
return entries.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear() {
|
public void clear() {
|
||||||
mEntries.clear();
|
entries.clear();
|
||||||
mModCount++;
|
modCount++;
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEmpty() {
|
public boolean isEmpty() {
|
||||||
return mEntries.isEmpty();
|
return entries.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean containsKey(Object pKey) {
|
public boolean containsKey(Object pKey) {
|
||||||
return mEntries.containsKey(pKey);
|
return entries.containsKey(pKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -166,18 +166,18 @@ abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements M
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Collection<V> values() {
|
public Collection<V> values() {
|
||||||
Collection<V> values = mValues;
|
Collection<V> values = this.values;
|
||||||
return values != null ? values : (mValues = new Values());
|
return values != null ? values : (this.values = new Values());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Entry<K, V>> entrySet() {
|
public Set<Entry<K, V>> entrySet() {
|
||||||
Set<Entry<K, V>> es = mEntrySet;
|
Set<Entry<K, V>> es = entrySet;
|
||||||
return es != null ? es : (mEntrySet = new EntrySet());
|
return es != null ? es : (entrySet = new EntrySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<K> keySet() {
|
public Set<K> keySet() {
|
||||||
Set<K> ks = mKeySet;
|
Set<K> ks = keySet;
|
||||||
return ks != null ? ks : (mKeySet = new KeySet());
|
return ks != null ? ks : (keySet = new KeySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -189,9 +189,9 @@ abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements M
|
|||||||
protected Object clone() throws CloneNotSupportedException {
|
protected Object clone() throws CloneNotSupportedException {
|
||||||
AbstractDecoratedMap map = (AbstractDecoratedMap) super.clone();
|
AbstractDecoratedMap map = (AbstractDecoratedMap) super.clone();
|
||||||
|
|
||||||
map.mValues = null;
|
map.values = null;
|
||||||
map.mEntrySet = null;
|
map.entrySet = null;
|
||||||
map.mKeySet = null;
|
map.keySet = null;
|
||||||
|
|
||||||
// TODO: Implement: Need to clone the backing map...
|
// TODO: Implement: Need to clone the backing map...
|
||||||
|
|
||||||
@@ -217,7 +217,7 @@ abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements M
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*protected*/ Entry<K, V> getEntry(K pKey) {
|
/*protected*/ Entry<K, V> getEntry(K pKey) {
|
||||||
return mEntries.get(pKey);
|
return entries.get(pKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -271,7 +271,7 @@ abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements M
|
|||||||
Entry e = (Entry) o;
|
Entry e = (Entry) o;
|
||||||
|
|
||||||
//noinspection SuspiciousMethodCalls
|
//noinspection SuspiciousMethodCalls
|
||||||
Entry<K, V> candidate = mEntries.get(e.getKey());
|
Entry<K, V> candidate = entries.get(e.getKey());
|
||||||
return candidate != null && candidate.equals(e);
|
return candidate != null && candidate.equals(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +284,7 @@ abstract class AbstractDecoratedMap<K, V> extends AbstractMap<K, V> implements M
|
|||||||
// NOTE: Extra cautions is taken, to only remove the entry if it
|
// NOTE: Extra cautions is taken, to only remove the entry if it
|
||||||
// equals the entry in the map
|
// equals the entry in the map
|
||||||
Object key = ((Entry) o).getKey();
|
Object key = ((Entry) o).getKey();
|
||||||
Entry entry = (Entry) mEntries.get(key);
|
Entry entry = (Entry) entries.get(key);
|
||||||
|
|
||||||
// Same entry?
|
// Same entry?
|
||||||
if (entry != null && entry.equals(o)) {
|
if (entry != null && entry.equals(o)) {
|
||||||
|
|||||||
@@ -40,20 +40,20 @@ import java.io.Serializable;
|
|||||||
/**
|
/**
|
||||||
* A {@code Map} adapter for a Java Bean.
|
* A {@code Map} adapter for a Java Bean.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Ruhtlessly stolen from
|
* Ruthlessly stolen from
|
||||||
* <a href="http://binkley.blogspot.com/2006/08/mapping-java-bean.html>Binkley's Blog</a>
|
* <a href="http://binkley.blogspot.com/2006/08/mapping-java-bean.html>Binkley's Blog</a>
|
||||||
*/
|
*/
|
||||||
public final class BeanMap extends AbstractMap<String, Object> implements Serializable, Cloneable {
|
public final class BeanMap extends AbstractMap<String, Object> implements Serializable, Cloneable {
|
||||||
private final Object mBean;
|
private final Object bean;
|
||||||
private transient Set<PropertyDescriptor> mDescriptors;
|
private transient Set<PropertyDescriptor> descriptors;
|
||||||
|
|
||||||
public BeanMap(Object pBean) throws IntrospectionException {
|
public BeanMap(Object pBean) throws IntrospectionException {
|
||||||
if (pBean == null) {
|
if (pBean == null) {
|
||||||
throw new IllegalArgumentException("bean == null");
|
throw new IllegalArgumentException("bean == null");
|
||||||
}
|
}
|
||||||
|
|
||||||
mBean = pBean;
|
bean = pBean;
|
||||||
mDescriptors = initDescriptors(pBean);
|
descriptors = initDescriptors(pBean);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Set<PropertyDescriptor> initDescriptors(Object pBean) throws IntrospectionException {
|
private static Set<PropertyDescriptor> initDescriptors(Object pBean) throws IntrospectionException {
|
||||||
@@ -100,7 +100,7 @@ public final class BeanMap extends AbstractMap<String, Object> implements Serial
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
return mDescriptors.size();
|
return descriptors.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String checkKey(final Object pKey) {
|
private String checkKey(final Object pKey) {
|
||||||
@@ -119,17 +119,17 @@ public final class BeanMap extends AbstractMap<String, Object> implements Serial
|
|||||||
|
|
||||||
private Object readResolve() throws IntrospectionException {
|
private Object readResolve() throws IntrospectionException {
|
||||||
// Initialize the property descriptors
|
// Initialize the property descriptors
|
||||||
mDescriptors = initDescriptors(mBean);
|
descriptors = initDescriptors(bean);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BeanSet extends AbstractSet<Entry<String, Object>> {
|
private class BeanSet extends AbstractSet<Entry<String, Object>> {
|
||||||
public Iterator<Entry<String, Object>> iterator() {
|
public Iterator<Entry<String, Object>> iterator() {
|
||||||
return new BeanIterator(mDescriptors.iterator());
|
return new BeanIterator(descriptors.iterator());
|
||||||
}
|
}
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
return mDescriptors.size();
|
return descriptors.size();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +173,7 @@ public final class BeanMap extends AbstractMap<String, Object> implements Serial
|
|||||||
throw new UnsupportedOperationException("No getter: " + mDescriptor.getName());
|
throw new UnsupportedOperationException("No getter: " + mDescriptor.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
return method.invoke(mBean);
|
return method.invoke(bean);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -188,7 +188,7 @@ public final class BeanMap extends AbstractMap<String, Object> implements Serial
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Object old = getValue();
|
final Object old = getValue();
|
||||||
method.invoke(mBean, pValue);
|
method.invoke(bean, pValue);
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,17 +28,21 @@
|
|||||||
|
|
||||||
package com.twelvemonkeys.util;
|
package com.twelvemonkeys.util;
|
||||||
|
|
||||||
|
import com.twelvemonkeys.lang.Validate;
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import static com.twelvemonkeys.lang.Validate.isTrue;
|
||||||
|
import static com.twelvemonkeys.lang.Validate.notNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A utility class with some useful collection-related functions.
|
* A utility class with some useful collection-related functions.
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
|
||||||
* @author <A href="mailto:eirik.torske@twelvemonkeys.no">Eirik Torske</A>
|
* @author <A href="mailto:eirik.torske@twelvemonkeys.no">Eirik Torske</A>
|
||||||
* @author last modified by $Author: haku $
|
* @author last modified by $Author: haku $
|
||||||
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/CollectionUtil.java#3 $
|
* @version $Id: com/twelvemonkeys/util/CollectionUtil.java#3 $
|
||||||
* @todo move makeList and makeSet to StringUtil?
|
|
||||||
* @see Collections
|
* @see Collections
|
||||||
* @see Arrays
|
* @see Arrays
|
||||||
*/
|
*/
|
||||||
@@ -51,8 +55,6 @@ public final class CollectionUtil {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings({"UnusedDeclaration", "UnusedAssignment", "unchecked"})
|
@SuppressWarnings({"UnusedDeclaration", "UnusedAssignment", "unchecked"})
|
||||||
public static void main(String[] pArgs) {
|
public static void main(String[] pArgs) {
|
||||||
test();
|
|
||||||
|
|
||||||
int howMany = 1000;
|
int howMany = 1000;
|
||||||
|
|
||||||
if (pArgs.length > 0) {
|
if (pArgs.length > 0) {
|
||||||
@@ -257,7 +259,7 @@ public final class CollectionUtil {
|
|||||||
* If the sub array is same length as the original
|
* If the sub array is same length as the original
|
||||||
* ({@code pStart == 0}), the original array will be returned.
|
* ({@code pStart == 0}), the original array will be returned.
|
||||||
*
|
*
|
||||||
* @param pArray the origianl array
|
* @param pArray the original array
|
||||||
* @param pStart the start index of the original array
|
* @param pStart the start index of the original array
|
||||||
* @return a subset of the original array, or the original array itself,
|
* @return a subset of the original array, or the original array itself,
|
||||||
* if {@code pStart} is 0.
|
* if {@code pStart} is 0.
|
||||||
@@ -270,16 +272,33 @@ public final class CollectionUtil {
|
|||||||
return subArray(pArray, pStart, -1);
|
return subArray(pArray, pStart, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an array containing a subset of the original array.
|
||||||
|
* If the sub array is same length as the original
|
||||||
|
* ({@code pStart == 0}), the original array will be returned.
|
||||||
|
*
|
||||||
|
* @param pArray the original array
|
||||||
|
* @param pStart the start index of the original array
|
||||||
|
* @return a subset of the original array, or the original array itself,
|
||||||
|
* if {@code pStart} is 0.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if {@code pArray} is {@code null}
|
||||||
|
* @throws ArrayIndexOutOfBoundsException if {@code pStart} < 0
|
||||||
|
*/
|
||||||
|
public static <T> T[] subArray(T[] pArray, int pStart) {
|
||||||
|
return subArray(pArray, pStart, -1);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an array containing a subset of the original array.
|
* Creates an array containing a subset of the original array.
|
||||||
* If the {@code pLength} parameter is negative, it will be ignored.
|
* If the {@code pLength} parameter is negative, it will be ignored.
|
||||||
* If there are not {@code pLength} elements in the original array
|
* If there are not {@code pLength} elements in the original array
|
||||||
* after {@code pStart}, the {@code pLength} paramter will be
|
* after {@code pStart}, the {@code pLength} parameter will be
|
||||||
* ignored.
|
* ignored.
|
||||||
* If the sub array is same length as the original, the original array will
|
* If the sub array is same length as the original, the original array will
|
||||||
* be returned.
|
* be returned.
|
||||||
*
|
*
|
||||||
* @param pArray the origianl array
|
* @param pArray the original array
|
||||||
* @param pStart the start index of the original array
|
* @param pStart the start index of the original array
|
||||||
* @param pLength the length of the new array
|
* @param pLength the length of the new array
|
||||||
* @return a subset of the original array, or the original array itself,
|
* @return a subset of the original array, or the original array itself,
|
||||||
@@ -292,9 +311,7 @@ public final class CollectionUtil {
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings({"SuspiciousSystemArraycopy"})
|
@SuppressWarnings({"SuspiciousSystemArraycopy"})
|
||||||
public static Object subArray(Object pArray, int pStart, int pLength) {
|
public static Object subArray(Object pArray, int pStart, int pLength) {
|
||||||
if (pArray == null) {
|
Validate.notNull(pArray, "array");
|
||||||
throw new IllegalArgumentException("array == null");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get component type
|
// Get component type
|
||||||
Class type;
|
Class type;
|
||||||
@@ -321,7 +338,7 @@ public final class CollectionUtil {
|
|||||||
Object result;
|
Object result;
|
||||||
|
|
||||||
if (newLength < originalLength) {
|
if (newLength < originalLength) {
|
||||||
// Create subarray & copy into
|
// Create sub array & copy into
|
||||||
result = Array.newInstance(type, newLength);
|
result = Array.newInstance(type, newLength);
|
||||||
System.arraycopy(pArray, pStart, result, 0, newLength);
|
System.arraycopy(pArray, pStart, result, 0, newLength);
|
||||||
}
|
}
|
||||||
@@ -335,7 +352,33 @@ public final class CollectionUtil {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an array containing a subset of the original array.
|
||||||
|
* If the {@code pLength} parameter is negative, it will be ignored.
|
||||||
|
* If there are not {@code pLength} elements in the original array
|
||||||
|
* after {@code pStart}, the {@code pLength} parameter will be
|
||||||
|
* ignored.
|
||||||
|
* If the sub array is same length as the original, the original array will
|
||||||
|
* be returned.
|
||||||
|
*
|
||||||
|
* @param pArray the original array
|
||||||
|
* @param pStart the start index of the original array
|
||||||
|
* @param pLength the length of the new array
|
||||||
|
* @return a subset of the original array, or the original array itself,
|
||||||
|
* if {@code pStart} is 0 and {@code pLength} is either
|
||||||
|
* negative, or greater or equal to {@code pArray.length}.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException if {@code pArray} is {@code null}
|
||||||
|
* @throws ArrayIndexOutOfBoundsException if {@code pStart} < 0
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T[] subArray(T[] pArray, int pStart, int pLength) {
|
||||||
|
return (T[]) subArray((Object) pArray, pStart, pLength);
|
||||||
|
}
|
||||||
|
|
||||||
public static <T> Iterator<T> iterator(final Enumeration<T> pEnum) {
|
public static <T> Iterator<T> iterator(final Enumeration<T> pEnum) {
|
||||||
|
notNull(pEnum, "enumeration");
|
||||||
|
|
||||||
return new Iterator<T>() {
|
return new Iterator<T>() {
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return pEnum.hasMoreElements();
|
return pEnum.hasMoreElements();
|
||||||
@@ -361,8 +404,8 @@ public final class CollectionUtil {
|
|||||||
* the given collection.
|
* the given collection.
|
||||||
* @throws ClassCastException class of the specified element prevents it
|
* @throws ClassCastException class of the specified element prevents it
|
||||||
* from being added to this collection.
|
* from being added to this collection.
|
||||||
* @throws NullPointerException if the specified element is null and this
|
* @throws NullPointerException if the specified element is {@code null} and this
|
||||||
* collection does not support null elements.
|
* collection does not support {@code null} elements.
|
||||||
* @throws IllegalArgumentException some aspect of this element prevents
|
* @throws IllegalArgumentException some aspect of this element prevents
|
||||||
* it from being added to this collection.
|
* it from being added to this collection.
|
||||||
*/
|
*/
|
||||||
@@ -372,7 +415,7 @@ public final class CollectionUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is there a usecase where Arrays.asList(pArray).iterator() can't ne used?
|
// Is there a use case where Arrays.asList(pArray).iterator() can't ne used?
|
||||||
/**
|
/**
|
||||||
* Creates a thin {@link Iterator} wrapper around an array.
|
* Creates a thin {@link Iterator} wrapper around an array.
|
||||||
*
|
*
|
||||||
@@ -383,7 +426,7 @@ public final class CollectionUtil {
|
|||||||
* {@code pLength > pArray.length - pStart}
|
* {@code pLength > pArray.length - pStart}
|
||||||
*/
|
*/
|
||||||
public static <E> ListIterator<E> iterator(final E[] pArray) {
|
public static <E> ListIterator<E> iterator(final E[] pArray) {
|
||||||
return iterator(pArray, 0, pArray.length);
|
return iterator(pArray, 0, notNull(pArray).length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -408,7 +451,7 @@ public final class CollectionUtil {
|
|||||||
* @return a new {@code Map} of same type as {@code pSource}
|
* @return a new {@code Map} of same type as {@code pSource}
|
||||||
* @throws IllegalArgumentException if {@code pSource == null},
|
* @throws IllegalArgumentException if {@code pSource == null},
|
||||||
* or if a new map can't be instantiated,
|
* or if a new map can't be instantiated,
|
||||||
* or if source map contains duplaicates.
|
* or if source map contains duplicates.
|
||||||
*
|
*
|
||||||
* @see #invert(java.util.Map, java.util.Map, DuplicateHandler)
|
* @see #invert(java.util.Map, java.util.Map, DuplicateHandler)
|
||||||
*/
|
*/
|
||||||
@@ -424,7 +467,7 @@ public final class CollectionUtil {
|
|||||||
* @param pResult the map used to contain the result, may be {@code null},
|
* @param pResult the map used to contain the result, may be {@code null},
|
||||||
* in that case a new {@code Map} of same type as {@code pSource} is created.
|
* in that case a new {@code Map} of same type as {@code pSource} is created.
|
||||||
* The result map <em>should</em> be empty, otherwise duplicate values will need to be resolved.
|
* The result map <em>should</em> be empty, otherwise duplicate values will need to be resolved.
|
||||||
* @param pHandler duplicate handler, may be {@code null} if source map don't contain dupliate values
|
* @param pHandler duplicate handler, may be {@code null} if source map don't contain duplicate values
|
||||||
* @return {@code pResult}, or a new {@code Map} if {@code pResult == null}
|
* @return {@code pResult}, or a new {@code Map} if {@code pResult == null}
|
||||||
* @throws IllegalArgumentException if {@code pSource == null},
|
* @throws IllegalArgumentException if {@code pSource == null},
|
||||||
* or if result map is {@code null} and a new map can't be instantiated,
|
* or if result map is {@code null} and a new map can't be instantiated,
|
||||||
@@ -476,20 +519,20 @@ public final class CollectionUtil {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> Comparator<T> reverseOrder(Comparator<T> pOriginal) {
|
public static <T> Comparator<T> reverseOrder(final Comparator<T> pOriginal) {
|
||||||
return new ReverseComparator<T>(pOriginal);
|
return new ReverseComparator<T>(pOriginal);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ReverseComparator<T> implements Comparator<T> {
|
private static class ReverseComparator<T> implements Comparator<T> {
|
||||||
private Comparator<T> mComparator;
|
private final Comparator<T> comparator;
|
||||||
|
|
||||||
public ReverseComparator(Comparator<T> pComparator) {
|
public ReverseComparator(final Comparator<T> pComparator) {
|
||||||
mComparator = pComparator;
|
comparator = notNull(pComparator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public int compare(T pLeft, T pRight) {
|
public int compare(T pLeft, T pRight) {
|
||||||
int result = mComparator.compare(pLeft, pRight);
|
int result = comparator.compare(pLeft, pRight);
|
||||||
|
|
||||||
// We can't simply return -result, as -Integer.MIN_VALUE == Integer.MIN_VALUE.
|
// We can't simply return -result, as -Integer.MIN_VALUE == Integer.MIN_VALUE.
|
||||||
return -(result | (result >>> 1));
|
return -(result | (result >>> 1));
|
||||||
@@ -516,73 +559,21 @@ public final class CollectionUtil {
|
|||||||
return (T) pCollection;
|
return (T) pCollection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"UnusedDeclaration"})
|
|
||||||
static void test() {
|
|
||||||
List list = Collections.singletonList("foo");
|
|
||||||
@SuppressWarnings({"unchecked"})
|
|
||||||
Set set = new HashSet(list);
|
|
||||||
|
|
||||||
List<String> strs0 = CollectionUtil.generify(list, String.class);
|
|
||||||
List<Object> objs0 = CollectionUtil.generify(list, String.class);
|
|
||||||
// List<String> strs01 = CollectionUtil.generify(list, Object.class); // Not okay
|
|
||||||
try {
|
|
||||||
List<String> strs1 = CollectionUtil.generify(set, String.class); // Not ok, runtime CCE unless set is null
|
|
||||||
}
|
|
||||||
catch (RuntimeException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
ArrayList<String> strs01 = CollectionUtil.generify(list, String.class); // Not ok, runtime CCE unless list is null
|
|
||||||
}
|
|
||||||
catch (RuntimeException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<String> setstr1 = CollectionUtil.generify(set, String.class);
|
|
||||||
Set<Object> setobj1 = CollectionUtil.generify(set, String.class);
|
|
||||||
try {
|
|
||||||
Set<Object> setobj44 = CollectionUtil.generify(list, String.class); // Not ok, runtime CCE unless list is null
|
|
||||||
}
|
|
||||||
catch (RuntimeException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<String> strs2 = CollectionUtil.<List<String>, String>generify2(list);
|
|
||||||
List<Object> objs2 = CollectionUtil.<List<Object>, String>generify2(list);
|
|
||||||
// List<String> morestrs = CollectionUtil.<List<Object>, String>generify2(list); // Not ok
|
|
||||||
try {
|
|
||||||
List<String> strs3 = CollectionUtil.<List<String>, String>generify2(set); // Not ok, runtime CCE unless set is null
|
|
||||||
}
|
|
||||||
catch (RuntimeException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ArrayIterator<E> implements ListIterator<E> {
|
private static class ArrayIterator<E> implements ListIterator<E> {
|
||||||
private int mIndex;
|
private int next;
|
||||||
private final int mStart;
|
private final int start;
|
||||||
private final int mLength;
|
private final int length;
|
||||||
private final E[] mArray;
|
private final E[] array;
|
||||||
|
|
||||||
public ArrayIterator(E[] pArray, int pStart, int pLength) {
|
public ArrayIterator(final E[] pArray, final int pStart, final int pLength) {
|
||||||
if (pArray == null) {
|
array = notNull(pArray, "array");
|
||||||
throw new IllegalArgumentException("array == null");
|
start = isTrue(pStart >= 0, pStart, "start < 0: %d");
|
||||||
}
|
length = isTrue(pLength <= pArray.length - pStart, pLength, "length > array.length - start: %d");
|
||||||
if (pStart < 0) {
|
next = start;
|
||||||
throw new IllegalArgumentException("start < 0");
|
|
||||||
}
|
|
||||||
if (pLength > pArray.length - pStart) {
|
|
||||||
throw new IllegalArgumentException("length > array.length - start");
|
|
||||||
}
|
|
||||||
mArray = pArray;
|
|
||||||
mStart = pStart;
|
|
||||||
mLength = pLength;
|
|
||||||
mIndex = mStart;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return mIndex < mLength + mStart;
|
return next < length + start;
|
||||||
}
|
}
|
||||||
|
|
||||||
public E next() {
|
public E next() {
|
||||||
@@ -591,7 +582,7 @@ public final class CollectionUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return mArray[mIndex++];
|
return array[next++];
|
||||||
}
|
}
|
||||||
catch (ArrayIndexOutOfBoundsException e) {
|
catch (ArrayIndexOutOfBoundsException e) {
|
||||||
NoSuchElementException nse = new NoSuchElementException(e.getMessage());
|
NoSuchElementException nse = new NoSuchElementException(e.getMessage());
|
||||||
@@ -609,11 +600,11 @@ public final class CollectionUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasPrevious() {
|
public boolean hasPrevious() {
|
||||||
return mIndex > mStart;
|
return next > start;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int nextIndex() {
|
public int nextIndex() {
|
||||||
return mIndex + 1;
|
return next - start;
|
||||||
}
|
}
|
||||||
|
|
||||||
public E previous() {
|
public E previous() {
|
||||||
@@ -622,7 +613,7 @@ public final class CollectionUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return mArray[mIndex--];
|
return array[--next];
|
||||||
}
|
}
|
||||||
catch (ArrayIndexOutOfBoundsException e) {
|
catch (ArrayIndexOutOfBoundsException e) {
|
||||||
NoSuchElementException nse = new NoSuchElementException(e.getMessage());
|
NoSuchElementException nse = new NoSuchElementException(e.getMessage());
|
||||||
@@ -632,11 +623,11 @@ public final class CollectionUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int previousIndex() {
|
public int previousIndex() {
|
||||||
return mIndex - 1;
|
return nextIndex() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(E pElement) {
|
public void set(E pElement) {
|
||||||
mArray[mIndex] = pElement;
|
array[next - 1] = pElement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,11 +49,11 @@ import java.util.NoSuchElementException;
|
|||||||
*/
|
*/
|
||||||
public class FilterIterator<E> implements Iterator<E> {
|
public class FilterIterator<E> implements Iterator<E> {
|
||||||
|
|
||||||
protected final Filter<E> mFilter;
|
protected final Filter<E> filter;
|
||||||
protected final Iterator<E> mIterator;
|
protected final Iterator<E> iterator;
|
||||||
|
|
||||||
private E mNext = null;
|
private E next = null;
|
||||||
private E mCurrent = null;
|
private E current = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a {@code FilterIterator} that wraps the {@code Iterator}. Each
|
* Creates a {@code FilterIterator} that wraps the {@code Iterator}. Each
|
||||||
@@ -72,8 +72,8 @@ public class FilterIterator<E> implements Iterator<E> {
|
|||||||
throw new IllegalArgumentException("filter == null");
|
throw new IllegalArgumentException("filter == null");
|
||||||
}
|
}
|
||||||
|
|
||||||
mIterator = pIterator;
|
iterator = pIterator;
|
||||||
mFilter = pFilter;
|
filter = pFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -85,16 +85,16 @@ public class FilterIterator<E> implements Iterator<E> {
|
|||||||
* @see FilterIterator.Filter#accept
|
* @see FilterIterator.Filter#accept
|
||||||
*/
|
*/
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
while (mNext == null && mIterator.hasNext()) {
|
while (next == null && iterator.hasNext()) {
|
||||||
E element = mIterator.next();
|
E element = iterator.next();
|
||||||
|
|
||||||
if (mFilter.accept(element)) {
|
if (filter.accept(element)) {
|
||||||
mNext = element;
|
next = element;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mNext != null;
|
return next != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -105,11 +105,11 @@ public class FilterIterator<E> implements Iterator<E> {
|
|||||||
*/
|
*/
|
||||||
public E next() {
|
public E next() {
|
||||||
if (hasNext()) {
|
if (hasNext()) {
|
||||||
mCurrent = mNext;
|
current = next;
|
||||||
|
|
||||||
// Make sure we advance next time
|
// Make sure we advance next time
|
||||||
mNext = null;
|
next = null;
|
||||||
return mCurrent;
|
return current;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new NoSuchElementException("Iteration has no more elements.");
|
throw new NoSuchElementException("Iteration has no more elements.");
|
||||||
@@ -124,8 +124,8 @@ public class FilterIterator<E> implements Iterator<E> {
|
|||||||
* progress in any way other than by calling this method.
|
* progress in any way other than by calling this method.
|
||||||
*/
|
*/
|
||||||
public void remove() {
|
public void remove() {
|
||||||
if (mCurrent != null) {
|
if (current != null) {
|
||||||
mIterator.remove();
|
iterator.remove();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new IllegalStateException("Iteration has no current element.");
|
throw new IllegalStateException("Iteration has no current element.");
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ public class IgnoreCaseMap<V> extends AbstractDecoratedMap<String, V> implements
|
|||||||
*/
|
*/
|
||||||
public V put(String pKey, V pValue) {
|
public V put(String pKey, V pValue) {
|
||||||
String key = (String) toUpper(pKey);
|
String key = (String) toUpper(pKey);
|
||||||
return unwrap(mEntries.put(key, new BasicEntry<String, V>(key, pValue)));
|
return unwrap(entries.put(key, new BasicEntry<String, V>(key, pValue)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private V unwrap(Entry<String, V> pEntry) {
|
private V unwrap(Entry<String, V> pEntry) {
|
||||||
@@ -124,7 +124,7 @@ public class IgnoreCaseMap<V> extends AbstractDecoratedMap<String, V> implements
|
|||||||
* the key is not mapped to any value in this map.
|
* the key is not mapped to any value in this map.
|
||||||
*/
|
*/
|
||||||
public V get(Object pKey) {
|
public V get(Object pKey) {
|
||||||
return unwrap(mEntries.get(toUpper(pKey)));
|
return unwrap(entries.get(toUpper(pKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -137,7 +137,7 @@ public class IgnoreCaseMap<V> extends AbstractDecoratedMap<String, V> implements
|
|||||||
* or null if the key did not have a mapping.
|
* or null if the key did not have a mapping.
|
||||||
*/
|
*/
|
||||||
public V remove(Object pKey) {
|
public V remove(Object pKey) {
|
||||||
return unwrap(mEntries.remove(toUpper(pKey)));
|
return unwrap(entries.remove(toUpper(pKey)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -149,7 +149,7 @@ public class IgnoreCaseMap<V> extends AbstractDecoratedMap<String, V> implements
|
|||||||
* map, as determined by the equals method; false otherwise.
|
* map, as determined by the equals method; false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean containsKey(Object pKey) {
|
public boolean containsKey(Object pKey) {
|
||||||
return mEntries.containsKey(toUpper(pKey));
|
return entries.containsKey(toUpper(pKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -163,14 +163,14 @@ public class IgnoreCaseMap<V> extends AbstractDecoratedMap<String, V> implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Iterator<Entry<String, V>> newEntryIterator() {
|
protected Iterator<Entry<String, V>> newEntryIterator() {
|
||||||
return (Iterator) mEntries.entrySet().iterator();
|
return (Iterator) entries.entrySet().iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Iterator<String> newKeyIterator() {
|
protected Iterator<String> newKeyIterator() {
|
||||||
return mEntries.keySet().iterator();
|
return entries.keySet().iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Iterator<V> newValueIterator() {
|
protected Iterator<V> newValueIterator() {
|
||||||
return (Iterator<V>) mEntries.values().iterator();
|
return (Iterator<V>) entries.values().iterator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user