Fixed JavaDoc errors to enable Java 8 build.

This commit is contained in:
Harald Kuhr
2019-08-10 00:41:36 +02:00
parent 7d2c692663
commit 9e23413456
168 changed files with 34586 additions and 34396 deletions

View File

@@ -1,110 +1,109 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.image; package com.twelvemonkeys.image;
import java.awt.image.ImageConsumer; import java.awt.image.ImageConsumer;
import java.awt.image.ImageProducer; import java.awt.image.ImageProducer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* AbstractImageSource * AbstractImageSource
* <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/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> consumers = new ArrayList<ImageConsumer>();
private List<ImageConsumer> consumers = new ArrayList<ImageConsumer>(); protected int width;
protected int width; protected int height;
protected int height; protected int xOff;
protected int xOff; protected int yOff;
protected int yOff;
// ImageProducer interface
// ImageProducer interface public void addConsumer(final ImageConsumer pConsumer) {
public void addConsumer(final ImageConsumer pConsumer) { if (consumers.contains(pConsumer)) {
if (consumers.contains(pConsumer)) { return;
return; }
}
consumers.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);
// Get rid of "sticky" consumers...
// Get rid of "sticky" consumers... if (isConsumer(pConsumer)) {
if (isConsumer(pConsumer)) { pConsumer.imageComplete(ImageConsumer.IMAGEERROR);
pConsumer.imageComplete(ImageConsumer.IMAGEERROR); removeConsumer(pConsumer);
removeConsumer(pConsumer); }
} }
} }
} 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(final ImageConsumer pConsumer) {
public void removeConsumer(final ImageConsumer pConsumer) { consumers.remove(pConsumer);
consumers.remove(pConsumer); }
}
/**
/** * This implementation silently ignores this instruction. If pixel data 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(final ImageConsumer pConsumer) {
public void requestTopDownLeftRightResend(final ImageConsumer pConsumer) { // ignore
// ignore }
}
public void startProduction(final ImageConsumer pConsumer) {
public void startProduction(final ImageConsumer pConsumer) { addConsumer(pConsumer);
addConsumer(pConsumer); }
}
public boolean isConsumer(final ImageConsumer pConsumer) {
public boolean isConsumer(final ImageConsumer pConsumer) { return consumers.contains(pConsumer);
return consumers.contains(pConsumer); }
}
protected abstract void initConsumer(ImageConsumer pConsumer);
protected abstract void initConsumer(ImageConsumer pConsumer);
protected abstract void sendPixels(ImageConsumer pConsumer);
protected abstract void sendPixels(ImageConsumer pConsumer); }
}

View File

@@ -1,170 +1,173 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.image; package com.twelvemonkeys.image;
import java.awt.image.RGBImageFilter; import java.awt.image.RGBImageFilter;
/** /**
* Adjusts the contrast and brightness of an image. * Adjusts the contrast and brightness of an image.
* <p/> * <p>
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}. * For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
* A value of {@code 0.0} means no change. * A value of {@code 0.0} means no change.
* Negative values will make the pixels darker. * Negative values will make the pixels darker.
* Maximum negative value ({@code -2}) will make all filtered pixels black. * Maximum negative value ({@code -2}) will make all filtered pixels black.
* Positive values will make the pixels brighter. * Positive values will make the pixels brighter.
* Maximum positive value ({@code 2}) will make all filtered pixels white. * Maximum positive value ({@code 2}) will make all filtered pixels white.
* <p/> * </p>
* For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}. * <p>
* A value of {@code 0.0} means no change. * For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}.
* Negative values will reduce contrast. * A value of {@code 0.0} means no change.
* Maximum negative value ({@code -1}) will make all filtered pixels grey * Negative values will reduce contrast.
* (no contrast). * Maximum negative value ({@code -1}) will make all filtered pixels grey
* Positive values will increase contrast. * (no contrast).
* Maximum positive value ({@code 1}) will make all filtered pixels primary * Positive values will increase contrast.
* colors (either black, white, cyan, magenta, yellow, red, blue or green). * Maximum positive value ({@code 1}) will make all filtered pixels primary
* * colors (either black, white, cyan, magenta, yellow, red, blue or green).
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * </p>
* @author last modified by $Author: haku $ *
* * @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/BrightnessContrastFilter.java#1 $ * @author last modified by $Author: haku $
* *
* @todo consider doing something similar to http://archives.java.sun.com/cgi-bin/wa?A2=ind0302&L=jai-interest&F=&S=&P=15947 * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BrightnessContrastFilter.java#1 $
*/ */
// TODO: consider doing something similar to http://archives.java.sun.com/cgi-bin/wa?A2=ind0302&L=jai-interest&F=&S=&P=15947
public class BrightnessContrastFilter extends RGBImageFilter { public class BrightnessContrastFilter extends RGBImageFilter {
// TODO: Replace with RescaleOp? // TODO: Replace with RescaleOp?
// This filter can filter IndexColorModel, as it is does not depend on // This filter can filter IndexColorModel, as it is does not depend on
// the pixels' location // the pixels' location
{ {
canFilterIndexColorModel = true; canFilterIndexColorModel = true;
} }
// Use a pre-calculated lookup table for performance // Use a pre-calculated lookup table for performance
private final int[] LUT; private final int[] LUT;
/** /**
* Creates a BrightnessContrastFilter with default values * Creates a BrightnessContrastFilter with default values
* ({@code brightness=0.3, contrast=0.3}). * ({@code brightness=0.3, contrast=0.3}).
* <p/> * <p>
* This will slightly increase both brightness and contrast. * This will slightly increase both brightness and contrast.
*/ * </p>
public BrightnessContrastFilter() { */
this(0.3f, 0.3f); public BrightnessContrastFilter() {
} this(0.3f, 0.3f);
}
/**
* Creates a BrightnessContrastFilter with the given values for brightness /**
* and contrast. * Creates a BrightnessContrastFilter with the given values for brightness
* <p/> * and contrast.
* For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}. * <p>
* A value of {@code 0.0} means no change. * For brightness, the valid range is {@code -2.0,..,0.0,..,2.0}.
* Negative values will make the pixels darker. * A value of {@code 0.0} means no change.
* Maximum negative value ({@code -2}) will make all filtered pixels black. * Negative values will make the pixels darker.
* Positive values will make the pixels brighter. * Maximum negative value ({@code -2}) will make all filtered pixels black.
* Maximum positive value ({@code 2}) will make all filtered pixels white. * Positive values will make the pixels brighter.
* <p/> * Maximum positive value ({@code 2}) will make all filtered pixels white.
* For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}. * </p>
* A value of {@code 0.0} means no change. * <p>
* Negative values will reduce contrast. * For contrast, the valid range is {@code -1.0,..,0.0,..,1.0}.
* Maximum negative value ({@code -1}) will make all filtered pixels grey * A value of {@code 0.0} means no change.
* (no contrast). * Negative values will reduce contrast.
* Positive values will increase contrast. * Maximum negative value ({@code -1}) will make all filtered pixels grey
* Maximum positive value ({@code 1}) will make all filtered pixels primary * (no contrast).
* colors (either black, white, cyan, magenta, yellow, red, blue or green). * Positive values will increase contrast.
* * Maximum positive value ({@code 1}) will make all filtered pixels primary
* @param pBrightness adjust the brightness of the image, in the range * colors (either black, white, cyan, magenta, yellow, red, blue or green).
* {@code -2.0,..,0.0,..,2.0}. * </p>
* @param pContrast adjust the contrast of the image, in the range *
* {@code -1.0,..,0.0,..,1.0}. * @param pBrightness adjust the brightness of the image, in the range
*/ * {@code -2.0,..,0.0,..,2.0}.
public BrightnessContrastFilter(float pBrightness, float pContrast) { * @param pContrast adjust the contrast of the image, in the range
LUT = createLUT(pBrightness, pContrast); * {@code -1.0,..,0.0,..,1.0}.
} */
public BrightnessContrastFilter(float pBrightness, float pContrast) {
private static int[] createLUT(float pBrightness, float pContrast) { LUT = createLUT(pBrightness, pContrast);
int[] lut = new int[256]; }
// Hmmm.. This approximates Photoshop values.. Not good though.. private static int[] createLUT(float pBrightness, float pContrast) {
double contrast = pContrast > 0 ? Math.pow(pContrast, 7.0) * 127.0 : pContrast; int[] lut = new int[256];
// Convert range [-1,..,0,..,1] -> [0,..,1,..,2] // Hmmm.. This approximates Photoshop values.. Not good though..
double brightness = pBrightness + 1.0; double contrast = pContrast > 0 ? Math.pow(pContrast, 7.0) * 127.0 : pContrast;
for (int i = 0; i < 256; i++) { // Convert range [-1,..,0,..,1] -> [0,..,1,..,2]
lut[i] = clamp((int) (127.5 * brightness + (i - 127) * (contrast + 1.0))); double brightness = pBrightness + 1.0;
}
for (int i = 0; i < 256; i++) {
// Special case, to ensure only primary colors for max contrast lut[i] = clamp((int) (127.5 * brightness + (i - 127) * (contrast + 1.0)));
if (pContrast == 1f) { }
lut[127] = lut[126];
} // Special case, to ensure only primary colors for max contrast
if (pContrast == 1f) {
return lut; lut[127] = lut[126];
} }
private static int clamp(int i) { return lut;
if (i < 0) { }
return 0;
} private static int clamp(int i) {
if (i > 255) { if (i < 0) {
return 255; return 0;
} }
return i; if (i > 255) {
} return 255;
}
/** return i;
* Filters one pixel, adjusting brightness and contrast according to this }
* filter.
* /**
* @param pX x * Filters one pixel, adjusting brightness and contrast according to this
* @param pY y * filter.
* @param pARGB pixel value in default color space *
* * @param pX x
* @return the filtered pixel value in the default color space * @param pY y
*/ * @param pARGB pixel value in default color space
public int filterRGB(int pX, int pY, int pARGB) { *
// Get color components * @return the filtered pixel value in the default color space
int r = pARGB >> 16 & 0xFF; */
int g = pARGB >> 8 & 0xFF; public int filterRGB(int pX, int pY, int pARGB) {
int b = pARGB & 0xFF; // Get color components
int r = pARGB >> 16 & 0xFF;
// Scale to new contrast int g = pARGB >> 8 & 0xFF;
r = LUT[r]; int b = pARGB & 0xFF;
g = LUT[g];
b = LUT[b]; // Scale to new contrast
r = LUT[r];
// Return ARGB pixel, leave transparency as is g = LUT[g];
return (pARGB & 0xFF000000) | (r << 16) | (g << 8) | b; b = LUT[b];
}
} // Return ARGB pixel, leave transparency as is
return (pARGB & 0xFF000000) | (r << 16) | (g << 8) | b;
}
}

View File

@@ -44,14 +44,16 @@ import java.util.concurrent.CopyOnWriteArrayList;
* A faster, lighter and easier way to convert an {@code Image} to a * A faster, lighter and easier way to convert an {@code Image} to a
* {@code BufferedImage} than using a {@code PixelGrabber}. * {@code BufferedImage} than using a {@code PixelGrabber}.
* Clients may provide progress listeners to monitor conversion progress. * Clients may provide progress listeners to monitor conversion progress.
* <p/> * <p>
* Supports source image subsampling and source region extraction. * Supports source image subsampling and source region extraction.
* Supports source images with 16 bit {@link ColorModel} and * Supports source images with 16 bit {@link ColorModel} and
* {@link DataBuffer#TYPE_USHORT} transfer type, without converting to * {@link DataBuffer#TYPE_USHORT} transfer type, without converting to
* 32 bit/TYPE_INT. * 32 bit/TYPE_INT.
* <p/> * </p>
* <p>
* NOTE: Does not support images with more than one {@code ColorModel} or * NOTE: Does not support images with more than one {@code ColorModel} or
* different types of pixel data. This is not very common. * different types of pixel data. This is not very common.
* </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/image/BufferedImageFactory.java#1 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/BufferedImageFactory.java#1 $

View File

@@ -1,92 +1,91 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.image; package com.twelvemonkeys.image;
import com.twelvemonkeys.lang.Validate; import com.twelvemonkeys.lang.Validate;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
import java.awt.geom.AffineTransform; import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
/** /**
* An {@code Icon} implementation backed by a {@code BufferedImage}. * An {@code Icon} implementation backed by a {@code BufferedImage}.
* <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/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 image;
private final BufferedImage image; private int width;
private int width; private int height;
private int height; private final boolean fast;
private final boolean fast;
public BufferedImageIcon(BufferedImage pImage) {
public BufferedImageIcon(BufferedImage pImage) { this(pImage, pImage != null ? pImage.getWidth() : 0, pImage != null ? pImage.getHeight() : 0);
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) { this(pImage, pWidth, pHeight, pImage.getWidth() == pWidth && pImage.getHeight() == pHeight);
this(pImage, pWidth, pHeight, pImage.getWidth() == pWidth && pImage.getHeight() == pHeight); }
}
public BufferedImageIcon(BufferedImage pImage, int pWidth, int pHeight, boolean useFastRendering) {
public BufferedImageIcon(BufferedImage pImage, int pWidth, int pHeight, boolean useFastRendering) { image = Validate.notNull(pImage, "image");
image = Validate.notNull(pImage, "image"); width = Validate.isTrue(pWidth > 0, pWidth, "width must be positive: %d");
width = Validate.isTrue(pWidth > 0, pWidth, "width must be positive: %d"); height = Validate.isTrue(pHeight > 0, pHeight, "height must be positive: %d");
height = Validate.isTrue(pHeight > 0, pHeight, "height must be positive: %d");
fast = useFastRendering;
fast = useFastRendering; }
}
public int getIconHeight() {
public int getIconHeight() { return height;
return height; }
}
public int getIconWidth() {
public int getIconWidth() { return width;
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 (fast || !(g instanceof Graphics2D)) {
if (fast || !(g instanceof Graphics2D)) { //System.out.println("Scaling fast");
//System.out.println("Scaling fast"); g.drawImage(image, x, y, width, height, 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(width / (double) image.getWidth(), height / (double) image.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(image, xform, null);
g2.drawImage(image, xform, null); }
} }
} }
}

View File

@@ -1,483 +1,485 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.image; package com.twelvemonkeys.image;
import java.awt.*; import java.awt.*;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D; import java.awt.geom.Rectangle2D;
import java.awt.image.*; import java.awt.image.*;
import java.util.Random; import java.util.Random;
/** /**
* This {@code BufferedImageOp/RasterOp} implements basic * This {@code BufferedImageOp/RasterOp} implements basic
* Floyd-Steinberg error-diffusion algorithm for dithering. * Floyd-Steinberg error-diffusion algorithm for dithering.
* <P/> * <p>
* The weights used are 7/16, 3/16, 5/16 and 1/16, distributed like this: * The weights used are 7/16, 3/16, 5/16 and 1/16, distributed like this:
* <!-- - - * <!-- - -
* | |x|7| * | |x|7|
* - - - - * - - - -
* |3|5|1| * |3|5|1|
* - - --> * - - -->
* <P/> * </p>
* <TABLE border="1" cellpadding="4" cellspacing="0"> * <table border="1" cellpadding="4" cellspacing="0">
* <TR><TD bgcolor="#000000">&nbsp;</TD><TD class="TableHeadingColor" * <caption>Floyd-Steinberg error-diffusion weights</caption>
* align="center">X</TD><TD>7/16</TD></TR> * <tr><td bgcolor="#000000">&nbsp;</td><td class="TableHeadingColor"
* <TR><TD>3/16</TD><TD>5/16</TD><TD>1/16</TD></TR> * align="center">x</td><td>7/16</td></tr>
* </TABLE> * <tr><td>3/16</td><td>5/16</td><td>1/16</td></tr>
* <P/> * </table>
* See <A href="http://www.awprofessional.com/bookstore/product.asp?isbn=0201848406&rl=1">Computer Graphics (Foley et al.)</a> * <p>
* for more information. * See <A href="http://www.awprofessional.com/bookstore/product.asp?isbn=0201848406&rl=1">Computer Graphics (Foley et al.)</a>
* * for more information.
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * </p>
* @author last modified by $Author: haku $ *
* * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @version $Id: DiffusionDither.java#1 $ * @author last modified by $Author: haku $
*/ *
public class DiffusionDither implements BufferedImageOp, RasterOp { * @version $Id: DiffusionDither.java#1 $
*/
private static final int FS_SCALE = 1 << 8; public class DiffusionDither implements BufferedImageOp, RasterOp {
private static final Random RANDOM = new Random();
private static final int FS_SCALE = 1 << 8;
protected final IndexColorModel indexColorModel; private static final Random RANDOM = new Random();
private boolean alternateScans = true;
protected final IndexColorModel indexColorModel;
/** private boolean alternateScans = true;
* Creates a {@code DiffusionDither}, using the given
* {@code IndexColorModel} for dithering into. /**
* * Creates a {@code DiffusionDither}, using the given
* @param pICM an IndexColorModel. * {@code IndexColorModel} for dithering into.
*/ *
public DiffusionDither(final IndexColorModel pICM) { * @param pICM an IndexColorModel.
// Store color model */
indexColorModel = pICM; public DiffusionDither(final IndexColorModel pICM) {
} // Store color model
indexColorModel = pICM;
/** }
* Creates a {@code DiffusionDither}, with no fixed
* {@code IndexColorModel}. The color model will be generated for each /**
* filtering, unless the destination image already has an * Creates a {@code DiffusionDither}, with no fixed
* {@code IndexColorModel}. * {@code IndexColorModel}. The color model will be generated for each
*/ * filtering, unless the destination image already has an
public DiffusionDither() { * {@code IndexColorModel}.
this(null); */
} public DiffusionDither() {
this(null);
/** }
* Sets the scan mode. If the parameter is true, error distribution for
* every even line will be left-to-right, while odd lines will be /**
* right-to-left. * Sets the scan mode. If the parameter is true, error distribution for
* The default is {@code true}. * every even line will be left-to-right, while odd lines will be
* * right-to-left.
* @param pUse {@code true} if scan mode should be alternating left/right * The default is {@code true}.
*/ *
public void setAlternateScans(boolean pUse) { * @param pUse {@code true} if scan mode should be alternating left/right
alternateScans = pUse; */
} public void setAlternateScans(boolean pUse) {
alternateScans = pUse;
/** }
* Creates a compatible {@code BufferedImage} to dither into.
* Only {@code IndexColorModel} allowed. /**
* * Creates a compatible {@code BufferedImage} to dither into.
* @return a compatible {@code BufferedImage} * Only {@code IndexColorModel} allowed.
* *
* @throws ImageFilterException if {@code pDestCM} is not {@code null} or * @return a compatible {@code BufferedImage}
* an instance of {@code IndexColorModel}. *
*/ * @throws ImageFilterException if {@code pDestCM} is not {@code null} or
public final BufferedImage createCompatibleDestImage(BufferedImage pSource, ColorModel pDestCM) { * an instance of {@code IndexColorModel}.
if (pDestCM == null) { */
return new BufferedImage(pSource.getWidth(), pSource.getHeight(), public final BufferedImage createCompatibleDestImage(BufferedImage pSource, ColorModel pDestCM) {
BufferedImage.TYPE_BYTE_INDEXED, if (pDestCM == null) {
getICM(pSource)); return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
} BufferedImage.TYPE_BYTE_INDEXED,
else if (pDestCM instanceof IndexColorModel) { getICM(pSource));
return new BufferedImage(pSource.getWidth(), pSource.getHeight(), }
BufferedImage.TYPE_BYTE_INDEXED, else if (pDestCM instanceof IndexColorModel) {
(IndexColorModel) pDestCM); return new BufferedImage(pSource.getWidth(), pSource.getHeight(),
} BufferedImage.TYPE_BYTE_INDEXED,
else { (IndexColorModel) pDestCM);
throw new ImageFilterException("Only IndexColorModel allowed."); }
} else {
} throw new ImageFilterException("Only IndexColorModel allowed.");
}
/** }
* Creates a compatible {@code Raster} to dither into.
* Only {@code IndexColorModel} allowed. /**
* * Creates a compatible {@code Raster} to dither into.
* @param pSrc the source raster * Only {@code IndexColorModel} allowed.
* *
* @return a {@code WritableRaster} * @param pSrc the source raster
*/ *
public final WritableRaster createCompatibleDestRaster(Raster pSrc) { * @return a {@code WritableRaster}
return createCompatibleDestRaster(pSrc, getICM(pSrc)); */
} public final WritableRaster createCompatibleDestRaster(Raster pSrc) {
return createCompatibleDestRaster(pSrc, getICM(pSrc));
/** }
* Creates a compatible {@code Raster} to dither into.
* /**
* @param pSrc the source raster. * Creates a compatible {@code Raster} to dither into.
* @param pIndexColorModel the index color model used to create a {@code Raster}. *
* * @param pSrc the source raster.
* @return a {@code WritableRaster} * @param pIndexColorModel the index color model used to create a {@code Raster}.
*/ *
public final WritableRaster createCompatibleDestRaster(Raster pSrc, IndexColorModel pIndexColorModel) { * @return a {@code WritableRaster}
return pIndexColorModel.createCompatibleWritableRaster(pSrc.getWidth(), pSrc.getHeight()); */
} public final WritableRaster createCompatibleDestRaster(Raster pSrc, IndexColorModel pIndexColorModel) {
return pIndexColorModel.createCompatibleWritableRaster(pSrc.getWidth(), pSrc.getHeight());
}
/**
* Returns the bounding box of the filtered destination image. Since
* this is not a geometric operation, the bounding box does not /**
* change. * Returns the bounding box of the filtered destination image. Since
* @param pSrc the {@code BufferedImage} to be filtered * this is not a geometric operation, the bounding box does not
* @return the bounds of the filtered definition image. * change.
*/ * @param pSrc the {@code BufferedImage} to be filtered
public final Rectangle2D getBounds2D(BufferedImage pSrc) { * @return the bounds of the filtered definition image.
return getBounds2D(pSrc.getRaster()); */
} public final Rectangle2D getBounds2D(BufferedImage pSrc) {
return getBounds2D(pSrc.getRaster());
/** }
* Returns the bounding box of the filtered destination Raster. Since
* this is not a geometric operation, the bounding box does not /**
* change. * Returns the bounding box of the filtered destination Raster. Since
* @param pSrc the {@code Raster} to be filtered * this is not a geometric operation, the bounding box does not
* @return the bounds of the filtered definition {@code Raster}. * change.
*/ * @param pSrc the {@code Raster} to be filtered
public final Rectangle2D getBounds2D(Raster pSrc) { * @return the bounds of the filtered definition {@code Raster}.
return pSrc.getBounds(); */
} public final Rectangle2D getBounds2D(Raster pSrc) {
return pSrc.getBounds();
/** }
* Returns the location of the destination point given a
* point in the source. If {@code dstPt} is not /**
* {@code null}, it will be used to hold the return value. * Returns the location of the destination point given a
* Since this is not a geometric operation, the {@code srcPt} * point in the source. If {@code dstPt} is not
* will equal the {@code dstPt}. * {@code null}, it will be used to hold the return value.
* @param pSrcPt a {@code Point2D} that represents a point * Since this is not a geometric operation, the {@code srcPt}
* in the source image * will equal the {@code dstPt}.
* @param pDstPt a {@code Point2D}that represents the location * @param pSrcPt a {@code Point2D} that represents a point
* in the destination * in the source image
* @return the {@code Point2D} in the destination that * @param pDstPt a {@code Point2D}that represents the location
* corresponds to the specified point in the source. * in the destination
*/ * @return the {@code Point2D} in the destination that
public final Point2D getPoint2D(Point2D pSrcPt, Point2D pDstPt) { * corresponds to the specified point in the source.
// Create new Point, if needed */
if (pDstPt == null) { public final Point2D getPoint2D(Point2D pSrcPt, Point2D pDstPt) {
pDstPt = new Point2D.Float(); // Create new Point, if needed
} if (pDstPt == null) {
pDstPt = new Point2D.Float();
// Copy location }
pDstPt.setLocation(pSrcPt.getX(), pSrcPt.getY());
// Copy location
// Return dest pDstPt.setLocation(pSrcPt.getX(), pSrcPt.getY());
return pDstPt;
} // Return dest
return pDstPt;
/** }
* Returns the rendering mHints for this op.
* @return the {@code RenderingHints} object associated /**
* with this op. * Returns the rendering mHints for this op.
*/ * @return the {@code RenderingHints} object associated
public final RenderingHints getRenderingHints() { * with this op.
return null; */
} public final RenderingHints getRenderingHints() {
return null;
/** }
* Converts an int ARGB to int triplet.
*/ /**
private static int[] toRGBArray(int pARGB, int[] pBuffer) { * Converts an int ARGB to int triplet.
pBuffer[0] = ((pARGB & 0x00ff0000) >> 16); */
pBuffer[1] = ((pARGB & 0x0000ff00) >> 8); private static int[] toRGBArray(int pARGB, int[] pBuffer) {
pBuffer[2] = ((pARGB & 0x000000ff)); pBuffer[0] = ((pARGB & 0x00ff0000) >> 16);
//pBuffer[3] = ((pARGB & 0xff000000) >> 24); // alpha pBuffer[1] = ((pARGB & 0x0000ff00) >> 8);
pBuffer[2] = ((pARGB & 0x000000ff));
return pBuffer; //pBuffer[3] = ((pARGB & 0xff000000) >> 24); // alpha
}
return pBuffer;
/** }
* Converts a int triplet to int ARGB.
*/ /**
private static int toIntARGB(int[] pRGB) { * Converts a int triplet to int ARGB.
return 0xff000000 // All opaque */
| (pRGB[0] << 16) private static int toIntARGB(int[] pRGB) {
| (pRGB[1] << 8) return 0xff000000 // All opaque
| (pRGB[2]); | (pRGB[0] << 16)
/* | (pRGB[1] << 8)
| ((int) (pRGB[0] << 16) & 0x00ff0000) | (pRGB[2]);
| ((int) (pRGB[1] << 8) & 0x0000ff00) /*
| ((int) (pRGB[2] ) & 0x000000ff); | ((int) (pRGB[0] << 16) & 0x00ff0000)
*/ | ((int) (pRGB[1] << 8) & 0x0000ff00)
} | ((int) (pRGB[2] ) & 0x000000ff);
*/
}
/**
* Performs a single-input/single-output dither operation, applying basic
* Floyd-Steinberg error-diffusion to the image. /**
* * Performs a single-input/single-output dither operation, applying basic
* @param pSource the source image * Floyd-Steinberg error-diffusion to the image.
* @param pDest the destination image *
* * @param pSource the source image
* @return the destination image, or a new image, if {@code pDest} was * @param pDest the destination image
* {@code null}. *
*/ * @return the destination image, or a new image, if {@code pDest} was
public final BufferedImage filter(BufferedImage pSource, BufferedImage pDest) { * {@code null}.
// Create destination image, if none provided */
if (pDest == null) { public final BufferedImage filter(BufferedImage pSource, BufferedImage pDest) {
pDest = createCompatibleDestImage(pSource, getICM(pSource)); // Create destination image, if none provided
} if (pDest == null) {
else if (!(pDest.getColorModel() instanceof IndexColorModel)) { pDest = createCompatibleDestImage(pSource, getICM(pSource));
throw new ImageFilterException("Only IndexColorModel allowed."); }
} else if (!(pDest.getColorModel() instanceof IndexColorModel)) {
throw new ImageFilterException("Only IndexColorModel allowed.");
// Filter rasters }
filter(pSource.getRaster(), pDest.getRaster(), (IndexColorModel) pDest.getColorModel());
// Filter rasters
return pDest; filter(pSource.getRaster(), pDest.getRaster(), (IndexColorModel) pDest.getColorModel());
}
return pDest;
/** }
* Performs a single-input/single-output dither operation, applying basic
* Floyd-Steinberg error-diffusion to the image. /**
* * Performs a single-input/single-output dither operation, applying basic
* @param pSource the source raster, assumed to be in sRGB * Floyd-Steinberg error-diffusion to the image.
* @param pDest the destination raster, may be {@code null} *
* * @param pSource the source raster, assumed to be in sRGB
* @return the destination raster, or a new raster, if {@code pDest} was * @param pDest the destination raster, may be {@code null}
* {@code null}. *
*/ * @return the destination raster, or a new raster, if {@code pDest} was
public final WritableRaster filter(final Raster pSource, WritableRaster pDest) { * {@code null}.
return filter(pSource, pDest, getICM(pSource)); */
} public final WritableRaster filter(final Raster pSource, WritableRaster pDest) {
return filter(pSource, pDest, getICM(pSource));
private IndexColorModel getICM(BufferedImage pSource) { }
return (indexColorModel != null ? indexColorModel : IndexImage.getIndexColorModel(pSource, 256, IndexImage.TRANSPARENCY_BITMASK));
} private IndexColorModel getICM(BufferedImage pSource) {
private IndexColorModel getICM(Raster pSource) { return (indexColorModel != null ? indexColorModel : IndexImage.getIndexColorModel(pSource, 256, IndexImage.TRANSPARENCY_BITMASK));
return (indexColorModel != null ? indexColorModel : createIndexColorModel(pSource)); }
} private IndexColorModel getICM(Raster pSource) {
return (indexColorModel != null ? indexColorModel : createIndexColorModel(pSource));
private IndexColorModel createIndexColorModel(Raster pSource) { }
BufferedImage image = new BufferedImage(pSource.getWidth(), pSource.getHeight(),
BufferedImage.TYPE_INT_ARGB); private IndexColorModel createIndexColorModel(Raster pSource) {
image.setData(pSource); BufferedImage image = new BufferedImage(pSource.getWidth(), pSource.getHeight(),
return IndexImage.getIndexColorModel(image, 256, IndexImage.TRANSPARENCY_BITMASK); BufferedImage.TYPE_INT_ARGB);
} image.setData(pSource);
return IndexImage.getIndexColorModel(image, 256, IndexImage.TRANSPARENCY_BITMASK);
/** }
* Performs a single-input/single-output dither operation, applying basic
* Floyd-Steinberg error-diffusion to the image. /**
* * Performs a single-input/single-output dither operation, applying basic
* @param pSource the source raster, assumed to be in sRGB * Floyd-Steinberg error-diffusion to the image.
* @param pDest the destination raster, may be {@code null} *
* @param pColorModel the indexed color model to use * @param pSource the source raster, assumed to be in sRGB
* * @param pDest the destination raster, may be {@code null}
* @return the destination raster, or a new raster, if {@code pDest} was * @param pColorModel the indexed color model to use
* {@code null}. *
*/ * @return the destination raster, or a new raster, if {@code pDest} was
public final WritableRaster filter(final Raster pSource, WritableRaster pDest, IndexColorModel pColorModel) { * {@code null}.
int width = pSource.getWidth(); */
int height = pSource.getHeight(); public final WritableRaster filter(final Raster pSource, WritableRaster pDest, IndexColorModel pColorModel) {
int width = pSource.getWidth();
// Create destination raster if needed int height = pSource.getHeight();
if (pDest == null) {
pDest = createCompatibleDestRaster(pSource, pColorModel); // Create destination raster if needed
} if (pDest == null) {
pDest = createCompatibleDestRaster(pSource, pColorModel);
// Initialize Floyd-Steinberg error vectors. }
// +2 to handle the previous pixel and next pixel case minimally
// When reference for column, add 1 to reference as this buffer is // Initialize Floyd-Steinberg error vectors.
// offset from actual column position by one to allow FS to not check // +2 to handle the previous pixel and next pixel case minimally
// left/right edge conditions // When reference for column, add 1 to reference as this buffer is
int[][] currErr = new int[width + 2][3]; // offset from actual column position by one to allow FS to not check
int[][] nextErr = new int[width + 2][3]; // left/right edge conditions
int[][] currErr = new int[width + 2][3];
// Random errors in [-1 .. 1] - for first row int[][] nextErr = new int[width + 2][3];
for (int i = 0; i < width + 2; i++) {
// Note: This is broken for the strange cases where nextInt returns Integer.MIN_VALUE // Random errors in [-1 .. 1] - for first row
currErr[i][0] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE; for (int i = 0; i < width + 2; i++) {
currErr[i][1] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE; // Note: This is broken for the strange cases where nextInt returns Integer.MIN_VALUE
currErr[i][2] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE; currErr[i][0] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE;
} currErr[i][1] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE;
currErr[i][2] = RANDOM.nextInt(FS_SCALE * 2) - FS_SCALE;
// Temp buffers }
final int[] diff = new int[3]; // No alpha
final int[] inRGB = new int[4]; // Temp buffers
final int[] outRGB = new int[4]; final int[] diff = new int[3]; // No alpha
Object pixel = null; final int[] inRGB = new int[4];
boolean forward = true; final int[] outRGB = new int[4];
Object pixel = null;
// Loop through image data boolean forward = true;
for (int y = 0; y < height; y++) {
// Clear out next error rows for colour errors // Loop through image data
for (int i = nextErr.length; --i >= 0;) { for (int y = 0; y < height; y++) {
nextErr[i][0] = 0; // Clear out next error rows for colour errors
nextErr[i][1] = 0; for (int i = nextErr.length; --i >= 0;) {
nextErr[i][2] = 0; nextErr[i][0] = 0;
} nextErr[i][1] = 0;
nextErr[i][2] = 0;
// Set up start column and limit }
int x;
int limit; // Set up start column and limit
if (forward) { int x;
x = 0; int limit;
limit = width; if (forward) {
} x = 0;
else { limit = width;
x = width - 1; }
limit = -1; else {
} x = width - 1;
limit = -1;
// TODO: Use getPixels instead of getPixel for better performance? }
// Loop over row // TODO: Use getPixels instead of getPixel for better performance?
while (true) {
// Get RGB from original raster // Loop over row
// DON'T KNOW IF THIS WILL WORK FOR ALL TYPES. while (true) {
pSource.getPixel(x, y, inRGB); // Get RGB from original raster
// DON'T KNOW IF THIS WILL WORK FOR ALL TYPES.
// Get error for this pixel & add error to rgb pSource.getPixel(x, y, inRGB);
for (int i = 0; i < 3; i++) {
// Make a 28.4 FP number, add Error (with fraction), // Get error for this pixel & add error to rgb
// rounding and truncate to int for (int i = 0; i < 3; i++) {
inRGB[i] = ((inRGB[i] << 4) + currErr[x + 1][i] + 0x08) >> 4; // Make a 28.4 FP number, add Error (with fraction),
// rounding and truncate to int
// Clamp inRGB[i] = ((inRGB[i] << 4) + currErr[x + 1][i] + 0x08) >> 4;
if (inRGB[i] > 255) {
inRGB[i] = 255; // Clamp
} if (inRGB[i] > 255) {
else if (inRGB[i] < 0) { inRGB[i] = 255;
inRGB[i] = 0; }
} else if (inRGB[i] < 0) {
} inRGB[i] = 0;
}
// Get pixel value... }
// It is VERY important that we are using a IndexColorModel that
// support reverse color lookup for speed. // Get pixel value...
pixel = pColorModel.getDataElements(toIntARGB(inRGB), pixel); // It is VERY important that we are using a IndexColorModel that
// support reverse color lookup for speed.
// ...set it... pixel = pColorModel.getDataElements(toIntARGB(inRGB), pixel);
pDest.setDataElements(x, y, pixel);
// ...set it...
// ..and get back the closet match pDest.setDataElements(x, y, pixel);
pDest.getPixel(x, y, outRGB);
// ..and get back the closet match
// Convert the value to default sRGB pDest.getPixel(x, y, outRGB);
// Should work for all transfertypes supported by IndexColorModel
toRGBArray(pColorModel.getRGB(outRGB[0]), outRGB); // Convert the value to default sRGB
// Should work for all transfertypes supported by IndexColorModel
// Find diff toRGBArray(pColorModel.getRGB(outRGB[0]), outRGB);
diff[0] = inRGB[0] - outRGB[0];
diff[1] = inRGB[1] - outRGB[1]; // Find diff
diff[2] = inRGB[2] - outRGB[2]; diff[0] = inRGB[0] - outRGB[0];
diff[1] = inRGB[1] - outRGB[1];
// Apply F-S error diffusion diff[2] = inRGB[2] - outRGB[2];
// Serpentine scan: left-right
if (forward) { // Apply F-S error diffusion
// Row 1 (y) // Serpentine scan: left-right
// Update error in this pixel (x + 1) if (forward) {
currErr[x + 2][0] += diff[0] * 7; // Row 1 (y)
currErr[x + 2][1] += diff[1] * 7; // Update error in this pixel (x + 1)
currErr[x + 2][2] += diff[2] * 7; currErr[x + 2][0] += diff[0] * 7;
currErr[x + 2][1] += diff[1] * 7;
// Row 2 (y + 1) currErr[x + 2][2] += diff[2] * 7;
// Update error in this pixel (x - 1)
nextErr[x][0] += diff[0] * 3; // Row 2 (y + 1)
nextErr[x][1] += diff[1] * 3; // Update error in this pixel (x - 1)
nextErr[x][2] += diff[2] * 3; nextErr[x][0] += diff[0] * 3;
// Update error in this pixel (x) nextErr[x][1] += diff[1] * 3;
nextErr[x + 1][0] += diff[0] * 5; nextErr[x][2] += diff[2] * 3;
nextErr[x + 1][1] += diff[1] * 5; // Update error in this pixel (x)
nextErr[x + 1][2] += diff[2] * 5; nextErr[x + 1][0] += diff[0] * 5;
// Update error in this pixel (x + 1) nextErr[x + 1][1] += diff[1] * 5;
// TODO: Consider calculating this using nextErr[x + 1][2] += diff[2] * 5;
// error term = error - sum(error terms 1, 2 and 3) // Update error in this pixel (x + 1)
// See Computer Graphics (Foley et al.), p. 573 // TODO: Consider calculating this using
nextErr[x + 2][0] += diff[0]; // * 1; // error term = error - sum(error terms 1, 2 and 3)
nextErr[x + 2][1] += diff[1]; // * 1; // See Computer Graphics (Foley et al.), p. 573
nextErr[x + 2][2] += diff[2]; // * 1; nextErr[x + 2][0] += diff[0]; // * 1;
nextErr[x + 2][1] += diff[1]; // * 1;
// Next nextErr[x + 2][2] += diff[2]; // * 1;
x++;
// Next
// Done? x++;
if (x >= limit) {
break; // Done?
} if (x >= limit) {
break;
} }
else {
// Row 1 (y) }
// Update error in this pixel (x - 1) else {
currErr[x][0] += diff[0] * 7; // Row 1 (y)
currErr[x][1] += diff[1] * 7; // Update error in this pixel (x - 1)
currErr[x][2] += diff[2] * 7; currErr[x][0] += diff[0] * 7;
currErr[x][1] += diff[1] * 7;
// Row 2 (y + 1) currErr[x][2] += diff[2] * 7;
// Update error in this pixel (x + 1)
nextErr[x + 2][0] += diff[0] * 3; // Row 2 (y + 1)
nextErr[x + 2][1] += diff[1] * 3; // Update error in this pixel (x + 1)
nextErr[x + 2][2] += diff[2] * 3; nextErr[x + 2][0] += diff[0] * 3;
// Update error in this pixel (x) nextErr[x + 2][1] += diff[1] * 3;
nextErr[x + 1][0] += diff[0] * 5; nextErr[x + 2][2] += diff[2] * 3;
nextErr[x + 1][1] += diff[1] * 5; // Update error in this pixel (x)
nextErr[x + 1][2] += diff[2] * 5; nextErr[x + 1][0] += diff[0] * 5;
// Update error in this pixel (x - 1) nextErr[x + 1][1] += diff[1] * 5;
// TODO: Consider calculating this using nextErr[x + 1][2] += diff[2] * 5;
// error term = error - sum(error terms 1, 2 and 3) // Update error in this pixel (x - 1)
// See Computer Graphics (Foley et al.), p. 573 // TODO: Consider calculating this using
nextErr[x][0] += diff[0]; // * 1; // error term = error - sum(error terms 1, 2 and 3)
nextErr[x][1] += diff[1]; // * 1; // See Computer Graphics (Foley et al.), p. 573
nextErr[x][2] += diff[2]; // * 1; nextErr[x][0] += diff[0]; // * 1;
nextErr[x][1] += diff[1]; // * 1;
// Previous nextErr[x][2] += diff[2]; // * 1;
x--;
// Previous
// Done? x--;
if (x <= limit) {
break; // Done?
} if (x <= limit) {
} break;
} }
}
// Make next error info current for next iteration }
int[][] temperr;
temperr = currErr; // Make next error info current for next iteration
currErr = nextErr; int[][] temperr;
nextErr = temperr; temperr = currErr;
currErr = nextErr;
// Toggle direction nextErr = temperr;
if (alternateScans) {
forward = !forward; // Toggle direction
} if (alternateScans) {
} forward = !forward;
}
return pDest; }
}
return pDest;
}
} }

View File

@@ -1,84 +1,86 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.image; package com.twelvemonkeys.image;
import java.awt.*; import java.awt.*;
/** /**
* GraphicsUtil * GraphicsUtil
* *
* @author <a href="mailto:harald.kuhr@gmail.no">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.no">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/GraphicsUtil.java#1 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/GraphicsUtil.java#1 $
*/ */
public final class GraphicsUtil { public final class GraphicsUtil {
/** /**
* Enables anti-aliasing in the {@code Graphics} object. * Enables anti-aliasing in the {@code Graphics} object.
* <p/> * <p>
* Anti-aliasing is enabled by casting to {@code Graphics2D} and setting * Anti-aliasing is enabled by casting to {@code Graphics2D} and setting
* the rendering hint {@code RenderingHints.KEY_ANTIALIASING} to * the rendering hint {@code RenderingHints.KEY_ANTIALIASING} to
* {@code RenderingHints.VALUE_ANTIALIAS_ON}. * {@code RenderingHints.VALUE_ANTIALIAS_ON}.
* * </p>
* @param pGraphics the graphics object *
* @throws ClassCastException if {@code pGraphics} is not an instance of * @param pGraphics the graphics object
* {@code Graphics2D}. * @throws ClassCastException if {@code pGraphics} is not an instance of
* * {@code Graphics2D}.
* @see java.awt.RenderingHints#KEY_ANTIALIASING *
*/ * @see java.awt.RenderingHints#KEY_ANTIALIASING
public static void enableAA(final Graphics pGraphics) { */
((Graphics2D) pGraphics).setRenderingHint( public static void enableAA(final Graphics pGraphics) {
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ((Graphics2D) pGraphics).setRenderingHint(
); RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
} );
}
/**
* Sets the alpha in the {@code Graphics} object. /**
* <p/> * Sets the alpha in the {@code Graphics} object.
* Alpha is set by casting to {@code Graphics2D} and setting the composite * <p>
* to the rule {@code AlphaComposite.SRC_OVER} multiplied by the given * Alpha is set by casting to {@code Graphics2D} and setting the composite
* alpha. * to the rule {@code AlphaComposite.SRC_OVER} multiplied by the given
* * alpha.
* @param pGraphics the graphics object * </p>
* @param pAlpha the alpha level, {@code alpha} must be a floating point *
* number in the inclusive range [0.0,&nbsp;1.0]. * @param pGraphics the graphics object
* @throws ClassCastException if {@code pGraphics} is not an instance of * @param pAlpha the alpha level, {@code alpha} must be a floating point
* {@code Graphics2D}. * number in the inclusive range [0.0,&nbsp;1.0].
* * @throws ClassCastException if {@code pGraphics} is not an instance of
* @see java.awt.AlphaComposite#SRC_OVER * {@code Graphics2D}.
* @see java.awt.AlphaComposite#getInstance(int, float) *
*/ * @see java.awt.AlphaComposite#SRC_OVER
public static void setAlpha(final Graphics pGraphics, final float pAlpha) { * @see java.awt.AlphaComposite#getInstance(int, float)
((Graphics2D) pGraphics).setComposite( */
AlphaComposite.getInstance(AlphaComposite.SRC_OVER, pAlpha) public static void setAlpha(final Graphics pGraphics, final float pAlpha) {
); ((Graphics2D) pGraphics).setComposite(
} AlphaComposite.getInstance(AlphaComposite.SRC_OVER, pAlpha)
} );
}
}

View File

@@ -1,131 +1,132 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.image; package com.twelvemonkeys.image;
import java.awt.image.RGBImageFilter; import java.awt.image.RGBImageFilter;
/** /**
* This class can convert a color image to grayscale. * This class can convert a color image to grayscale.
* <P/> * <p>
* Uses ITU standard conversion: (222 * Red + 707 * Green + 71 * Blue) / 1000. * Uses ITU standard conversion: (222 * Red + 707 * Green + 71 * Blue) / 1000.
* * </p>
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> *
* @author last modified by $Author: haku $ * @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/image/GrayFilter.java#1 $ *
* * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/GrayFilter.java#1 $
*/ *
public class GrayFilter extends RGBImageFilter { */
public class GrayFilter extends RGBImageFilter {
// This filter can filter IndexColorModel
{ // This filter can filter IndexColorModel
canFilterIndexColorModel = true; {
} canFilterIndexColorModel = true;
}
private int low = 0;
private float range = 1.0f; private int low = 0;
private float range = 1.0f;
/**
* Constructs a GrayFilter using ITU color-conversion. /**
*/ * Constructs a GrayFilter using ITU color-conversion.
public GrayFilter() { */
} public GrayFilter() {
}
/**
* Constructs a GrayFilter using ITU color-conversion, and a dynamic range between /**
* pLow and pHigh. * Constructs a GrayFilter using ITU color-conversion, and a dynamic range between
* * pLow and pHigh.
* @param pLow float in the range 0..1 *
* @param pHigh float in the range 0..1 and >= pLow * @param pLow float in the range 0..1
*/ * @param pHigh float in the range 0..1 and &gt;= pLow
public GrayFilter(float pLow, float pHigh) { */
if (pLow > pHigh) { public GrayFilter(float pLow, float pHigh) {
pLow = 0f; if (pLow > pHigh) {
} pLow = 0f;
// Make sure high and low are inside range }
if (pLow < 0f) { // Make sure high and low are inside range
pLow = 0f; if (pLow < 0f) {
} pLow = 0f;
else if (pLow > 1f) { }
pLow = 1f; else if (pLow > 1f) {
} pLow = 1f;
if (pHigh < 0f) { }
pHigh = 0f; if (pHigh < 0f) {
} pHigh = 0f;
else if (pHigh > 1f) { }
pHigh = 1f; else if (pHigh > 1f) {
} pHigh = 1f;
}
low = (int) (pLow * 255f);
range = pHigh - pLow; low = (int) (pLow * 255f);
range = pHigh - pLow;
}
}
/**
* Constructs a GrayFilter using ITU color-conversion, and a dynamic /**
* range between pLow and pHigh. * Constructs a GrayFilter using ITU color-conversion, and a dynamic
* * range between pLow and pHigh.
* @param pLow integer in the range 0..255 *
* @param pHigh inteeger in the range 0..255 and >= pLow * @param pLow integer in the range 0..255
*/ * @param pHigh integer in the range 0..255 and &gt;= pLow
public GrayFilter(int pLow, int pHigh) { */
this(pLow / 255f, pHigh / 255f); public GrayFilter(int pLow, int pHigh) {
} this(pLow / 255f, pHigh / 255f);
}
/**
* Filters one pixel using ITU color-conversion. /**
* * Filters one pixel using ITU color-conversion.
* @param pX x *
* @param pY y * @param pX x
* @param pARGB pixel value in default color space * @param pY y
* * @param pARGB pixel value in default color space
* @return the filtered pixel value in the default color space *
*/ * @return the filtered pixel value in the default color space
public int filterRGB(int pX, int pY, int pARGB) { */
// Get color components public int filterRGB(int pX, int pY, int pARGB) {
int r = pARGB >> 16 & 0xFF; // Get color components
int g = pARGB >> 8 & 0xFF; int r = pARGB >> 16 & 0xFF;
int b = pARGB & 0xFF; int g = pARGB >> 8 & 0xFF;
int b = pARGB & 0xFF;
// ITU standard: Gray scale=(222*Red+707*Green+71*Blue)/1000
int gray = (222 * r + 707 * g + 71 * b) / 1000; // ITU standard: Gray scale=(222*Red+707*Green+71*Blue)/1000
int gray = (222 * r + 707 * g + 71 * b) / 1000;
//int gray = (int) ((float) (r + g + b) / 3.0f);
//int gray = (int) ((float) (r + g + b) / 3.0f);
if (range != 1.0f) {
// Apply range if (range != 1.0f) {
gray = low + (int) (gray * range); // Apply range
} gray = low + (int) (gray * range);
}
// Return ARGB pixel
return (pARGB & 0xFF000000) | (gray << 16) | (gray << 8) | gray; // Return ARGB pixel
} return (pARGB & 0xFF000000) | (gray << 16) | (gray << 8) | gray;
} }
}

View File

@@ -1,213 +1,214 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.image; package com.twelvemonkeys.image;
/** /**
* Inverse Colormap to provide efficient lookup of any given input color * Inverse Colormap to provide efficient lookup of any given input color
* to the closest match to the given color map. * to the closest match to the given color map.
* <p/> * <p>
* Based on "Efficient Inverse Color Map Computation" by Spencer W. Thomas * Based on "Efficient Inverse Color Map Computation" by Spencer W. Thomas
* in "Graphics Gems Volume II" * in "Graphics Gems Volume II".
* * </p>
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> *
* @author Robin Luiten (Java port) * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author Spencer W. Thomas (original c version). * @author Robin Luiten (Java port)
* * @author Spencer W. Thomas (original c version).
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/InverseColorMap.java#1 $ *
*/ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/InverseColorMap.java#1 $
class InverseColorMap { */
/** class InverseColorMap {
* Number of high bits of each color channel to use to lookup near match /**
*/ * Number of high bits of each color channel to use to lookup near match
final static int QUANTBITS = 5; */
final static int QUANTBITS = 5;
/**
* Truncated bits of each color channel /**
*/ * Truncated bits of each color channel
final static int TRUNCBITS = 8 - QUANTBITS; */
final static int TRUNCBITS = 8 - QUANTBITS;
/**
* BITMASK representing the bits for blue in the color lookup /**
*/ * BITMASK representing the bits for blue in the color lookup
final static int QUANTMASK_BLUE = (1 << 5) - 1; */
final static int QUANTMASK_BLUE = (1 << 5) - 1;
/**
* BITMASK representing the bits for green in the color lookup /**
*/ * BITMASK representing the bits for green in the color lookup
final static int QUANTMASK_GREEN = (QUANTMASK_BLUE << QUANTBITS); */
final static int QUANTMASK_GREEN = (QUANTMASK_BLUE << QUANTBITS);
/**
* BITMASK representing the bits for red in the color lookup /**
*/ * BITMASK representing the bits for red in the color lookup
final static int QUANTMASK_RED = (QUANTMASK_GREEN << QUANTBITS); */
final static int QUANTMASK_RED = (QUANTMASK_GREEN << QUANTBITS);
/**
* Maximum value a quantised color channel can have /**
*/ * Maximum value a quantised color channel can have
final static int MAXQUANTVAL = 1 << 5; */
final static int MAXQUANTVAL = 1 << 5;
byte[] rgbMapByte;
int[] rgbMapInt; byte[] rgbMapByte;
int numColors; int[] rgbMapInt;
int maxColor; int numColors;
byte[] inverseRGB; // inverse rgb color map int maxColor;
int transparentIndex = -1; byte[] inverseRGB; // inverse rgb color map
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.
InverseColorMap(byte[] pRGBColorMap) { */
this(pRGBColorMap, -1); InverseColorMap(byte[] pRGBColorMap) {
} this(pRGBColorMap, -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.
// HaraldK 20040801: Added support for int[] */
InverseColorMap(int[] pRGBColorMap) { // HaraldK 20040801: Added support for int[]
this(pRGBColorMap, -1); InverseColorMap(int[] pRGBColorMap) {
} this(pRGBColorMap, -1);
}
/**
* @param pRGBColorMap the rgb color map to create inverse color map for. /**
* @param pTransparent the index of the transparent pixel in the map * @param pRGBColorMap the rgb color map to create inverse color map for.
*/ * @param pTransparent the index of the transparent pixel in the map
InverseColorMap(byte[] pRGBColorMap, int pTransparent) { */
rgbMapByte = pRGBColorMap; InverseColorMap(byte[] pRGBColorMap, int pTransparent) {
numColors = rgbMapByte.length / 4; rgbMapByte = pRGBColorMap;
transparentIndex = pTransparent; numColors = rgbMapByte.length / 4;
transparentIndex = pTransparent;
inverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]); inverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
} initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]);
}
/**
* @param pRGBColorMap the rgb color map to create inverse color map for. /**
* @param pTransparent the index of the transparent pixel in the map * @param pRGBColorMap the rgb color map to create inverse color map for.
*/ * @param pTransparent the index of the transparent pixel in the map
InverseColorMap(int[] pRGBColorMap, int pTransparent) { */
rgbMapInt = pRGBColorMap; InverseColorMap(int[] pRGBColorMap, int pTransparent) {
numColors = rgbMapInt.length; rgbMapInt = pRGBColorMap;
transparentIndex = pTransparent; numColors = rgbMapInt.length;
transparentIndex = pTransparent;
inverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]); inverseRGB = new byte[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL];
} initIRGB(new int[MAXQUANTVAL * MAXQUANTVAL * MAXQUANTVAL]);
}
/**
* Simple inverse color table creation method. /**
* @param pTemp temp array * Simple inverse color table creation method.
*/ * @param pTemp temp array
void initIRGB(int[] pTemp) { */
final int x = (1 << TRUNCBITS); // 8 the size of 1 Dimension of each quantized cell void initIRGB(int[] pTemp) {
final int xsqr = 1 << (TRUNCBITS * 2); // 64 - twice the smallest step size vale of quantized colors final int x = (1 << TRUNCBITS); // 8 the size of 1 Dimension of each quantized cell
final int xsqr2 = xsqr + xsqr; final int xsqr = 1 << (TRUNCBITS * 2); // 64 - twice the smallest step size vale of quantized colors
final int xsqr2 = xsqr + xsqr;
for (int i = 0; i < numColors; ++i) {
if (i == transparentIndex) { for (int i = 0; i < numColors; ++i) {
// Skip the transparent pixel if (i == transparentIndex) {
continue; // Skip the transparent pixel
} continue;
}
int red, r, rdist, rinc, rxx;
int green, g, gdist, ginc, gxx; int red, r, rdist, rinc, rxx;
int blue, b, bdist, binc, bxx; int green, g, gdist, ginc, gxx;
int blue, b, bdist, binc, bxx;
// HaraldK 20040801: Added support for int[]
if (rgbMapByte != null) { // HaraldK 20040801: Added support for int[]
red = rgbMapByte[i * 4] & 0xFF; if (rgbMapByte != null) {
green = rgbMapByte[i * 4 + 1] & 0xFF; red = rgbMapByte[i * 4] & 0xFF;
blue = rgbMapByte[i * 4 + 2] & 0xFF; green = rgbMapByte[i * 4 + 1] & 0xFF;
} blue = rgbMapByte[i * 4 + 2] & 0xFF;
else if (rgbMapInt != null) { }
red = (rgbMapInt[i] >> 16) & 0xFF; else if (rgbMapInt != null) {
green = (rgbMapInt[i] >> 8) & 0xFF; red = (rgbMapInt[i] >> 16) & 0xFF;
blue = rgbMapInt[i] & 0xFF; green = (rgbMapInt[i] >> 8) & 0xFF;
} blue = rgbMapInt[i] & 0xFF;
else { }
throw new IllegalStateException("colormap == null"); else {
} throw new IllegalStateException("colormap == null");
}
rdist = red - x / 2; // distance of red to center of current cell
gdist = green - x / 2; // green rdist = red - x / 2; // distance of red to center of current cell
bdist = blue - x / 2; // blue gdist = green - x / 2; // green
rdist = rdist * rdist + gdist * gdist + bdist * bdist; bdist = blue - x / 2; // blue
rdist = rdist * rdist + gdist * gdist + bdist * bdist;
rinc = 2 * (xsqr - (red << TRUNCBITS));
ginc = 2 * (xsqr - (green << TRUNCBITS)); rinc = 2 * (xsqr - (red << TRUNCBITS));
binc = 2 * (xsqr - (blue << TRUNCBITS)); ginc = 2 * (xsqr - (green << TRUNCBITS));
binc = 2 * (xsqr - (blue << TRUNCBITS));
int rgbI = 0;
for (r = 0, rxx = rinc; r < MAXQUANTVAL; rdist += rxx, ++r, rxx += xsqr2) { int rgbI = 0;
for (g = 0, gdist = rdist, gxx = ginc; g < MAXQUANTVAL; gdist += gxx, ++g, gxx += xsqr2) { for (r = 0, rxx = rinc; r < MAXQUANTVAL; rdist += rxx, ++r, rxx += xsqr2) {
for (b = 0, bdist = gdist, bxx = binc; b < MAXQUANTVAL; bdist += bxx, ++b, ++rgbI, bxx += xsqr2) { for (g = 0, gdist = rdist, gxx = ginc; g < MAXQUANTVAL; gdist += gxx, ++g, gxx += xsqr2) {
if (i == 0 || pTemp[rgbI] > bdist) { for (b = 0, bdist = gdist, bxx = binc; b < MAXQUANTVAL; bdist += bxx, ++b, ++rgbI, bxx += xsqr2) {
pTemp[rgbI] = bdist; if (i == 0 || pTemp[rgbI] > bdist) {
inverseRGB[rgbI] = (byte) i; pTemp[rgbI] = bdist;
} inverseRGB[rgbI] = (byte) i;
} }
} }
} }
} }
} }
}
/**
* Gets the index of the nearest color to from the color map. /**
* * Gets the index of the nearest color to from the color map.
* @param pColor the color to get the nearest color to from color map *
* color must be of format {@code 0x00RRGGBB} - standard default RGB * @param pColor the color to get the nearest color to from color map
* @return index of color which closest matches input color by using the * color must be of format {@code 0x00RRGGBB} - standard default RGB
* created inverse color map. * @return index of color which closest matches input color by using the
*/ * created inverse color map.
public final int getIndexNearest(int pColor) { */
return inverseRGB[((pColor >> (3 * TRUNCBITS)) & QUANTMASK_RED) + public final int getIndexNearest(int pColor) {
((pColor >> (2 * TRUNCBITS)) & QUANTMASK_GREEN) + return inverseRGB[((pColor >> (3 * TRUNCBITS)) & QUANTMASK_RED) +
((pColor >> (/* 1 * */ TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF; ((pColor >> (2 * TRUNCBITS)) & QUANTMASK_GREEN) +
} ((pColor >> (/* 1 * */ TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
}
/**
* Gets the index of the nearest color to from the color map. /**
* * Gets the index of the nearest color to from the color map.
* @param pRed red component of the color to get the nearest color to from color map *
* @param pGreen green component of the color to get the nearest color to from color map * @param pRed red component of the color to get the nearest color to from color map
* @param pBlue blue component of the color to get the nearest color to from color map * @param pGreen green component of the color to get the nearest color to from color map
* @return index of color which closest matches input color by using the * @param pBlue blue component of the color to get the nearest color to from color map
* created inverse color map. * @return index of color which closest matches input color by using the
*/ * created inverse color map.
public final int getIndexNearest(int pRed, int pGreen, int pBlue) { */
// NOTE: the third line in expression for blue is shifting DOWN not UP. public final int getIndexNearest(int pRed, int pGreen, int pBlue) {
return inverseRGB[((pRed << (2 * QUANTBITS - TRUNCBITS)) & QUANTMASK_RED) + // NOTE: the third line in expression for blue is shifting DOWN not UP.
((pGreen << (/* 1 * */ QUANTBITS - TRUNCBITS)) & QUANTMASK_GREEN) + return inverseRGB[((pRed << (2 * QUANTBITS - TRUNCBITS)) & QUANTMASK_RED) +
((pBlue >> (TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF; ((pGreen << (/* 1 * */ QUANTBITS - TRUNCBITS)) & QUANTMASK_GREEN) +
} ((pBlue >> (TRUNCBITS)) & QUANTMASK_BLUE)] & 0xFF;
} }
}

View File

@@ -1,187 +1,187 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.image; package com.twelvemonkeys.image;
import com.twelvemonkeys.lang.SystemUtil; import com.twelvemonkeys.lang.SystemUtil;
import magick.MagickImage; import magick.MagickImage;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp; import java.awt.image.BufferedImageOp;
/** /**
* This class accelerates certain graphics operations, using * This class accelerates certain graphics operations, using
* JMagick and ImageMagick, if available. * JMagick and ImageMagick, if available.
* If those libraries are not installed, this class silently does nothing. * If those libraries are not installed, this class silently does nothing.
* <p/> * <p>
* Set the system property {@code "com.twelvemonkeys.image.accel"} to * Set the system property {@code "com.twelvemonkeys.image.accel"} to
* {@code false}, to disable, even if JMagick is installed. * {@code false}, to disable, even if JMagick is installed.
* Set the system property {@code "com.twelvemonkeys.image.magick.debug"} to * Set the system property {@code "com.twelvemonkeys.image.magick.debug"} to
* <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/image/MagickAccelerator.java#3 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/image/MagickAccelerator.java#3 $
*/ */
final class MagickAccelerator { final class MagickAccelerator {
private static final boolean DEBUG = Magick.DEBUG; private static final boolean DEBUG = Magick.DEBUG;
private static final boolean USE_MAGICK = useMagick(); private static final boolean USE_MAGICK = useMagick();
private static final int RESAMPLE_OP = 0; private static final int RESAMPLE_OP = 0;
private static Class[] nativeOp = new Class[1]; private static Class[] nativeOp = new Class[1];
static { static {
try { try {
nativeOp[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);
} }
} }
private static boolean useMagick() { private static boolean useMagick() {
try { try {
boolean available = SystemUtil.isClassAvailable("magick.MagickImage"); boolean available = SystemUtil.isClassAvailable("magick.MagickImage");
if (DEBUG && !available) { if (DEBUG && !available) {
System.err.print("ImageMagick bindings not available."); System.err.print("ImageMagick bindings not available.");
} }
boolean useMagick = boolean useMagick =
available && !"FALSE".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.image.accel")); available && !"FALSE".equalsIgnoreCase(System.getProperty("com.twelvemonkeys.image.accel"));
if (DEBUG) { if (DEBUG) {
System.err.println( System.err.println(
useMagick useMagick
? "Will use ImageMagick bindings to accelerate image resampling operations." ? "Will use ImageMagick bindings to accelerate image resampling operations."
: "Will not use ImageMagick to accelerate image resampling operations." : "Will not use ImageMagick to accelerate image resampling operations."
); );
} }
return useMagick; return useMagick;
} }
catch (Throwable t) { catch (Throwable t) {
// Most probably in case of a SecurityManager // Most probably in case of a SecurityManager
System.err.println("Could not enable ImageMagick bindings: " + t); System.err.println("Could not enable ImageMagick bindings: " + t);
return false; return false;
} }
} }
private static int getNativeOpIndex(Class pOpClass) { private static int getNativeOpIndex(Class pOpClass) {
for (int i = 0; i < nativeOp.length; i++) { for (int i = 0; i < nativeOp.length; i++) {
if (pOpClass == nativeOp[i]) { if (pOpClass == nativeOp[i]) {
return i; return i;
} }
} }
return -1; return -1;
} }
public static BufferedImage filter(BufferedImageOp pOperation, BufferedImage pInput, BufferedImage pOutput) { public static BufferedImage filter(BufferedImageOp pOperation, BufferedImage pInput, BufferedImage pOutput) {
if (!USE_MAGICK) { if (!USE_MAGICK) {
return null; return null;
} }
BufferedImage result = null; BufferedImage result = null;
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.width, resample.height, resample.filterType); 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
if (pOutput != null) { if (pOutput != null) {
//pOutput.setData(result.getRaster()); // Fast, but less compatible //pOutput.setData(result.getRaster()); // Fast, but less compatible
// NOTE: For some reason, this is sometimes super-slow...? // NOTE: For some reason, this is sometimes super-slow...?
ImageUtil.drawOnto(pOutput, result); ImageUtil.drawOnto(pOutput, result);
result = pOutput; result = pOutput;
} }
break; break;
default: default:
// Simply fall through, allowing acceleration to be added later // Simply fall through, allowing acceleration to be added later
break; break;
} }
return result; return result;
} }
private static BufferedImage resampleMagick(BufferedImage pSrc, int pWidth, int pHeight, int pFilterType) { private static BufferedImage resampleMagick(BufferedImage pSrc, int pWidth, int pHeight, int pFilterType) {
// Convert to Magick, scale and convert back // Convert to Magick, scale and convert back
MagickImage image = null; MagickImage image = null;
MagickImage scaled = null; MagickImage scaled = null;
try { try {
image = MagickUtil.toMagick(pSrc); image = MagickUtil.toMagick(pSrc);
long start = 0; long start = 0;
if (DEBUG) { if (DEBUG) {
start = System.currentTimeMillis(); start = System.currentTimeMillis();
} }
// NOTE: setFilter affects zoomImage, NOT scaleImage // NOTE: setFilter affects zoomImage, NOT scaleImage
image.setFilter(pFilterType); image.setFilter(pFilterType);
scaled = image.zoomImage(pWidth, pHeight); scaled = image.zoomImage(pWidth, pHeight);
//scaled = image.scaleImage(pWidth, pHeight); // AREA_AVERAGING //scaled = image.scaleImage(pWidth, pHeight); // AREA_AVERAGING
if (DEBUG) { if (DEBUG) {
long time = System.currentTimeMillis() - start; long time = System.currentTimeMillis() - start;
System.out.println("Filtered: " + time + " ms"); System.out.println("Filtered: " + time + " ms");
} }
return MagickUtil.toBuffered(scaled); return MagickUtil.toBuffered(scaled);
} }
//catch (MagickException e) { //catch (MagickException e) {
catch (Exception e) { catch (Exception e) {
// NOTE: Stupid workaround: If MagickException is caught, a // NOTE: Stupid workaround: If MagickException is caught, a
// NoClassDefFoundError is thrown, when MagickException class is // NoClassDefFoundError is thrown, when MagickException class is
// unavailable... // unavailable...
if (e instanceof RuntimeException) { if (e instanceof RuntimeException) {
throw (RuntimeException) e; throw (RuntimeException) e;
} }
throw new ImageConversionException(e.getMessage(), e); throw new ImageConversionException(e.getMessage(), e);
} }
finally { finally {
// NOTE: ImageMagick might be unstable after a while, if image data // NOTE: ImageMagick might be unstable after a while, if image data
// is not deallocated. The GC/finalize method handles this, but in // is not deallocated. The GC/finalize method handles this, but in
// special circumstances, it's not triggered often enough. // special circumstances, it's not triggered often enough.
if (image != null) { if (image != null) {
image.destroyImages(); image.destroyImages();
} }
if (scaled != null) { if (scaled != null) {
scaled.destroyImages(); scaled.destroyImages();
} }
} }
} }
} }

View File

@@ -1,79 +1,80 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.image; package com.twelvemonkeys.image;
import java.awt.image.ReplicateScaleFilter; import java.awt.image.ReplicateScaleFilter;
/** /**
* An {@code ImageFilter} class for subsampling images. * An {@code ImageFilter} class for subsampling images.
* <p/> * <p>
* It is meant to be used in conjunction with a {@code FilteredImageSource} * It is meant to be used in conjunction with a {@code FilteredImageSource}
* object to produce subsampled versions of existing images. * object to produce subsampled versions of existing images.
* * </p>
* @see java.awt.image.FilteredImageSource *
* * @see java.awt.image.FilteredImageSource
* @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/SubsamplingFilter.java#1 $ * @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/SubsamplingFilter.java#1 $
public class SubsamplingFilter extends ReplicateScaleFilter { */
private int xSub; public class SubsamplingFilter extends ReplicateScaleFilter {
private int ySub; private int xSub;
private int ySub;
/**
* Creates a {@code SubsamplingFilter}. /**
* * Creates a {@code SubsamplingFilter}.
* @param pXSub *
* @param pYSub * @param pXSub
* * @param pYSub
* @throws IllegalArgumentException if {@code pXSub} or {@code pYSub} is *
* less than 1. * @throws IllegalArgumentException if {@code pXSub} or {@code pYSub} is
*/ * less than 1.
public SubsamplingFilter(int pXSub, int pYSub) { */
super(1, 1); // These are NOT REAL values, but we have to defer setting public SubsamplingFilter(int pXSub, int pYSub) {
// until w/h is available, in setDimensions below super(1, 1); // These are NOT REAL values, but we have to defer setting
// until w/h is available, in setDimensions below
if (pXSub < 1 || pYSub < 1) {
throw new IllegalArgumentException("Subsampling factors must be positive."); if (pXSub < 1 || pYSub < 1) {
} throw new IllegalArgumentException("Subsampling factors must be positive.");
}
xSub = pXSub;
ySub = pYSub; xSub = pXSub;
} ySub = pYSub;
}
/** {@code ImageFilter} implementation, do not invoke. */
public void setDimensions(int pWidth, int pHeight) { /** {@code ImageFilter} implementation, do not invoke. */
destWidth = (pWidth + xSub - 1) / xSub; public void setDimensions(int pWidth, int pHeight) {
destHeight = (pHeight + ySub - 1) / ySub; destWidth = (pWidth + xSub - 1) / xSub;
destHeight = (pHeight + ySub - 1) / ySub;
//System.out.println("Subsampling: " + xSub + "," + ySub + "-> " + destWidth + ", " + destHeight);
super.setDimensions(pWidth, pHeight); //System.out.println("Subsampling: " + xSub + "," + ySub + "-> " + destWidth + ", " + destHeight);
} super.setDimensions(pWidth, pHeight);
} }
}

View File

@@ -30,8 +30,9 @@
/** /**
* Classes for image manipulation. * Classes for image manipulation.
* <p/> * <p>
* See the class {@link com.twelvemonkeys.image.ImageUtil}. * See the class {@link com.twelvemonkeys.image.ImageUtil}.
* </p>
* *
* @version 1.0 * @version 1.0
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>

View File

@@ -218,9 +218,10 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
/** /**
* Writes a series of bytes at the current read/write position. The read/write position will be increased by * Writes a series of bytes at the current read/write position. The read/write position will be increased by
* {@code pLength}. * {@code pLength}.
* <p/> * <p>
* This implementation invokes {@link #write(int)} {@code pLength} times. * This implementation invokes {@link #write(int)} {@code pLength} times.
* Subclasses may override this method for performance. * Subclasses may override this method for performance.
* </p>
* *
* @param pBuffer the bytes to write. * @param pBuffer the bytes to write.
* @param pOffset the starting offset into the buffer. * @param pOffset the starting offset into the buffer.
@@ -246,9 +247,10 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
/** /**
* Writes a series of bytes at the current read/write position. The read/write position will be increased by * Writes a series of bytes at the current read/write position. The read/write position will be increased by
* {@code pLength}. * {@code pLength}.
* <p/> * <p>
* This implementation invokes {@link #read()} {@code pLength} times. * This implementation invokes {@link #read()} {@code pLength} times.
* Subclasses may override this method for performance. * Subclasses may override this method for performance.
* </p>
* *
* @param pBuffer the bytes to write * @param pBuffer the bytes to write
* @param pOffset the starting offset into the buffer. * @param pOffset the starting offset into the buffer.
@@ -283,12 +285,14 @@ abstract class AbstractCachedSeekableStream extends SeekableInputStream {
/** /**
* Optionally flushes any data prior to the given position. * Optionally flushes any data prior to the given position.
* <p/> * <p>
* Attempting to perform a seek operation, and/or a read or write operation to a position equal to or before * Attempting to perform a seek operation, and/or a read or write operation to a position equal to or before
* the flushed position may result in exceptions or undefined behaviour. * the flushed position may result in exceptions or undefined behaviour.
* <p/> * </p>
* <p>
* Subclasses should override this method for performance reasons, to avoid holding on to unnecessary resources. * Subclasses should override this method for performance reasons, to avoid holding on to unnecessary resources.
* This implementation does nothing. * This implementation does nothing.
* </p>
* *
* @param pPosition the last position to flush. * @param pPosition the last position to flush.
*/ */

View File

@@ -1,222 +1,221 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate; import com.twelvemonkeys.lang.Validate;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
/** /**
* A Reader implementation that can read from multiple sources. * A Reader implementation that can read from multiple sources.
* <p/> *
* * @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/io/CompoundReader.java#2 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/CompoundReader.java#2 $ */
*/ public class CompoundReader extends Reader {
public class CompoundReader extends Reader {
private Reader current;
private Reader current; private List<Reader> readers;
private List<Reader> readers;
protected final Object finalLock;
protected final Object finalLock;
protected final boolean markSupported;
protected final boolean markSupported;
private int currentReader;
private int currentReader; private int markedReader;
private int markedReader; private int mark;
private int mark; private int mNext;
private int mNext;
/**
/** * Create a new compound reader.
* Create a new compound reader. *
* * @param pReaders {@code Iterator} containting {@code Reader}s,
* @param pReaders {@code Iterator} containting {@code Reader}s, * providing the character stream.
* providing the character stream. *
* * @throws NullPointerException if {@code pReaders} is {@code null}, or
* @throws NullPointerException if {@code pReaders} is {@code null}, or * any of the elements in the iterator is {@code null}.
* any of the elements in the iterator is {@code null}. * @throws ClassCastException if any element of the iterator is not a
* @throws ClassCastException if any element of the iterator is not a * {@code java.io.Reader}
* {@code java.io.Reader} */
*/ public CompoundReader(final Iterator<Reader> pReaders) {
public CompoundReader(final Iterator<Reader> pReaders) { super(Validate.notNull(pReaders, "readers"));
super(Validate.notNull(pReaders, "readers"));
finalLock = 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
readers = new ArrayList<Reader>();
readers = new ArrayList<Reader>();
boolean markSupported = true;
boolean markSupported = true; while (pReaders.hasNext()) {
while (pReaders.hasNext()) { Reader reader = pReaders.next();
Reader reader = pReaders.next(); if (reader == null) {
if (reader == null) { throw new NullPointerException("readers cannot contain null-elements");
throw new NullPointerException("readers cannot contain null-elements"); }
} readers.add(reader);
readers.add(reader); markSupported = markSupported && reader.markSupported();
markSupported = markSupported && reader.markSupported(); }
} this.markSupported = markSupported;
this.markSupported = markSupported;
current = nextReader();
current = nextReader(); }
}
protected final Reader nextReader() {
protected final Reader nextReader() { if (currentReader >= readers.size()) {
if (currentReader >= readers.size()) { current = new EmptyReader();
current = new EmptyReader(); }
} else {
else { current = readers.get(currentReader++);
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 current;
return current; }
}
/**
/** * Check to make sure that the stream has not been closed
* Check to make sure that the stream has not been closed *
* * @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 (readers == 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 : readers) {
for (Reader reader : readers) { reader.close();
reader.close(); }
}
readers = null;
readers = null; }
}
@Override
@Override public void mark(int pReadLimit) throws IOException {
public void mark(int pReadLimit) throws IOException { if (pReadLimit < 0) {
if (pReadLimit < 0) { throw new IllegalArgumentException("Read limit < 0");
throw new IllegalArgumentException("Read limit < 0"); }
}
// 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 (finalLock) {
synchronized (finalLock) { ensureOpen();
ensureOpen(); mark = mNext;
mark = mNext; markedReader = currentReader;
markedReader = currentReader;
current.mark(pReadLimit);
current.mark(pReadLimit); }
} }
}
@Override
@Override public void reset() throws IOException {
public void reset() throws IOException { synchronized (finalLock) {
synchronized (finalLock) { ensureOpen();
ensureOpen();
if (currentReader != markedReader) {
if (currentReader != markedReader) { // Reset any reader before this
// Reset any reader before this for (int i = currentReader; i >= markedReader; i--) {
for (int i = currentReader; i >= markedReader; i--) { readers.get(i).reset();
readers.get(i).reset(); }
}
currentReader = markedReader - 1;
currentReader = markedReader - 1; nextReader();
nextReader(); }
} current.reset();
current.reset();
mNext = mark;
mNext = mark; }
} }
}
@Override
@Override public boolean markSupported() {
public boolean markSupported() { return markSupported;
return markSupported; }
}
@Override
@Override public int read() throws IOException {
public int read() throws IOException { synchronized (finalLock) {
synchronized (finalLock) { int read = current.read();
int read = current.read();
if (read < 0 && currentReader < readers.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 }
}
mNext++;
mNext++;
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 (finalLock) {
synchronized (finalLock) { int read = current.read(pBuffer, pOffset, pLength);
int read = current.read(pBuffer, pOffset, pLength);
if (read < 0 && currentReader < readers.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 }
}
mNext += read;
mNext += read;
return read;
return read; }
} }
}
@Override
@Override public boolean ready() throws IOException {
public boolean ready() throws IOException { return current.ready();
return current.ready(); }
}
@Override
@Override public long skip(long pChars) throws IOException {
public long skip(long pChars) throws IOException { synchronized (finalLock) {
synchronized (finalLock) { long skipped = current.skip(pChars);
long skipped = current.skip(pChars);
if (skipped == 0 && currentReader < readers.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 }
}
mNext += skipped;
mNext += skipped;
return skipped;
return skipped; }
} }
} }
}

View File

@@ -1,47 +1,46 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import java.io.StringReader; import java.io.StringReader;
/** /**
* EmptyReader * EmptyReader
* <p/> *
* * @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/io/EmptyReader.java#1 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/EmptyReader.java#1 $ */
*/ final class EmptyReader extends StringReader {
final class EmptyReader extends StringReader { public EmptyReader() {
public EmptyReader() { super("");
super(""); }
} }
}

View File

@@ -1,136 +1,137 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
/** /**
* An unsynchronized {@code ByteArrayOutputStream} implementation. This version * An unsynchronized {@code ByteArrayOutputStream} implementation. This version
* also has a constructor that lets you create a stream with initial content. * also has a constructor that lets you create a stream with initial content.
* <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: 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
// 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 amount of bytes) */
/** Max grow size (unless if writing more than this amount of bytes) */ protected int maxGrowSize = 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 * size.
* size. *
* * @param pSize initial buffer size
* @param pSize initial buffer size */
*/ public FastByteArrayOutputStream(int pSize) {
public FastByteArrayOutputStream(int pSize) { super(pSize);
super(pSize); }
}
/**
/** * Creates a {@code ByteArrayOutputStream} with the given initial content.
* Creates a {@code ByteArrayOutputStream} with the given initial content. * <p>
* <p/> * Note that the buffer is not cloned, for maximum performance.
* Note that the buffer is not cloned, for maximum performance. * </p>
* *
* @param pBuffer initial buffer * @param pBuffer initial buffer
*/ */
public FastByteArrayOutputStream(byte[] pBuffer) { public FastByteArrayOutputStream(byte[] pBuffer) {
super(0); // Don't allocate array super(0); // Don't allocate array
buf = pBuffer; buf = pBuffer;
count = pBuffer.length; count = pBuffer.length;
} }
@Override @Override
public 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();
} }
else if (pLength == 0) { else if (pLength == 0) {
return; return;
} }
int newCount = count + pLength; int newCount = count + pLength;
growIfNeeded(newCount); growIfNeeded(newCount);
System.arraycopy(pBytes, pOffset, buf, count, pLength); System.arraycopy(pBytes, pOffset, buf, count, pLength);
count = newCount; count = newCount;
} }
@Override @Override
public 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 + maxGrowSize), 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;
} }
} }
// Non-synchronized version of writeTo // Non-synchronized version of writeTo
@Override @Override
public void writeTo(OutputStream pOut) throws IOException { public void writeTo(OutputStream pOut) throws IOException {
pOut.write(buf, 0, count); pOut.write(buf, 0, count);
} }
// 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;
} }
/** /**
* Creates a {@code ByteArrayInputStream} that reads directly from this * Creates a {@code ByteArrayInputStream} that reads directly from this
* {@code FastByteArrayOutputStream}'s byte buffer. * {@code FastByteArrayOutputStream}'s byte buffer.
* The buffer is not cloned, for maximum performance. * The buffer is not cloned, for maximum performance.
* <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 behaviour. * Failing to do so, may result in unpredictable behaviour.
* * </p>
* @return a new {@code ByteArrayInputStream}, reading from this stream's buffer. *
*/ * @return a new {@code ByteArrayInputStream}, reading from this stream's buffer.
public ByteArrayInputStream createInputStream() { */
return new ByteArrayInputStream(buf, 0, count); public ByteArrayInputStream createInputStream() {
} return new ByteArrayInputStream(buf, 0, count);
}
} }

View File

@@ -1,240 +1,241 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate; import com.twelvemonkeys.lang.Validate;
import java.io.*; import java.io.*;
/** /**
* A {@code SeekableInputStream} implementation that caches data in a temporary {@code File}. * A {@code SeekableInputStream} implementation that caches data in a temporary {@code File}.
* <p/> * <p>
* Temporary files are created as specified in {@link File#createTempFile(String, String, java.io.File)}. * Temporary files are created as specified in {@link File#createTempFile(String, String, java.io.File)}.
* * </p>
* @see MemoryCacheSeekableStream *
* @see FileSeekableStream * @see MemoryCacheSeekableStream
* * @see FileSeekableStream
* @see File#createTempFile(String, String) *
* @see RandomAccessFile * @see File#createTempFile(String, String)
* * @see RandomAccessFile
* @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/FileCacheSeekableStream.java#5 $ * @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/FileCacheSeekableStream.java#5 $
public final class FileCacheSeekableStream extends AbstractCachedSeekableStream { */
public final class FileCacheSeekableStream extends AbstractCachedSeekableStream {
private byte[] buffer;
private byte[] buffer;
/**
* Creates a {@code FileCacheSeekableStream} reading from the given /**
* {@code InputStream}. Data will be cached in a temporary file. * Creates a {@code FileCacheSeekableStream} reading from the given
* * {@code InputStream}. Data will be cached in a temporary file.
* @param pStream the {@code InputStream} to read from *
* * @param pStream the {@code InputStream} to read from
* @throws IOException if the temporary file cannot be created, *
* or cannot be opened for random access. * @throws IOException if the temporary file cannot be created,
*/ * or cannot be opened for random access.
public FileCacheSeekableStream(final InputStream pStream) throws IOException { */
this(pStream, "iocache", null); public FileCacheSeekableStream(final InputStream pStream) throws IOException {
} this(pStream, "iocache", null);
}
/**
* Creates a {@code FileCacheSeekableStream} reading from the given /**
* {@code InputStream}. Data will be cached in a temporary file, with * Creates a {@code FileCacheSeekableStream} reading from the given
* the given base name. * {@code InputStream}. Data will be cached in a temporary file, with
* * the given base name.
* @param pStream the {@code InputStream} to read from *
* @param pTempBaseName optional base name for the temporary file * @param pStream the {@code InputStream} to read from
* * @param pTempBaseName optional base name for the temporary file
* @throws IOException if the temporary file cannot be created, *
* or cannot be opened for random access. * @throws IOException if the temporary file cannot be created,
*/ * or cannot be opened for random access.
public FileCacheSeekableStream(final InputStream pStream, final String pTempBaseName) throws IOException { */
this(pStream, pTempBaseName, null); public FileCacheSeekableStream(final InputStream pStream, final String pTempBaseName) throws IOException {
} this(pStream, pTempBaseName, null);
}
/**
* Creates a {@code FileCacheSeekableStream} reading from the given /**
* {@code InputStream}. Data will be cached in a temporary file, with * Creates a {@code FileCacheSeekableStream} reading from the given
* the given base name, in the given directory * {@code InputStream}. Data will be cached in a temporary file, with
* * the given base name, in the given directory
* @param pStream the {@code InputStream} to read from *
* @param pTempBaseName optional base name for the temporary file * @param pStream the {@code InputStream} to read from
* @param pTempDir optional temp directory * @param pTempBaseName optional base name for the temporary file
* * @param pTempDir optional temp directory
* @throws IOException if the temporary file cannot be created, *
* or cannot be opened for random access. * @throws IOException if the temporary file cannot be created,
*/ * or cannot be opened for random access.
public FileCacheSeekableStream(final InputStream pStream, final String pTempBaseName, final File pTempDir) throws IOException { */
// NOTE: We do validation BEFORE we create temp file, to avoid orphan files public FileCacheSeekableStream(final InputStream pStream, final String pTempBaseName, final File pTempDir) throws IOException {
this(Validate.notNull(pStream, "stream"), createTempFile(pTempBaseName, pTempDir)); // NOTE: We do validation BEFORE we create temp file, to avoid orphan files
} this(Validate.notNull(pStream, "stream"), createTempFile(pTempBaseName, pTempDir));
}
/*protected*/ static File createTempFile(String pTempBaseName, File pTempDir) throws IOException {
Validate.notNull(pTempBaseName, "tempBaseName"); /*protected*/ static File createTempFile(String pTempBaseName, File pTempDir) throws IOException {
Validate.notNull(pTempBaseName, "tempBaseName");
File file = File.createTempFile(pTempBaseName, null, pTempDir);
file.deleteOnExit(); File file = File.createTempFile(pTempBaseName, null, pTempDir);
file.deleteOnExit();
return file;
} return file;
}
// TODO: Consider exposing this for external use
/*protected*/ FileCacheSeekableStream(final InputStream pStream, final File pFile) throws FileNotFoundException { // TODO: Consider exposing this for external use
super(pStream, new FileCache(pFile)); /*protected*/ FileCacheSeekableStream(final InputStream pStream, final File pFile) throws FileNotFoundException {
super(pStream, new FileCache(pFile));
// TODO: Allow for custom buffer sizes?
buffer = new byte[1024]; // TODO: Allow for custom buffer sizes?
} buffer = new byte[1024];
}
public final boolean isCachedMemory() {
return false; public final boolean isCachedMemory() {
} return false;
}
public final boolean isCachedFile() {
return true; public final boolean isCachedFile() {
} return true;
}
@Override
protected void closeImpl() throws IOException { @Override
// TODO: Close cache file protected void closeImpl() throws IOException {
super.closeImpl(); // TODO: Close cache file
super.closeImpl();
buffer = null;
} buffer = null;
}
@Override
public int read() throws IOException { @Override
checkOpen(); public int read() throws IOException {
checkOpen();
int read;
if (position == streamPosition) { int read;
// Read ahead into buffer, for performance if (position == streamPosition) {
read = readAhead(buffer, 0, buffer.length); // Read ahead into buffer, for performance
if (read >= 0) { read = readAhead(buffer, 0, buffer.length);
read = buffer[0] & 0xff; if (read >= 0) {
} 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));
else { }
// ..or read byte from the cache else {
syncPosition(); // ..or read byte from the cache
read = getCache().read(); syncPosition();
read = getCache().read();
//System.out.println("Read 1 byte from cache: " + Integer.toHexString(read & 0xff));
} //System.out.println("Read 1 byte from cache: " + Integer.toHexString(read & 0xff));
}
// TODO: This field is not REALLY considered accessible.. :-P
if (read != -1) { // TODO: This field is not REALLY considered accessible.. :-P
position++; if (read != -1) {
} position++;
return read; }
} return read;
}
@Override
public int read(byte[] pBytes, int pOffset, int pLength) throws IOException { @Override
checkOpen(); public int read(byte[] pBytes, int pOffset, int pLength) throws IOException {
checkOpen();
int length;
if (position == streamPosition) { int length;
// Read bytes from the stream if (position == streamPosition) {
length = readAhead(pBytes, pOffset, pLength); // Read bytes from the stream
length = readAhead(pBytes, pOffset, pLength);
//System.out.println("Read " + length + " byte from stream");
} //System.out.println("Read " + length + " byte from stream");
else { }
// ...or read bytes from the cache else {
syncPosition(); // ...or read bytes from the cache
length = getCache().read(pBytes, pOffset, (int) Math.min(pLength, streamPosition - position)); syncPosition();
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
if (length > 0) { // TODO: This field is not REALLY considered accessible.. :-P
position += length; if (length > 0) {
} position += length;
return length; }
} return length;
}
private int readAhead(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
int length; private int readAhead(final byte[] pBytes, final int pOffset, final int pLength) throws IOException {
length = stream.read(pBytes, pOffset, pLength); int length;
length = stream.read(pBytes, pOffset, pLength);
if (length > 0) {
streamPosition += length; if (length > 0) {
getCache().write(pBytes, pOffset, length); streamPosition += length;
} getCache().write(pBytes, pOffset, length);
return length; }
} return length;
}
// TODO: We need to close the cache file!!! Otherwise we are leaking file descriptors
// TODO: We need to close the cache file!!! Otherwise we are leaking file descriptors
final static class FileCache extends StreamCache {
private RandomAccessFile cacheFile; final static class FileCache extends StreamCache {
private RandomAccessFile cacheFile;
public FileCache(final File pFile) throws FileNotFoundException {
Validate.notNull(pFile, "file"); public FileCache(final File pFile) throws FileNotFoundException {
cacheFile = new RandomAccessFile(pFile, "rw"); Validate.notNull(pFile, "file");
} cacheFile = new RandomAccessFile(pFile, "rw");
}
public void write(final int pByte) throws IOException {
cacheFile.write(pByte); public void write(final int pByte) throws IOException {
} cacheFile.write(pByte);
}
@Override
public void write(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException { @Override
cacheFile.write(pBuffer, pOffset, pLength); public void write(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
} cacheFile.write(pBuffer, pOffset, pLength);
}
public int read() throws IOException {
return cacheFile.read(); public int read() throws IOException {
} return cacheFile.read();
}
@Override
public int read(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException { @Override
return cacheFile.read(pBuffer, pOffset, pLength); public int read(final byte[] pBuffer, final int pOffset, final int pLength) throws IOException {
} return cacheFile.read(pBuffer, pOffset, pLength);
}
public void seek(final long pPosition) throws IOException {
cacheFile.seek(pPosition); public void seek(final long pPosition) throws IOException {
} cacheFile.seek(pPosition);
}
public long getPosition() throws IOException {
return cacheFile.getFilePointer(); public long getPosition() throws IOException {
} return cacheFile.getFilePointer();
}
@Override
void close() throws IOException { @Override
cacheFile.close(); void close() throws IOException {
} cacheFile.close();
} }
} }
}

View File

@@ -1,135 +1,135 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
/** /**
* A {@code SeekableInputStream} implementation that uses random access directly to a {@code File}. * A {@code SeekableInputStream} implementation that uses random access directly to a {@code File}.
* <p/>
* @see FileCacheSeekableStream * @see FileCacheSeekableStream
* @see MemoryCacheSeekableStream * @see MemoryCacheSeekableStream
* @see RandomAccessFile * @see RandomAccessFile
* *
* @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/FileSeekableStream.java#4 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/FileSeekableStream.java#4 $
*/ */
public final class FileSeekableStream extends SeekableInputStream { public final class FileSeekableStream extends SeekableInputStream {
// TODO: Figure out why this class is SLOWER than FileCacheSeekableStream in // TODO: Figure out why this class is SLOWER than FileCacheSeekableStream in
// my tests..? // my tests..?
final RandomAccessFile mRandomAccess; final RandomAccessFile mRandomAccess;
/** /**
* Creates a {@code FileSeekableStream} that reads from the given * Creates a {@code FileSeekableStream} that reads from the given
* {@code File}. * {@code File}.
* *
* @param pInput file to read from * @param pInput file to read from
* @throws FileNotFoundException if {@code pInput} does not exist * @throws FileNotFoundException if {@code pInput} does not exist
*/ */
public FileSeekableStream(final File pInput) throws FileNotFoundException { public FileSeekableStream(final File pInput) throws FileNotFoundException {
this(new RandomAccessFile(pInput, "r")); this(new RandomAccessFile(pInput, "r"));
} }
/** /**
* Creates a {@code FileSeekableStream} that reads from the given file. * Creates a {@code FileSeekableStream} that reads from the given file.
* The {@code RandomAccessFile} needs only to be open in read * The {@code RandomAccessFile} needs only to be open in read
* ({@code "r"}) mode. * ({@code "r"}) mode.
* *
* @param pInput file to read from * @param pInput file to read from
*/ */
public FileSeekableStream(final RandomAccessFile pInput) { public FileSeekableStream(final RandomAccessFile pInput) {
mRandomAccess = pInput; mRandomAccess = pInput;
} }
/// Seekable /// Seekable
public boolean isCached() { public boolean isCached() {
return false; return false;
} }
public boolean isCachedFile() { public boolean isCachedFile() {
return false; return false;
} }
public boolean isCachedMemory() { public boolean isCachedMemory() {
return false; return false;
} }
/// InputStream /// InputStream
@Override @Override
public int available() throws IOException { public int available() throws IOException {
long length = mRandomAccess.length() - position; 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;
} }
public void closeImpl() throws IOException { public void closeImpl() throws IOException {
mRandomAccess.close(); mRandomAccess.close();
} }
public int read() throws IOException { public int read() throws IOException {
checkOpen(); checkOpen();
int read = mRandomAccess.read(); int read = mRandomAccess.read();
if (read >= 0) { if (read >= 0) {
position++; position++;
} }
return read; return read;
} }
@Override @Override
public int read(byte pBytes[], int pOffset, int pLength) throws IOException { public int read(byte pBytes[], int pOffset, int pLength) throws IOException {
checkOpen(); checkOpen();
int read = mRandomAccess.read(pBytes, pOffset, pLength); int read = mRandomAccess.read(pBytes, pOffset, pLength);
if (read > 0) { if (read > 0) {
position += read; position += read;
} }
return read; return read;
} }
/** /**
* Does nothing, as we don't really do any caching here. * Does nothing, as we don't really do any caching here.
* *
* @param pPosition the position to flush to * @param pPosition the position to flush to
*/ */
protected void flushBeforeImpl(long pPosition) { protected void flushBeforeImpl(long pPosition) {
} }
protected void seekImpl(long pPosition) throws IOException { protected void seekImpl(long pPosition) throws IOException {
mRandomAccess.seek(pPosition); mRandomAccess.seek(pPosition);
} }
} }

View File

@@ -1,103 +1,102 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
/** /**
* FileSystem * FileSystem
* <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: FileSystem.java#1 $
* @version $Id: FileSystem.java#1 $ */
*/ abstract class FileSystem {
abstract class FileSystem { abstract long getFreeSpace(File pPath);
abstract long getFreeSpace(File pPath);
abstract long getTotalSpace(File pPath);
abstract long getTotalSpace(File pPath);
abstract String getName();
abstract String getName();
static BufferedReader exec(String[] pArgs) throws IOException {
static BufferedReader exec(String[] pArgs) throws IOException { Process cmd = Runtime.getRuntime().exec(pArgs);
Process cmd = Runtime.getRuntime().exec(pArgs); return new BufferedReader(new InputStreamReader(cmd.getInputStream()));
return new BufferedReader(new InputStreamReader(cmd.getInputStream())); }
}
static FileSystem get() {
static FileSystem get() { String os = System.getProperty("os.name");
String os = System.getProperty("os.name"); //System.out.println("os = " + os);
//System.out.println("os = " + os);
os = os.toLowerCase();
os = os.toLowerCase(); if (os.contains("windows")) {
if (os.contains("windows")) { return new Win32FileSystem();
return new Win32FileSystem(); }
} else if (os.contains("linux") ||
else if (os.contains("linux") || os.contains("sun os") ||
os.contains("sun os") || os.contains("sunos") ||
os.contains("sunos") || os.contains("solaris") ||
os.contains("solaris") || os.contains("mpe/ix") ||
os.contains("mpe/ix") || os.contains("hp-ux") ||
os.contains("hp-ux") || os.contains("aix") ||
os.contains("aix") || os.contains("freebsd") ||
os.contains("freebsd") || os.contains("irix") ||
os.contains("irix") || os.contains("digital unix") ||
os.contains("digital unix") || os.contains("unix") ||
os.contains("unix") || os.contains("mac os x")) {
os.contains("mac os x")) { return new UnixFileSystem();
return new UnixFileSystem(); }
} else {
else { return new UnknownFileSystem(os);
return new UnknownFileSystem(os); }
} }
}
private static class UnknownFileSystem extends FileSystem {
private static class UnknownFileSystem extends FileSystem { private final String osName;
private final String osName;
UnknownFileSystem(String pOSName) {
UnknownFileSystem(String pOSName) { osName = pOSName;
osName = pOSName; }
}
long getFreeSpace(File pPath) {
long getFreeSpace(File pPath) { return 0l;
return 0l; }
}
long getTotalSpace(File pPath) {
long getTotalSpace(File pPath) { return 0l;
return 0l; }
}
String getName() {
String getName() { return "Unknown (" + osName + ")";
return "Unknown (" + osName + ")"; }
} }
} }
}

View File

@@ -1,244 +1,249 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import com.twelvemonkeys.util.regex.WildcardStringParser; import com.twelvemonkeys.util.regex.WildcardStringParser;
import java.io.File; import java.io.File;
import java.io.FilenameFilter; import java.io.FilenameFilter;
/** /**
* A Java Bean used for approving file names which are to be included in a * A Java Bean used for approving file names which are to be included in a
* {@code java.io.File} listing. * {@code java.io.File} listing.
* The mask is given as a well-known DOS filename format, with '*' and '?' as * The mask is given as a well-known DOS filename format, with '*' and '?' as
* wildcards. * wildcards.
* All other characters counts as ordinary characters. * All other characters counts as ordinary characters.
* <p/> * <p>
* The file name masks are used as a filter input and is given to the class via * The file name masks are used as a filter input and is given to the class via
* the string array property:<br> * the string array property:
* <dd>{@code filenameMasksForInclusion} - Filename mask for exclusion of * </p>
* files (default if both properties are defined) * <dl>
* <dd>{@code filenameMasksForExclusion} - Filename mask for exclusion of * <dt>{@code filenameMasksForInclusion}</dt>
* files. * <dd>Filename mask for exclusion of
* <p/> * files (default if both properties are defined).</dd>
* A recommended way of doing this is by referencing to the component which uses * <dt>{@code filenameMasksForExclusion}</dt>
* this class for file listing. In this way all properties are set in the same * <dd>Filename mask for exclusion of files.</dd>
* component and this utility component is kept in the background with only * </dl>
* initial configuration necessary. * <p>
* * A recommended way of doing this is by referencing to the component which uses
* @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a> * this class for file listing. In this way all properties are set in the same
* @see File#list(java.io.FilenameFilter) java.io.File.list * component and this utility component is kept in the background with only
* @see FilenameFilter java.io.FilenameFilter * initial configuration necessary.
* @see WildcardStringParser * </p>
* @deprecated *
*/ * @author <a href="mailto:eirik.torske@iconmedialab.no">Eirik Torske</a>
public class FilenameMaskFilter implements FilenameFilter { * @see File#list(java.io.FilenameFilter) java.io.File.list
* @see FilenameFilter java.io.FilenameFilter
// TODO: Rewrite to use regexp, or create new class * @see WildcardStringParser
* @deprecated
// Members */
private String[] filenameMasksForInclusion; public class FilenameMaskFilter implements FilenameFilter {
private String[] filenameMasksForExclusion;
private boolean inclusion = true; // TODO: Rewrite to use regexp, or create new class
// Members
/** private String[] filenameMasksForInclusion;
* Creates a {@code FilenameMaskFilter} private String[] filenameMasksForExclusion;
*/ private boolean inclusion = true;
public FilenameMaskFilter() {
}
/**
/** * Creates a {@code FilenameMaskFilter}
* Creates a {@code FilenameMaskFilter} */
* public FilenameMaskFilter() {
* @param pFilenameMask the filename mask }
*/
public FilenameMaskFilter(final String pFilenameMask) { /**
String[] filenameMask = {pFilenameMask}; * Creates a {@code FilenameMaskFilter}
setFilenameMasksForInclusion(filenameMask); *
} * @param pFilenameMask the filename mask
*/
/** public FilenameMaskFilter(final String pFilenameMask) {
* Creates a {@code FilenameMaskFilter} String[] filenameMask = {pFilenameMask};
* setFilenameMasksForInclusion(filenameMask);
* @param pFilenameMasks the filename masks }
*/
public FilenameMaskFilter(final String[] pFilenameMasks) { /**
this(pFilenameMasks, false); * Creates a {@code FilenameMaskFilter}
} *
* @param pFilenameMasks the filename masks
/** */
* Creates a {@code FilenameMaskFilter} public FilenameMaskFilter(final String[] pFilenameMasks) {
* this(pFilenameMasks, false);
* @param pFilenameMask the filename masks }
* @param pExclusion if {@code true}, the masks will be excluded
*/ /**
public FilenameMaskFilter(final String pFilenameMask, final boolean pExclusion) { * Creates a {@code FilenameMaskFilter}
String[] filenameMask = {pFilenameMask}; *
* @param pFilenameMask the filename masks
if (pExclusion) { * @param pExclusion if {@code true}, the masks will be excluded
setFilenameMasksForExclusion(filenameMask); */
} public FilenameMaskFilter(final String pFilenameMask, final boolean pExclusion) {
else { String[] filenameMask = {pFilenameMask};
setFilenameMasksForInclusion(filenameMask);
} if (pExclusion) {
} setFilenameMasksForExclusion(filenameMask);
}
/** else {
* Creates a {@code FilenameMaskFilter} setFilenameMasksForInclusion(filenameMask);
* }
* @param pFilenameMasks the filename masks }
* @param pExclusion if {@code true}, the masks will be excluded
*/ /**
public FilenameMaskFilter(final String[] pFilenameMasks, final boolean pExclusion) { * Creates a {@code FilenameMaskFilter}
if (pExclusion) { *
setFilenameMasksForExclusion(pFilenameMasks); * @param pFilenameMasks the filename masks
} * @param pExclusion if {@code true}, the masks will be excluded
else { */
setFilenameMasksForInclusion(pFilenameMasks); public FilenameMaskFilter(final String[] pFilenameMasks, final boolean pExclusion) {
} if (pExclusion) {
} setFilenameMasksForExclusion(pFilenameMasks);
}
/** else {
* setFilenameMasksForInclusion(pFilenameMasks);
* @param pFilenameMasksForInclusion the filename masks to include }
*/ }
public void setFilenameMasksForInclusion(String[] pFilenameMasksForInclusion) {
filenameMasksForInclusion = pFilenameMasksForInclusion; /**
} *
* @param pFilenameMasksForInclusion the filename masks to include
/** */
* @return the current inclusion masks public void setFilenameMasksForInclusion(String[] pFilenameMasksForInclusion) {
*/ filenameMasksForInclusion = pFilenameMasksForInclusion;
public String[] getFilenameMasksForInclusion() { }
return filenameMasksForInclusion.clone();
} /**
* @return the current inclusion masks
/** */
* @param pFilenameMasksForExclusion the filename masks to exclude public String[] getFilenameMasksForInclusion() {
*/ return filenameMasksForInclusion.clone();
public void setFilenameMasksForExclusion(String[] pFilenameMasksForExclusion) { }
filenameMasksForExclusion = pFilenameMasksForExclusion;
inclusion = false; /**
} * @param pFilenameMasksForExclusion the filename masks to exclude
*/
/** public void setFilenameMasksForExclusion(String[] pFilenameMasksForExclusion) {
* @return the current exclusion masks filenameMasksForExclusion = pFilenameMasksForExclusion;
*/ inclusion = false;
public String[] getFilenameMasksForExclusion() { }
return filenameMasksForExclusion.clone();
} /**
* @return the current exclusion masks
/** */
* This method implements the {@code java.io.FilenameFilter} interface. public String[] getFilenameMasksForExclusion() {
* return filenameMasksForExclusion.clone();
* @param pDir the directory in which the file was found. }
* @param pName the name of the file.
* @return {@code true} if the file {@code pName} should be included in the file /**
* list; {@code false} otherwise. * This method implements the {@code java.io.FilenameFilter} interface.
*/ *
public boolean accept(File pDir, String pName) { * @param pDir the directory in which the file was found.
WildcardStringParser parser; * @param pName the name of the file.
* @return {@code true} if the file {@code pName} should be included in the file
// Check each filename string mask whether the file is to be accepted * list; {@code false} otherwise.
if (inclusion) { // Inclusion */
for (String mask : filenameMasksForInclusion) { public boolean accept(File pDir, String pName) {
parser = new WildcardStringParser(mask); WildcardStringParser parser;
if (parser.parseString(pName)) {
// Check each filename string mask whether the file is to be accepted
// The filename was accepted by the filename masks provided if (inclusion) { // Inclusion
// - include it in filename list for (String mask : filenameMasksForInclusion) {
return true; parser = new WildcardStringParser(mask);
} if (parser.parseString(pName)) {
}
// The filename was accepted by the filename masks provided
// The filename not was accepted by any of the filename masks // - include it in filename list
// provided - NOT to be included in the filename list return true;
return false; }
} }
else {
// Exclusion // The filename not was accepted by any of the filename masks
for (String mask : filenameMasksForExclusion) { // provided - NOT to be included in the filename list
parser = new WildcardStringParser(mask); return false;
if (parser.parseString(pName)) { }
else {
// The filename was accepted by the filename masks provided // Exclusion
// - NOT to be included in the filename list for (String mask : filenameMasksForExclusion) {
return false; parser = new WildcardStringParser(mask);
} if (parser.parseString(pName)) {
}
// The filename was accepted by the filename masks provided
// The filename was not accepted by any of the filename masks // - NOT to be included in the filename list
// provided - include it in filename list return false;
return true; }
} }
}
// The filename was not accepted by any of the filename masks
/** // provided - include it in filename list
* @return a string representation for debug purposes return true;
*/ }
public String toString() { }
StringBuilder retVal = new StringBuilder();
int i; /**
* @return a string representation for debug purposes
if (inclusion) { */
// Inclusion public String toString() {
if (filenameMasksForInclusion == null) { StringBuilder retVal = new StringBuilder();
retVal.append("No filename masks set - property filenameMasksForInclusion is null!"); int i;
}
else { if (inclusion) {
retVal.append(filenameMasksForInclusion.length); // Inclusion
retVal.append(" filename mask(s) - "); if (filenameMasksForInclusion == null) {
for (i = 0; i < filenameMasksForInclusion.length; i++) { retVal.append("No filename masks set - property filenameMasksForInclusion is null!");
retVal.append("\""); }
retVal.append(filenameMasksForInclusion[i]); else {
retVal.append("\", \""); retVal.append(filenameMasksForInclusion.length);
} retVal.append(" filename mask(s) - ");
} for (i = 0; i < filenameMasksForInclusion.length; i++) {
} retVal.append("\"");
else { retVal.append(filenameMasksForInclusion[i]);
// Exclusion retVal.append("\", \"");
if (filenameMasksForExclusion == null) { }
retVal.append("No filename masks set - property filenameMasksForExclusion is null!"); }
} }
else { else {
retVal.append(filenameMasksForExclusion.length); // Exclusion
retVal.append(" exclusion filename mask(s) - "); if (filenameMasksForExclusion == null) {
for (i = 0; i < filenameMasksForExclusion.length; i++) { retVal.append("No filename masks set - property filenameMasksForExclusion is null!");
retVal.append("\""); }
retVal.append(filenameMasksForExclusion[i]); else {
retVal.append("\", \""); retVal.append(filenameMasksForExclusion.length);
} retVal.append(" exclusion filename mask(s) - ");
} for (i = 0; i < filenameMasksForExclusion.length; i++) {
} retVal.append("\"");
return retVal.toString(); retVal.append(filenameMasksForExclusion[i]);
} retVal.append("\", \"");
} }
}
}
return retVal.toString();
}
}

View File

@@ -1,452 +1,449 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/* /*
* From http://www.cafeaulait.org/books/javaio/ioexamples/index.html: * From http://www.cafeaulait.org/books/javaio/ioexamples/index.html:
* *
* Please feel free to use any fragment of this code you need in your own work. * Please feel free to use any fragment of this code you need in your own work.
* As far as I am concerned, it's in the public domain. No permission is necessary * As far as I am concerned, it's in the public domain. No permission is necessary
* or required. Credit is always appreciated if you use a large chunk or base a * or required. Credit is always appreciated if you use a large chunk or base a
* significant product on one of my examples, but that's not required either. * significant product on one of my examples, but that's not required either.
* *
* Elliotte Rusty Harold * Elliotte Rusty Harold
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate; import com.twelvemonkeys.lang.Validate;
import java.io.*; import java.io.*;
/** /**
* A little endian input stream reads two's complement, * A little endian input stream reads two's complement,
* little endian integers, floating point numbers, and characters * little endian integers, floating point numbers, and characters
* and returns them as Java primitive types. * and returns them as Java primitive types.
* <p/> * <p>
* The standard {@code java.io.DataInputStream} class * The standard {@code java.io.DataInputStream} class
* which this class imitates reads big endian quantities. * which this class imitates reads big endian quantities.
* <p/> * </p>
* <em>Warning: * <p>
* The {@code DataInput} and {@code DataOutput} interfaces * <em>Warning:
* specifies big endian byte order in their documentation. * The {@code DataInput} and {@code DataOutput} interfaces
* This means that this class is, strictly speaking, not a proper * specifies big endian byte order in their documentation.
* implementation. However, I don't see a reason for the these interfaces to * This means that this class is, strictly speaking, not a proper
* specify the byte order of their underlying representations. * implementation. However, I don't see a reason for the these interfaces to
* </em> * specify the byte order of their underlying representations.
* * </em>
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile * </p>
* @see java.io.DataInputStream *
* @see java.io.DataInput * @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
* @see java.io.DataOutput * @see java.io.DataInputStream
* * @see java.io.DataInput
* @author Elliotte Rusty Harold * @see java.io.DataOutput
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> *
* @version 2 * @author Elliotte Rusty Harold
*/ * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
public class LittleEndianDataInputStream extends FilterInputStream implements DataInput { * @version 2
// TODO: Optimize by reading into a fixed size (8 bytes) buffer instead of individual read operations? */
/** public class LittleEndianDataInputStream extends FilterInputStream implements DataInput {
* Creates a new little endian input stream and chains it to the // TODO: Optimize by reading into a fixed size (8 bytes) buffer instead of individual read operations?
* input stream specified by the {@code pStream} argument. /**
* * Creates a new little endian input stream and chains it to the
* @param pStream the underlying input stream. * input stream specified by the {@code pStream} argument.
* @see java.io.FilterInputStream#in *
*/ * @param pStream the underlying input stream.
public LittleEndianDataInputStream(final InputStream pStream) { * @see java.io.FilterInputStream#in
super(Validate.notNull(pStream, "stream")); */
} public LittleEndianDataInputStream(final InputStream pStream) {
super(Validate.notNull(pStream, "stream"));
/** }
* Reads a {@code boolean} from the underlying input stream by
* reading a single byte. If the byte is zero, false is returned. /**
* If the byte is positive, true is returned. * Reads a {@code boolean} from the underlying input stream by
* * reading a single byte. If the byte is zero, false is returned.
* @return the {@code boolean} value read. * If the byte is positive, true is returned.
* @throws EOFException if the end of the underlying input stream *
* has been reached * @return the {@code boolean} value read.
* @throws IOException if the underlying stream throws an IOException. * @throws EOFException if the end of the underlying input stream
*/ * has been reached
public boolean readBoolean() throws IOException { * @throws IOException if the underlying stream throws an IOException.
int b = in.read(); */
public boolean readBoolean() throws IOException {
if (b < 0) { int b = in.read();
throw new EOFException();
} if (b < 0) {
throw new EOFException();
return b != 0; }
}
return b != 0;
/** }
* Reads a signed {@code byte} from the underlying input stream
* with value between -128 and 127 /**
* * Reads a signed {@code byte} from the underlying input stream
* @return the {@code byte} value read. * with value between -128 and 127
* @throws EOFException if the end of the underlying input stream *
* has been reached * @return the {@code byte} value read.
* @throws IOException if the underlying stream throws an IOException. * @throws EOFException if the end of the underlying input stream
*/ * has been reached
public byte readByte() throws IOException { * @throws IOException if the underlying stream throws an IOException.
int b = in.read(); */
public byte readByte() throws IOException {
if (b < 0) { int b = in.read();
throw new EOFException();
} if (b < 0) {
throw new EOFException();
return (byte) b; }
} return (byte) b;
/** }
* Reads an unsigned {@code byte} from the underlying
* input stream with value between 0 and 255 /**
* * Reads an unsigned {@code byte} from the underlying
* @return the {@code byte} value read. * input stream with value between 0 and 255
* @throws EOFException if the end of the underlying input *
* stream has been reached * @return the {@code byte} value read.
* @throws IOException if the underlying stream throws an IOException. * @throws EOFException if the end of the underlying input
*/ * stream has been reached
public int readUnsignedByte() throws IOException { * @throws IOException if the underlying stream throws an IOException.
int b = in.read(); */
public int readUnsignedByte() throws IOException {
if (b < 0) { int b = in.read();
throw new EOFException();
} if (b < 0) {
throw new EOFException();
return b; }
}
return b;
/** }
* Reads a two byte signed {@code short} from the underlying
* input stream in little endian order, low byte first. /**
* * Reads a two byte signed {@code short} from the underlying
* @return the {@code short} read. * input stream in little endian order, low byte first.
* @throws EOFException if the end of the underlying input stream *
* has been reached * @return the {@code short} read.
* @throws IOException if the underlying stream throws an IOException. * @throws EOFException if the end of the underlying input stream
*/ * has been reached
public short readShort() throws IOException { * @throws IOException if the underlying stream throws an IOException.
int byte1 = in.read(); */
int byte2 = in.read(); public short readShort() throws IOException {
int byte1 = in.read();
// only need to test last byte read int byte2 = in.read();
// if byte1 is -1 so is byte2
if (byte2 < 0) { // only need to test last byte read
throw new EOFException(); // if byte1 is -1 so is byte2
} if (byte2 < 0) {
throw new EOFException();
return (short) (((byte2 << 24) >>> 16) | (byte1 << 24) >>> 24); }
}
return (short) (((byte2 << 24) >>> 16) | (byte1 << 24) >>> 24);
/** }
* Reads a two byte unsigned {@code short} from the underlying
* input stream in little endian order, low byte first. /**
* * Reads a two byte unsigned {@code short} from the underlying
* @return the int value of the unsigned short read. * input stream in little endian order, low byte first.
* @throws EOFException if the end of the underlying input stream *
* has been reached * @return the int value of the unsigned short read.
* @throws IOException if the underlying stream throws an IOException. * @throws EOFException if the end of the underlying input stream
*/ * has been reached
public int readUnsignedShort() throws IOException { * @throws IOException if the underlying stream throws an IOException.
int byte1 = in.read(); */
int byte2 = in.read(); public int readUnsignedShort() throws IOException {
int byte1 = in.read();
if (byte2 < 0) { int byte2 = in.read();
throw new EOFException();
} if (byte2 < 0) {
throw new EOFException();
return (byte2 << 8) + byte1; }
}
return (byte2 << 8) + byte1;
/** }
* Reads a two byte Unicode {@code char} from the underlying
* input stream in little endian order, low byte first. /**
* * Reads a two byte Unicode {@code char} from the underlying
* @return the int value of the unsigned short read. * input stream in little endian order, low byte first.
* @throws EOFException if the end of the underlying input stream *
* has been reached * @return the int value of the unsigned short read.
* @throws IOException if the underlying stream throws an IOException. * @throws EOFException if the end of the underlying input stream
*/ * has been reached
public char readChar() throws IOException { * @throws IOException if the underlying stream throws an IOException.
int byte1 = in.read(); */
int byte2 = in.read(); public char readChar() throws IOException {
int byte1 = in.read();
if (byte2 < 0) { int byte2 = in.read();
throw new EOFException();
} if (byte2 < 0) {
throw new EOFException();
return (char) (((byte2 << 24) >>> 16) | ((byte1 << 24) >>> 24)); }
}
return (char) (((byte2 << 24) >>> 16) | ((byte1 << 24) >>> 24));
}
/**
* Reads a four byte signed {@code int} from the underlying
* input stream in little endian order, low byte first. /**
* * Reads a four byte signed {@code int} from the underlying
* @return the {@code int} read. * input stream in little endian order, low byte first.
* @throws EOFException if the end of the underlying input stream *
* has been reached * @return the {@code int} read.
* @throws IOException if the underlying stream throws an IOException. * @throws EOFException if the end of the underlying input stream
*/ * has been reached
public int readInt() throws IOException { * @throws IOException if the underlying stream throws an IOException.
int byte1 = in.read(); */
int byte2 = in.read(); public int readInt() throws IOException {
int byte3 = in.read(); int byte1 = in.read();
int byte4 = in.read(); int byte2 = in.read();
int byte3 = in.read();
if (byte4 < 0) { int byte4 = in.read();
throw new EOFException();
} if (byte4 < 0) {
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);
/** }
* Reads an eight byte signed {@code int} from the underlying
* input stream in little endian order, low byte first. /**
* * Reads an eight byte signed {@code int} from the underlying
* @return the {@code int} read. * input stream in little endian order, low byte first.
* @throws EOFException if the end of the underlying input stream *
* has been reached * @return the {@code int} read.
* @throws IOException if the underlying stream throws an IOException. * @throws EOFException if the end of the underlying input stream
*/ * has been reached
public long readLong() throws IOException { * @throws IOException if the underlying stream throws an IOException.
long byte1 = in.read(); */
long byte2 = in.read(); public long readLong() throws IOException {
long byte3 = in.read(); long byte1 = in.read();
long byte4 = in.read(); long byte2 = in.read();
long byte5 = in.read(); long byte3 = in.read();
long byte6 = in.read(); long byte4 = in.read();
long byte7 = in.read(); long byte5 = in.read();
long byte8 = in.read(); long byte6 = in.read();
long byte7 = in.read();
if (byte8 < 0) { long byte8 = in.read();
throw new EOFException();
} if (byte8 < 0) {
throw new EOFException();
return (byte8 << 56) | ((byte7 << 56) >>> 8) }
| ((byte6 << 56) >>> 16) | ((byte5 << 56) >>> 24)
| ((byte4 << 56) >>> 32) | ((byte3 << 56) >>> 40) return (byte8 << 56) | ((byte7 << 56) >>> 8)
| ((byte2 << 56) >>> 48) | ((byte1 << 56) >>> 56); | ((byte6 << 56) >>> 16) | ((byte5 << 56) >>> 24)
} | ((byte4 << 56) >>> 32) | ((byte3 << 56) >>> 40)
| ((byte2 << 56) >>> 48) | ((byte1 << 56) >>> 56);
/** }
* Reads a string of no more than 65,535 characters
* from the underlying input stream using UTF-8 /**
* encoding. This method first reads a two byte short * Reads a string of no more than 65,535 characters
* in <b>big</b> endian order as required by the * from the underlying input stream using UTF-8
* UTF-8 specification. This gives the number of bytes in * encoding. This method first reads a two byte short
* the UTF-8 encoded version of the string. * in <b>big</b> endian order as required by the
* Next this many bytes are read and decoded as UTF-8 * UTF-8 specification. This gives the number of bytes in
* encoded characters. * the UTF-8 encoded version of the string.
* * Next this many bytes are read and decoded as UTF-8
* @return the decoded string * encoded characters.
* @throws UTFDataFormatException if the string cannot be decoded *
* @throws IOException if the underlying stream throws an IOException. * @return the decoded string
*/ * @throws UTFDataFormatException if the string cannot be decoded
public String readUTF() throws IOException { * @throws IOException if the underlying stream throws an IOException.
int byte1 = in.read(); */
int byte2 = in.read(); public String readUTF() throws IOException {
int byte1 = in.read();
if (byte2 < 0) { int byte2 = in.read();
throw new EOFException();
} if (byte2 < 0) {
throw new EOFException();
int numbytes = (byte1 << 8) + byte2; }
char result[] = new char[numbytes];
int numread = 0; int numbytes = (byte1 << 8) + byte2;
int numchars = 0; char result[] = new char[numbytes];
int numread = 0;
while (numread < numbytes) { int numchars = 0;
int c1 = readUnsignedByte();
int c2, c3; while (numread < numbytes) {
int c1 = readUnsignedByte();
// The first four bits of c1 determine how many bytes are in this char int c2, c3;
int test = c1 >> 4;
if (test < 8) { // one byte // The first four bits of c1 determine how many bytes are in this char
numread++; int test = c1 >> 4;
result[numchars++] = (char) c1; if (test < 8) { // one byte
} numread++;
else if (test == 12 || test == 13) { // two bytes result[numchars++] = (char) c1;
numread += 2; }
else if (test == 12 || test == 13) { // two bytes
if (numread > numbytes) { numread += 2;
throw new UTFDataFormatException();
} if (numread > numbytes) {
throw new UTFDataFormatException();
c2 = readUnsignedByte(); }
if ((c2 & 0xC0) != 0x80) { c2 = readUnsignedByte();
throw new UTFDataFormatException();
} if ((c2 & 0xC0) != 0x80) {
throw new UTFDataFormatException();
result[numchars++] = (char) (((c1 & 0x1F) << 6) | (c2 & 0x3F)); }
}
else if (test == 14) { // three bytes result[numchars++] = (char) (((c1 & 0x1F) << 6) | (c2 & 0x3F));
numread += 3; }
else if (test == 14) { // three bytes
if (numread > numbytes) { numread += 3;
throw new UTFDataFormatException();
} if (numread > numbytes) {
throw new UTFDataFormatException();
c2 = readUnsignedByte(); }
c3 = readUnsignedByte();
c2 = readUnsignedByte();
if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) { c3 = readUnsignedByte();
throw new UTFDataFormatException();
} if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) {
throw new UTFDataFormatException();
result[numchars++] = (char) (((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F)); }
}
else { // malformed result[numchars++] = (char) (((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F));
throw new UTFDataFormatException(); }
} else { // malformed
throw new UTFDataFormatException();
} // end while }
return new String(result, 0, numchars); } // end while
} return new String(result, 0, numchars);
/** }
* @return the next eight bytes of this input stream, interpreted as a
* little endian {@code double}. /**
* @throws EOFException if end of stream occurs before eight bytes * @return the next eight bytes of this input stream, interpreted as a
* have been read. * little endian {@code double}.
* @throws IOException if an I/O error occurs. * @throws EOFException if end of stream occurs before eight bytes
*/ * have been read.
public final double readDouble() throws IOException { * @throws IOException if an I/O error occurs.
return Double.longBitsToDouble(readLong()); */
} public final double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
/** }
* @return the next four bytes of this input stream, interpreted as a
* little endian {@code int}. /**
* @throws EOFException if end of stream occurs before four bytes * @return the next four bytes of this input stream, interpreted as a
* have been read. * little endian {@code int}.
* @throws IOException if an I/O error occurs. * @throws EOFException if end of stream occurs before four bytes
*/ * have been read.
public final float readFloat() throws IOException { * @throws IOException if an I/O error occurs.
return Float.intBitsToFloat(readInt()); */
} public final float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
/** }
* See the general contract of the {@code skipBytes}
* method of {@code DataInput}. /**
* <p> * See the general contract of the {@code skipBytes}
* Bytes for this operation are read from the contained input stream. * method of {@code DataInput}.
* * <p>
* @param pLength the number of bytes to be skipped. * Bytes for this operation are read from the contained input stream.
* @return the actual number of bytes skipped. *
* @exception IOException if an I/O error occurs. * @param pLength the number of bytes to be skipped.
*/ * @return the actual number of bytes skipped.
public final int skipBytes(int pLength) throws IOException { * @exception IOException if an I/O error occurs.
// NOTE: There was probably a bug in ERH's original code here, as skip */
// never returns -1, but returns 0 if no more bytes can be skipped... public final int skipBytes(int pLength) throws IOException {
int total = 0; // NOTE: There was probably a bug in ERH's original code here, as skip
int skipped; // never returns -1, but returns 0 if no more bytes can be skipped...
int total = 0;
while ((total < pLength) && ((skipped = (int) in.skip(pLength - total)) > 0)) { int skipped;
total += skipped;
} while ((total < pLength) && ((skipped = (int) in.skip(pLength - total)) > 0)) {
total += skipped;
return total; }
}
return total;
/** }
* See the general contract of the {@code readFully}
* method of {@code DataInput}. /**
* <p/> * See the general contract of the {@code readFully} method of {@code DataInput}.
* Bytes * <p>
* for this operation are read from the contained * Bytes for this operation are read from the contained input stream.
* input stream. * </p>
* *
* @param pBytes the buffer into which the data is read. * @param pBytes the buffer into which the data is read.
* @throws EOFException if this input stream reaches the end before * @throws EOFException if this input stream reaches the end before
* reading all the bytes. * reading all the bytes.
* @throws IOException if an I/O error occurs. * @throws IOException if an I/O error occurs.
* @see java.io.FilterInputStream#in * @see java.io.FilterInputStream#in
*/ */
public final void readFully(byte pBytes[]) throws IOException { public final void readFully(byte pBytes[]) throws IOException {
readFully(pBytes, 0, pBytes.length); readFully(pBytes, 0, pBytes.length);
} }
/** /**
* See the general contract of the {@code readFully} * See the general contract of the {@code readFully} method of {@code DataInput}.
* method of {@code DataInput}. * <p>
* <p/> * Bytes for this operation are read from the contained input stream.
* Bytes * </p>
* for this operation are read from the contained *
* input stream. * @param pBytes the buffer into which the data is read.
* * @param pOffset the start offset of the data.
* @param pBytes the buffer into which the data is read. * @param pLength the number of bytes to read.
* @param pOffset the start offset of the data. * @throws EOFException if this input stream reaches the end before
* @param pLength the number of bytes to read. * reading all the bytes.
* @throws EOFException if this input stream reaches the end before * @throws IOException if an I/O error occurs.
* reading all the bytes. * @see java.io.FilterInputStream#in
* @throws IOException if an I/O error occurs. */
* @see java.io.FilterInputStream#in public final void readFully(byte pBytes[], int pOffset, int pLength) throws IOException {
*/ if (pLength < 0) {
public final void readFully(byte pBytes[], int pOffset, int pLength) throws IOException { throw new IndexOutOfBoundsException();
if (pLength < 0) { }
throw new IndexOutOfBoundsException();
} int count = 0;
int count = 0; while (count < pLength) {
int read = in.read(pBytes, pOffset + count, pLength - count);
while (count < pLength) {
int read = in.read(pBytes, pOffset + count, pLength - count); if (read < 0) {
throw new EOFException();
if (read < 0) { }
throw new EOFException();
} count += read;
}
count += read; }
}
} /**
* See the general contract of the {@code readLine}
/** * method of {@code DataInput}.
* See the general contract of the {@code readLine} * <p>
* method of {@code DataInput}. * Bytes for this operation are read from the contained input stream.
* <p> *
* Bytes for this operation are read from the contained input stream. * @deprecated This method does not properly convert bytes to characters.
* *
* @deprecated This method does not properly convert bytes to characters. * @return the next line of text from this input stream.
* * @exception IOException if an I/O error occurs.
* @return the next line of text from this input stream. * @see java.io.BufferedReader#readLine()
* @exception IOException if an I/O error occurs. * @see java.io.DataInputStream#readLine()
* @see java.io.BufferedReader#readLine() */
* @see java.io.DataInputStream#readLine() public String readLine() throws IOException {
* @noinspection deprecation DataInputStream ds = new DataInputStream(in);
*/ return ds.readLine();
public String readLine() throws IOException { }
DataInputStream ds = new DataInputStream(in); }
return ds.readLine();
}
}

View File

@@ -1,340 +1,342 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/* /*
* From http://www.cafeaulait.org/books/javaio/ioexamples/index.html: * From http://www.cafeaulait.org/books/javaio/ioexamples/index.html:
* *
* Please feel free to use any fragment of this code you need in your own work. * Please feel free to use any fragment of this code you need in your own work.
* As far as I am concerned, it's in the public domain. No permission is necessary * As far as I am concerned, it's in the public domain. No permission is necessary
* or required. Credit is always appreciated if you use a large chunk or base a * or required. Credit is always appreciated if you use a large chunk or base a
* significant product on one of my examples, but that's not required either. * significant product on one of my examples, but that's not required either.
* *
* Elliotte Rusty Harold * Elliotte Rusty Harold
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate; import com.twelvemonkeys.lang.Validate;
import java.io.*; import java.io.*;
/** /**
* A little endian output stream writes primitive Java numbers * A little endian output stream writes primitive Java numbers
* and characters to an output stream in a little endian format. * and characters to an output stream in a little endian format.
* <p/> * <p>
* The standard {@code java.io.DataOutputStream} class which this class * The standard {@code java.io.DataOutputStream} class which this class
* imitates uses big endian integers. * imitates uses big endian integers.
* <p/> * </p>
* <em>Warning: * <p>
* The {@code DataInput} and {@code DataOutput} interfaces * <em>Warning:
* specifies big endian byte order in their documentation. * The {@code DataInput} and {@code DataOutput} interfaces
* This means that this class is, strictly speaking, not a proper * specifies big endian byte order in their documentation.
* implementation. However, I don't see a reason for the these interfaces to * This means that this class is, strictly speaking, not a proper
* specify the byte order of their underlying representations. * implementation. However, I don't see a reason for the these interfaces to
* </em> * specify the byte order of their underlying representations.
* * </em>
* @see com.twelvemonkeys.io.LittleEndianRandomAccessFile * <p>
* @see java.io.DataOutputStream *
* @see java.io.DataInput * @see com.twelvemonkeys.io.LittleEndianRandomAccessFile
* @see java.io.DataOutput * @see java.io.DataOutputStream
* * @see java.io.DataInput
* @author Elliotte Rusty Harold * @see java.io.DataOutput
* @version 1.0.1, 19 May 1999 *
*/ * @author Elliotte Rusty Harold
public class LittleEndianDataOutputStream extends FilterOutputStream implements DataOutput { * @version 1.0.1, 19 May 1999
*/
/** public class LittleEndianDataOutputStream extends FilterOutputStream implements DataOutput {
* The number of bytes written so far to the little endian output stream.
*/ /**
protected int bytesWritten; * The number of bytes written so far to the little endian output stream.
*/
/** protected int bytesWritten;
* Creates a new little endian output stream and chains it to the
* output stream specified by the {@code pStream} argument. /**
* * Creates a new little endian output stream and chains it to the
* @param pStream the underlying output stream. * output stream specified by the {@code pStream} argument.
* @see java.io.FilterOutputStream#out *
*/ * @param pStream the underlying output stream.
public LittleEndianDataOutputStream(OutputStream pStream) { * @see java.io.FilterOutputStream#out
super(Validate.notNull(pStream, "stream")); */
} public LittleEndianDataOutputStream(OutputStream pStream) {
super(Validate.notNull(pStream, "stream"));
/** }
* Writes the specified byte value to the underlying output stream.
* /**
* @param pByte the {@code byte} value to be written. * Writes the specified byte value to the underlying output stream.
* @throws IOException if the underlying stream throws an IOException. *
*/ * @param pByte the {@code byte} value to be written.
public synchronized void write(int pByte) throws IOException { * @throws IOException if the underlying stream throws an IOException.
out.write(pByte); */
bytesWritten++; public synchronized void write(int pByte) throws IOException {
} out.write(pByte);
bytesWritten++;
/** }
* Writes {@code pLength} bytes from the specified byte array
* starting at {@code pOffset} to the underlying output stream. /**
* * Writes {@code pLength} bytes from the specified byte array
* @param pBytes the data. * starting at {@code pOffset} to the underlying output stream.
* @param pOffset the start offset in the data. *
* @param pLength the number of bytes to write. * @param pBytes the data.
* @throws IOException if the underlying stream throws an IOException. * @param pOffset the start offset in the data.
*/ * @param pLength the number of bytes to write.
public synchronized void write(byte[] pBytes, int pOffset, int pLength) throws IOException { * @throws IOException if the underlying stream throws an IOException.
out.write(pBytes, pOffset, pLength); */
bytesWritten += pLength; public synchronized void write(byte[] pBytes, int pOffset, int pLength) throws IOException {
} out.write(pBytes, pOffset, pLength);
bytesWritten += pLength;
}
/**
* Writes a {@code boolean} to the underlying output stream as
* a single byte. If the argument is true, the byte value 1 is written. /**
* If the argument is false, the byte value {@code 0} in written. * Writes a {@code boolean} to the underlying output stream as
* * a single byte. If the argument is true, the byte value 1 is written.
* @param pBoolean the {@code boolean} value to be written. * If the argument is false, the byte value {@code 0} in written.
* @throws IOException if the underlying stream throws an IOException. *
*/ * @param pBoolean the {@code boolean} value to be written.
public void writeBoolean(boolean pBoolean) throws IOException { * @throws IOException if the underlying stream throws an IOException.
if (pBoolean) { */
write(1); public void writeBoolean(boolean pBoolean) throws IOException {
} if (pBoolean) {
else { write(1);
write(0); }
} else {
} write(0);
}
/** }
* Writes out a {@code byte} to the underlying output stream
* /**
* @param pByte the {@code byte} value to be written. * Writes out a {@code byte} to the underlying output stream
* @throws IOException if the underlying stream throws an IOException. *
*/ * @param pByte the {@code byte} value to be written.
public void writeByte(int pByte) throws IOException { * @throws IOException if the underlying stream throws an IOException.
out.write(pByte); */
bytesWritten++; public void writeByte(int pByte) throws IOException {
} out.write(pByte);
bytesWritten++;
/** }
* Writes a two byte {@code short} to the underlying output stream in
* little endian order, low byte first. /**
* * Writes a two byte {@code short} to the underlying output stream in
* @param pShort the {@code short} to be written. * little endian order, low byte first.
* @throws IOException if the underlying stream throws an IOException. *
*/ * @param pShort the {@code short} to be written.
public void writeShort(int pShort) throws IOException { * @throws IOException if the underlying stream throws an IOException.
out.write(pShort & 0xFF); */
out.write((pShort >>> 8) & 0xFF); public void writeShort(int pShort) throws IOException {
bytesWritten += 2; out.write(pShort & 0xFF);
} out.write((pShort >>> 8) & 0xFF);
bytesWritten += 2;
/** }
* Writes a two byte {@code char} to the underlying output stream
* in little endian order, low byte first. /**
* * Writes a two byte {@code char} to the underlying output stream
* @param pChar the {@code char} value to be written. * in little endian order, low byte first.
* @throws IOException if the underlying stream throws an IOException. *
*/ * @param pChar the {@code char} value to be written.
public void writeChar(int pChar) throws IOException { * @throws IOException if the underlying stream throws an IOException.
out.write(pChar & 0xFF); */
out.write((pChar >>> 8) & 0xFF); public void writeChar(int pChar) throws IOException {
bytesWritten += 2; out.write(pChar & 0xFF);
} out.write((pChar >>> 8) & 0xFF);
bytesWritten += 2;
/** }
* Writes a four-byte {@code int} to the underlying output stream
* in little endian order, low byte first, high byte last /**
* * Writes a four-byte {@code int} to the underlying output stream
* @param pInt the {@code int} to be written. * in little endian order, low byte first, high byte last
* @throws IOException if the underlying stream throws an IOException. *
*/ * @param pInt the {@code int} to be written.
public void writeInt(int pInt) throws IOException { * @throws IOException if the underlying stream throws an IOException.
out.write(pInt & 0xFF); */
out.write((pInt >>> 8) & 0xFF); public void writeInt(int pInt) throws IOException {
out.write((pInt >>> 16) & 0xFF); out.write(pInt & 0xFF);
out.write((pInt >>> 24) & 0xFF); out.write((pInt >>> 8) & 0xFF);
bytesWritten += 4; out.write((pInt >>> 16) & 0xFF);
out.write((pInt >>> 24) & 0xFF);
} bytesWritten += 4;
/** }
* Writes an eight-byte {@code long} to the underlying output stream
* in little endian order, low byte first, high byte last /**
* * Writes an eight-byte {@code long} to the underlying output stream
* @param pLong the {@code long} to be written. * in little endian order, low byte first, high byte last
* @throws IOException if the underlying stream throws an IOException. *
*/ * @param pLong the {@code long} to be written.
public void writeLong(long pLong) throws IOException { * @throws IOException if the underlying stream throws an IOException.
out.write((int) pLong & 0xFF); */
out.write((int) (pLong >>> 8) & 0xFF); public void writeLong(long pLong) throws IOException {
out.write((int) (pLong >>> 16) & 0xFF); out.write((int) pLong & 0xFF);
out.write((int) (pLong >>> 24) & 0xFF); out.write((int) (pLong >>> 8) & 0xFF);
out.write((int) (pLong >>> 32) & 0xFF); out.write((int) (pLong >>> 16) & 0xFF);
out.write((int) (pLong >>> 40) & 0xFF); out.write((int) (pLong >>> 24) & 0xFF);
out.write((int) (pLong >>> 48) & 0xFF); out.write((int) (pLong >>> 32) & 0xFF);
out.write((int) (pLong >>> 56) & 0xFF); out.write((int) (pLong >>> 40) & 0xFF);
bytesWritten += 8; out.write((int) (pLong >>> 48) & 0xFF);
} out.write((int) (pLong >>> 56) & 0xFF);
bytesWritten += 8;
/** }
* Writes a 4 byte Java float to the underlying output stream in
* little endian order. /**
* * Writes a 4 byte Java float to the underlying output stream in
* @param f the {@code float} value to be written. * little endian order.
* @throws IOException if an I/O error occurs. *
*/ * @param f the {@code float} value to be written.
public final void writeFloat(float f) throws IOException { * @throws IOException if an I/O error occurs.
writeInt(Float.floatToIntBits(f)); */
} public final void writeFloat(float f) throws IOException {
writeInt(Float.floatToIntBits(f));
/** }
* Writes an 8 byte Java double to the underlying output stream in
* little endian order. /**
* * Writes an 8 byte Java double to the underlying output stream in
* @param d the {@code double} value to be written. * little endian order.
* @throws IOException if an I/O error occurs. *
*/ * @param d the {@code double} value to be written.
public final void writeDouble(double d) throws IOException { * @throws IOException if an I/O error occurs.
writeLong(Double.doubleToLongBits(d)); */
} public final void writeDouble(double d) throws IOException {
writeLong(Double.doubleToLongBits(d));
/** }
* Writes a string to the underlying output stream as a sequence of
* bytes. Each character is written to the data output stream as /**
* if by the {@link #writeByte(int)} method. * Writes a string to the underlying output stream as a sequence of
* * bytes. Each character is written to the data output stream as
* @param pString the {@code String} value to be written. * if by the {@link #writeByte(int)} method.
* @throws IOException if the underlying stream throws an IOException. *
* @see #writeByte(int) * @param pString the {@code String} value to be written.
* @see #out * @throws IOException if the underlying stream throws an IOException.
*/ * @see #writeByte(int)
public void writeBytes(String pString) throws IOException { * @see #out
int length = pString.length(); */
public void writeBytes(String pString) throws IOException {
for (int i = 0; i < length; i++) { int length = pString.length();
out.write((byte) pString.charAt(i));
} for (int i = 0; i < length; i++) {
out.write((byte) pString.charAt(i));
bytesWritten += length; }
}
bytesWritten += length;
/** }
* Writes a string to the underlying output stream as a sequence of
* characters. Each character is written to the data output stream as /**
* if by the {@code writeChar} method. * Writes a string to the underlying output stream as a sequence of
* * characters. Each character is written to the data output stream as
* @param pString a {@code String} value to be written. * if by the {@code writeChar} method.
* @throws IOException if the underlying stream throws an IOException. *
* @see #writeChar(int) * @param pString a {@code String} value to be written.
* @see #out * @throws IOException if the underlying stream throws an IOException.
*/ * @see #writeChar(int)
public void writeChars(String pString) throws IOException { * @see #out
int length = pString.length(); */
public void writeChars(String pString) throws IOException {
for (int i = 0; i < length; i++) { int length = pString.length();
int c = pString.charAt(i);
out.write(c & 0xFF); for (int i = 0; i < length; i++) {
out.write((c >>> 8) & 0xFF); int c = pString.charAt(i);
} out.write(c & 0xFF);
out.write((c >>> 8) & 0xFF);
bytesWritten += length * 2; }
}
bytesWritten += length * 2;
/** }
* Writes a string of no more than 65,535 characters
* to the underlying output stream using UTF-8 /**
* encoding. This method first writes a two byte short * Writes a string of no more than 65,535 characters
* in <b>big</b> endian order as required by the * to the underlying output stream using UTF-8
* UTF-8 specification. This gives the number of bytes in the * encoding. This method first writes a two byte short
* UTF-8 encoded version of the string, not the number of characters * in <b>big</b> endian order as required by the
* in the string. Next each character of the string is written * UTF-8 specification. This gives the number of bytes in the
* using the UTF-8 encoding for the character. * UTF-8 encoded version of the string, not the number of characters
* * in the string. Next each character of the string is written
* @param pString the string to be written. * using the UTF-8 encoding for the character.
* @throws UTFDataFormatException if the string is longer than *
* 65,535 characters. * @param pString the string to be written.
* @throws IOException if the underlying stream throws an IOException. * @throws UTFDataFormatException if the string is longer than
*/ * 65,535 characters.
public void writeUTF(String pString) throws IOException { * @throws IOException if the underlying stream throws an IOException.
int numchars = pString.length(); */
int numbytes = 0; public void writeUTF(String pString) throws IOException {
int numchars = pString.length();
for (int i = 0; i < numchars; i++) { int numbytes = 0;
int c = pString.charAt(i);
for (int i = 0; i < numchars; i++) {
if ((c >= 0x0001) && (c <= 0x007F)) { int c = pString.charAt(i);
numbytes++;
} if ((c >= 0x0001) && (c <= 0x007F)) {
else if (c > 0x07FF) { numbytes++;
numbytes += 3; }
} else if (c > 0x07FF) {
else { numbytes += 3;
numbytes += 2; }
} else {
} numbytes += 2;
}
if (numbytes > 65535) { }
throw new UTFDataFormatException();
} if (numbytes > 65535) {
throw new UTFDataFormatException();
out.write((numbytes >>> 8) & 0xFF); }
out.write(numbytes & 0xFF);
out.write((numbytes >>> 8) & 0xFF);
for (int i = 0; i < numchars; i++) { out.write(numbytes & 0xFF);
int c = pString.charAt(i);
for (int i = 0; i < numchars; i++) {
if ((c >= 0x0001) && (c <= 0x007F)) { int c = pString.charAt(i);
out.write(c);
} if ((c >= 0x0001) && (c <= 0x007F)) {
else if (c > 0x07FF) { out.write(c);
out.write(0xE0 | ((c >> 12) & 0x0F)); }
out.write(0x80 | ((c >> 6) & 0x3F)); else if (c > 0x07FF) {
out.write(0x80 | (c & 0x3F)); out.write(0xE0 | ((c >> 12) & 0x0F));
bytesWritten += 2; out.write(0x80 | ((c >> 6) & 0x3F));
} out.write(0x80 | (c & 0x3F));
else { bytesWritten += 2;
out.write(0xC0 | ((c >> 6) & 0x1F)); }
out.write(0x80 | (c & 0x3F)); else {
bytesWritten += 1; out.write(0xC0 | ((c >> 6) & 0x1F));
} out.write(0x80 | (c & 0x3F));
} bytesWritten += 1;
}
bytesWritten += numchars + 2; }
}
bytesWritten += numchars + 2;
/** }
* Returns the number of bytes written to this little endian output stream.
* (This class is not thread-safe with respect to this method. It is /**
* possible that this number is temporarily less than the actual * Returns the number of bytes written to this little endian output stream.
* number of bytes written.) * (This class is not thread-safe with respect to this method. It is
* @return the value of the {@code written} field. * possible that this number is temporarily less than the actual
* @see #bytesWritten * number of bytes written.)
*/ * @return the value of the {@code written} field.
public int size() { * @see #bytesWritten
return bytesWritten; */
} public int size() {
return bytesWritten;
}
} }

View File

@@ -1,198 +1,197 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* A {@code SeekableInputStream} implementation that caches data in memory. * A {@code SeekableInputStream} implementation that caches data in memory.
* <p/> *
* * @see FileCacheSeekableStream
* @see FileCacheSeekableStream *
* * @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/MemoryCacheSeekableStream.java#3 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/MemoryCacheSeekableStream.java#3 $ */
*/ public final class MemoryCacheSeekableStream extends AbstractCachedSeekableStream {
public final class MemoryCacheSeekableStream extends AbstractCachedSeekableStream {
/**
/** * Creates a {@code MemoryCacheSeekableStream}, reading from the given
* Creates a {@code MemoryCacheSeekableStream}, reading from the given * {@code InputStream}. Data will be cached in memory.
* {@code InputStream}. Data will be cached in memory. *
* * @param pStream the {@code InputStream} to read from.
* @param pStream the {@code InputStream} to read from. */
*/ public MemoryCacheSeekableStream(final InputStream pStream) {
public MemoryCacheSeekableStream(final InputStream pStream) { super(pStream, new MemoryCache());
super(pStream, new MemoryCache()); }
}
public final boolean isCachedMemory() {
public final boolean isCachedMemory() { return true;
return true; }
}
public final boolean isCachedFile() {
public final boolean isCachedFile() { return false;
return false; }
}
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[]> cache = new ArrayList<>();
private final List<byte[]> cache = new ArrayList<>(); private long length;
private long length; private long position;
private long position; private long start;
private long start;
private byte[] getBlock() throws IOException {
private byte[] getBlock() throws IOException { final long currPos = position - start;
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"); }
}
long index = currPos / BLOCK_SIZE;
long index = currPos / BLOCK_SIZE;
if (index >= Integer.MAX_VALUE) {
if (index >= Integer.MAX_VALUE) { throw new IOException("Memory cache max size exceeded");
throw new IOException("Memory cache max size exceeded"); }
}
if (index >= cache.size()) {
if (index >= cache.size()) { try {
try { cache.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: " + cache.size() * BLOCK_SIZE + " (" + cache.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: " + cache.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 cache.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) (position % BLOCK_SIZE);
int idx = (int) (position % BLOCK_SIZE); buffer[idx] = (byte) pByte;
buffer[idx] = (byte) pByte; position++;
position++;
if (position > length) {
if (position > length) { length = position;
length = position; }
} }
}
// TODO: OptimizeMe!!!
// TODO: OptimizeMe!!! @Override
@Override 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) position % 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];
position++;
position++; }
} if (position > length) {
if (position > length) { length = position;
length = position; }
} }
}
public int read() throws IOException {
public int read() throws IOException { if (position >= length) {
if (position >= length) { return -1;
return -1; }
}
byte[] buffer = getBlock();
byte[] buffer = getBlock();
int idx = (int) (position % BLOCK_SIZE);
int idx = (int) (position % BLOCK_SIZE); position++;
position++;
return buffer[idx] & 0xff;
return buffer[idx] & 0xff; }
}
// 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 (position >= length) {
if (position >= length) { return -1;
return -1; }
}
byte[] buffer = getBlock();
byte[] buffer = getBlock();
int bufferPos = (int) (position % 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), length - position);
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 < length - position; 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]; }
}
position += 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 < start) {
if (pPosition < start) { throw new IOException("Seek before flush position");
throw new IOException("Seek before flush position"); }
} position = pPosition;
position = pPosition; }
}
@Override
@Override public void flush(final long pPosition) {
public void flush(final long pPosition) { 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++) { cache.remove(0);
cache.remove(0); }
}
start = pPosition;
start = pPosition; }
}
@Override
@Override void close() throws IOException {
void close() throws IOException { cache.clear();
cache.clear(); }
}
public long getPosition() {
public long getPosition() { return position;
return position; }
} }
} }
}

View File

@@ -1,82 +1,81 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
/** /**
* An {@code InputStream} that contains no bytes. * An {@code InputStream} that contains no bytes.
* <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/NullInputStream.java#2 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/NullInputStream.java#2 $ */
*/ public class NullInputStream extends InputStream {
public class NullInputStream extends InputStream {
/**
/** * Creates a {@code NullInputStream}.
* Creates a {@code NullInputStream}. */
*/ public NullInputStream() {
public NullInputStream() { }
}
/**
/** * This implementation returns {@code -1} (EOF), always.
* This implementation returns {@code -1} (EOF), always. *
* * @return {@code -1}
* @return {@code -1} * @throws IOException
* @throws IOException */
*/ public int read() throws IOException {
public int read() throws IOException { return -1;
return -1; }
}
/**
/** * This implementation returns {@code 0}, always.
* This implementation returns {@code 0}, always. *
* * @return {@code 0}
* @return {@code 0} * @throws IOException
* @throws IOException */
*/ @Override
@Override public int available() throws IOException {
public int available() throws IOException { return 0;
return 0; }
}
/**
/** * This implementation returns {@code 0}, always.
* This implementation returns {@code 0}, always. *
* * @return {@code 0}
* @return {@code 0} * @throws IOException
* @throws IOException */
*/ @Override
@Override public long skip(long pOffset) throws IOException {
public long skip(long pOffset) throws IOException { return 0l;
return 0l; }
} }
}

View File

@@ -1,70 +1,69 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
/** /**
* An {@code OutputStream} implementation that works as a sink. * An {@code OutputStream} implementation that works as a sink.
* <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/NullOutputStream.java#2 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/NullOutputStream.java#2 $ */
*/ public class NullOutputStream extends OutputStream {
public class NullOutputStream extends OutputStream {
/**
/** * Creates a {@code NullOutputStream}.
* Creates a {@code NullOutputStream}. */
*/ public NullOutputStream() {
public NullOutputStream() { }
}
/**
/** * This implementation does nothing.
* This implementation does nothing. */
*/ public void write(int pByte) throws IOException {
public void write(int pByte) throws IOException { }
}
/**
/** * This implementation does nothing.
* This implementation does nothing. */
*/ @Override
@Override public void write(byte pBytes[]) throws IOException {
public void write(byte pBytes[]) throws IOException { }
}
/**
/** * This implementation does nothing.
* This implementation does nothing. */
*/ @Override
@Override public void write(byte pBytes[], int pOffset, int pLength) throws IOException {
public void write(byte pBytes[], int pOffset, int pLength) throws IOException { }
}
} }

View File

@@ -1,241 +1,242 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import java.io.DataInput; import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
/** /**
* A data stream that is both readable and writable, much like a * A data stream that is both readable and writable, much like a
* {@code RandomAccessFile}, except it may be backed by something other than a file. * {@code RandomAccessFile}, except it may be backed by something other than a file.
* <p/> *
* * @see java.io.RandomAccessFile
* @see java.io.RandomAccessFile *
* * @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/io/RandomAccessStream.java#3 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/RandomAccessStream.java#3 $ */
*/ public abstract class RandomAccessStream implements Seekable, DataInput, DataOutput {
public abstract class RandomAccessStream implements Seekable, DataInput, DataOutput { // TODO: Use a RandomAcceessFile as backing in impl, probably
// TODO: Use a RandomAcceessFile as backing in impl, probably // TODO: Create an in-memory implementation too?
// TODO: Create an in-memory implementation too? // 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 position = -1;
//private int position = -1;
/** This random access stream, wrapped in an {@code InputStream} */
/** This random access stream, wrapped in an {@code InputStream} */ SeekableInputStream inputView = null;
SeekableInputStream inputView = null; /** This random access stream, wrapped in an {@code OutputStream} */
/** This random access stream, wrapped in an {@code OutputStream} */ SeekableOutputStream outputView = 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 { try {
try { return readByte() & 0xff;
return readByte() & 0xff; }
} catch (EOFException e) {
catch (EOFException e) { return -1;
return -1; }
} }
}
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 (pBytes == null) {
if (pBytes == null) { throw new NullPointerException("bytes == null");
throw new NullPointerException("bytes == null"); }
} else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
else 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(); }
} else if (pLength == 0) {
else if (pLength == 0) { return 0;
return 0; }
}
// Special case, allready at EOF
// Special case, allready at EOF int c = read();
int c = read(); if (c == -1) {
if (c == -1) { return -1;
return -1; }
}
// Otherwise, read as many as bytes as possible
// Otherwise, read as many as bytes as possible pBytes[pOffset] = (byte) c;
pBytes[pOffset] = (byte) c;
int i = 1;
int i = 1; try {
try { for (; i < pLength; i++) {
for (; i < pLength; i++) { c = read();
c = read(); if (c == -1) {
if (c == -1) { break;
break; }
} pBytes[pOffset + i] = (byte) c;
pBytes[pOffset + i] = (byte) c; }
} }
} catch (IOException ignore) {
catch (IOException ignore) { // Ignore exception, just return length
// Ignore exception, just return length }
}
return i;
return i; }
}
public final int read(byte[] pBytes) throws IOException {
public final int read(byte[] pBytes) throws IOException { return read(pBytes, 0, pBytes != null ? pBytes.length : 1);
return read(pBytes, 0, pBytes != null ? pBytes.length : 1); }
}
/**
/** * Returns an input view of this {@code RandomAccessStream}.
* Returns an input view of this {@code RandomAccessStream}. * Invoking this method several times, will return the same object.
* Invoking this method several times, will return the same object. * <p>
* <p/> * <em>Note that read access is NOT synchronized.</em>
* <em>Note that read access is NOT synchronized.</em> * </p>
* *
* @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 (inputView == null) { if (inputView == null) {
inputView = new InputStreamView(this); inputView = new InputStreamView(this);
} }
return inputView; return inputView;
} }
/** /**
* Returns an output view of this {@code RandomAccessStream}. * Returns an output view of this {@code RandomAccessStream}.
* Invoking this method several times, will return the same object. * Invoking this method several times, will return the same object.
* <p/> * <p>
* <em>Note that write access is NOT synchronized.</em> * <em>Note that write access is NOT synchronized.</em>
* * </p>
* @return a {@code SeekableOutputStream} writing to this stream *
*/ * @return a {@code SeekableOutputStream} writing to this stream
public final SeekableOutputStream asOutputStream() { */
if (outputView == null) { public final SeekableOutputStream asOutputStream() {
outputView = new OutputStreamView(this); if (outputView == null) {
} outputView = new OutputStreamView(this);
return outputView; }
} return outputView;
}
static final class InputStreamView extends SeekableInputStream {
// TODO: Consider adding synchonization (on stream) for all operations static final class InputStreamView extends SeekableInputStream {
// TODO: Is is a good thing that close/flush etc works on stream? // TODO: Consider adding synchonization (on stream) for all operations
// - Or should it rather just work on the views? // TODO: Is is a good thing that close/flush etc works on stream?
// - Allow multiple views? // - Or should it rather just work on the views?
// - Allow multiple views?
final private RandomAccessStream mStream;
final private RandomAccessStream mStream;
public InputStreamView(RandomAccessStream pStream) {
if (pStream == null) { public InputStreamView(RandomAccessStream pStream) {
throw new IllegalArgumentException("stream == null"); if (pStream == null) {
} throw new IllegalArgumentException("stream == null");
mStream = pStream; }
} mStream = pStream;
}
public boolean isCached() {
return mStream.isCached(); public boolean isCached() {
} return mStream.isCached();
}
public boolean isCachedFile() {
return mStream.isCachedFile(); public boolean isCachedFile() {
} return mStream.isCachedFile();
}
public boolean isCachedMemory() {
return mStream.isCachedMemory(); public boolean isCachedMemory() {
} return mStream.isCachedMemory();
}
protected void closeImpl() throws IOException {
mStream.close(); protected void closeImpl() throws IOException {
} mStream.close();
}
protected void flushBeforeImpl(long pPosition) throws IOException {
mStream.flushBefore(pPosition); protected void flushBeforeImpl(long pPosition) throws IOException {
} mStream.flushBefore(pPosition);
}
protected void seekImpl(long pPosition) throws IOException {
mStream.seek(pPosition); protected void seekImpl(long pPosition) throws IOException {
} mStream.seek(pPosition);
}
public int read() throws IOException {
return mStream.read(); public int read() throws IOException {
} return mStream.read();
}
@Override
public int read(byte pBytes[], int pOffset, int pLength) throws IOException { @Override
return mStream.read(pBytes, pOffset, pLength); public int read(byte pBytes[], int pOffset, int pLength) throws IOException {
} return mStream.read(pBytes, pOffset, pLength);
} }
}
static final class OutputStreamView extends SeekableOutputStream {
// TODO: Consider adding synchonization (on stream) for all operations static final class OutputStreamView extends SeekableOutputStream {
// TODO: Is is a good thing that close/flush etc works on stream? // TODO: Consider adding synchonization (on stream) for all operations
// - Or should it rather just work on the views? // TODO: Is is a good thing that close/flush etc works on stream?
// - Allow multiple views? // - Or should it rather just work on the views?
// - Allow multiple views?
final private RandomAccessStream mStream;
final private RandomAccessStream mStream;
public OutputStreamView(RandomAccessStream pStream) {
if (pStream == null) { public OutputStreamView(RandomAccessStream pStream) {
throw new IllegalArgumentException("stream == null"); if (pStream == null) {
} throw new IllegalArgumentException("stream == null");
mStream = pStream; }
} mStream = pStream;
}
public boolean isCached() {
return mStream.isCached(); public boolean isCached() {
} return mStream.isCached();
}
public boolean isCachedFile() {
return mStream.isCachedFile(); public boolean isCachedFile() {
} return mStream.isCachedFile();
}
public boolean isCachedMemory() {
return mStream.isCachedMemory(); public boolean isCachedMemory() {
} return mStream.isCachedMemory();
}
protected void closeImpl() throws IOException {
mStream.close(); protected void closeImpl() throws IOException {
} mStream.close();
}
protected void flushBeforeImpl(long pPosition) throws IOException {
mStream.flushBefore(pPosition); protected void flushBeforeImpl(long pPosition) throws IOException {
} mStream.flushBefore(pPosition);
}
protected void seekImpl(long pPosition) throws IOException {
mStream.seek(pPosition); protected void seekImpl(long pPosition) throws IOException {
} mStream.seek(pPosition);
}
public void write(int pByte) throws IOException {
mStream.write(pByte); public void write(int pByte) throws IOException {
} mStream.write(pByte);
}
@Override
public void write(byte pBytes[], int pOffset, int pLength) throws IOException { @Override
mStream.write(pBytes, pOffset, pLength); public void write(byte pBytes[], int pOffset, int pLength) throws IOException {
} mStream.write(pBytes, pOffset, pLength);
} }
} }
}

View File

@@ -1,186 +1,193 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import java.io.IOException; import java.io.IOException;
/** /**
* Interface for seekable streams. * Interface for seekable streams.
* <p/> *
* @see SeekableInputStream * @see SeekableInputStream
* @see SeekableOutputStream * @see SeekableOutputStream
* *
* @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/Seekable.java#1 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Seekable.java#1 $
*/ */
public interface Seekable { public interface Seekable {
/** /**
* Returns the current byte position of the stream. The next read will take * Returns the current byte position of the stream. The next read will take
* place starting at this offset. * place starting at this offset.
* *
* @return a {@code long} containing the position of the stream. * @return a {@code long} containing the position of the stream.
* @throws IOException if an I/O error occurs. * @throws IOException if an I/O error occurs.
*/ */
long getStreamPosition() throws IOException; long getStreamPosition() throws IOException;
/** /**
* Sets the current stream position to the desired location. * Sets the current stream position to the desired location.
* The next read will occur at this location. * The next read will occur at this location.
* <p/> * <p>
* An {@code IndexOutOfBoundsException} will be thrown if pPosition is smaller * An {@code IndexOutOfBoundsException} will be thrown if pPosition is smaller
* than the flushed position (as returned by {@link #getFlushedPosition()}). * than the flushed position (as returned by {@link #getFlushedPosition()}).
* <p/> * </p>
* It is legal to seek past the end of the file; an {@code EOFException} * <p>
* will be thrown only if a read is performed. * It is legal to seek past the end of the file; an {@code EOFException}
* * will be thrown only if a read is performed.
* @param pPosition a long containing the desired file pointer position. * </p>
* *
* @throws IndexOutOfBoundsException if {@code pPosition} is smaller than * @param pPosition a long containing the desired file pointer position.
* the flushed position. *
* @throws IOException if any other I/O error occurs. * @throws IndexOutOfBoundsException if {@code pPosition} is smaller than
*/ * the flushed position.
void seek(long pPosition) throws IOException; * @throws IOException if any other I/O error occurs.
*/
/** void seek(long pPosition) throws IOException;
* Marks a position in the stream to be returned to by a subsequent call to
* reset. /**
* Unlike a standard {@code InputStream}, all {@code Seekable} * Marks a position in the stream to be returned to by a subsequent call to
* streams upport marking. Additionally, calls to {@code mark} and * reset.
* {@code reset} may be nested arbitrarily. * Unlike a standard {@code InputStream}, all {@code Seekable}
* <p/> * streams upport marking. Additionally, calls to {@code mark} and
* Unlike the {@code mark} methods declared by the {@code Reader} or * {@code reset} may be nested arbitrarily.
* {@code InputStream} * <p>
* interfaces, no {@code readLimit} parameter is used. An arbitrary amount * Unlike the {@code mark} methods declared by the {@code Reader} or
* of data may be read following the call to {@code mark}. * {@code InputStream}
*/ * interfaces, no {@code readLimit} parameter is used. An arbitrary amount
void mark(); * of data may be read following the call to {@code mark}.
* </p>
/** */
* Returns the file pointer to its previous position, void mark();
* at the time of the most recent unmatched call to mark.
* <p/> /**
* Calls to reset without a corresponding call to mark will either: * Returns the file pointer to its previous position,
* <ul> * at the time of the most recent unmatched call to mark.
* <li>throw an {@code IOException}</li> * <p>
* <li>or, reset to the beginning of the stream.</li> * Calls to reset without a corresponding call to mark will either:
* </ul> * </p>
* An {@code IOException} will be thrown if the previous marked position * <ul>
* lies in the discarded portion of the stream. * <li>throw an {@code IOException}</li>
* * <li>or, reset to the beginning of the stream.</li>
* @throws IOException if an I/O error occurs. * </ul>
* @see java.io.InputStream#reset() * <p>
*/ * An {@code IOException} will be thrown if the previous marked position
void reset() throws IOException; * lies in the discarded portion of the stream.
* </p>
/** *
* Discards the initial portion of the stream prior to the indicated * @throws IOException if an I/O error occurs.
* postion. Attempting to seek to an offset within the flushed portion of * @see java.io.InputStream#reset()
* the stream will result in an {@code IndexOutOfBoundsException}. */
* <p/> void reset() throws IOException;
* Calling {@code flushBefore} may allow classes implementing this
* interface to free up resources such as memory or disk space that are /**
* being used to store data from the stream. * Discards the initial portion of the stream prior to the indicated
* * postion. Attempting to seek to an offset within the flushed portion of
* @param pPosition a long containing the length of the file prefix that * the stream will result in an {@code IndexOutOfBoundsException}.
* may be flushed. * <p>
* * Calling {@code flushBefore} may allow classes implementing this
* @throws IndexOutOfBoundsException if {@code pPosition} lies in the * interface to free up resources such as memory or disk space that are
* flushed portion of the stream or past the current stream position. * being used to store data from the stream.
* @throws IOException if an I/O error occurs. * </p>
*/ *
void flushBefore(long pPosition) throws IOException; * @param pPosition a long containing the length of the file prefix that
* may be flushed.
/** *
* Discards the initial position of the stream prior to the current stream * @throws IndexOutOfBoundsException if {@code pPosition} lies in the
* position. Equivalent to {@code flushBefore(getStreamPosition())}. * flushed portion of the stream or past the current stream position.
* * @throws IOException if an I/O error occurs.
* @throws IOException if an I/O error occurs. */
*/ void flushBefore(long pPosition) throws IOException;
void flush() throws IOException;
/**
/** * Discards the initial position of the stream prior to the current stream
* Returns the earliest position in the stream to which seeking may be * position. Equivalent to {@code flushBefore(getStreamPosition())}.
* performed. The returned value will be the maximum of all values passed *
* into previous calls to {@code flushBefore}. * @throws IOException if an I/O error occurs.
* */
* @return the earliest legal position for seeking, as a {@code long}. void flush() throws IOException;
*
* @throws IOException if an I/O error occurs. /**
*/ * Returns the earliest position in the stream to which seeking may be
long getFlushedPosition() throws IOException; * performed. The returned value will be the maximum of all values passed
* into previous calls to {@code flushBefore}.
/** *
* Returns true if this {@code Seekable} stream caches data itself in order * @return the earliest legal position for seeking, as a {@code long}.
* to allow seeking backwards. Applications may consult this in order to *
* decide how frequently, or whether, to flush in order to conserve cache * @throws IOException if an I/O error occurs.
* resources. */
* long getFlushedPosition() throws IOException;
* @return {@code true} if this {@code Seekable} caches data.
* @see #isCachedMemory() /**
* @see #isCachedFile() * Returns true if this {@code Seekable} stream caches data itself in order
*/ * to allow seeking backwards. Applications may consult this in order to
boolean isCached(); * decide how frequently, or whether, to flush in order to conserve cache
* resources.
/** *
* Returns true if this {@code Seekable} stream caches data itself in order * @return {@code true} if this {@code Seekable} caches data.
* to allow seeking backwards, and the cache is kept in main memory. * @see #isCachedMemory()
* Applications may consult this in order to decide how frequently, or * @see #isCachedFile()
* whether, to flush in order to conserve cache resources. */
* boolean isCached();
* @return {@code true} if this {@code Seekable} caches data in main
* memory. /**
* @see #isCached() * Returns true if this {@code Seekable} stream caches data itself in order
* @see #isCachedFile() * to allow seeking backwards, and the cache is kept in main memory.
*/ * Applications may consult this in order to decide how frequently, or
boolean isCachedMemory(); * whether, to flush in order to conserve cache resources.
*
/** * @return {@code true} if this {@code Seekable} caches data in main
* Returns true if this {@code Seekable} stream caches data itself in * memory.
* order to allow seeking backwards, and the cache is kept in a * @see #isCached()
* temporary file. * @see #isCachedFile()
* Applications may consult this in order to decide how frequently, */
* or whether, to flush in order to conserve cache resources. boolean isCachedMemory();
*
* @return {@code true} if this {@code Seekable} caches data in a /**
* temporary file. * Returns true if this {@code Seekable} stream caches data itself in
* @see #isCached * order to allow seeking backwards, and the cache is kept in a
* @see #isCachedMemory * temporary file.
*/ * Applications may consult this in order to decide how frequently,
boolean isCachedFile(); * or whether, to flush in order to conserve cache resources.
*
/** * @return {@code true} if this {@code Seekable} caches data in a
* Closes the stream. * temporary file.
* * @see #isCached
* @throws java.io.IOException if the stream can't be closed. * @see #isCachedMemory
*/ */
void close() throws IOException; boolean isCachedFile();
}
/**
* Closes the stream.
*
* @throws java.io.IOException if the stream can't be closed.
*/
void close() throws IOException;
}

View File

@@ -1,238 +1,238 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Stack; import java.util.Stack;
/** /**
* Abstract base class for {@code InputStream}s implementing the {@code Seekable} interface. * Abstract base class for {@code InputStream}s implementing the {@code Seekable} interface.
* <p/> *
* @see SeekableOutputStream * @see SeekableOutputStream
* *
* @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/SeekableInputStream.java#4 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/SeekableInputStream.java#4 $
*/ */
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 position. position 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 position; long position;
long flushedPosition; long flushedPosition;
boolean closed; boolean closed;
protected Stack<Long> markedPositions = new Stack<Long>(); protected Stack<Long> markedPositions = new Stack<Long>();
/// InputStream overrides /// InputStream overrides
@Override @Override
public final int read(byte[] pBytes) throws IOException { public final int read(byte[] pBytes) throws IOException {
return read(pBytes, 0, pBytes != null ? pBytes.length : 1); return read(pBytes, 0, pBytes != null ? pBytes.length : 1);
} }
/** /**
* Implemented using {@code seek(currentPos + pLength)}. * Implemented using {@code seek(currentPos + pLength)}.
* *
* @param pLength the number of bytes to skip * @param pLength the number of bytes to skip
* @return the actual number of bytes skipped (may be equal to or less * @return the actual number of bytes skipped (may be equal to or less
* than {@code pLength}) * than {@code pLength})
* *
* @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(final long pLength) throws IOException { public final long skip(final long pLength) throws IOException {
long pos = position; long pos = position;
long wantedPosition = pos + pLength; long wantedPosition = pos + pLength;
if (wantedPosition < flushedPosition) { if (wantedPosition < flushedPosition) {
throw new IOException("position < flushedPosition"); throw new IOException("position < flushedPosition");
} }
// Stop at stream length for compatibility, even though it might be 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
int available = available(); int available = available();
if (available > 0) { if (available > 0) {
seek(Math.min(wantedPosition, pos + available)); seek(Math.min(wantedPosition, pos + available));
} }
// TODO: Add optimization for streams with known length! // TODO: Add optimization for streams with known length!
else { else {
// Slow mode... // Slow mode...
int toSkip = (int) Math.max(Math.min(pLength, 512), -512); int toSkip = (int) Math.max(Math.min(pLength, 512), -512);
while (toSkip > 0 && read() >= 0) { while (toSkip > 0 && read() >= 0) {
toSkip--; toSkip--;
} }
} }
return position - pos; return position - pos;
} }
@Override @Override
public final void mark(int pLimit) { public final void mark(int pLimit) {
mark(); mark();
// 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(position - pLimit, flushedPosition)); 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
} }
} }
/** /**
* Returns {@code true}, as marking is always supported. * Returns {@code true}, as marking is always supported.
* *
* @return {@code true}. * @return {@code true}.
*/ */
@Override @Override
public final boolean markSupported() { public final boolean markSupported() {
return true; return true;
} }
/// Seekable implementation /// Seekable implementation
public final void seek(long pPosition) throws IOException { public final void seek(long pPosition) throws IOException {
checkOpen(); checkOpen();
// 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 < flushedPosition) { if (pPosition < flushedPosition) {
throw new IndexOutOfBoundsException("position < flushedPosition"); throw new IndexOutOfBoundsException("position < flushedPosition");
} }
seekImpl(pPosition); seekImpl(pPosition);
position = 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() {
markedPositions.push(position); markedPositions.push(position);
} }
@Override @Override
public final void reset() throws IOException { public final void reset() throws IOException {
checkOpen(); checkOpen();
if (!markedPositions.isEmpty()) { if (!markedPositions.isEmpty()) {
long newPos = markedPositions.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 < flushedPosition) { if (newPos < flushedPosition) {
throw new IOException("Previous marked position has been discarded"); throw new IOException("Previous marked position has been discarded");
} }
seek(newPos); seek(newPos);
} }
else { else {
// TODO: To iron out some wrinkles due to conflicting contracts // TODO: To iron out some wrinkles due to conflicting contracts
// (InputStream and Seekable both declare reset), // (InputStream and Seekable both declare reset),
// we might need to reset to the last marked position instead.. // we might need to reset to the last marked position instead..
// However, that becomes REALLY confusing if that position is after // However, that becomes REALLY confusing if that position is after
// the current position... // the current position...
seek(0); seek(0);
} }
} }
public final void flushBefore(long pPosition) throws IOException { public final void flushBefore(long pPosition) throws IOException {
if (pPosition < flushedPosition) { if (pPosition < flushedPosition) {
throw new IndexOutOfBoundsException("position < flushedPosition"); throw new IndexOutOfBoundsException("position < flushedPosition");
} }
if (pPosition > getStreamPosition()) { if (pPosition > getStreamPosition()) {
throw new IndexOutOfBoundsException("position > stream position"); throw new IndexOutOfBoundsException("position > stream position");
} }
checkOpen(); checkOpen();
flushBeforeImpl(pPosition); flushBeforeImpl(pPosition);
flushedPosition = pPosition; flushedPosition = pPosition;
} }
/** /**
* Discards the initial portion of the stream prior to the indicated postion. * Discards the initial portion of the stream prior to the indicated postion.
* *
* @param pPosition the position to flush to * @param pPosition the position to flush to
* @throws IOException if an I/O exception occurs during the flush operation * @throws IOException if an I/O exception occurs during the flush operation
* *
* @see #flushBefore(long) * @see #flushBefore(long)
*/ */
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(flushedPosition); flushBefore(flushedPosition);
} }
public final long getFlushedPosition() throws IOException { public final long getFlushedPosition() throws IOException {
checkOpen(); checkOpen();
return flushedPosition; return flushedPosition;
} }
public final long getStreamPosition() throws IOException { public final long getStreamPosition() throws IOException {
checkOpen(); checkOpen();
return position; return position;
} }
protected final void checkOpen() throws IOException { protected final void checkOpen() throws IOException {
if (closed) { if (closed) {
throw new IOException("closed"); throw new IOException("closed");
} }
} }
@Override @Override
public final void close() throws IOException { public final void close() throws IOException {
checkOpen(); checkOpen();
closed = true; closed = true;
closeImpl(); closeImpl();
} }
protected abstract void closeImpl() throws IOException; protected abstract void closeImpl() throws IOException;
/** /**
* Finalizes this object prior to garbage collection. The * Finalizes this object prior to garbage collection. The
* {@code close} method is called to close any open input * {@code close} method is called to close any open input
* source. This method should not be called from application * source. This method should not be called from application
* code. * code.
* *
* @exception Throwable if an error occurs during superclass * @exception Throwable if an error occurs during superclass
* finalization. * finalization.
*/ */
@Override @Override
protected void finalize() throws Throwable { protected void finalize() throws Throwable {
if (!closed) { if (!closed) {
try { try {
close(); close();
} }
catch (IOException ignore) { catch (IOException ignore) {
// Ignroe // Ignroe
} }
} }
super.finalize(); super.finalize();
} }
} }

View File

@@ -1,140 +1,140 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Stack; import java.util.Stack;
/** /**
* Abstract base class for {@code OutputStream}s implementing the * Abstract base class for {@code OutputStream}s implementing the
* {@code Seekable} interface. * {@code Seekable} interface.
* <p/> *
* @see SeekableInputStream * @see SeekableInputStream
* *
* @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/SeekableOutputStream.java#2 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/SeekableOutputStream.java#2 $
*/ */
public abstract class SeekableOutputStream extends OutputStream implements Seekable { public abstract class SeekableOutputStream extends OutputStream implements Seekable {
// TODO: Implement // TODO: Implement
long position; long position;
long flushedPosition; long flushedPosition;
boolean closed; boolean closed;
protected Stack<Long> markedPositions = new Stack<Long>(); protected Stack<Long> markedPositions = new Stack<Long>();
/// Outputstream overrides /// Outputstream overrides
@Override @Override
public final void write(byte pBytes[]) throws IOException { public final void write(byte pBytes[]) throws IOException {
write(pBytes, 0, pBytes != null ? pBytes.length : 1); write(pBytes, 0, pBytes != null ? pBytes.length : 1);
} }
/// Seekable implementation /// Seekable implementation
// TODO: This is common behaviour/implementation with SeekableInputStream, // TODO: This is common behaviour/implementation with SeekableInputStream,
// probably a good idea to extract a delegate..? // probably a good idea to extract a delegate..?
public final void seek(long pPosition) throws IOException { public final void seek(long pPosition) throws IOException {
checkOpen(); checkOpen();
// 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 < flushedPosition) { if (pPosition < flushedPosition) {
throw new IndexOutOfBoundsException("position < flushedPosition!"); throw new IndexOutOfBoundsException("position < flushedPosition!");
} }
seekImpl(pPosition); seekImpl(pPosition);
position = 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() {
markedPositions.push(position); markedPositions.push(position);
} }
public final void reset() throws IOException { public final void reset() throws IOException {
checkOpen(); checkOpen();
if (!markedPositions.isEmpty()) { if (!markedPositions.isEmpty()) {
long newPos = markedPositions.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 < flushedPosition) { if (newPos < flushedPosition) {
throw new IOException("Previous marked position has been discarded!"); throw new IOException("Previous marked position has been discarded!");
} }
seek(newPos); seek(newPos);
} }
} }
public final void flushBefore(long pPosition) throws IOException { public final void flushBefore(long pPosition) throws IOException {
if (pPosition < flushedPosition) { if (pPosition < flushedPosition) {
throw new IndexOutOfBoundsException("position < flushedPosition!"); throw new IndexOutOfBoundsException("position < flushedPosition!");
} }
if (pPosition > getStreamPosition()) { if (pPosition > getStreamPosition()) {
throw new IndexOutOfBoundsException("position > getStreamPosition()!"); throw new IndexOutOfBoundsException("position > getStreamPosition()!");
} }
checkOpen(); checkOpen();
flushBeforeImpl(pPosition); flushBeforeImpl(pPosition);
flushedPosition = 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(flushedPosition); flushBefore(flushedPosition);
} }
public final long getFlushedPosition() throws IOException { public final long getFlushedPosition() throws IOException {
checkOpen(); checkOpen();
return flushedPosition; return flushedPosition;
} }
public final long getStreamPosition() throws IOException { public final long getStreamPosition() throws IOException {
checkOpen(); checkOpen();
return position; return position;
} }
protected final void checkOpen() throws IOException { protected final void checkOpen() throws IOException {
if (closed) { if (closed) {
throw new IOException("closed"); throw new IOException("closed");
} }
} }
@Override @Override
public final void close() throws IOException { public final void close() throws IOException {
checkOpen(); checkOpen();
closed = true; closed = true;
closeImpl(); closeImpl();
} }
protected abstract void closeImpl() throws IOException; protected abstract void closeImpl() throws IOException;
} }

View File

@@ -1,189 +1,188 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate; import com.twelvemonkeys.lang.Validate;
import java.io.IOException; import java.io.IOException;
import java.io.Reader; import java.io.Reader;
import java.io.StringReader; import java.io.StringReader;
/** /**
* StringArrayReader * StringArrayReader
* <p/> *
* * @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/io/StringArrayReader.java#2 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/StringArrayReader.java#2 $ */
*/ public class StringArrayReader extends StringReader {
public class StringArrayReader extends StringReader {
private StringReader current;
private StringReader current; private String[] strings;
private String[] strings; protected final Object finalLock;
protected final Object finalLock; private int currentSting;
private int currentSting; private int markedString;
private int markedString; private int mark;
private int mark; private int next;
private int next;
/**
/** * Create a new string array reader.
* Create a new string array reader. *
* * @param pStrings {@code String}s providing the character stream.
* @param pStrings {@code String}s providing the character stream. */
*/ public StringArrayReader(final String[] pStrings) {
public StringArrayReader(final String[] pStrings) { super("");
super("");
Validate.notNull(pStrings, "strings");
Validate.notNull(pStrings, "strings");
finalLock = lock = pStrings; // NOTE: It's ok to sync on pStrings, as the
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
strings = pStrings.clone(); // Defensive copy for content
strings = pStrings.clone(); // Defensive copy for content nextReader();
nextReader(); }
}
protected final Reader nextReader() {
protected final Reader nextReader() { if (currentSting >= strings.length) {
if (currentSting >= strings.length) { current = new EmptyReader();
current = new EmptyReader(); }
} else {
else { current = new StringReader(strings[currentSting++]);
current = new StringReader(strings[currentSting++]); }
}
// NOTE: Reset next for every reader, and record marked reader in mark/reset methods!
// NOTE: Reset next for every reader, and record marked reader in mark/reset methods! next = 0;
next = 0;
return current;
return current; }
}
/**
/** * Check to make sure that the stream has not been closed
* Check to make sure that the stream has not been closed *
* * @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 (strings == null) {
if (strings == null) { throw new IOException("Stream closed");
throw new IOException("Stream closed"); }
} }
}
public void close() {
public void close() { super.close();
super.close(); strings = null;
strings = null; current.close();
current.close(); }
}
public void mark(int pReadLimit) throws IOException {
public void mark(int pReadLimit) throws IOException { if (pReadLimit < 0){
if (pReadLimit < 0){ throw new IllegalArgumentException("Read limit < 0");
throw new IllegalArgumentException("Read limit < 0"); }
}
synchronized (finalLock) {
synchronized (finalLock) { ensureOpen();
ensureOpen(); mark = next;
mark = next; markedString = currentSting;
markedString = currentSting;
current.mark(pReadLimit);
current.mark(pReadLimit); }
} }
}
public void reset() throws IOException {
public void reset() throws IOException { synchronized (finalLock) {
synchronized (finalLock) { ensureOpen();
ensureOpen();
if (currentSting != markedString) {
if (currentSting != markedString) { currentSting = markedString - 1;
currentSting = markedString - 1; nextReader();
nextReader(); current.skip(mark);
current.skip(mark); }
} else {
else { current.reset();
current.reset(); }
}
next = mark;
next = mark; }
} }
}
public boolean markSupported() {
public boolean markSupported() { return true;
return true; }
}
public int read() throws IOException {
public int read() throws IOException { synchronized (finalLock) {
synchronized (finalLock) { int read = current.read();
int read = current.read();
if (read < 0 && currentSting < strings.length) {
if (read < 0 && currentSting < strings.length) { nextReader();
nextReader(); return read(); // In case of empty strings
return read(); // In case of empty strings }
}
next++;
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 (finalLock) {
synchronized (finalLock) { int read = current.read(pBuffer, pOffset, pLength);
int read = current.read(pBuffer, pOffset, pLength);
if (read < 0 && currentSting < strings.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 }
}
next += read;
next += read;
return read;
return read; }
} }
}
public boolean ready() throws IOException {
public boolean ready() throws IOException { return current.ready();
return current.ready(); }
}
public long skip(long pChars) throws IOException {
public long skip(long pChars) throws IOException { synchronized (finalLock) {
synchronized (finalLock) { long skipped = current.skip(pChars);
long skipped = current.skip(pChars);
if (skipped == 0 && currentSting < strings.length) {
if (skipped == 0 && currentSting < strings.length) { nextReader();
nextReader(); return skip(pChars);
return skip(pChars); }
}
next += skipped;
next += skipped;
return skipped;
return skipped; }
} }
}
}
}

View File

@@ -1,137 +1,136 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.Validate; import com.twelvemonkeys.lang.Validate;
import java.io.FilterInputStream; import java.io.FilterInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
/** /**
* An {@code InputStream} reading up to a specified number of bytes from an * An {@code InputStream} reading up to a specified number of bytes from an
* underlying stream. * underlying stream.
* <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/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 bytesLeft;
private long bytesLeft; private int markLimit;
private int markLimit;
/**
/** * Creates a {@code SubStream} of the given {@code pStream}.
* Creates a {@code SubStream} of the given {@code pStream}. *
* * @param pStream the underlying input stream
* @param pStream the underlying input stream * @param pLength maximum number of bytes to read drom this stream
* @param pLength maximum number of bytes to read drom this stream */
*/ 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")); bytesLeft = pLength;
bytesLeft = pLength; }
}
/**
/** * Marks this stream as closed.
* Marks this stream as closed. * This implementation does <em>not</em> close the underlying stream.
* This implementation does <em>not</em> close the underlying stream. */
*/ @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 (bytesLeft > 0) {
while (bytesLeft > 0) { //noinspection ResultOfMethodCallIgnored
//noinspection ResultOfMethodCallIgnored skip(bytesLeft);
skip(bytesLeft); }
} }
}
@Override
@Override public int available() throws IOException {
public int available() throws IOException { return (int) Math.min(super.available(), bytesLeft);
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... markLimit = 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 bytesLeft += markLimit;
bytesLeft += markLimit; }
}
@Override
@Override public int read() throws IOException {
public int read() throws IOException { if (bytesLeft-- <= 0) {
if (bytesLeft-- <= 0) { return -1;
return -1; }
} return super.read();
return super.read(); }
}
@Override
@Override public final int read(byte[] pBytes) throws IOException {
public final int read(byte[] pBytes) throws IOException { return read(pBytes, 0, pBytes.length);
return read(pBytes, 0, pBytes.length); }
}
@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 (bytesLeft <= 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)); bytesLeft = read < 0 ? 0 : bytesLeft - read;
bytesLeft = read < 0 ? 0 : bytesLeft - read; return read;
return read; }
}
/**
/** * Finds the maximum number of bytes we can read or skip, from this stream.
* Finds the maximum number of bytes we can read or skip, from this stream. *
* * @param pLength the requested length
* @param pLength the requested length * @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 (bytesLeft < pLength) {
if (bytesLeft < pLength) { return (int) Math.max(bytesLeft, 0);
return (int) Math.max(bytesLeft, 0); }
} else {
else { return pLength;
return pLength; }
} }
}
@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 bytesLeft -= skipped;
bytesLeft -= skipped; return skipped;
return skipped; }
} }
}

View File

@@ -1,107 +1,106 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import com.twelvemonkeys.util.StringTokenIterator; import com.twelvemonkeys.util.StringTokenIterator;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
/** /**
* UnixFileSystem * UnixFileSystem
* <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/UnixFileSystem.java#1 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/UnixFileSystem.java#1 $ */
*/ final class UnixFileSystem extends FileSystem {
final class UnixFileSystem extends FileSystem { long getFreeSpace(File pPath) {
long getFreeSpace(File pPath) { try {
try { return getNumber(pPath, 3);
return getNumber(pPath, 3); }
} catch (IOException e) {
catch (IOException e) { return 0l;
return 0l; }
} }
}
long getTotalSpace(File pPath) {
long getTotalSpace(File pPath) { try {
try { return getNumber(pPath, 5);
return getNumber(pPath, 5); }
} catch (IOException e) {
catch (IOException e) { return 0l;
return 0l; }
} }
}
private long getNumber(File pPath, int pIndex) throws IOException {
private long getNumber(File pPath, int pIndex) throws IOException { // TODO: Test on other platforms
// TODO: Test on other platforms // Tested on Mac OS X, CygWin
// Tested on Mac OS X, CygWin BufferedReader reader = exec(new String[] {"df", "-k", pPath.getAbsolutePath()});
BufferedReader reader = exec(new String[] {"df", "-k", pPath.getAbsolutePath()});
String last = null;
String last = null; String line;
String line; try {
try { while ((line = reader.readLine()) != null) {
while ((line = reader.readLine()) != null) { last = line;
last = line; }
} }
} finally {
finally { FileUtil.close(reader);
FileUtil.close(reader); }
}
if (last != null) {
if (last != null) { String blocks = null;
String blocks = null; StringTokenIterator tokens = new StringTokenIterator(last, " ", StringTokenIterator.REVERSE);
StringTokenIterator tokens = new StringTokenIterator(last, " ", StringTokenIterator.REVERSE); int count = 0;
int count = 0; // We want the 3rd last token
// We want the 3rd last token while (count < pIndex && tokens.hasNext()) {
while (count < pIndex && tokens.hasNext()) { blocks = tokens.nextToken();
blocks = tokens.nextToken(); count++;
count++; }
}
if (blocks != null) {
if (blocks != null) { try {
try { return Long.parseLong(blocks) * 1024L;
return Long.parseLong(blocks) * 1024L; }
} catch (NumberFormatException ignore) {
catch (NumberFormatException ignore) { // Ignore
// Ignore }
} }
} }
}
return 0l;
return 0l; }
}
String getName() {
String getName() { return "Unix";
return "Unix"; }
} }
}

View File

@@ -1,195 +1,194 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import java.io.File; import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
import java.io.FilenameFilter; import java.io.FilenameFilter;
import java.io.IOException; import java.io.IOException;
/** /**
* Win32File * Win32File
* <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/Win32File.java#2 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Win32File.java#2 $ */
*/ final class Win32File extends File {
final class Win32File extends File { private final static boolean IS_WINDOWS = isWindows();
private final static boolean IS_WINDOWS = isWindows();
private static boolean isWindows() {
private static boolean isWindows() { try {
try { String os = System.getProperty("os.name");
String os = System.getProperty("os.name"); return os.toLowerCase().indexOf("windows") >= 0;
return os.toLowerCase().indexOf("windows") >= 0; }
} catch (Throwable t) {
catch (Throwable t) { // Ignore
// Ignore }
} return false;
return false; }
}
private Win32File(File pPath) {
private Win32File(File pPath) { super(pPath.getPath());
super(pPath.getPath()); }
}
public static void main(String[] pArgs) {
public static void main(String[] pArgs) { int argIdx = 0;
int argIdx = 0; boolean recursive = false;
boolean recursive = false; while (pArgs.length > argIdx + 1 && pArgs[argIdx].charAt(0) == '-' && pArgs[argIdx].length() > 1) {
while (pArgs.length > argIdx + 1 && pArgs[argIdx].charAt(0) == '-' && pArgs[argIdx].length() > 1) { if (pArgs[argIdx].charAt(1) == 'R' || pArgs[argIdx].equals("--recursive")) {
if (pArgs[argIdx].charAt(1) == 'R' || pArgs[argIdx].equals("--recursive")) { recursive = true;
recursive = true; }
} else {
else { System.err.println("Unknown option: " + pArgs[argIdx]);
System.err.println("Unknown option: " + pArgs[argIdx]); }
} argIdx++;
argIdx++; }
}
File file = wrap(new File(pArgs[argIdx]));
File file = wrap(new File(pArgs[argIdx])); System.out.println("file: " + file);
System.out.println("file: " + file); System.out.println("file.getClass(): " + file.getClass());
System.out.println("file.getClass(): " + file.getClass());
listFiles(file, 0, recursive);
listFiles(file, 0, recursive); }
}
private static void listFiles(File pFile, int pLevel, boolean pRecursive) {
private static void listFiles(File pFile, int pLevel, boolean pRecursive) { if (pFile.isDirectory()) {
if (pFile.isDirectory()) { File[] files = pFile.listFiles();
File[] files = pFile.listFiles(); for (int l = 0; l < pLevel; l++) {
for (int l = 0; l < pLevel; l++) { System.out.print(" ");
System.out.print(" "); }
} System.out.println("Contents of " + pFile + ": ");
System.out.println("Contents of " + pFile + ": "); for (File file : files) {
for (File file : files) { for (int l = 0; l < pLevel; l++) {
for (int l = 0; l < pLevel; l++) { System.out.print(" ");
System.out.print(" "); }
} System.out.println(" " + file);
System.out.println(" " + file); if (pRecursive) {
if (pRecursive) { listFiles(file, pLevel + 1, pLevel < 4);
listFiles(file, pLevel + 1, pLevel < 4); }
} }
} }
} }
}
/**
/** * Wraps a {@code File} object pointing to a Windows symbolic link
* Wraps a {@code File} object pointing to a Windows symbolic link * ({@code .lnk} file) in a {@code Win32Lnk}.
* ({@code .lnk} file) in a {@code Win32Lnk}. * If the operating system is not Windows, the
* If the operating system is not Windows, the * {@code pPath} parameter is returned unwrapped.
* {@code pPath} parameter is returned unwrapped. *
* * @param pPath any path, possibly pointing to a Windows symbolic link file.
* @param pPath any path, possibly pointing to a Windows symbolic link file. * May be {@code null}, in which case {@code null} is returned.
* May be {@code null}, in which case {@code null} is returned. *
* * @return a new {@code Win32Lnk} object if the current os is Windows, and
* @return a new {@code Win32Lnk} object if the current os is Windows, and * the file is a Windows symbolic link ({@code .lnk} file), otherwise
* the file is a Windows symbolic link ({@code .lnk} file), otherwise * {@code pPath}
* {@code pPath} */
*/ public static File wrap(final File pPath) {
public static File wrap(final File pPath) { if (pPath == null) {
if (pPath == null) { return null;
return null; }
}
if (IS_WINDOWS) {
if (IS_WINDOWS) { // Don't wrap if allready wrapped
// Don't wrap if allready wrapped if (pPath instanceof Win32File || pPath instanceof Win32Lnk) {
if (pPath instanceof Win32File || pPath instanceof Win32Lnk) { return pPath;
return pPath; }
}
if (pPath.exists() && pPath.getName().endsWith(".lnk")) {
if (pPath.exists() && pPath.getName().endsWith(".lnk")) { // If Win32 .lnk, let's wrap
// If Win32 .lnk, let's wrap try {
try { return new Win32Lnk(pPath);
return new Win32Lnk(pPath); }
} catch (IOException e) {
catch (IOException e) { // TODO: FixMe!
// TODO: FixMe! e.printStackTrace();
e.printStackTrace(); }
} }
}
// Wwrap even if not a .lnk, as the listFiles() methods etc,
// Wwrap even if not a .lnk, as the listFiles() methods etc, // could potentially return .lnk's, that we want to wrap later...
// could potentially return .lnk's, that we want to wrap later... return new Win32File(pPath);
return new Win32File(pPath); }
}
return pPath;
return pPath; }
}
/**
/** * Wraps a {@code File} array, possibly pointing to Windows symbolic links
* Wraps a {@code File} array, possibly pointing to Windows symbolic links * ({@code .lnk} files) in {@code Win32Lnk}s.
* ({@code .lnk} files) in {@code Win32Lnk}s. *
* * @param pPaths an array of {@code File}s, possibly pointing to Windows
* @param pPaths an array of {@code File}s, possibly pointing to Windows * symbolic link files.
* symbolic link files. * May be {@code null}, in which case {@code null} is returned.
* May be {@code null}, in which case {@code null} is returned. *
* * @return {@code pPaths}, with any {@code File} representing a Windows
* @return {@code pPaths}, with any {@code File} representing a Windows * symbolic link ({@code .lnk} file) wrapped in a {@code Win32Lnk}.
* symbolic link ({@code .lnk} file) wrapped in a {@code Win32Lnk}. */
*/ public static File[] wrap(File[] pPaths) {
public static File[] wrap(File[] pPaths) { if (IS_WINDOWS) {
if (IS_WINDOWS) { for (int i = 0; pPaths != null && i < pPaths.length; i++) {
for (int i = 0; pPaths != null && i < pPaths.length; i++) { pPaths[i] = wrap(pPaths[i]);
pPaths[i] = wrap(pPaths[i]); }
} }
} return pPaths;
return pPaths; }
}
// File overrides
// File overrides @Override
@Override public File getAbsoluteFile() {
public File getAbsoluteFile() { return wrap(super.getAbsoluteFile());
return wrap(super.getAbsoluteFile()); }
}
@Override
@Override public File getCanonicalFile() throws IOException {
public File getCanonicalFile() throws IOException { return wrap(super.getCanonicalFile());
return wrap(super.getCanonicalFile()); }
}
@Override
@Override public File getParentFile() {
public File getParentFile() { return wrap(super.getParentFile());
return wrap(super.getParentFile()); }
}
@Override
@Override public File[] listFiles() {
public File[] listFiles() { return wrap(super.listFiles());
return wrap(super.listFiles()); }
}
@Override
@Override public File[] listFiles(FileFilter filter) {
public File[] listFiles(FileFilter filter) { return wrap(super.listFiles(filter));
return wrap(super.listFiles(filter)); }
}
@Override
@Override public File[] listFiles(FilenameFilter filter) {
public File[] listFiles(FilenameFilter filter) { return wrap(super.listFiles(filter));
return wrap(super.listFiles(filter)); }
} }
}

View File

@@ -1,92 +1,91 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
/** /**
* WindowsFileSystem * WindowsFileSystem
* <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/Win32FileSystem.java#2 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/Win32FileSystem.java#2 $ */
*/ final class Win32FileSystem extends FileSystem {
final class Win32FileSystem extends FileSystem { public long getFreeSpace(File pPath) {
public long getFreeSpace(File pPath) { try {
try { // Windows version
// Windows version // TODO: Test on W2K/95/98/etc... (tested on XP)
// TODO: Test on W2K/95/98/etc... (tested on XP) BufferedReader reader = exec(new String[] {"CMD.EXE", "/C", "DIR", "/-C", pPath.getAbsolutePath()});
BufferedReader reader = exec(new String[] {"CMD.EXE", "/C", "DIR", "/-C", pPath.getAbsolutePath()});
String last = null;
String last = null; String line;
String line; try {
try { while ((line = reader.readLine()) != null) {
while ((line = reader.readLine()) != null) { last = line;
last = line; }
} }
} finally {
finally { FileUtil.close(reader);
FileUtil.close(reader); }
}
if (last != null) {
if (last != null) { int end = last.lastIndexOf(" bytes free");
int end = last.lastIndexOf(" bytes free"); int start = last.lastIndexOf(' ', end - 1);
int start = last.lastIndexOf(' ', end - 1);
if (start >= 0 && end >= 0) {
if (start >= 0 && end >= 0) { try {
try { return Long.parseLong(last.substring(start + 1, end));
return Long.parseLong(last.substring(start + 1, end)); }
} catch (NumberFormatException ignore) {
catch (NumberFormatException ignore) { // Ignore
// Ignore }
} }
} }
} }
} catch (IOException ignore) {
catch (IOException ignore) { // Ignore
// Ignore }
}
return 0l;
return 0l; }
}
long getTotalSpace(File pPath) {
long getTotalSpace(File pPath) { // TODO: Implement, probably need some JNI stuff...
// TODO: Implement, probably need some JNI stuff... // Distribute df.exe and execute from temp!? ;-)
// Distribute df.exe and execute from temp!? ;-) return getFreeSpace(pPath);
return getFreeSpace(pPath); }
}
String getName() {
String getName() { return "Win32";
return "Win32"; }
} }
}

View File

@@ -1,475 +1,477 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import java.io.*; import java.io.*;
import java.util.Arrays; import java.util.Arrays;
/** /**
* A {@code File} implementation that resolves the Windows {@code .lnk} files as symbolic links. * A {@code File} implementation that resolves the Windows {@code .lnk} files as symbolic links.
* <p/> * <p>
* This class is based on example code from * This class is based on example code from
* <a href="http://www.oreilly.com/catalog/swinghks/index.html">Swing Hacks</a>, * <a href="http://www.oreilly.com/catalog/swinghks/index.html">Swing Hacks</a>,
* By Joshua Marinacci, Chris Adamson (O'Reilly, ISBN: 0-596-00907-0), Hack 30. * By Joshua Marinacci, Chris Adamson (O'Reilly, ISBN: 0-596-00907-0), Hack 30.
* * </p>
* @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/Win32Lnk.java#2 $ * @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/Win32Lnk.java#2 $
final class Win32Lnk extends File { */
private final static byte[] LNK_MAGIC = { final class Win32Lnk extends File {
'L', 0x00, 0x00, 0x00, // Magic private final static byte[] LNK_MAGIC = {
}; 'L', 0x00, 0x00, 0x00, // Magic
private final static byte[] LNK_GUID = { };
0x01, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Shell Link GUID private final static byte[] LNK_GUID = {
(byte) 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'F' 0x01, 0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, // Shell Link GUID
}; (byte) 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 'F'
};
private final File target;
private final File target;
private static final int FLAG_ITEM_ID_LIST = 0x01;
private static final int FLAG_FILE_LOC_INFO = 0x02; private static final int FLAG_ITEM_ID_LIST = 0x01;
private static final int FLAG_DESC_STRING = 0x04; private static final int FLAG_FILE_LOC_INFO = 0x02;
private static final int FLAG_REL_PATH_STRING = 0x08; private static final int FLAG_DESC_STRING = 0x04;
private static final int FLAG_WORKING_DIRECTORY = 0x10; private static final int FLAG_REL_PATH_STRING = 0x08;
private static final int FLAG_COMMAND_LINE_ARGS = 0x20; private static final int FLAG_WORKING_DIRECTORY = 0x10;
private static final int FLAG_ICON_FILENAME = 0x40; private static final int FLAG_COMMAND_LINE_ARGS = 0x20;
private static final int FLAG_ADDITIONAL_INFO = 0x80; private static final int FLAG_ICON_FILENAME = 0x40;
private static final int FLAG_ADDITIONAL_INFO = 0x80;
private Win32Lnk(final String pPath) throws IOException {
super(pPath); private Win32Lnk(final String pPath) throws IOException {
File target = parse(this); super(pPath);
if (target == this) { File target = parse(this);
// NOTE: This is a workaround if (target == this) {
// target = this causes infinite loops in some methods // NOTE: This is a workaround
target = new File(pPath); // target = this causes infinite loops in some methods
} target = new File(pPath);
this.target = target; }
} this.target = target;
}
Win32Lnk(final File pPath) throws IOException {
this(pPath.getPath()); Win32Lnk(final File pPath) throws IOException {
} this(pPath.getPath());
}
/**
* Parses a {@code .lnk} file to find the real file. /**
* * Parses a {@code .lnk} file to find the real file.
* @param pPath the path to the {@code .lnk} file *
* @return a new file object that * @param pPath the path to the {@code .lnk} file
* @throws java.io.IOException if the {@code .lnk} cannot be parsed * @return a new file object that
*/ * @throws java.io.IOException if the {@code .lnk} cannot be parsed
static File parse(final File pPath) throws IOException { */
if (!pPath.getName().endsWith(".lnk")) { static File parse(final File pPath) throws IOException {
return pPath; if (!pPath.getName().endsWith(".lnk")) {
} return pPath;
}
File result = pPath;
File result = pPath;
LittleEndianDataInputStream in = new LittleEndianDataInputStream(new BufferedInputStream(new FileInputStream(pPath)));
try { LittleEndianDataInputStream in = new LittleEndianDataInputStream(new BufferedInputStream(new FileInputStream(pPath)));
byte[] magic = new byte[4]; try {
in.readFully(magic); byte[] magic = new byte[4];
in.readFully(magic);
byte[] guid = new byte[16];
in.readFully(guid); byte[] guid = new byte[16];
in.readFully(guid);
if (!(Arrays.equals(LNK_MAGIC, magic) && Arrays.equals(LNK_GUID, guid))) {
//System.out.println("Not a symlink"); if (!(Arrays.equals(LNK_MAGIC, magic) && Arrays.equals(LNK_GUID, guid))) {
// Not a symlink //System.out.println("Not a symlink");
return pPath; // Not a symlink
} return pPath;
}
// Get the flags
int flags = in.readInt(); // Get the flags
//System.out.println("flags: " + Integer.toBinaryString(flags & 0xff)); int flags = in.readInt();
//System.out.println("flags: " + Integer.toBinaryString(flags & 0xff));
// Get to the file settings
/*int attributes = */in.readInt(); // Get to the file settings
/*int attributes = */in.readInt();
// File attributes
// 0 Target is read only. // File attributes
// 1 Target is hidden. // 0 Target is read only.
// 2 Target is a system file. // 1 Target is hidden.
// 3 Target is a volume label. (Not possible) // 2 Target is a system file.
// 4 Target is a directory. // 3 Target is a volume label. (Not possible)
// 5 Target has been modified since last backup. (archive) // 4 Target is a directory.
// 6 Target is encrypted (NTFS EFS) // 5 Target has been modified since last backup. (archive)
// 7 Target is Normal?? // 6 Target is encrypted (NTFS EFS)
// 8 Target is temporary. // 7 Target is Normal??
// 9 Target is a sparse file. // 8 Target is temporary.
// 10 Target has reparse point data. // 9 Target is a sparse file.
// 11 Target is compressed. // 10 Target has reparse point data.
// 12 Target is offline. // 11 Target is compressed.
//System.out.println("attributes: " + Integer.toBinaryString(attributes)); // 12 Target is offline.
// NOTE: Cygwin .lnks are not directory links, can't rely on this.. :-/ //System.out.println("attributes: " + Integer.toBinaryString(attributes));
// NOTE: Cygwin .lnks are not directory links, can't rely on this.. :-/
in.skipBytes(48); // TODO: Make sense of this data...
in.skipBytes(48); // TODO: Make sense of this data...
// Skipped data:
// long time 1 (creation) // Skipped data:
// long time 2 (modification) // long time 1 (creation)
// long time 3 (last access) // long time 2 (modification)
// int file length // long time 3 (last access)
// int icon number // int file length
// int ShowVnd value // int icon number
// int hotkey // int ShowVnd value
// int, int - unknown: 0,0 // int hotkey
// int, int - unknown: 0,0
// If the shell settings are present, skip them
if ((flags & FLAG_ITEM_ID_LIST) != 0) { // If the shell settings are present, skip them
// Shell Item Id List present if ((flags & FLAG_ITEM_ID_LIST) != 0) {
//System.out.println("Shell Item Id List present"); // Shell Item Id List present
int shellLen = in.readShort(); // Short //System.out.println("Shell Item Id List present");
//System.out.println("shellLen: " + shellLen); int shellLen = in.readShort(); // Short
//System.out.println("shellLen: " + shellLen);
// TODO: Probably need to parse this data, to determine
// Cygwin folders... // TODO: Probably need to parse this data, to determine
// Cygwin folders...
/*
int read = 2; /*
int itemLen = in.readShort(); int read = 2;
while (itemLen > 0) { int itemLen = in.readShort();
System.out.println("--> ITEM: " + itemLen); while (itemLen > 0) {
System.out.println("--> ITEM: " + itemLen);
BufferedReader reader = new BufferedReader(new InputStreamReader(new SubStream(in, itemLen - 2)));
//byte[] itemBytes = new byte[itemLen - 2]; // NOTE: Lenght included BufferedReader reader = new BufferedReader(new InputStreamReader(new SubStream(in, itemLen - 2)));
//in.readFully(itemBytes); //byte[] itemBytes = new byte[itemLen - 2]; // NOTE: Lenght included
//in.readFully(itemBytes);
String item = reader.readLine();
System.out.println("item: \"" + item + "\""); String item = reader.readLine();
System.out.println("item: \"" + item + "\"");
itemLen = in.readShort();
read += itemLen; itemLen = in.readShort();
} read += itemLen;
}
System.out.println("read: " + read);
*/ System.out.println("read: " + read);
*/
in.skipBytes(shellLen);
} in.skipBytes(shellLen);
}
if ((flags & FLAG_FILE_LOC_INFO) != 0) {
// File Location Info Table present if ((flags & FLAG_FILE_LOC_INFO) != 0) {
//System.out.println("File Location Info Table present"); // File Location Info Table present
//System.out.println("File Location Info Table present");
// 0h 1 dword This is the total length of this structure and all following data
// 4h 1 dword This is a pointer to first offset after this structure. 1Ch // 0h 1 dword This is the total length of this structure and all following data
// 8h 1 dword Flags // 4h 1 dword This is a pointer to first offset after this structure. 1Ch
// Ch 1 dword Offset of local volume info // 8h 1 dword Flags
// 10h 1 dword Offset of base pathname on local system // Ch 1 dword Offset of local volume info
// 14h 1 dword Offset of network volume info // 10h 1 dword Offset of base pathname on local system
// 18h 1 dword Offset of remaining pathname // 14h 1 dword Offset of network volume info
// 18h 1 dword Offset of remaining pathname
// Flags:
// Bit Meaning // Flags:
// 0 Available on a local volume // Bit Meaning
// 1 Available on a network share // 0 Available on a local volume
// TODO: Make sure the path is on a local disk, etc.. // 1 Available on a network share
// TODO: Make sure the path is on a local disk, etc..
int tableLen = in.readInt(); // Int
//System.out.println("tableLen: " + tableLen); int tableLen = in.readInt(); // Int
//System.out.println("tableLen: " + tableLen);
in.readInt(); // Skip
in.readInt(); // Skip
int locFlags = in.readInt();
//System.out.println("locFlags: " + Integer.toBinaryString(locFlags)); int locFlags = in.readInt();
if ((locFlags & 0x01) != 0) { //System.out.println("locFlags: " + Integer.toBinaryString(locFlags));
//System.out.println("Available local"); if ((locFlags & 0x01) != 0) {
} //System.out.println("Available local");
if ((locFlags & 0x02) != 0) { }
//System.err.println("Available on network path"); if ((locFlags & 0x02) != 0) {
} //System.err.println("Available on network path");
}
// Get the local volume and local system values
in.skipBytes(4); // TODO: see above for structure // Get the local volume and local system values
in.skipBytes(4); // TODO: see above for structure
int localSysOff = in.readInt();
//System.out.println("localSysOff: " + localSysOff); int localSysOff = in.readInt();
in.skipBytes(localSysOff - 20); // Relative to start of chunk //System.out.println("localSysOff: " + localSysOff);
in.skipBytes(localSysOff - 20); // Relative to start of chunk
byte[] pathBytes = new byte[tableLen - localSysOff - 1];
in.readFully(pathBytes, 0, pathBytes.length); byte[] pathBytes = new byte[tableLen - localSysOff - 1];
String path = new String(pathBytes, 0, pathBytes.length - 1); in.readFully(pathBytes, 0, pathBytes.length);
/* String path = new String(pathBytes, 0, pathBytes.length - 1);
ByteArrayOutputStream bytes = new ByteArrayOutputStream(); /*
byte read; ByteArrayOutputStream bytes = new ByteArrayOutputStream();
// Read bytes until the null (0) character byte read;
while (true) { // Read bytes until the null (0) character
read = in.readByte(); while (true) {
if (read == 0) { read = in.readByte();
break; if (read == 0) {
} break;
bytes.write(read & 0xff); }
} bytes.write(read & 0xff);
}
String path = new String(bytes.toByteArray(), 0, bytes.size());
//*/ String path = new String(bytes.toByteArray(), 0, bytes.size());
//*/
// Recurse to end of link chain
// TODO: This may cause endless loop if cyclic chain... // Recurse to end of link chain
//System.out.println("path: \"" + path + "\""); // TODO: This may cause endless loop if cyclic chain...
try { //System.out.println("path: \"" + path + "\"");
result = parse(new File(path)); try {
} result = parse(new File(path));
catch (StackOverflowError e) { }
throw new IOException("Cannot resolve cyclic link: " + e.getMessage()); catch (StackOverflowError e) {
} throw new IOException("Cannot resolve cyclic link: " + e.getMessage());
} }
}
if ((flags & FLAG_DESC_STRING) != 0) {
// Description String present, skip it. if ((flags & FLAG_DESC_STRING) != 0) {
//System.out.println("Description String present"); // Description String present, skip it.
//System.out.println("Description String present");
// The string length is the first word which must also be skipped.
int descLen = in.readShort(); // The string length is the first word which must also be skipped.
//System.out.println("descLen: " + descLen); int descLen = in.readShort();
//System.out.println("descLen: " + descLen);
byte[] descBytes = new byte[descLen];
in.readFully(descBytes, 0, descLen); byte[] descBytes = new byte[descLen];
in.readFully(descBytes, 0, descLen);
//String desc = new String(descBytes, 0, descLen);
//System.out.println("desc: " + desc); //String desc = new String(descBytes, 0, descLen);
} //System.out.println("desc: " + desc);
}
if ((flags & FLAG_REL_PATH_STRING) != 0) {
// Relative Path String present if ((flags & FLAG_REL_PATH_STRING) != 0) {
//System.out.println("Relative Path String present"); // Relative Path String present
//System.out.println("Relative Path String present");
// The string length is the first word which must also be skipped.
int pathLen = in.readShort(); // The string length is the first word which must also be skipped.
//System.out.println("pathLen: " + pathLen); int pathLen = in.readShort();
//System.out.println("pathLen: " + pathLen);
byte[] pathBytes = new byte[pathLen];
in.readFully(pathBytes, 0, pathLen); byte[] pathBytes = new byte[pathLen];
in.readFully(pathBytes, 0, pathLen);
String path = new String(pathBytes, 0, pathLen);
String path = new String(pathBytes, 0, pathLen);
// TODO: This may cause endless loop if cyclic chain...
//System.out.println("path: \"" + path + "\""); // TODO: This may cause endless loop if cyclic chain...
if (result == pPath) { //System.out.println("path: \"" + path + "\"");
try { if (result == pPath) {
result = parse(new File(pPath.getParentFile(), path)); try {
} result = parse(new File(pPath.getParentFile(), path));
catch (StackOverflowError e) { }
throw new IOException("Cannot resolve cyclic link: " + e.getMessage()); catch (StackOverflowError e) {
} throw new IOException("Cannot resolve cyclic link: " + e.getMessage());
} }
} }
}
if ((flags & FLAG_WORKING_DIRECTORY) != 0) {
//System.out.println("Working Directory present"); if ((flags & FLAG_WORKING_DIRECTORY) != 0) {
} //System.out.println("Working Directory present");
if ((flags & FLAG_COMMAND_LINE_ARGS) != 0) { }
//System.out.println("Command Line Arguments present"); if ((flags & FLAG_COMMAND_LINE_ARGS) != 0) {
// NOTE: This means this .lnk is not a folder, don't follow //System.out.println("Command Line Arguments present");
result = pPath; // NOTE: This means this .lnk is not a folder, don't follow
} result = pPath;
if ((flags & FLAG_ICON_FILENAME) != 0) { }
//System.out.println("Icon Filename present"); if ((flags & FLAG_ICON_FILENAME) != 0) {
} //System.out.println("Icon Filename present");
if ((flags & FLAG_ADDITIONAL_INFO) != 0) { }
//System.out.println("Additional Info present"); if ((flags & FLAG_ADDITIONAL_INFO) != 0) {
} //System.out.println("Additional Info present");
} }
finally { }
in.close(); finally {
} in.close();
}
return result;
} return result;
}
/*
private static String getNullDelimitedString(byte[] bytes, int off) { /*
int len = 0; private static String getNullDelimitedString(byte[] bytes, int off) {
// Count bytes until the null (0) character int len = 0;
while (true) { // Count bytes until the null (0) character
if (bytes[off + len] == 0) { while (true) {
break; if (bytes[off + len] == 0) {
} break;
len++; }
} len++;
}
System.err.println("--> " + len);
System.err.println("--> " + len);
return new String(bytes, off, len);
} return new String(bytes, off, len);
*/ }
*/
/**
* Converts two bytes into a short. /**
* <p/> * Converts two bytes into a short.
* NOTE: this is little endian because it's for an * <p>
* Intel only OS * NOTE: this is little endian because it's for an
* * Intel only OS
* @ param bytes * </p>
* @ param off *
* @return the bytes as a short. * @ param bytes
*/ * @ param off
/* * @return the bytes as a short.
private static int bytes2short(byte[] bytes, int off) { */
return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff); /*
} private static int bytes2short(byte[] bytes, int off) {
*/ return ((bytes[off + 1] & 0xff) << 8) | (bytes[off] & 0xff);
}
public File getTarget() { */
return target;
} public File getTarget() {
return target;
// java.io.File overrides below }
@Override // java.io.File overrides below
public boolean isDirectory() {
return target.isDirectory(); @Override
} public boolean isDirectory() {
return target.isDirectory();
@Override }
public boolean canRead() {
return target.canRead(); @Override
} public boolean canRead() {
return target.canRead();
@Override }
public boolean canWrite() {
return target.canWrite(); @Override
} public boolean canWrite() {
return target.canWrite();
// NOTE: equals is implemented using compareto == 0 }
/*
public int compareTo(File pathname) { // NOTE: equals is implemented using compareto == 0
// TODO: Verify this /*
// Probably not a good idea, as it IS NOT THE SAME file public int compareTo(File pathname) {
// It's probably better to not override // TODO: Verify this
return target.compareTo(pathname); // Probably not a good idea, as it IS NOT THE SAME file
} // It's probably better to not override
*/ return target.compareTo(pathname);
}
// Should probably never allow creating a new .lnk */
// public boolean createNewFile() throws IOException
// Should probably never allow creating a new .lnk
// Deletes only the .lnk // public boolean createNewFile() throws IOException
// public boolean delete() {
//public void deleteOnExit() { // Deletes only the .lnk
// public boolean delete() {
@Override //public void deleteOnExit() {
public boolean exists() {
return target.exists(); @Override
} public boolean exists() {
return target.exists();
// A .lnk may be absolute }
//public File getAbsoluteFile() {
//public String getAbsolutePath() { // A .lnk may be absolute
//public File getAbsoluteFile() {
// Theses should be resolved according to the API (for Unix). //public String getAbsolutePath() {
@Override
public File getCanonicalFile() throws IOException { // Theses should be resolved according to the API (for Unix).
return target.getCanonicalFile(); @Override
} public File getCanonicalFile() throws IOException {
return target.getCanonicalFile();
@Override }
public String getCanonicalPath() throws IOException {
return target.getCanonicalPath(); @Override
} public String getCanonicalPath() throws IOException {
return target.getCanonicalPath();
//public String getName() { }
// I guess the parent should be the parent of the .lnk, not the target //public String getName() {
//public String getParent() {
//public File getParentFile() { // I guess the parent should be the parent of the .lnk, not the target
//public String getParent() {
// public boolean isAbsolute() { //public File getParentFile() {
@Override
public boolean isFile() { // public boolean isAbsolute() {
return target.isFile(); @Override
} public boolean isFile() {
return target.isFile();
@Override }
public boolean isHidden() {
return target.isHidden(); @Override
} public boolean isHidden() {
return target.isHidden();
@Override }
public long lastModified() {
return target.lastModified(); @Override
} public long lastModified() {
return target.lastModified();
@Override }
public long length() {
return target.length(); @Override
} public long length() {
return target.length();
@Override }
public String[] list() {
return target.list(); @Override
} public String[] list() {
return target.list();
@Override }
public String[] list(final FilenameFilter filter) {
return target.list(filter); @Override
} public String[] list(final FilenameFilter filter) {
return target.list(filter);
@Override }
public File[] listFiles() {
return Win32File.wrap(target.listFiles()); @Override
} public File[] listFiles() {
return Win32File.wrap(target.listFiles());
@Override }
public File[] listFiles(final FileFilter filter) {
return Win32File.wrap(target.listFiles(filter)); @Override
} public File[] listFiles(final FileFilter filter) {
return Win32File.wrap(target.listFiles(filter));
@Override }
public File[] listFiles(final FilenameFilter filter) {
return Win32File.wrap(target.listFiles(filter)); @Override
} public File[] listFiles(final FilenameFilter filter) {
return Win32File.wrap(target.listFiles(filter));
// Makes no sense, does it? }
//public boolean mkdir() {
//public boolean mkdirs() { // Makes no sense, does it?
//public boolean mkdir() {
// Only rename the lnk //public boolean mkdirs() {
//public boolean renameTo(File dest) {
// Only rename the lnk
@Override //public boolean renameTo(File dest) {
public boolean setLastModified(long time) {
return target.setLastModified(time); @Override
} public boolean setLastModified(long time) {
return target.setLastModified(time);
@Override }
public boolean setReadOnly() {
return target.setReadOnly(); @Override
} public boolean setReadOnly() {
return target.setReadOnly();
@Override }
public String toString() {
if (target.equals(this)) { @Override
return super.toString(); public String toString() {
} if (target.equals(this)) {
return super.toString() + " -> " + target.toString(); return super.toString();
} }
} return super.toString() + " -> " + target.toString();
}
}

View File

@@ -1,239 +1,240 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io; package com.twelvemonkeys.io;
import com.twelvemonkeys.lang.DateUtil; import com.twelvemonkeys.lang.DateUtil;
import java.io.*; import java.io.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.CharBuffer; import java.nio.CharBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
/** /**
* Wraps a {@code Writer} in an {@code OutputStream}. * Wraps a {@code Writer} in an {@code OutputStream}.
* <p/> * <p>
* <em>Instances of this class are not thread-safe.</em> * <em>Instances of this class are not thread-safe.</em>
* <p/> * </p>
* <em>NOTE: This class is probably not the right way of solving your problem, * <p>
* however it might prove useful in JSPs etc. * <em>NOTE: This class is probably not the right way of solving your problem,
* If possible, it's always better to use the {@code Writer}'s underlying * however it might prove useful in JSPs etc.
* {@code OutputStream}, or wrap it's native backing. * If possible, it's always better to use the {@code Writer}'s underlying
* </em> * {@code OutputStream}, or wrap it's native backing.
* <p/> * </em>
* * </p>
* @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/WriterOutputStream.java#2 $ * @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/WriterOutputStream.java#2 $
public class WriterOutputStream extends OutputStream { */
protected Writer writer; public class WriterOutputStream extends OutputStream {
final protected Decoder decoder; protected Writer writer;
final ByteArrayOutputStream bufferStream = new FastByteArrayOutputStream(1024); final protected Decoder decoder;
final ByteArrayOutputStream bufferStream = new FastByteArrayOutputStream(1024);
private volatile boolean isFlushing = 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();
private static boolean isNIOAvailable() {
try { private static boolean isNIOAvailable() {
Class.forName("java.nio.charset.Charset"); try {
return true; Class.forName("java.nio.charset.Charset");
} return true;
catch (Throwable t) { }
// Ignore catch (Throwable t) {
} // Ignore
}
return false;
} return false;
}
public WriterOutputStream(final Writer pWriter, final String pCharset) {
writer = pWriter; public WriterOutputStream(final Writer pWriter, final String pCharset) {
decoder = getDecoder(pCharset); writer = pWriter;
} decoder = getDecoder(pCharset);
}
public WriterOutputStream(final Writer pWriter) {
this(pWriter, null); public WriterOutputStream(final Writer pWriter) {
} this(pWriter, null);
}
private static Decoder getDecoder(final String pCharset) {
// NOTE: The CharsetDecoder is typically 10-20% faster than private static Decoder getDecoder(final String pCharset) {
// StringDecoder according to my tests // NOTE: The CharsetDecoder is typically 10-20% faster than
// StringEncoder is horribly slow on 1.2 systems, but there's no // StringDecoder according to my tests
// alternative... // StringEncoder is horribly slow on 1.2 systems, but there's no
if (NIO_AVAILABLE) { // alternative...
return new CharsetDecoder(pCharset); if (NIO_AVAILABLE) {
} return new CharsetDecoder(pCharset);
}
return new StringDecoder(pCharset);
} return new StringDecoder(pCharset);
}
@Override
public void close() throws IOException { @Override
flush(); public void close() throws IOException {
writer.close(); flush();
writer = null; writer.close();
} writer = null;
}
@Override
public void flush() throws IOException { @Override
flushBuffer(); public void flush() throws IOException {
writer.flush(); flushBuffer();
} writer.flush();
}
@Override
public final void write(byte[] pBytes) throws IOException { @Override
if (pBytes == null) { public final void write(byte[] pBytes) throws IOException {
throw new NullPointerException("bytes == null"); if (pBytes == null) {
} throw new NullPointerException("bytes == null");
write(pBytes, 0, pBytes.length); }
} write(pBytes, 0, pBytes.length);
}
@Override
public final void write(byte[] pBytes, int pOffset, int pLength) throws IOException { @Override
flushBuffer(); public final void write(byte[] pBytes, int pOffset, int pLength) throws IOException {
decoder.decodeTo(writer, pBytes, pOffset, pLength); flushBuffer();
} decoder.decodeTo(writer, pBytes, pOffset, pLength);
}
@Override
public final void write(int pByte) { @Override
// TODO: Is it possible to know if this is a good place in the stream to public final void write(int pByte) {
// flush? It might be in the middle of a multi-byte encoded character.. // TODO: Is it possible to know if this is a good place in the stream to
bufferStream.write(pByte); // flush? It might be in the middle of a multi-byte encoded character..
} bufferStream.write(pByte);
}
private void flushBuffer() throws IOException {
if (!isFlushing && bufferStream.size() > 0) { private void flushBuffer() throws IOException {
isFlushing = true; if (!isFlushing && bufferStream.size() > 0) {
bufferStream.writeTo(this); // NOTE: Avoids cloning buffer array isFlushing = true;
bufferStream.reset(); bufferStream.writeTo(this); // NOTE: Avoids cloning buffer array
isFlushing = false; bufferStream.reset();
} isFlushing = false;
} }
}
///////////////////////////////////////////////////////////////////////////
public static void main(String[] pArgs) throws IOException { ///////////////////////////////////////////////////////////////////////////
int iterations = 1000000; public static void main(String[] pArgs) throws IOException {
int iterations = 1000000;
byte[] bytes = "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> klashf lkash ljah lhaaklhghdfgu ksd".getBytes("UTF-8");
byte[] bytes = "<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> klashf lkash ljah lhaaklhghdfgu ksd".getBytes("UTF-8");
Decoder d;
long start; Decoder d;
long time; long start;
Writer sink = new PrintWriter(new NullOutputStream()); long time;
StringWriter writer; Writer sink = new PrintWriter(new NullOutputStream());
String str; StringWriter writer;
String str;
d = new StringDecoder("UTF-8");
for (int i = 0; i < 10000; i++) { d = new StringDecoder("UTF-8");
d.decodeTo(sink, bytes, 0, bytes.length); for (int i = 0; i < 10000; i++) {
} d.decodeTo(sink, bytes, 0, bytes.length);
start = System.currentTimeMillis(); }
for (int i = 0; i < iterations; i++) { start = System.currentTimeMillis();
d.decodeTo(sink, bytes, 0, bytes.length); for (int i = 0; i < iterations; i++) {
} d.decodeTo(sink, bytes, 0, bytes.length);
time = DateUtil.delta(start); }
System.out.println("StringDecoder"); time = DateUtil.delta(start);
System.out.println("time: " + time); System.out.println("StringDecoder");
System.out.println("time: " + time);
writer = new StringWriter();
d.decodeTo(writer, bytes, 0, bytes.length); writer = new StringWriter();
str = writer.toString(); d.decodeTo(writer, bytes, 0, bytes.length);
System.out.println("str: \"" + str + "\""); str = writer.toString();
System.out.println("chars.length: " + str.length()); System.out.println("str: \"" + str + "\"");
System.out.println(); System.out.println("chars.length: " + str.length());
System.out.println();
if (NIO_AVAILABLE) {
d = new CharsetDecoder("UTF-8"); if (NIO_AVAILABLE) {
for (int i = 0; i < 10000; i++) { d = new CharsetDecoder("UTF-8");
d.decodeTo(sink, bytes, 0, bytes.length); for (int i = 0; i < 10000; i++) {
} d.decodeTo(sink, bytes, 0, bytes.length);
start = System.currentTimeMillis(); }
for (int i = 0; i < iterations; i++) { start = System.currentTimeMillis();
d.decodeTo(sink, bytes, 0, bytes.length); for (int i = 0; i < iterations; i++) {
} d.decodeTo(sink, bytes, 0, bytes.length);
time = DateUtil.delta(start); }
System.out.println("CharsetDecoder"); time = DateUtil.delta(start);
System.out.println("time: " + time); System.out.println("CharsetDecoder");
writer = new StringWriter(); System.out.println("time: " + time);
d.decodeTo(writer, bytes, 0, bytes.length); writer = new StringWriter();
str = writer.toString(); d.decodeTo(writer, bytes, 0, bytes.length);
System.out.println("str: \"" + str + "\""); str = writer.toString();
System.out.println("chars.length: " + str.length()); System.out.println("str: \"" + str + "\"");
System.out.println(); System.out.println("chars.length: " + str.length());
} System.out.println();
}
OutputStream os = new WriterOutputStream(new PrintWriter(System.out), "UTF-8");
os.write(bytes); OutputStream os = new WriterOutputStream(new PrintWriter(System.out), "UTF-8");
os.flush(); os.write(bytes);
System.out.println(); os.flush();
System.out.println();
for (byte b : bytes) {
os.write(b & 0xff); for (byte b : bytes) {
} os.write(b & 0xff);
os.flush(); }
} os.flush();
}
///////////////////////////////////////////////////////////////////////////
private static interface Decoder { ///////////////////////////////////////////////////////////////////////////
void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException; private static interface Decoder {
} void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException;
}
private static final class CharsetDecoder implements Decoder {
final Charset mCharset; private static final class CharsetDecoder implements Decoder {
final Charset mCharset;
CharsetDecoder(String pCharset) {
// Handle null-case, to get default charset CharsetDecoder(String pCharset) {
String charset = pCharset != null ? pCharset : // Handle null-case, to get default charset
System.getProperty("file.encoding", "ISO-8859-1"); String charset = pCharset != null ? pCharset :
mCharset = Charset.forName(charset); System.getProperty("file.encoding", "ISO-8859-1");
} mCharset = Charset.forName(charset);
}
public void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException {
CharBuffer cb = mCharset.decode(ByteBuffer.wrap(pBytes, pOffset, pLength)); public void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException {
pWriter.write(cb.array(), 0, cb.length()); CharBuffer cb = mCharset.decode(ByteBuffer.wrap(pBytes, pOffset, pLength));
} pWriter.write(cb.array(), 0, cb.length());
} }
}
private static final class StringDecoder implements Decoder {
final String mCharset; private static final class StringDecoder implements Decoder {
final String mCharset;
StringDecoder(String pCharset) {
mCharset = pCharset; StringDecoder(String pCharset) {
} mCharset = pCharset;
}
public void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException {
String str = mCharset == null ? public void decodeTo(Writer pWriter, byte[] pBytes, int pOffset, int pLength) throws IOException {
new String(pBytes, pOffset, pLength) : String str = mCharset == null ?
new String(pBytes, pOffset, pLength, mCharset); new String(pBytes, pOffset, pLength) :
new String(pBytes, pOffset, pLength, mCharset);
pWriter.write(str);
} pWriter.write(str);
} }
}
} }

View File

@@ -1,188 +1,188 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io.enc; package com.twelvemonkeys.io.enc;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/** /**
* {@code Decoder} implementation for standard base64 encoding. * {@code Decoder} implementation for standard base64 encoding.
* <p/> *
* @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421</a> * @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421</a>
* @see <a href="http://tools.ietf.org/html/rfc2045"RFC 2045</a> * @see <a href="http://tools.ietf.org/html/rfc2045">RFC 2045</a>
* *
* @see Base64Encoder * @see Base64Encoder
* *
* @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/enc/Base64Decoder.java#2 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/Base64Decoder.java#2 $
*/ */
public final class Base64Decoder implements Decoder { public final class Base64Decoder implements Decoder {
/** /**
* This array maps the characters to their 6 bit values * This array maps the characters to their 6 bit values
*/ */
final static byte[] PEM_ARRAY = { final static byte[] PEM_ARRAY = {
//0 1 2 3 4 5 6 7 //0 1 2 3 4 5 6 7
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 1 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 1
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 2 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 2
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 3 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 3
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 4 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 4
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 5 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 5
'w', 'x', 'y', 'z', '0', '1', '2', '3', // 6 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 6
'4', '5', '6', '7', '8', '9', '+', '/' // 7 '4', '5', '6', '7', '8', '9', '+', '/' // 7
}; };
final static byte[] PEM_CONVERT_ARRAY; final static byte[] PEM_CONVERT_ARRAY;
private byte[] decodeBuffer = new byte[4]; private byte[] decodeBuffer = new byte[4];
static { static {
PEM_CONVERT_ARRAY = new byte[256]; PEM_CONVERT_ARRAY = new byte[256];
for (int i = 0; i < 255; i++) { for (int i = 0; i < 255; i++) {
PEM_CONVERT_ARRAY[i] = -1; PEM_CONVERT_ARRAY[i] = -1;
} }
for (int i = 0; i < PEM_ARRAY.length; i++) { for (int i = 0; i < PEM_ARRAY.length; i++) {
PEM_CONVERT_ARRAY[PEM_ARRAY[i]] = (byte) i; PEM_CONVERT_ARRAY[PEM_ARRAY[i]] = (byte) i;
} }
} }
protected static int readFully(final InputStream pStream, final byte pBytes[], final int pOffset, final int pLength) protected static int readFully(final InputStream pStream, final byte pBytes[], final int pOffset, final int pLength)
throws IOException throws IOException
{ {
for (int i = 0; i < pLength; i++) { for (int i = 0; i < pLength; i++) {
int read = pStream.read(); int read = pStream.read();
if (read == -1) { if (read == -1) {
return i != 0 ? i : -1; return i != 0 ? i : -1;
} }
pBytes[i + pOffset] = (byte) read; pBytes[i + pOffset] = (byte) read;
} }
return pLength; return pLength;
} }
protected boolean decodeAtom(final InputStream pInput, final ByteBuffer pOutput, final int pLength) protected boolean decodeAtom(final InputStream pInput, final ByteBuffer pOutput, final int pLength)
throws IOException { throws IOException {
byte byte0 = -1; byte byte0 = -1;
byte byte1 = -1; byte byte1 = -1;
byte byte2 = -1; byte byte2 = -1;
byte byte3 = -1; byte byte3 = -1;
if (pLength < 2) { if (pLength < 2) {
throw new IOException("BASE64Decoder: Not enough bytes for an atom."); throw new IOException("BASE64Decoder: Not enough bytes for an atom.");
} }
int read; int read;
// Skip line feeds // Skip line feeds
do { do {
read = pInput.read(); read = pInput.read();
if (read == -1) { if (read == -1) {
return false; return false;
} }
} while (read == 10 || read == 13); } while (read == 10 || read == 13);
decodeBuffer[0] = (byte) read; decodeBuffer[0] = (byte) read;
read = readFully(pInput, decodeBuffer, 1, pLength - 1); read = readFully(pInput, decodeBuffer, 1, pLength - 1);
if (read == -1) { if (read == -1) {
return false; return false;
} }
int length = pLength; int length = pLength;
if (length > 3 && decodeBuffer[3] == 61) { if (length > 3 && decodeBuffer[3] == 61) {
length = 3; length = 3;
} }
if (length > 2 && decodeBuffer[2] == 61) { if (length > 2 && decodeBuffer[2] == 61) {
length = 2; length = 2;
} }
switch (length) { switch (length) {
case 4: case 4:
byte3 = PEM_CONVERT_ARRAY[decodeBuffer[3] & 255]; byte3 = PEM_CONVERT_ARRAY[decodeBuffer[3] & 255];
// fall through // fall through
case 3: case 3:
byte2 = PEM_CONVERT_ARRAY[decodeBuffer[2] & 255]; byte2 = PEM_CONVERT_ARRAY[decodeBuffer[2] & 255];
// fall through // fall through
case 2: case 2:
byte1 = PEM_CONVERT_ARRAY[decodeBuffer[1] & 255]; byte1 = PEM_CONVERT_ARRAY[decodeBuffer[1] & 255];
byte0 = PEM_CONVERT_ARRAY[decodeBuffer[0] & 255]; byte0 = PEM_CONVERT_ARRAY[decodeBuffer[0] & 255];
// fall through // fall through
default: default:
switch (length) { switch (length) {
case 2: case 2:
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3)); pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
break; break;
case 3: case 3:
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3)); pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15)); pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
break; break;
case 4: case 4:
pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3)); pOutput.put((byte) (byte0 << 2 & 252 | byte1 >>> 4 & 3));
pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15)); pOutput.put((byte) (byte1 << 4 & 240 | byte2 >>> 2 & 15));
pOutput.put((byte) (byte2 << 6 & 192 | byte3 & 63)); pOutput.put((byte) (byte2 << 6 & 192 | byte3 & 63));
break; break;
} }
break; break;
} }
return true; return true;
} }
public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException { public int decode(final InputStream stream, final ByteBuffer buffer) throws IOException {
do { do {
int k = 72; int k = 72;
int i; int i;
for (i = 0; i + 4 < k; i += 4) { for (i = 0; i + 4 < k; i += 4) {
if(!decodeAtom(stream, buffer, 4)) { if(!decodeAtom(stream, buffer, 4)) {
break; break;
} }
} }
if (!decodeAtom(stream, buffer, k - i)) { if (!decodeAtom(stream, buffer, k - i)) {
break; break;
} }
} }
while (buffer.remaining() > 54); // 72 char lines should produce no more than 54 bytes while (buffer.remaining() > 54); // 72 char lines should produce no more than 54 bytes
return buffer.position(); return buffer.position();
} }
} }

View File

@@ -1,106 +1,106 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io.enc; package com.twelvemonkeys.io.enc;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/** /**
* {@code Encoder} implementation for standard base64 encoding. * {@code Encoder} implementation for standard base64 encoding.
* <p/> *
* @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421</a> * @see <a href="http://tools.ietf.org/html/rfc1421">RFC 1421</a>
* @see <a href="http://tools.ietf.org/html/rfc2045"RFC 2045</a> * @see <a href="http://tools.ietf.org/html/rfc2045">RFC 2045</a>
* *
* @see Base64Decoder * @see Base64Decoder
* *
* @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/enc/Base64Encoder.java#2 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/Base64Encoder.java#2 $
*/ */
public class Base64Encoder implements Encoder { public class Base64Encoder implements Encoder {
public void encode(final OutputStream stream, final ByteBuffer buffer) public void encode(final OutputStream stream, final ByteBuffer buffer)
throws IOException throws IOException
{ {
// TODO: Implement // TODO: Implement
// NOTE: This is impossible, given the current spec, as we need to either: // NOTE: This is impossible, given the current spec, as we need to either:
// - buffer all data in the EncoderStream // - buffer all data in the EncoderStream
// - or have flush/end method(s) in the Encoder // - or have flush/end method(s) in the Encoder
// to ensure proper end of stream handling // to ensure proper end of stream handling
int length; int length;
// TODO: Temp impl, will only work for single writes // TODO: Temp impl, will only work for single writes
while (buffer.hasRemaining()) { while (buffer.hasRemaining()) {
byte a, b, c; byte a, b, c;
// if ((buffer.remaining()) > 2) { // if ((buffer.remaining()) > 2) {
// length = 3; // length = 3;
// } // }
// else { // else {
// length = buffer.remaining(); // length = buffer.remaining();
// } // }
length = Math.min(3, buffer.remaining()); length = Math.min(3, buffer.remaining());
switch (length) { switch (length) {
case 1: case 1:
a = buffer.get(); a = buffer.get();
b = 0; b = 0;
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]); stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]); stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
stream.write('='); stream.write('=');
stream.write('='); stream.write('=');
break; break;
case 2: case 2:
a = buffer.get(); a = buffer.get();
b = buffer.get(); b = buffer.get();
c = 0; c = 0;
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]); stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]); stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
stream.write(Base64Decoder.PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]); stream.write(Base64Decoder.PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
stream.write('='); stream.write('=');
break; break;
default: default:
a = buffer.get(); a = buffer.get();
b = buffer.get(); b = buffer.get();
c = buffer.get(); c = buffer.get();
stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]); stream.write(Base64Decoder.PEM_ARRAY[(a >>> 2) & 0x3F]);
stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]); stream.write(Base64Decoder.PEM_ARRAY[((a << 4) & 0x30) + ((b >>> 4) & 0xf)]);
stream.write(Base64Decoder.PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]); stream.write(Base64Decoder.PEM_ARRAY[((b << 2) & 0x3c) + ((c >>> 6) & 0x3)]);
stream.write(Base64Decoder.PEM_ARRAY[c & 0x3F]); stream.write(Base64Decoder.PEM_ARRAY[c & 0x3F]);
break; break;
} }
} }
} }
} }

View File

@@ -1,56 +1,55 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io.enc; package com.twelvemonkeys.io.enc;
import java.io.IOException; import java.io.IOException;
/** /**
* Thrown by {@code Decoder}s when encoded data can not be decoded. * Thrown by {@code Decoder}s when encoded data can not be decoded.
* <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/enc/DecodeException.java#2 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DecodeException.java#2 $ */
*/ public class DecodeException extends IOException {
public class DecodeException extends IOException {
public DecodeException(final String pMessage) {
public DecodeException(final String pMessage) { super(pMessage);
super(pMessage); }
}
public DecodeException(final String pMessage, final Throwable pCause) {
public DecodeException(final String pMessage, final Throwable pCause) { super(pMessage);
super(pMessage); initCause(pCause);
initCause(pCause); }
}
public DecodeException(final Throwable pCause) {
public DecodeException(final Throwable pCause) { this(pCause.getMessage(), pCause);
this(pCause.getMessage(), pCause); }
}
} }

View File

@@ -1,68 +1,69 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io.enc; package com.twelvemonkeys.io.enc;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/** /**
* Interface for decoders. * Interface for decoders.
* A {@code Decoder} may be used with a {@code DecoderStream}, to perform * A {@code Decoder} may be used with a {@code DecoderStream}, to perform
* on-the-fly decoding from an {@code InputStream}. * on-the-fly decoding from an {@code InputStream}.
* <p/> * <p>
* Important note: Decoder implementations are typically not synchronized. * Important note: Decoder implementations are typically not synchronized.
* <p/> * </p>
* @see Encoder *
* @see DecoderStream * @see Encoder
* * @see DecoderStream
* @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/enc/Decoder.java#2 $ * @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/enc/Decoder.java#2 $
public interface Decoder { */
public interface Decoder {
/**
* Decodes up to {@code buffer.length} bytes from the given input stream, /**
* into the given buffer. * Decodes up to {@code buffer.length} bytes from the given input stream,
* * into the given buffer.
* @param stream the input stream to decode data from *
* @param buffer buffer to store the read data * @param stream the input stream to decode data from
* * @param buffer buffer to store the read data
* @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. * @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.
* @throws DecodeException if encoded data is corrupt. *
* @throws IOException if an I/O error occurs. * @throws DecodeException if encoded data is corrupt.
* @throws java.io.EOFException if a premature end-of-file is encountered. * @throws IOException if an I/O error occurs.
* @throws java.lang.NullPointerException if either argument is {@code null}. * @throws java.io.EOFException if a premature end-of-file is encountered.
*/ * @throws java.lang.NullPointerException if either argument is {@code null}.
int decode(InputStream stream, ByteBuffer buffer) throws IOException; */
} int decode(InputStream stream, ByteBuffer buffer) throws IOException;
}

View File

@@ -1,200 +1,199 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io.enc; package com.twelvemonkeys.io.enc;
import java.io.FilterInputStream; import java.io.FilterInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/** /**
* An {@code InputStream} that provides on-the-fly decoding from an underlying * An {@code InputStream} that provides on-the-fly decoding from an underlying stream.
* stream. *
* <p/> * @see EncoderStream
* @see EncoderStream * @see Decoder
* @see Decoder *
* * @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/enc/DecoderStream.java#2 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/DecoderStream.java#2 $ */
*/ public final class DecoderStream extends FilterInputStream {
public final class DecoderStream extends FilterInputStream { protected final ByteBuffer buffer;
protected final ByteBuffer buffer; protected final Decoder decoder;
protected final Decoder decoder;
/**
/** * Creates a new decoder stream and chains it to the
* Creates a new decoder stream and chains it to the * input stream specified by the {@code pStream} argument.
* input stream specified by the {@code pStream} argument. * The stream will use a default decode buffer size.
* The stream will use a default decode buffer size. *
* * @param pStream the underlying input stream.
* @param pStream the underlying input stream. * @param pDecoder the decoder that will be used to decode the underlying stream
* @param pDecoder the decoder that will be used to decode the underlying stream *
* * @see java.io.FilterInputStream#in
* @see java.io.FilterInputStream#in */
*/ public DecoderStream(final InputStream pStream, final Decoder pDecoder) {
public DecoderStream(final InputStream pStream, final Decoder pDecoder) { // TODO: Let the decoder decide preferred buffer size
// TODO: Let the decoder decide preferred buffer size this(pStream, pDecoder, 1024);
this(pStream, pDecoder, 1024); }
}
/**
/** * Creates a new decoder stream and chains it to the
* Creates a new decoder stream and chains it to the * input stream specified by the {@code pStream} argument.
* input stream specified by the {@code pStream} argument. *
* * @param pStream the underlying input stream.
* @param pStream the underlying input stream. * @param pDecoder the decoder that will be used to decode the underlying stream
* @param pDecoder the decoder that will be used to decode the underlying stream * @param pBufferSize the size of the decode buffer
* @param pBufferSize the size of the decode buffer *
* * @see java.io.FilterInputStream#in
* @see java.io.FilterInputStream#in */
*/ 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);
decoder = pDecoder;
decoder = pDecoder; buffer = ByteBuffer.allocate(pBufferSize);
buffer = ByteBuffer.allocate(pBufferSize); buffer.flip();
buffer.flip(); }
}
public int available() throws IOException {
public int available() throws IOException { return buffer.remaining();
return buffer.remaining(); }
}
public int read() throws IOException {
public int read() throws IOException { if (!buffer.hasRemaining()) {
if (!buffer.hasRemaining()) { if (fill() < 0) {
if (fill() < 0) { return -1;
return -1; }
} }
}
return buffer.get() & 0xff;
return buffer.get() & 0xff; }
}
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 (pBytes == null) {
if (pBytes == null) { throw new NullPointerException();
throw new NullPointerException(); }
} else if ((pOffset < 0) || (pOffset > pBytes.length) || (pLength < 0) ||
else 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("bytes.length=" + pBytes.length + " offset=" + pOffset + " length=" + pLength);
throw new IndexOutOfBoundsException("bytes.length=" + pBytes.length + " offset=" + pOffset + " length=" + pLength); }
} else if (pLength == 0) {
else if (pLength == 0) { return 0;
return 0; }
}
// End of file?
// End of file? if (!buffer.hasRemaining()) {
if (!buffer.hasRemaining()) { if (fill() < 0) {
if (fill() < 0) { return -1;
return -1; }
} }
}
// Read until we have read pLength bytes, or have reached EOF
// Read until we have read pLength bytes, or have reached EOF int count = 0;
int count = 0; int off = pOffset;
int off = pOffset;
while (pLength > count) {
while (pLength > count) { if (!buffer.hasRemaining()) {
if (!buffer.hasRemaining()) { if (fill() < 0) {
if (fill() < 0) { break;
break; }
} }
}
// Copy as many bytes as possible
// Copy as many bytes as possible int dstLen = Math.min(pLength - count, buffer.remaining());
int dstLen = Math.min(pLength - count, buffer.remaining()); buffer.get(pBytes, off, dstLen);
buffer.get(pBytes, off, dstLen);
// Update offset (rest)
// Update offset (rest) off += dstLen;
off += dstLen;
// Increase count
// Increase count count += dstLen;
count += dstLen; }
}
return count;
return count; }
}
public long skip(final long pLength) throws IOException {
public long skip(final long pLength) throws IOException { // End of file?
// End of file? if (!buffer.hasRemaining()) {
if (!buffer.hasRemaining()) { if (fill() < 0) {
if (fill() < 0) { return 0; // Yes, 0, not -1
return 0; // Yes, 0, not -1 }
} }
}
// Skip until we have skipped pLength bytes, or have reached EOF
// Skip until we have skipped pLength bytes, or have reached EOF long total = 0;
long total = 0;
while (total < pLength) {
while (total < pLength) { if (!buffer.hasRemaining()) {
if (!buffer.hasRemaining()) { if (fill() < 0) {
if (fill() < 0) { break;
break; }
} }
}
// NOTE: Skipped can never be more than avail, which is an int, so the cast is safe
// NOTE: Skipped can never be more than avail, which is an int, so the cast is safe int skipped = (int) Math.min(pLength - total, buffer.remaining());
int skipped = (int) Math.min(pLength - total, buffer.remaining()); buffer.position(buffer.position() + skipped);
buffer.position(buffer.position() + skipped); total += skipped;
total += skipped; }
}
return total;
return total; }
}
/**
/** * Fills the buffer, by decoding data from the underlying input stream.
* Fills the buffer, by decoding data from the underlying input stream. *
* * @return the number of bytes decoded, or {@code -1} if the end of the
* @return the number of bytes decoded, or {@code -1} if the end of the * file is reached
* file is reached *
* * @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 { buffer.clear();
buffer.clear(); int read = decoder.decode(in, buffer);
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 > buffer.capacity()) {
if (read > buffer.capacity()) { 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)", buffer.capacity(), read, decoder.getClass().getName()
buffer.capacity(), read, decoder.getClass().getName() )
) );
); }
}
buffer.flip();
buffer.flip();
if (read == 0) {
if (read == 0) { return -1;
return -1; }
}
return read;
return read; }
} }
}

View File

@@ -1,65 +1,66 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io.enc; package com.twelvemonkeys.io.enc;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/** /**
* Interface for encoders. * Interface for encoders.
* An {@code Encoder} may be used with an {@code EncoderStream}, to perform * An {@code Encoder} may be used with an {@code EncoderStream}, to perform
* on-the-fly encoding to an {@code OutputStream}. * on-the-fly encoding to an {@code OutputStream}.
* <p/> * <p>
* Important note: Encoder implementations are typically not synchronized. * Important note: Encoder implementations are typically not synchronized.
* * </p>
* @see Decoder *
* @see EncoderStream * @see Decoder
* * @see EncoderStream
* @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/enc/Encoder.java#2 $ * @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/enc/Encoder.java#2 $
public interface Encoder { */
public interface Encoder {
/**
* Encodes up to {@code buffer.remaining()} bytes into the given input stream, /**
* from the given buffer. * Encodes up to {@code buffer.remaining()} bytes into the given input stream,
* * from the given buffer.
* @param stream the output stream to encode data to *
* @param buffer buffer to read data from * @param stream the output stream to encode data to
* * @param buffer buffer to read data from
* @throws java.io.IOException if an I/O error occurs *
*/ * @throws java.io.IOException if an I/O error occurs
void encode(OutputStream stream, ByteBuffer buffer) throws IOException; */
void encode(OutputStream stream, ByteBuffer buffer) throws IOException;
//TODO: int requiredBufferSize(): -1 == any, otherwise, use this buffer size
// void flush()? //TODO: int requiredBufferSize(): -1 == any, otherwise, use this buffer size
} // void flush()?
}

View File

@@ -1,137 +1,136 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io.enc; package com.twelvemonkeys.io.enc;
import java.io.FilterOutputStream; import java.io.FilterOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/** /**
* An {@code OutputStream} that provides on-the-fly encoding to an underlying * An {@code OutputStream} that provides on-the-fly encoding to an underlying stream.
* stream. *
* <p/> * @see DecoderStream
* @see DecoderStream * @see Encoder
* @see Encoder *
* * @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/enc/EncoderStream.java#2 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/EncoderStream.java#2 $ */
*/ public final class EncoderStream extends FilterOutputStream {
public final class EncoderStream extends FilterOutputStream { // TODO: This class need a test case ASAP!!!
// TODO: This class need a test case ASAP!!!
protected final Encoder encoder;
protected final Encoder encoder; private final boolean flushOnWrite;
private final boolean flushOnWrite;
protected final ByteBuffer buffer;
protected final ByteBuffer buffer;
/**
/** * Creates an output stream filter built on top of the specified
* Creates an output stream filter built on top of the specified * underlying output stream.
* underlying output stream. *
* * @param pStream the underlying output stream
* @param pStream the underlying output stream * @param pEncoder the encoder to use
* @param pEncoder the encoder to use */
*/ public EncoderStream(final OutputStream pStream, final Encoder pEncoder) {
public EncoderStream(final OutputStream pStream, final Encoder pEncoder) { this(pStream, pEncoder, false);
this(pStream, pEncoder, false); }
}
/**
/** * Creates an output stream filter built on top of the specified
* Creates an output stream filter built on top of the specified * underlying output stream.
* underlying output stream. *
* * @param pStream the underlying output stream
* @param pStream the underlying output stream * @param pEncoder the encoder to use
* @param pEncoder the encoder to use * @param pFlushOnWrite if {@code true}, calls to the byte-array
* @param pFlushOnWrite if {@code true}, calls to the byte-array * {@code write} methods will automatically flush the buffer.
* {@code write} methods will automatically flush the buffer. */
*/ 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);
encoder = pEncoder;
encoder = pEncoder; flushOnWrite = pFlushOnWrite;
flushOnWrite = pFlushOnWrite;
buffer = ByteBuffer.allocate(1024);
buffer = ByteBuffer.allocate(1024); buffer.flip();
buffer.flip(); }
}
public void close() throws IOException {
public void close() throws IOException { flush();
flush(); super.close();
super.close(); }
}
public void flush() throws IOException {
public void flush() throws IOException { encodeBuffer();
encodeBuffer(); super.flush();
super.flush(); }
}
private void encodeBuffer() throws IOException {
private void encodeBuffer() throws IOException { if (buffer.position() != 0) {
if (buffer.position() != 0) { buffer.flip();
buffer.flip();
// Make sure all remaining data in buffer is written to the stream
// Make sure all remaining data in buffer is written to the stream encoder.encode(out, buffer);
encoder.encode(out, buffer);
// Reset buffer
// Reset buffer buffer.clear();
buffer.clear(); }
} }
}
public final void write(final byte[] pBytes) throws IOException {
public final void write(final byte[] pBytes) throws IOException { write(pBytes, 0, pBytes.length);
write(pBytes, 0, pBytes.length); }
}
// TODO: Verify that this works for the general case (it probably won't)...
// TODO: Verify that this works for the general case (it probably won't)... // TODO: We might need a way to explicitly flush the encoder, or specify
// TODO: We might need a way to explicitly flush the encoder, or specify // 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 (!flushOnWrite && pLength < buffer.remaining()) {
if (!flushOnWrite && pLength < buffer.remaining()) { // Buffer data
// Buffer data buffer.put(pBytes, pOffset, pLength);
buffer.put(pBytes, pOffset, pLength); }
} else {
else { // Encode data already in the buffer
// Encode data already in the buffer encodeBuffer();
encodeBuffer();
// Encode rest without buffering
// Encode rest without buffering encoder.encode(out, ByteBuffer.wrap(pBytes, pOffset, pLength));
encoder.encode(out, ByteBuffer.wrap(pBytes, pOffset, pLength)); }
} }
}
public void write(final int pByte) throws IOException {
public void write(final int pByte) throws IOException { if (!buffer.hasRemaining()) {
if (!buffer.hasRemaining()) { encodeBuffer(); // Resets bufferPos to 0
encodeBuffer(); // Resets bufferPos to 0 }
}
buffer.put((byte) pByte);
buffer.put((byte) pByte); }
} }
}

View File

@@ -37,30 +37,36 @@ import java.nio.ByteBuffer;
/** /**
* Decoder implementation for Apple PackBits run-length encoding. * Decoder implementation for Apple PackBits run-length encoding.
* <p/> * <p>
* <small>From Wikipedia, the free encyclopedia</small><br/> * <small>From Wikipedia, the free encyclopedia</small>
* <br>
* PackBits is a fast, simple compression scheme for run-length encoding of * PackBits is a fast, simple compression scheme for run-length encoding of
* data. * data.
* <p/> * </p>
* <p>
* Apple introduced the PackBits format with the release of MacPaint on the * Apple introduced the PackBits format with the release of MacPaint on the
* Macintosh computer. This compression scheme is one of the types of * Macintosh computer. This compression scheme is one of the types of
* compression that can be used in TIFF-files. * compression that can be used in TIFF-files.
* <p/> * </p>
* <p>
* A PackBits data stream consists of packets of one byte of header followed by * A PackBits data stream consists of packets of one byte of header followed by
* data. The header is a signed byte; the data can be signed, unsigned, or * data. The header is a signed byte; the data can be signed, unsigned, or
* packed (such as MacPaint pixels). * packed (such as MacPaint pixels).
* <p/> * </p>
* <table><tr><th>Header byte</th><th>Data</th></tr> * <table>
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr> * <caption>PackBits</caption>
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in * <tr><th>Header byte</th><th>Data</th></tr>
* the decompressed output</td></tr> * <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
* <tr><td>-128</td> <td>No operation</td></tr></table> * <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in the decompressed output</td></tr>
* <p/> * <tr><td>-128</td> <td>No operation</td></tr>
* </table>
* <p>
* Note that interpreting 0 as positive or negative makes no difference in the * Note that interpreting 0 as positive or negative makes no difference in the
* output. Runs of two bytes adjacent to non-runs are typically written as * output. Runs of two bytes adjacent to non-runs are typically written as
* literal data. * literal data.
* <p/> * </p>
* See <a href="http://developer.apple.com/technotes/tn/tn1023.html">Understanding PackBits</a> *
* @see <a href="http://developer.apple.com/technotes/tn/tn1023.html">Understanding PackBits</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/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 $
@@ -80,10 +86,11 @@ 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 for compatibility. * a compressed run, instead of a no-op, it's possible to disable no-ops 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.
* </p>
* *
* @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op * @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
*/ */
@@ -93,10 +100,11 @@ 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 for compatibility. * a compressed run, instead of a no-op, it's possible to disable no-ops 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.
* </p>
* *
* @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op * @param disableNoOp {@code true} if {@code -128} should be treated as a compressed run, and not a no-op
*/ */

View File

@@ -1,132 +1,138 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io.enc; package com.twelvemonkeys.io.enc;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
/** /**
* Encoder implementation for Apple PackBits run-length encoding. * Encoder implementation for Apple PackBits run-length encoding.
* <p/> * <p>
* From Wikipedia, the free encyclopedia<br/> * From Wikipedia, the free encyclopedia
* PackBits is a fast, simple compression scheme for run-length encoding of * <br>
* data. * PackBits is a fast, simple compression scheme for run-length encoding of
* <p/> * data.
* Apple introduced the PackBits format with the release of MacPaint on the * </p>
* Macintosh computer. This compression scheme is one of the types of * <p>
* compression that can be used in TIFF-files. * Apple introduced the PackBits format with the release of MacPaint on the
* <p/> * Macintosh computer. This compression scheme is one of the types of
* A PackBits data stream consists of packets of one byte of header followed by * compression that can be used in TIFF-files.
* data. The header is a signed byte; the data can be signed, unsigned, or * </p>
* packed (such as MacPaint pixels). * <p>
* <p/> * A PackBits data stream consists of packets of one byte of header followed by
* <table><tr><th>Header byte</th><th>Data</th></tr> * data. The header is a signed byte; the data can be signed, unsigned, or
* <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr> * packed (such as MacPaint pixels).
* <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in * </p>
* the decompressed output</td></tr> * <table>
* <tr><td>-128</td> <td>No operation</td></tr></table> * <caption>PackBits</caption>
* <p/> * <tr><th>Header byte</th><th>Data</th></tr>
* Note that interpreting 0 as positive or negative makes no difference in the * <tr><td>0 to 127</td> <td>1 + <i>n</i> literal bytes of data</td></tr>
* output. Runs of two bytes adjacent to non-runs are typically written as * <tr><td>0 to -127</td> <td>One byte of data, repeated 1 - <i>n</i> times in the decompressed output</td></tr>
* literal data. * <tr><td>-128</td> <td>No operation</td></tr>
* <p/> * </table>
* See <a href="http://developer.apple.com/technotes/tn/tn1023.html">Understanding PackBits</a> * <p>
* * Note that interpreting 0 as positive or negative makes no difference in the
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * output. Runs of two bytes adjacent to non-runs are typically written as
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/enc/PackBitsEncoder.java#1 $ * literal data.
*/ * </p>
public final class PackBitsEncoder implements Encoder { *
* @see <a href="http://developer.apple.com/technotes/tn/tn1023.html">Understanding PackBits</a>
final private byte[] buffer = new byte[128]; *
* @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/enc/PackBitsEncoder.java#1 $
* Creates a {@code PackBitsEncoder}. */
*/ public final class PackBitsEncoder implements Encoder {
public PackBitsEncoder() {
} final private byte[] buffer = new byte[128];
public void encode(final OutputStream stream, final ByteBuffer buffer) throws IOException { /**
encode(stream, buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining()); * Creates a {@code PackBitsEncoder}.
buffer.position(buffer.remaining()); */
} public PackBitsEncoder() {
}
private void encode(OutputStream pStream, byte[] pBuffer, int pOffset, int pLength) throws IOException {
// NOTE: It's best to encode a 2 byte repeat public void encode(final OutputStream stream, final ByteBuffer buffer) throws IOException {
// run as a replicate run except when preceded and followed by a encode(stream, buffer.array(), buffer.arrayOffset() + buffer.position(), buffer.remaining());
// literal run, in which case it's best to merge the three into one buffer.position(buffer.remaining());
// literal run. Always encode 3 byte repeats as replicate runs. }
// NOTE: Worst case: output = input + (input + 127) / 128
private void encode(OutputStream pStream, byte[] pBuffer, int pOffset, int pLength) throws IOException {
int offset = pOffset; // NOTE: It's best to encode a 2 byte repeat
final int max = pOffset + pLength - 1; // run as a replicate run except when preceded and followed by a
final int maxMinus1 = max - 1; // literal run, in which case it's best to merge the three into one
// literal run. Always encode 3 byte repeats as replicate runs.
while (offset <= max) { // NOTE: Worst case: output = input + (input + 127) / 128
// Compressed run
int run = 1; int offset = pOffset;
byte replicate = pBuffer[offset]; final int max = pOffset + pLength - 1;
while (run < 127 && offset < max && pBuffer[offset] == pBuffer[offset + 1]) { final int maxMinus1 = max - 1;
offset++;
run++; while (offset <= max) {
} // Compressed run
int run = 1;
if (run > 1) { byte replicate = pBuffer[offset];
offset++; while (run < 127 && offset < max && pBuffer[offset] == pBuffer[offset + 1]) {
pStream.write(-(run - 1)); offset++;
pStream.write(replicate); run++;
} }
// Literal run if (run > 1) {
run = 0; offset++;
while ((run < 128 && ((offset < max && pBuffer[offset] != pBuffer[offset + 1]) pStream.write(-(run - 1));
|| (offset < maxMinus1 && pBuffer[offset] != pBuffer[offset + 2])))) { pStream.write(replicate);
buffer[run++] = pBuffer[offset++]; }
}
// Literal run
// If last byte, include it in literal run, if space run = 0;
if (offset == max && run > 0 && run < 128) { while ((run < 128 && ((offset < max && pBuffer[offset] != pBuffer[offset + 1])
buffer[run++] = pBuffer[offset++]; || (offset < maxMinus1 && pBuffer[offset] != pBuffer[offset + 2])))) {
} buffer[run++] = pBuffer[offset++];
}
if (run > 0) {
pStream.write(run - 1); // If last byte, include it in literal run, if space
pStream.write(buffer, 0, run); if (offset == max && run > 0 && run < 128) {
} buffer[run++] = pBuffer[offset++];
}
// If last byte, and not space, start new literal run
if (offset == max && (run <= 0 || run >= 128)) { if (run > 0) {
pStream.write(0); pStream.write(run - 1);
pStream.write(pBuffer[offset++]); pStream.write(buffer, 0, run);
} }
}
} // If last byte, and not space, start new literal run
} if (offset == max && (run <= 0 || run >= 128)) {
pStream.write(0);
pStream.write(pBuffer[offset++]);
}
}
}
}

View File

@@ -1,342 +1,344 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.io.ole2; package com.twelvemonkeys.io.ole2;
import com.twelvemonkeys.io.SeekableInputStream; 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.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;
/** /**
* Represents an OLE 2 compound document entry. * Represents an OLE 2 compound document entry.
* This is similar to a file in a file system, or an entry in a ZIP or JAR file. * This is similar to a file in a file system, or an entry in a ZIP or JAR file.
* *
* @author <a href="mailto:harald.kuhr@gmail.no">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.no">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/io/ole2/Entry.java#4 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/io/ole2/Entry.java#4 $
* @see com.twelvemonkeys.io.ole2.CompoundDocument * @see com.twelvemonkeys.io.ole2.CompoundDocument
*/ */
// TODO: Consider extending java.io.File... // TODO: Consider extending java.io.File...
public final class Entry implements Comparable<Entry> { public final class Entry implements Comparable<Entry> {
String name; String name;
byte type; byte type;
byte nodeColor; byte nodeColor;
int prevDId; int prevDId;
int nextDId; int nextDId;
int rootNodeDId; int rootNodeDId;
long createdTimestamp; long createdTimestamp;
long modifiedTimestamp; long modifiedTimestamp;
int startSId; int startSId;
int streamSize; int streamSize;
CompoundDocument document; CompoundDocument document;
Entry parent; Entry parent;
SortedSet<Entry> children; SortedSet<Entry> children;
public final static int LENGTH = 128; public final static int LENGTH = 128;
static final int EMPTY = 0; static final int EMPTY = 0;
static final int USER_STORAGE = 1; static final int USER_STORAGE = 1;
static final int USER_STREAM = 2; static final int USER_STREAM = 2;
static final int LOCK_BYTES = 3; static final int LOCK_BYTES = 3;
static final int PROPERTY = 4; static final int PROPERTY = 4;
static final int ROOT_STORAGE = 5; static final int ROOT_STORAGE = 5;
private static final SortedSet<Entry> NO_CHILDREN = Collections.unmodifiableSortedSet(new TreeSet<Entry>()); private static final SortedSet<Entry> NO_CHILDREN = Collections.unmodifiableSortedSet(new TreeSet<Entry>());
private Entry() { private Entry() {
} }
/** /**
* Reads an entry from the input. * Reads an entry from the input.
* *
* @param pInput the input data * @param pInput the input data
* @return the {@code Entry} read from the input data * @return the {@code Entry} read from the input data
* @throws IOException if an i/o exception occurs during reading * @throws IOException if an i/o exception occurs during reading
*/ */
static Entry readEntry(final DataInput pInput) throws IOException { static Entry readEntry(final DataInput pInput) throws IOException {
Entry p = new Entry(); Entry p = new Entry();
p.read(pInput); p.read(pInput);
return p; return p;
} }
/** /**
* Reads this entry * Reads this entry
* *
* @param pInput the input data * @param pInput the input data
* @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 {
byte[] bytes = new byte[64]; byte[] bytes = new byte[64];
pInput.readFully(bytes); pInput.readFully(bytes);
// 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(bytes, 0, nameLength - 2, Charset.forName("UTF-16LE")); 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) {
throw new CorruptDocumentException(); throw new CorruptDocumentException();
} }
createdTimestamp = CompoundDocument.toJavaTimeInMillis(pInput.readLong()); createdTimestamp = CompoundDocument.toJavaTimeInMillis(pInput.readLong());
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();
} }
/** /**
* If {@code true} this {@code Entry} is the root {@code Entry}. * If {@code true} this {@code Entry} is the root {@code Entry}.
* *
* @return {@code true} if this is the root {@code Entry} * @return {@code true} if this is the root {@code Entry}
*/ */
public boolean isRoot() { public boolean isRoot() {
return type == ROOT_STORAGE; return type == ROOT_STORAGE;
} }
/** /**
* If {@code true} this {@code Entry} is a directory * If {@code true} this {@code Entry} is a directory
* {@code Entry}. * {@code Entry}.
* *
* @return {@code true} if this is a directory {@code Entry} * @return {@code true} if this is a directory {@code Entry}
*/ */
public boolean isDirectory() { public boolean isDirectory() {
return type == USER_STORAGE; return type == USER_STORAGE;
} }
/** /**
* If {@code true} this {@code Entry} is a file (document) * If {@code true} this {@code Entry} is a file (document)
* {@code Entry}. * {@code Entry}.
* *
* @return {@code true} if this is a document {@code Entry} * @return {@code true} if this is a document {@code Entry}
*/ */
public boolean isFile() { public boolean isFile() {
return type == USER_STREAM; return type == USER_STREAM;
} }
/** /**
* Returns the name of this {@code Entry} * Returns the name of this {@code Entry}
* *
* @return the name of this {@code Entry} * @return the name of this {@code Entry}
*/ */
public String getName() { public String getName() {
return name; return name;
} }
/** /**
* Returns the {@code InputStream} for this {@code Entry} * Returns the {@code InputStream} for this {@code Entry}
* *
* @return an {@code InputStream} containing the data for this * @return an {@code InputStream} containing the data for this
* {@code Entry} or {@code null} if this is a directory {@code Entry} * {@code Entry} or {@code null} if this is a directory {@code Entry}
* @throws java.io.IOException if an I/O exception occurs * @throws java.io.IOException if an I/O exception occurs
* @see #length() * @see #length()
*/ */
public SeekableInputStream getInputStream() throws IOException { public SeekableInputStream getInputStream() throws IOException {
if (!isFile()) { if (!isFile()) {
return null; return null;
} }
return document.getInputStreamForSId(startSId, streamSize); return document.getInputStreamForSId(startSId, streamSize);
} }
/** /**
* Returns the length of this entry * Returns the length of this entry
* *
* @return the length of the stream for this entry, or {@code 0} if this is * @return the length of the stream for this entry, or {@code 0} if this is
* a directory {@code Entry} * a directory {@code Entry}
* @see #getInputStream() * @see #getInputStream()
*/ */
public long length() { public long length() {
if (!isFile()) { if (!isFile()) {
return 0L; return 0L;
} }
return streamSize; return streamSize;
} }
/** /**
* Returns the time that this entry was created. * Returns the time that this entry was created.
* The time is converted from its internal representation to standard Java * The time is converted from its internal representation to standard Java
* representation, milliseconds since the epoch * representation, milliseconds since the epoch
* (00:00:00 GMT, January 1, 1970). * (00:00:00 GMT, January 1, 1970).
* <p/> * <p>
* Note that most applications leaves this value empty ({@code 0L}). * Note that most applications leaves this value empty ({@code 0L}).
* * </p>
* @return A {@code long} value representing the time this entry was *
* created, measured in milliseconds since the epoch * @return A {@code long} value representing the time this entry was
* (00:00:00 GMT, January 1, 1970), or {@code 0L} if no * created, measured in milliseconds since the epoch
* creation time stamp exists for this entry. * (00:00:00 GMT, January 1, 1970), or {@code 0L} if no
*/ * creation time stamp exists for this entry.
public long created() { */
return createdTimestamp; public long created() {
} return createdTimestamp;
}
/**
* Returns the time that this entry was last modified. /**
* The time is converted from its internal representation to standard Java * Returns the time that this entry was last modified.
* representation, milliseconds since the epoch * The time is converted from its internal representation to standard Java
* (00:00:00 GMT, January 1, 1970). * representation, milliseconds since the epoch
* <p/> * (00:00:00 GMT, January 1, 1970).
* Note that many applications leaves this value empty ({@code 0L}). * <p>
* * Note that many applications leaves this value empty ({@code 0L}).
* @return A {@code long} value representing the time this entry was * </p>
* last modified, measured in milliseconds since the epoch *
* (00:00:00 GMT, January 1, 1970), or {@code 0L} if no * @return A {@code long} value representing the time this entry was
* modification time stamp exists for this entry. * last modified, measured in milliseconds since the epoch
*/ * (00:00:00 GMT, January 1, 1970), or {@code 0L} if no
public long lastModified() { * modification time stamp exists for this entry.
return modifiedTimestamp; */
} public long lastModified() {
return modifiedTimestamp;
/** }
* Return the parent of this {@code Entry}
* /**
* @return the parent of this {@code Entry}, or {@code null} if this is * Return the parent of this {@code Entry}
* the root {@code Entry} *
*/ * @return the parent of this {@code Entry}, or {@code null} if this is
public Entry getParentEntry() { * the root {@code Entry}
return parent; */
} public Entry getParentEntry() {
return parent;
/** }
* Returns the child of this {@code Entry} with the given name.
* /**
* @param pName the name of the child {@code Entry} * Returns the child of this {@code Entry} with the given name.
* @return the child {@code Entry} or {@code null} if thee is no such *
* child * @param pName the name of the child {@code Entry}
* @throws java.io.IOException if an I/O exception occurs * @return the child {@code Entry} or {@code null} if thee is no such
*/ * child
public Entry getChildEntry(final String pName) throws IOException { * @throws java.io.IOException if an I/O exception occurs
if (isFile() || rootNodeDId == -1) { */
return null; public Entry getChildEntry(final String pName) throws IOException {
} if (isFile() || rootNodeDId == -1) {
return null;
Entry dummy = new Entry(); }
dummy.name = pName;
dummy.parent = this; Entry dummy = new Entry();
dummy.name = pName;
SortedSet child = getChildEntries().tailSet(dummy); dummy.parent = this;
return (Entry) child.first();
} SortedSet child = getChildEntries().tailSet(dummy);
return (Entry) child.first();
/** }
* Returns the children of this {@code Entry}.
* /**
* @return a {@code SortedSet} of {@code Entry} objects * Returns the children of this {@code Entry}.
* @throws java.io.IOException if an I/O exception occurs *
*/ * @return a {@code SortedSet} of {@code Entry} objects
public SortedSet<Entry> getChildEntries() throws IOException { * @throws java.io.IOException if an I/O exception occurs
if (children == null) { */
if (isFile() || rootNodeDId == -1) { public SortedSet<Entry> getChildEntries() throws IOException {
children = NO_CHILDREN; if (children == null) {
} if (isFile() || rootNodeDId == -1) {
else { children = NO_CHILDREN;
// Start at root node in R/B tree, and read to the left and right, }
// re-build tree, according to the docs else {
children = Collections.unmodifiableSortedSet(document.getEntries(rootNodeDId, this)); // Start at root node in R/B tree, and read to the left and right,
} // re-build tree, according to the docs
} children = Collections.unmodifiableSortedSet(document.getEntries(rootNodeDId, this));
}
return children; }
}
return children;
@Override }
public String toString() {
return "\"" + name + "\"" @Override
+ " (" + (isFile() ? "Document" : (isDirectory() ? "Directory" : "Root")) public String toString() {
+ (parent != null ? ", parent: \"" + parent.getName() + "\"" : "") return "\"" + name + "\""
+ (isFile() ? "" : ", children: " + (children != null ? String.valueOf(children.size()) : "(unknown)")) + " (" + (isFile() ? "Document" : (isDirectory() ? "Directory" : "Root"))
+ ", SId=" + startSId + ", length=" + streamSize + ")"; + (parent != null ? ", parent: \"" + parent.getName() + "\"" : "")
} + (isFile() ? "" : ", children: " + (children != null ? String.valueOf(children.size()) : "(unknown)"))
+ ", SId=" + startSId + ", length=" + streamSize + ")";
@Override }
public boolean equals(final Object pOther) {
if (pOther == this) { @Override
return true; public boolean equals(final Object pOther) {
} if (pOther == this) {
if (!(pOther instanceof Entry)) { return true;
return false; }
} if (!(pOther instanceof Entry)) {
return false;
Entry other = (Entry) pOther; }
return name.equals(other.name) && (parent == other.parent
|| (parent != null && parent.equals(other.parent))); Entry other = (Entry) pOther;
} return name.equals(other.name) && (parent == other.parent
|| (parent != null && parent.equals(other.parent)));
@Override }
public int hashCode() {
return name.hashCode() ^ startSId; @Override
} public int hashCode() {
return name.hashCode() ^ startSId;
public int compareTo(final Entry pOther) { }
if (this == pOther) {
return 0; public int compareTo(final Entry pOther) {
} if (this == pOther) {
return 0;
// NOTE: This is the sorting algorthm defined by the Compound Document: }
// - first sort by name length
// - if lengths are equal, sort by comparing strings, case sensitive // NOTE: This is the sorting algorthm defined by the Compound Document:
// - first sort by name length
int diff = name.length() - pOther.name.length(); // - if lengths are equal, sort by comparing strings, case sensitive
if (diff != 0) {
return diff; int diff = name.length() - pOther.name.length();
} if (diff != 0) {
return diff;
return name.compareTo(pOther.name); }
}
} return name.compareTo(pOther.name);
}
}

View File

@@ -1,314 +1,313 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.net; package com.twelvemonkeys.net;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.lang.SystemUtil; import com.twelvemonkeys.lang.SystemUtil;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
/** /**
* Contains mappings from file extension to mime-types and from mime-type to file-types. * Contains mappings from file extension to mime-types and from mime-type to file-types.
* <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/net/MIMEUtil.java#5 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/net/MIMEUtil.java#5 $ *
* * @see <A href="http://www.iana.org/assignments/media-types/">MIME Media Types</A>
* @see <A href="http://www.iana.org/assignments/media-types/">MIME Media Types</A> */
*/ public final class MIMEUtil {
public final class MIMEUtil { // TODO: Piggy-back on the mappings form the JRE? (1.6 comes with javax.activation)
// TODO: Piggy-back on the mappings form the JRE? (1.6 comes with javax.activation) // TODO: Piggy-back on mappings from javax.activation?
// TODO: Piggy-back on mappings from javax.activation? // See: http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/activation/MimetypesFileTypeMap.html
// See: http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/activation/MimetypesFileTypeMap.html // See: http://java.sun.com/javase/6/docs/api/javax/activation/MimetypesFileTypeMap.html
// See: http://java.sun.com/javase/6/docs/api/javax/activation/MimetypesFileTypeMap.html // TODO: Use the format (and lookup) specified by the above URLs
// TODO: Use the format (and lookup) specified by the above URLs // TODO: Allow 3rd party to add mappings? Will need application context support to do it safe.. :-P
// TODO: Allow 3rd party to add mappings? Will need application context support to do it safe.. :-P
private static Map<String, List<String>> sExtToMIME = new HashMap<String, List<String>>();
private static Map<String, List<String>> sExtToMIME = new HashMap<String, List<String>>(); private static Map<String, List<String>> sUnmodifiableExtToMIME = Collections.unmodifiableMap(sExtToMIME);
private static Map<String, List<String>> sUnmodifiableExtToMIME = Collections.unmodifiableMap(sExtToMIME);
private static Map<String, List<String>> sMIMEToExt = new HashMap<String, List<String>>();
private static Map<String, List<String>> sMIMEToExt = new HashMap<String, List<String>>(); private static Map<String, List<String>> sUnmodifiableMIMEToExt = Collections.unmodifiableMap(sMIMEToExt);
private static Map<String, List<String>> sUnmodifiableMIMEToExt = Collections.unmodifiableMap(sMIMEToExt);
static {
static { // Load mapping for MIMEUtil
// Load mapping for MIMEUtil try {
try { Properties mappings = SystemUtil.loadProperties(MIMEUtil.class);
Properties mappings = SystemUtil.loadProperties(MIMEUtil.class);
for (Map.Entry entry : mappings.entrySet()) {
for (Map.Entry entry : mappings.entrySet()) { // Convert and break up extensions and mimeTypes
// Convert and break up extensions and mimeTypes String extStr = StringUtil.toLowerCase((String) entry.getKey());
String extStr = StringUtil.toLowerCase((String) entry.getKey()); List<String> extensions =
List<String> extensions = Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(extStr, ";, ")));
Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(extStr, ";, ")));
String typeStr = StringUtil.toLowerCase((String) entry.getValue());
String typeStr = StringUtil.toLowerCase((String) entry.getValue()); List<String> mimeTypes =
List<String> mimeTypes = Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(typeStr, ";, ")));
Collections.unmodifiableList(Arrays.asList(StringUtil.toStringArray(typeStr, ";, ")));
// TODO: Handle duplicates in MIME to extension mapping, like
// TODO: Handle duplicates in MIME to extension mapping, like // xhtml=application/xhtml+xml;application/xml
// xhtml=application/xhtml+xml;application/xml // xml=text/xml;application/xml
// xml=text/xml;application/xml
// Populate normal and reverse MIME-mappings
// Populate normal and reverse MIME-mappings for (String extension : extensions) {
for (String extension : extensions) { sExtToMIME.put(extension, mimeTypes);
sExtToMIME.put(extension, mimeTypes); }
}
for (String mimeType : mimeTypes) {
for (String mimeType : mimeTypes) { sMIMEToExt.put(mimeType, extensions);
sMIMEToExt.put(mimeType, extensions); }
} }
} }
} catch (IOException e) {
catch (IOException e) { System.err.println("Could not read properties for MIMEUtil: " + e.getMessage());
System.err.println("Could not read properties for MIMEUtil: " + e.getMessage()); e.printStackTrace();
e.printStackTrace(); }
} }
}
// Disallow construction
// Disallow construction private MIMEUtil() {
private MIMEUtil() { }
}
/**
/** * Returns the default MIME type for the given file extension.
* Returns the default MIME type for the given file extension. *
* * @param pFileExt the file extension
* @param pFileExt the file extension *
* * @return a {@code String} containing the MIME type, or {@code null} if
* @return a {@code String} containing the MIME type, or {@code null} if * there are no known MIME types for the given file extension.
* there are no known MIME types for the given file extension. */
*/ public static String getMIMEType(final String pFileExt) {
public static String getMIMEType(final String pFileExt) { List<String> types = sExtToMIME.get(StringUtil.toLowerCase(pFileExt));
List<String> types = sExtToMIME.get(StringUtil.toLowerCase(pFileExt)); return (types == null || types.isEmpty()) ? null : types.get(0);
return (types == null || types.isEmpty()) ? null : types.get(0); }
}
/**
/** * Returns all MIME types for the given file extension.
* Returns all MIME types for the given file extension. *
* * @param pFileExt the file extension
* @param pFileExt the file extension *
* * @return a {@link List} of {@code String}s containing the MIME types, or an empty
* @return a {@link List} of {@code String}s containing the MIME types, or an empty * list, if there are no known MIME types for the given file extension.
* list, if there are no known MIME types for the given file extension. */
*/ public static List<String> getMIMETypes(final String pFileExt) {
public static List<String> getMIMETypes(final String pFileExt) { List<String> types = sExtToMIME.get(StringUtil.toLowerCase(pFileExt));
List<String> types = sExtToMIME.get(StringUtil.toLowerCase(pFileExt)); return maskNull(types);
return maskNull(types); }
}
/**
/** * Returns an unmodifiabale {@link Map} view of the extension to
* Returns an unmodifiabale {@link Map} view of the extension to * MIME mapping, to use as the default mapping in client applications.
* MIME mapping, to use as the default mapping in client applications. *
* * @return an unmodifiabale {@code Map} view of the extension to
* @return an unmodifiabale {@code Map} view of the extension to * MIME mapping.
* MIME mapping. */
*/ public static Map<String, List<String>> getMIMETypeMappings() {
public static Map<String, List<String>> getMIMETypeMappings() { return sUnmodifiableExtToMIME;
return sUnmodifiableExtToMIME; }
}
/**
/** * Returns the default file extension for the given MIME type.
* Returns the default file extension for the given MIME type. * Specifying a wildcard type will return {@code null}.
* Specifying a wildcard type will return {@code null}. *
* * @param pMIME the MIME type
* @param pMIME the MIME type *
* * @return a {@code String} containing the file extension, or {@code null}
* @return a {@code String} containing the file extension, or {@code null} * if there are no known file extensions for the given MIME type.
* if there are no known file extensions for the given MIME type. */
*/ public static String getExtension(final String pMIME) {
public static String getExtension(final String pMIME) { String mime = bareMIME(StringUtil.toLowerCase(pMIME));
String mime = bareMIME(StringUtil.toLowerCase(pMIME)); List<String> extensions = sMIMEToExt.get(mime);
List<String> extensions = sMIMEToExt.get(mime); return (extensions == null || extensions.isEmpty()) ? null : extensions.get(0);
return (extensions == null || extensions.isEmpty()) ? null : extensions.get(0); }
}
/**
/** * Returns all file extension for the given MIME type.
* Returns all file extension for the given MIME type. * The default extension will be the first in the list.
* The default extension will be the first in the list. * Note that no specific order is given for wildcard types (image/*, *&#47;* etc).
* Note that no specific order is given for wildcard types (image/*, *&#47;* etc). *
* * @param pMIME the MIME type
* @param pMIME the MIME type *
* * @return a {@link List} of {@code String}s containing the MIME types, or an empty
* @return a {@link List} of {@code String}s containing the MIME types, or an empty * list, if there are no known file extensions for the given MIME type.
* list, if there are no known file extensions for the given MIME type. */
*/ public static List<String> getExtensions(final String pMIME) {
public static List<String> getExtensions(final String pMIME) { String mime = bareMIME(StringUtil.toLowerCase(pMIME));
String mime = bareMIME(StringUtil.toLowerCase(pMIME)); if (mime.endsWith("/*")) {
if (mime.endsWith("/*")) { return getExtensionForWildcard(mime);
return getExtensionForWildcard(mime); }
} List<String> extensions = sMIMEToExt.get(mime);
List<String> extensions = sMIMEToExt.get(mime); return maskNull(extensions);
return maskNull(extensions); }
}
// Gets all extensions for a wildcard MIME type
// Gets all extensions for a wildcard MIME type private static List<String> getExtensionForWildcard(final String pMIME) {
private static List<String> getExtensionForWildcard(final String pMIME) { final String family = pMIME.substring(0, pMIME.length() - 1);
final String family = pMIME.substring(0, pMIME.length() - 1); Set<String> extensions = new LinkedHashSet<String>();
Set<String> extensions = new LinkedHashSet<String>(); for (Map.Entry<String, List<String>> mimeToExt : sMIMEToExt.entrySet()) {
for (Map.Entry<String, List<String>> mimeToExt : sMIMEToExt.entrySet()) { if ("*/".equals(family) || mimeToExt.getKey().startsWith(family)) {
if ("*/".equals(family) || mimeToExt.getKey().startsWith(family)) { extensions.addAll(mimeToExt.getValue());
extensions.addAll(mimeToExt.getValue()); }
} }
} return Collections.unmodifiableList(new ArrayList<String>(extensions));
return Collections.unmodifiableList(new ArrayList<String>(extensions)); }
}
/**
/** * Returns an unmodifiabale {@link Map} view of the MIME to
* Returns an unmodifiabale {@link Map} view of the MIME to * extension mapping, to use as the default mapping in client applications.
* extension mapping, to use as the default mapping in client applications. *
* * @return an unmodifiabale {@code Map} view of the MIME to
* @return an unmodifiabale {@code Map} view of the MIME to * extension mapping.
* extension mapping. */
*/ public static Map<String, List<String>> getExtensionMappings() {
public static Map<String, List<String>> getExtensionMappings() { return sUnmodifiableMIMEToExt;
return sUnmodifiableMIMEToExt; }
}
/**
/** * Tests wehter the type is a subtype of the type family.
* Tests wehter the type is a subtype of the type family. *
* * @param pTypeFamily the MIME type family ({@code image/*, *&#47;*}, etc)
* @param pTypeFamily the MIME type family ({@code image/*, *&#47;*}, etc) * @param pType the MIME type
* @param pType the MIME type * @return {@code true} if {@code pType} is a subtype of {@code pTypeFamily}, otherwise {@code false}
* @return {@code true} if {@code pType} is a subtype of {@code pTypeFamily}, otherwise {@code false} */
*/ // TODO: Rename? isSubtype?
// TODO: Rename? isSubtype? // TODO: Make public
// TODO: Make public static boolean includes(final String pTypeFamily, final String pType) {
static boolean includes(final String pTypeFamily, final String pType) { // TODO: Handle null in a well-defined way
// TODO: Handle null in a well-defined way // - Is null family same as */*?
// - Is null family same as */*? // - Is null subtype of any family? Subtype of no family?
// - Is null subtype of any family? Subtype of no family?
String type = bareMIME(pType);
String type = bareMIME(pType); return type.equals(pTypeFamily)
return type.equals(pTypeFamily) || "*/*".equals(pTypeFamily)
|| "*/*".equals(pTypeFamily) || pTypeFamily.endsWith("/*") && pTypeFamily.startsWith(type.substring(0, type.indexOf('/')));
|| pTypeFamily.endsWith("/*") && pTypeFamily.startsWith(type.substring(0, type.indexOf('/'))); }
}
/**
/** * Removes any charset or extra info from the mime-type string (anything after a semicolon, {@code ;}, inclusive).
* Removes any charset or extra info from the mime-type string (anything after a semicolon, {@code ;}, inclusive). *
* * @param pMIME the mime-type string
* @param pMIME the mime-type string * @return the bare mime-type
* @return the bare mime-type */
*/ public static String bareMIME(final String pMIME) {
public static String bareMIME(final String pMIME) { int idx;
int idx; if (pMIME != null && (idx = pMIME.indexOf(';')) >= 0) {
if (pMIME != null && (idx = pMIME.indexOf(';')) >= 0) { return pMIME.substring(0, idx);
return pMIME.substring(0, idx); }
} return pMIME;
return pMIME; }
}
// Returns the list or empty list if list is null
// Returns the list or empty list if list is null private static List<String> maskNull(List<String> pTypes) {
private static List<String> maskNull(List<String> pTypes) { return (pTypes == null) ? Collections.<String>emptyList() : pTypes;
return (pTypes == null) ? Collections.<String>emptyList() : pTypes; }
}
/**
/** * For debugging. Prints all known MIME types and file extensions.
* For debugging. Prints all known MIME types and file extensions. *
* * @param pArgs command line arguments
* @param pArgs command line arguments */
*/ public static void main(String[] pArgs) {
public static void main(String[] pArgs) { if (pArgs.length > 1) {
if (pArgs.length > 1) { String type = pArgs[0];
String type = pArgs[0]; String family = pArgs[1];
String family = pArgs[1]; boolean incuded = includes(family, type);
boolean incuded = includes(family, type); System.out.println(
System.out.println( "Mime type family " + family
"Mime type family " + family + (incuded ? " includes " : " does not include ")
+ (incuded ? " includes " : " does not include ") + "type " + type
+ "type " + type );
); }
} if (pArgs.length > 0) {
if (pArgs.length > 0) { String str = pArgs[0];
String str = pArgs[0];
if (str.indexOf('/') >= 0) {
if (str.indexOf('/') >= 0) { // MIME
// MIME String extension = getExtension(str);
String extension = getExtension(str); System.out.println("Default extension for MIME type '" + str + "' is "
System.out.println("Default extension for MIME type '" + str + "' is " + (extension != null ? ": '" + extension + "'" : "unknown") + ".");
+ (extension != null ? ": '" + extension + "'" : "unknown") + "."); System.out.println("All possible: " + getExtensions(str));
System.out.println("All possible: " + getExtensions(str)); }
} else {
else { // EXT
// EXT String mimeType = getMIMEType(str);
String mimeType = getMIMEType(str); System.out.println("Default MIME type for extension '" + str + "' is "
System.out.println("Default MIME type for extension '" + str + "' is " + (mimeType != null ? ": '" + mimeType + "'" : "unknown") + ".");
+ (mimeType != null ? ": '" + mimeType + "'" : "unknown") + "."); System.out.println("All possible: " + getMIMETypes(str));
System.out.println("All possible: " + getMIMETypes(str)); }
}
return;
return; }
}
Set set = sMIMEToExt.keySet();
Set set = sMIMEToExt.keySet(); String[] mimeTypes = new String[set.size()];
String[] mimeTypes = new String[set.size()]; int i = 0;
int i = 0; for (Iterator iterator = set.iterator(); iterator.hasNext(); i++) {
for (Iterator iterator = set.iterator(); iterator.hasNext(); i++) { String mime = (String) iterator.next();
String mime = (String) iterator.next(); mimeTypes[i] = mime;
mimeTypes[i] = mime; }
} Arrays.sort(mimeTypes);
Arrays.sort(mimeTypes);
System.out.println("Known MIME types (" + mimeTypes.length + "):");
System.out.println("Known MIME types (" + mimeTypes.length + "):"); for (int j = 0; j < mimeTypes.length; j++) {
for (int j = 0; j < mimeTypes.length; j++) { String mimeType = mimeTypes[j];
String mimeType = mimeTypes[j];
if (j != 0) {
if (j != 0) { System.out.print(", ");
System.out.print(", "); }
}
System.out.print(mimeType);
System.out.print(mimeType); }
}
System.out.println("\n");
System.out.println("\n");
set = sExtToMIME.keySet();
set = sExtToMIME.keySet(); String[] extensions = new String[set.size()];
String[] extensions = new String[set.size()]; i = 0;
i = 0; for (Iterator iterator = set.iterator(); iterator.hasNext(); i++) {
for (Iterator iterator = set.iterator(); iterator.hasNext(); i++) { String ext = (String) iterator.next();
String ext = (String) iterator.next(); extensions[i] = ext;
extensions[i] = ext; }
} Arrays.sort(extensions);
Arrays.sort(extensions);
System.out.println("Known file types (" + extensions.length + "):");
System.out.println("Known file types (" + extensions.length + "):"); for (int j = 0; j < extensions.length; j++) {
for (int j = 0; j < extensions.length; j++) { String extension = extensions[j];
String extension = extensions[j];
if (j != 0) {
if (j != 0) { System.out.print(", ");
System.out.print(", "); }
}
System.out.print(extension);
System.out.print(extension); }
} System.out.println();
System.out.println(); }
}
} }

View File

@@ -102,8 +102,9 @@ public final class DOMSerializer {
/** /**
* Specifies wether the serializer should use indentation and optimize for * Specifies wether the serializer should use indentation and optimize for
* readability. * readability.
* <p/> * <p>
* Note: This is a hint, and may be ignored by DOM implemenations. * Note: This is a hint, and may be ignored by DOM implementations.
* </p>
* *
* @param pPrettyPrint {@code true} to enable pretty printing * @param pPrettyPrint {@code true} to enable pretty printing
*/ */

View File

@@ -1,204 +1,203 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.lang; package com.twelvemonkeys.lang;
import java.util.Date; import java.util.Date;
import java.util.TimeZone; import java.util.TimeZone;
/** /**
* A utility class with useful date manipulation methods and constants. * A utility class with useful date manipulation methods and constants.
* <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/lang/DateUtil.java#1 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/DateUtil.java#1 $ */
*/ public final class DateUtil {
public final class DateUtil {
/** One second: 1000 milliseconds. */
/** One second: 1000 milliseconds. */ public static final long SECOND = 1000l;
public static final long SECOND = 1000l;
/** One minute: 60 seconds (60 000 milliseconds). */
/** One minute: 60 seconds (60 000 milliseconds). */ public static final long MINUTE = 60l * SECOND;
public static final long MINUTE = 60l * SECOND;
/**
/** * One hour: 60 minutes (3 600 000 milliseconds).
* One hour: 60 minutes (3 600 000 milliseconds). * 60 minutes = 3 600 seconds = 3 600 000 milliseconds
* 60 minutes = 3 600 seconds = 3 600 000 milliseconds */
*/ public static final long HOUR = 60l * MINUTE;
public static final long HOUR = 60l * MINUTE;
/**
/** * One day: 24 hours (86 400 000 milliseconds).
* One day: 24 hours (86 400 000 milliseconds). * 24 hours = 1 440 minutes = 86 400 seconds = 86 400 000 milliseconds.
* 24 hours = 1 440 minutes = 86 400 seconds = 86 400 000 milliseconds. */
*/ public static final long DAY = 24l * HOUR;
public static final long DAY = 24l * HOUR;
/**
/** * One calendar year: 365.2425 days (31556952000 milliseconds).
* One calendar year: 365.2425 days (31556952000 milliseconds). * 365.2425 days = 8765.82 hours = 525949.2 minutes = 31556952 seconds
* 365.2425 days = 8765.82 hours = 525949.2 minutes = 31556952 seconds * = 31556952000 milliseconds.
* = 31556952000 milliseconds. */
*/ public static final long CALENDAR_YEAR = 3652425l * 24l * 60l * 6l;
public static final long CALENDAR_YEAR = 3652425l * 24l * 60l * 6l;
private DateUtil() {
private DateUtil() { }
}
/**
/** * Returns the time between the given start time and now (as defined by
* Returns the time between the given start time and now (as defined by * {@link System#currentTimeMillis()}).
* {@link System#currentTimeMillis()}). *
* * @param pStart the start time
* @param pStart the start time *
* * @return the time between the given start time and now.
* @return the time between the given start time and now. */
*/ public static long delta(long pStart) {
public static long delta(long pStart) { return System.currentTimeMillis() - pStart;
return System.currentTimeMillis() - pStart; }
}
/**
/** * Returns the time between the given start time and now (as defined by
* Returns the time between the given start time and now (as defined by * {@link System#currentTimeMillis()}).
* {@link System#currentTimeMillis()}). *
* * @param pStart the start time
* @param pStart the start time *
* * @return the time between the given start time and now.
* @return the time between the given start time and now. */
*/ public static long delta(Date pStart) {
public static long delta(Date pStart) { return System.currentTimeMillis() - pStart.getTime();
return System.currentTimeMillis() - pStart.getTime(); }
}
/**
/** * Gets the current time, rounded down to the closest second.
* Gets the current time, rounded down to the closest second. * Equivalent to invoking
* Equivalent to invoking * {@code roundToSecond(System.currentTimeMillis())}.
* {@code roundToSecond(System.currentTimeMillis())}. *
* * @return the current time, rounded to the closest second.
* @return the current time, rounded to the closest second. */
*/ public static long currentTimeSecond() {
public static long currentTimeSecond() { return roundToSecond(System.currentTimeMillis());
return roundToSecond(System.currentTimeMillis()); }
}
/**
/** * Gets the current time, rounded down to the closest minute.
* Gets the current time, rounded down to the closest minute. * Equivalent to invoking
* Equivalent to invoking * {@code roundToMinute(System.currentTimeMillis())}.
* {@code roundToMinute(System.currentTimeMillis())}. *
* * @return the current time, rounded to the closest minute.
* @return the current time, rounded to the closest minute. */
*/ public static long currentTimeMinute() {
public static long currentTimeMinute() { return roundToMinute(System.currentTimeMillis());
return roundToMinute(System.currentTimeMillis()); }
}
/**
/** * Gets the current time, rounded down to the closest hour.
* Gets the current time, rounded down to the closest hour. * Equivalent to invoking
* Equivalent to invoking * {@code roundToHour(System.currentTimeMillis())}.
* {@code roundToHour(System.currentTimeMillis())}. *
* * @return the current time, rounded to the closest hour.
* @return the current time, rounded to the closest hour. */
*/ public static long currentTimeHour() {
public static long currentTimeHour() { return roundToHour(System.currentTimeMillis());
return roundToHour(System.currentTimeMillis()); }
}
/**
/** * Gets the current time, rounded down to the closest day.
* Gets the current time, rounded down to the closest day. * Equivalent to invoking
* Equivalent to invoking * {@code roundToDay(System.currentTimeMillis())}.
* {@code roundToDay(System.currentTimeMillis())}. *
* * @return the current time, rounded to the closest day.
* @return the current time, rounded to the closest day. */
*/ public static long currentTimeDay() {
public static long currentTimeDay() { return roundToDay(System.currentTimeMillis());
return roundToDay(System.currentTimeMillis()); }
}
/**
/** * Rounds the given time down to the closest second.
* Rounds the given time down to the closest second. *
* * @param pTime time
* @param pTime time * @return the time rounded to the closest second.
* @return the time rounded to the closest second. */
*/ public static long roundToSecond(final long pTime) {
public static long roundToSecond(final long pTime) { return (pTime / SECOND) * SECOND;
return (pTime / SECOND) * SECOND; }
}
/**
/** * Rounds the given time down to the closest minute.
* Rounds the given time down to the closest minute. *
* * @param pTime time
* @param pTime time * @return the time rounded to the closest minute.
* @return the time rounded to the closest minute. */
*/ public static long roundToMinute(final long pTime) {
public static long roundToMinute(final long pTime) { return (pTime / MINUTE) * MINUTE;
return (pTime / MINUTE) * MINUTE; }
}
/**
/** * Rounds the given time down to the closest hour, using the default timezone.
* Rounds the given time down to the closest hour, using the default timezone. *
* * @param pTime time
* @param pTime time * @return the time rounded to the closest hour.
* @return the time rounded to the closest hour. */
*/ public static long roundToHour(final long pTime) {
public static long roundToHour(final long pTime) { return roundToHour(pTime, TimeZone.getDefault());
return roundToHour(pTime, TimeZone.getDefault()); }
}
/**
/** * Rounds the given time down to the closest hour, using the given timezone.
* Rounds the given time down to the closest hour, using the given timezone. *
* * @param pTime time
* @param pTime time * @param pTimeZone the timezone to use when rounding
* @param pTimeZone the timezone to use when rounding * @return the time rounded to the closest hour.
* @return the time rounded to the closest hour. */
*/ public static long roundToHour(final long pTime, final TimeZone pTimeZone) {
public static long roundToHour(final long pTime, final TimeZone pTimeZone) { int offset = pTimeZone.getOffset(pTime);
int offset = pTimeZone.getOffset(pTime); return ((pTime / HOUR) * HOUR) - offset;
return ((pTime / HOUR) * HOUR) - offset; }
}
/**
/** * Rounds the given time down to the closest day, using the default timezone.
* Rounds the given time down to the closest day, using the default timezone. *
* * @param pTime time
* @param pTime time * @return the time rounded to the closest day.
* @return the time rounded to the closest day. */
*/ public static long roundToDay(final long pTime) {
public static long roundToDay(final long pTime) { return roundToDay(pTime, TimeZone.getDefault());
return roundToDay(pTime, TimeZone.getDefault()); }
}
/**
/** * Rounds the given time down to the closest day, using the given timezone.
* Rounds the given time down to the closest day, using the given timezone. *
* * @param pTime time
* @param pTime time * @param pTimeZone the timezone to use when rounding
* @param pTimeZone the timezone to use when rounding * @return the time rounded to the closest day.
* @return the time rounded to the closest day. */
*/ public static long roundToDay(final long pTime, final TimeZone pTimeZone) {
public static long roundToDay(final long pTime, final TimeZone pTimeZone) { int offset = pTimeZone.getOffset(pTime);
int offset = pTimeZone.getOffset(pTime); return (((pTime + offset) / DAY) * DAY) - offset;
return (((pTime + offset) / DAY) * DAY) - offset; }
} }
}

View File

@@ -199,9 +199,10 @@ public final class Platform {
/** /**
* Enumeration of common System {@code Architecture}s. * Enumeration of common System {@code Architecture}s.
* <p/> * <p>
* For {@link #Unknown unknown architectures}, {@code toString()} will return * For {@link #Unknown unknown architectures}, {@code toString()} will return
* the the same value as {@code System.getProperty("os.arch")}. * the the same value as {@code System.getProperty("os.arch")}.
* </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/lang/Platform.java#1 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/Platform.java#1 $
@@ -228,9 +229,10 @@ public final class Platform {
/** /**
* Enumeration of common {@code OperatingSystem}s. * Enumeration of common {@code OperatingSystem}s.
* <p/> * <p>
* For {@link #Unknown unknown operating systems}, {@code getName()} will return * For {@link #Unknown unknown operating systems}, {@code getName()} will return
* the the same value as {@code System.getProperty("os.name")}. * the the same value as {@code System.getProperty("os.name")}.
* </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/lang/Platform.java#1 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/lang/Platform.java#1 $

View File

@@ -1,139 +1,140 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.lang; package com.twelvemonkeys.lang;
/** /**
* Util class for various reflection-based operations. * Util class for various reflection-based operations.
* <p/> * <p>
* <em>NOTE: This class is not considered part of the public API and may be * <em>NOTE: This class is not considered part of the public API and may be
* changed without notice</em> * changed without notice</em>
* * </p>
* @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/lang/ReflectUtil.java#1 $ * @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/lang/ReflectUtil.java#1 $
public final class ReflectUtil { */
public final class ReflectUtil {
/** Don't allow instances */
private ReflectUtil() {} /** Don't allow instances */
private ReflectUtil() {}
/**
* Returns the primitive type for the given wrapper type. /**
* * Returns the primitive type for the given wrapper type.
* @param pType the wrapper type *
* * @param pType the wrapper type
* @return the primitive type *
* * @return the primitive type
* @throws IllegalArgumentException if {@code pType} is not a primitive *
* wrapper * @throws IllegalArgumentException if {@code pType} is not a primitive
*/ * wrapper
public static Class unwrapType(Class pType) { */
if (pType == Boolean.class) { public static Class unwrapType(Class pType) {
return Boolean.TYPE; if (pType == Boolean.class) {
} return Boolean.TYPE;
else if (pType == Byte.class) { }
return Byte.TYPE; else if (pType == Byte.class) {
} return Byte.TYPE;
else if (pType == Character.class) { }
return Character.TYPE; else if (pType == Character.class) {
} return Character.TYPE;
else if (pType == Double.class) { }
return Double.TYPE; else if (pType == Double.class) {
} return Double.TYPE;
else if (pType == Float.class) { }
return Float.TYPE; else if (pType == Float.class) {
} return Float.TYPE;
else if (pType == Integer.class) { }
return Integer.TYPE; else if (pType == Integer.class) {
} return Integer.TYPE;
else if (pType == Long.class) { }
return Long.TYPE; else if (pType == Long.class) {
} return Long.TYPE;
else if (pType == Short.class) { }
return Short.TYPE; else if (pType == Short.class) {
} return Short.TYPE;
}
throw new IllegalArgumentException("Not a primitive wrapper: " + pType);
} throw new IllegalArgumentException("Not a primitive wrapper: " + pType);
}
/**
* Returns the wrapper type for the given primitive type. /**
* * Returns the wrapper type for the given primitive type.
* @param pType the primitive tpye *
* * @param pType the primitive tpye
* @return the wrapper type *
* * @return the wrapper type
* @throws IllegalArgumentException if {@code pType} is not a primitive *
* type * @throws IllegalArgumentException if {@code pType} is not a primitive
*/ * type
public static Class wrapType(Class pType) { */
if (pType == Boolean.TYPE) { public static Class wrapType(Class pType) {
return Boolean.class; if (pType == Boolean.TYPE) {
} return Boolean.class;
else if (pType == Byte.TYPE) { }
return Byte.class; else if (pType == Byte.TYPE) {
} return Byte.class;
else if (pType == Character.TYPE) { }
return Character.class; else if (pType == Character.TYPE) {
} return Character.class;
else if (pType == Double.TYPE) { }
return Double.class; else if (pType == Double.TYPE) {
} return Double.class;
else if (pType == Float.TYPE) { }
return Float.class; else if (pType == Float.TYPE) {
} return Float.class;
else if (pType == Integer.TYPE) { }
return Integer.class; else if (pType == Integer.TYPE) {
} return Integer.class;
else if (pType == Long.TYPE) { }
return Long.class; else if (pType == Long.TYPE) {
} return Long.class;
else if (pType == Short.TYPE) { }
return Short.class; else if (pType == Short.TYPE) {
} return Short.class;
}
throw new IllegalArgumentException("Not a primitive type: " + pType);
} throw new IllegalArgumentException("Not a primitive type: " + pType);
}
/**
* Returns {@code true} if the given type is a primitive wrapper. /**
* * Returns {@code true} if the given type is a primitive wrapper.
* @param pType *
* * @param pType
* @return {@code true} if the given type is a primitive wrapper, otherwise *
* {@code false} * @return {@code true} if the given type is a primitive wrapper, otherwise
*/ * {@code false}
public static boolean isPrimitiveWrapper(Class pType) { */
return pType == Boolean.class || pType == Byte.class public static boolean isPrimitiveWrapper(Class pType) {
|| pType == Character.class || pType == Double.class return pType == Boolean.class || pType == Byte.class
|| pType == Float.class || pType == Integer.class || pType == Character.class || pType == Double.class
|| pType == Long.class || pType == Short.class; || pType == Float.class || pType == Integer.class
} || pType == Long.class || pType == Short.class;
} }
}

View File

@@ -36,10 +36,11 @@ import java.util.Map;
/** /**
* Kind of like {@code org.apache.commons.lang.Validate}. Just smarter. ;-) * Kind of like {@code org.apache.commons.lang.Validate}. Just smarter. ;-)
* <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.
* </p>
* *
* @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 $

View File

@@ -1,402 +1,404 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.util.*;
/** /**
* AbstractDecoratedMap * AbstractDecoratedMap
* <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/util/AbstractDecoratedMap.java#2 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/AbstractDecoratedMap.java#2 $ */
*/ // 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>> entries;
protected Map<K, Entry<K, V>> entries; protected transient volatile int modCount;
protected transient volatile int modCount;
private transient volatile Set<Entry<K, V>> entrySet = null;
private transient volatile Set<Entry<K, V>> entrySet = null; private transient volatile Set<K> keySet = null;
private transient volatile Set<K> keySet = null; private transient volatile Collection<V> values = 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}. */
*/ public AbstractDecoratedMap() {
public AbstractDecoratedMap() { this(new HashMap<K, Entry<K, V>>(), null);
this(new HashMap<K, Entry<K, V>>(), null); }
}
/**
/** * Creates a {@code Map} backed by a {@code HashMap}, containing all
* Creates a {@code Map} backed by a {@code HashMap}, containing all * key/value mappings from the given {@code Map}.
* key/value mappings from the given {@code Map}. * <p>
* <p/> * <small>This is constructor is here to comply with the reccomendations for
* <small>This is constructor is here to comply with the reccomendations for * "standard" constructors in the {@code Map} interface.</small>
* "standard" constructors in the {@code Map} interface.</small> * </p>
* *
* @see #AbstractDecoratedMap(java.util.Map, java.util.Map) * @see #AbstractDecoratedMap(java.util.Map, java.util.Map)
* *
* @param pContents the map whose mappings are to be placed in this map. * @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}. * May be {@code null}.
*/ */
public AbstractDecoratedMap(Map<? extends K, ? extends V> pContents) { public AbstractDecoratedMap(Map<? extends K, ? extends V> pContents) {
this(new HashMap<K, Entry<K, V>>(), pContents); this(new HashMap<K, Entry<K, V>>(), pContents);
} }
/** /**
* Creates a {@code Map} backed by the given backing-{@code Map}, * Creates a {@code Map} backed by the given backing-{@code Map},
* containing all key/value mappings from the given contents-{@code Map}. * containing all key/value mappings from the given contents-{@code Map}.
* <p/> * <p>
* NOTE: The backing map is structuraly cahnged, and it should NOT be * NOTE: The backing map is structuraly cahnged, and it should NOT be
* accessed directly, after the wrapped map is created. * accessed directly, after the wrapped map is created.
* * </p>
* @param pBacking the backing map of this map. Must be either empty, or *
* the same map as {@code pContents}. * @param pBacking the backing map of this map. Must be either empty, or
* @param pContents the map whose mappings are to be placed in this map. * the same map as {@code pContents}.
* May be {@code null}. * @param pContents the map whose mappings are to be placed in this map.
* * May be {@code null}.
* @throws IllegalArgumentException if {@code pBacking} is {@code null} *
* or if {@code pBacking} differs from {@code pContent} and is not empty. * @throws IllegalArgumentException if {@code pBacking} is {@code null}
*/ * or if {@code pBacking} differs from {@code pContent} and is not empty.
public AbstractDecoratedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents) { */
if (pBacking == null) { public AbstractDecoratedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents) {
throw new IllegalArgumentException("backing == null"); if (pBacking == null) {
} throw new IllegalArgumentException("backing == null");
}
Entry<? extends K, ? extends V>[] entries = null;
if (pBacking == pContents) { Entry<? extends K, ? extends V>[] entries = null;
// NOTE: Special treatment to avoid ClassCastExceptions if (pBacking == pContents) {
Set<? extends Entry<? extends K, ? extends V>> es = pContents.entrySet(); // NOTE: Special treatment to avoid ClassCastExceptions
//noinspection unchecked Set<? extends Entry<? extends K, ? extends V>> es = pContents.entrySet();
entries = new Entry[es.size()]; //noinspection unchecked
entries = es.toArray(entries); entries = new Entry[es.size()];
pContents = null; entries = es.toArray(entries);
pBacking.clear(); pContents = null;
} pBacking.clear();
else if (!pBacking.isEmpty()) { }
throw new IllegalArgumentException("backing must be empty"); else if (!pBacking.isEmpty()) {
} throw new IllegalArgumentException("backing must be empty");
}
this.entries = pBacking;
init(); this.entries = pBacking;
init();
if (pContents != null) {
putAll(pContents); if (pContents != null) {
} putAll(pContents);
else if (entries != null) { }
// Reinsert entries, this time wrapped else if (entries != null) {
for (Entry<? extends K, ? extends V> entry : entries) { // Reinsert entries, this time wrapped
put(entry.getKey(), entry.getValue()); for (Entry<? extends K, ? extends V> entry : entries) {
} put(entry.getKey(), entry.getValue());
} }
} }
}
/**
* Default implementation, does nothing. /**
*/ * Default implementation, does nothing.
protected void init() { */
} protected void init() {
}
public int size() {
return entries.size(); public int size() {
} return entries.size();
}
public void clear() {
entries.clear(); public void clear() {
modCount++; entries.clear();
init(); modCount++;
} init();
}
public boolean isEmpty() {
return entries.isEmpty(); public boolean isEmpty() {
} return entries.isEmpty();
}
public boolean containsKey(Object pKey) {
return entries.containsKey(pKey); public boolean containsKey(Object pKey) {
} return entries.containsKey(pKey);
}
/**
* Returns {@code true} if this map maps one or more keys to the /**
* specified pValue. More formally, returns {@code true} if and only if * Returns {@code true} if this map maps one or more keys to the
* this map contains at least one mapping to a pValue {@code v} such that * specified pValue. More formally, returns {@code true} if and only if
* {@code (pValue==null ? v==null : pValue.equals(v))}. * this map contains at least one mapping to a pValue {@code v} such that
* <p/> * {@code (pValue==null ? v==null : pValue.equals(v))}.
* This implementation requires time linear in the map size for this * <p>
* operation. * This implementation requires time linear in the map size for this
* * operation.
* @param pValue pValue whose presence in this map is to be tested. * </p>
* @return {@code true} if this map maps one or more keys to the *
* specified pValue. * @param pValue pValue whose presence in this map is to be tested.
*/ * @return {@code true} if this map maps one or more keys to the
public boolean containsValue(Object pValue) { * specified pValue.
for (V value : values()) { */
if (value == pValue || (value != null && value.equals(pValue))) { public boolean containsValue(Object pValue) {
return true; for (V value : values()) {
} if (value == pValue || (value != null && value.equals(pValue))) {
} return true;
}
return false; }
}
return false;
public Collection<V> values() { }
Collection<V> values = this.values;
return values != null ? values : (this.values = new Values()); public Collection<V> values() {
} Collection<V> values = this.values;
return values != null ? values : (this.values = new Values());
public Set<Entry<K, V>> entrySet() { }
Set<Entry<K, V>> es = entrySet;
return es != null ? es : (entrySet = new EntrySet()); public Set<Entry<K, V>> entrySet() {
} Set<Entry<K, V>> es = entrySet;
return es != null ? es : (entrySet = new EntrySet());
public Set<K> keySet() { }
Set<K> ks = keySet;
return ks != null ? ks : (keySet = new KeySet()); public Set<K> keySet() {
} Set<K> ks = keySet;
return ks != null ? ks : (keySet = new KeySet());
/** }
* Returns a shallow copy of this {@code AbstractMap} instance: the keys
* and values themselves are not cloned. /**
* * Returns a shallow copy of this {@code AbstractMap} instance: the keys
* @return a shallow copy of this map. * and values themselves are not cloned.
*/ *
protected Object clone() throws CloneNotSupportedException { * @return a shallow copy of this map.
AbstractDecoratedMap map = (AbstractDecoratedMap) super.clone(); */
protected Object clone() throws CloneNotSupportedException {
map.values = null; AbstractDecoratedMap map = (AbstractDecoratedMap) super.clone();
map.entrySet = null;
map.keySet = null; map.values = null;
map.entrySet = null;
// TODO: Implement: Need to clone the backing map... map.keySet = null;
return map; // TODO: Implement: Need to clone the backing map...
}
return map;
// Subclass overrides these to alter behavior of views' iterator() method }
protected abstract Iterator<K> newKeyIterator();
// Subclass overrides these to alter behavior of views' iterator() method
protected abstract Iterator<V> newValueIterator(); protected abstract Iterator<K> newKeyIterator();
protected abstract Iterator<Entry<K, V>> newEntryIterator(); protected abstract Iterator<V> newValueIterator();
// TODO: Implement these (get/put/remove)? protected abstract Iterator<Entry<K, V>> newEntryIterator();
public abstract V get(Object pKey);
// TODO: Implement these (get/put/remove)?
public abstract V remove(Object pKey); public abstract V get(Object pKey);
public abstract V put(K pKey, V pValue); public abstract V remove(Object pKey);
/*protected*/ Entry<K, V> createEntry(K pKey, V pValue) { public abstract V put(K pKey, V pValue);
return new BasicEntry<K, V>(pKey, pValue);
} /*protected*/ Entry<K, V> createEntry(K pKey, V pValue) {
return new BasicEntry<K, V>(pKey, pValue);
/*protected*/ Entry<K, V> getEntry(K pKey) { }
return entries.get(pKey);
} /*protected*/ Entry<K, V> getEntry(K pKey) {
return entries.get(pKey);
/** }
* Removes the given entry from the Map.
* /**
* @param pEntry the entry to be removed * Removes the given entry from the Map.
* *
* @return the removed entry, or {@code null} if nothing was removed. * @param pEntry the entry to be removed
*/ *
protected Entry<K, V> removeEntry(Entry<K, V> pEntry) { * @return the removed entry, or {@code null} if nothing was removed.
if (pEntry == null) { */
return null; protected Entry<K, V> removeEntry(Entry<K, V> pEntry) {
} if (pEntry == null) {
return null;
// Find candidate entry for this key }
Entry<K, V> candidate = getEntry(pEntry.getKey());
if (candidate == pEntry || (candidate != null && candidate.equals(pEntry))) { // Find candidate entry for this key
// Remove Entry<K, V> candidate = getEntry(pEntry.getKey());
remove(pEntry.getKey()); if (candidate == pEntry || (candidate != null && candidate.equals(pEntry))) {
return pEntry; // Remove
} remove(pEntry.getKey());
return null; return pEntry;
} }
return null;
protected class Values extends AbstractCollection<V> { }
public Iterator<V> iterator() {
return newValueIterator(); protected class Values extends AbstractCollection<V> {
} public Iterator<V> iterator() {
return newValueIterator();
public int size() { }
return AbstractDecoratedMap.this.size();
} public int size() {
return AbstractDecoratedMap.this.size();
public boolean contains(Object o) { }
return containsValue(o);
} public boolean contains(Object o) {
return containsValue(o);
public void clear() { }
AbstractDecoratedMap.this.clear();
} public void clear() {
} AbstractDecoratedMap.this.clear();
}
protected class EntrySet extends AbstractSet<Entry<K, V>> { }
public Iterator<Entry<K, V>> iterator() {
return newEntryIterator(); protected class EntrySet extends AbstractSet<Entry<K, V>> {
} public Iterator<Entry<K, V>> iterator() {
return newEntryIterator();
public boolean contains(Object o) { }
if (!(o instanceof Entry))
return false; public boolean contains(Object o) {
Entry e = (Entry) o; if (!(o instanceof Entry))
return false;
//noinspection SuspiciousMethodCalls Entry e = (Entry) o;
Entry<K, V> candidate = entries.get(e.getKey());
return candidate != null && candidate.equals(e); //noinspection SuspiciousMethodCalls
} Entry<K, V> candidate = entries.get(e.getKey());
return candidate != null && candidate.equals(e);
public boolean remove(Object o) { }
if (!(o instanceof Entry)) {
return false; public boolean remove(Object o) {
} if (!(o instanceof Entry)) {
return false;
/* }
// NOTE: Extra cautions is taken, to only remove the entry if it
// equals the entry in the map /*
Object key = ((Entry) o).getKey(); // NOTE: Extra cautions is taken, to only remove the entry if it
Entry entry = (Entry) entries.get(key); // equals the entry in the map
Object key = ((Entry) o).getKey();
// Same entry? Entry entry = (Entry) entries.get(key);
if (entry != null && entry.equals(o)) {
return AbstractWrappedMap.this.remove(key) != null; // Same entry?
} if (entry != null && entry.equals(o)) {
return AbstractWrappedMap.this.remove(key) != null;
return false; }
*/
return false;
//noinspection unchecked */
return AbstractDecoratedMap.this.removeEntry((Entry) o) != null;
} //noinspection unchecked
return AbstractDecoratedMap.this.removeEntry((Entry) o) != null;
public int size() { }
return AbstractDecoratedMap.this.size();
} public int size() {
return AbstractDecoratedMap.this.size();
public void clear() { }
AbstractDecoratedMap.this.clear();
} public void clear() {
} AbstractDecoratedMap.this.clear();
}
protected class KeySet extends AbstractSet<K> { }
public Iterator<K> iterator() {
return newKeyIterator(); protected class KeySet extends AbstractSet<K> {
} public Iterator<K> iterator() {
public int size() { return newKeyIterator();
return AbstractDecoratedMap.this.size(); }
} public int size() {
public boolean contains(Object o) { return AbstractDecoratedMap.this.size();
return containsKey(o); }
} public boolean contains(Object o) {
public boolean remove(Object o) { return containsKey(o);
return AbstractDecoratedMap.this.remove(o) != null; }
} public boolean remove(Object o) {
public void clear() { return AbstractDecoratedMap.this.remove(o) != null;
AbstractDecoratedMap.this.clear(); }
} public void clear() {
} AbstractDecoratedMap.this.clear();
}
/** }
* A simple Map.Entry implementaton.
*/ /**
static class BasicEntry<K, V> implements Entry<K, V>, Serializable { * A simple Map.Entry implementaton.
K mKey; */
V mValue; static class BasicEntry<K, V> implements Entry<K, V>, Serializable {
K mKey;
BasicEntry(K pKey, V pValue) { V mValue;
mKey = pKey;
mValue = pValue; BasicEntry(K pKey, V pValue) {
} mKey = pKey;
mValue = pValue;
/** }
* Default implementation does nothing.
* /**
* @param pMap the map that is accessed * Default implementation does nothing.
*/ *
protected void recordAccess(Map<K, V> pMap) { * @param pMap the map that is accessed
} */
protected void recordAccess(Map<K, V> pMap) {
/** }
* Default implementation does nothing.
* @param pMap the map that is removed from /**
*/ * Default implementation does nothing.
protected void recordRemoval(Map<K, V> pMap) { * @param pMap the map that is removed from
} */
protected void recordRemoval(Map<K, V> pMap) {
public V getValue() { }
return mValue;
} public V getValue() {
return mValue;
public V setValue(V pValue) { }
V oldValue = mValue;
mValue = pValue; public V setValue(V pValue) {
return oldValue; V oldValue = mValue;
} mValue = pValue;
return oldValue;
public K getKey() { }
return mKey;
} public K getKey() {
return mKey;
public boolean equals(Object pOther) { }
if (!(pOther instanceof Map.Entry)) {
return false; public boolean equals(Object pOther) {
} if (!(pOther instanceof Map.Entry)) {
return false;
Map.Entry entry = (Map.Entry) pOther; }
Object k1 = mKey; Map.Entry entry = (Map.Entry) pOther;
Object k2 = entry.getKey();
Object k1 = mKey;
if (k1 == k2 || (k1 != null && k1.equals(k2))) { Object k2 = entry.getKey();
Object v1 = mValue;
Object v2 = entry.getValue(); if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = mValue;
if (v1 == v2 || (v1 != null && v1.equals(v2))) { Object v2 = entry.getValue();
return true;
} if (v1 == v2 || (v1 != null && v1.equals(v2))) {
} return true;
}
return false; }
}
return false;
public int hashCode() { }
return (mKey == null ? 0 : mKey.hashCode()) ^
(mValue == null ? 0 : mValue.hashCode()); public int hashCode() {
} return (mKey == null ? 0 : mKey.hashCode()) ^
(mValue == null ? 0 : mValue.hashCode());
public String toString() { }
return getKey() + "=" + getValue();
} public String toString() {
} return getKey() + "=" + getValue();
}
}
} }

View File

@@ -1,89 +1,88 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
/** /**
* Abstract base class for {@code TokenIterator}s to extend. * Abstract base class for {@code TokenIterator}s to extend.
* <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/util/AbstractTokenIterator.java#1 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/AbstractTokenIterator.java#1 $ */
*/ public abstract class AbstractTokenIterator implements TokenIterator {
public abstract class AbstractTokenIterator implements TokenIterator {
/**
/** * Not supported.
* Not supported. *
* * @throws UnsupportedOperationException {@code remove} is not supported by
* @throws UnsupportedOperationException {@code remove} is not supported by * this Iterator.
* this Iterator. */
*/ public void remove() {
public void remove() { // TODO: This is not difficult:
// TODO: This is not difficult: // - Convert String to StringBuilder in constructor
// - Convert String to StringBuilder in constructor // - delete(pos, next.lenght())
// - delete(pos, next.lenght()) // - Add toString() method
// - Add toString() method // BUT: Would it ever be useful? :-)
// BUT: Would it ever be useful? :-)
throw new UnsupportedOperationException("remove");
throw new UnsupportedOperationException("remove"); }
}
public final boolean hasMoreTokens() {
public final boolean hasMoreTokens() { return hasNext();
return hasNext(); }
}
/**
/** * Returns the next element in the iteration as a {@code String}.
* Returns the next element in the iteration as a {@code String}. * This implementation simply returns {@code (String) next()}.
* This implementation simply returns {@code (String) next()}. *
* * @return the next element in the iteration.
* @return the next element in the iteration. * @exception java.util.NoSuchElementException iteration has no more elements.
* @exception java.util.NoSuchElementException iteration has no more elements. * @see #next()
* @see #next() */
*/ public final String nextToken() {
public final String nextToken() { return next();
return next(); }
}
/**
/** * This implementation simply returns {@code hasNext()}.
* This implementation simply returns {@code hasNext()}. * @see #hasNext()
* @see #hasNext() */
*/ public final boolean hasMoreElements() {
public final boolean hasMoreElements() { return hasNext();
return hasNext(); }
}
/**
/** * This implementation simply returns {@code next()}.
* This implementation simply returns {@code next()}. * @see #next()
* @see #next() */
*/ public final String nextElement() {
public final String nextElement() { return next();
return next(); }
} }
}

View File

@@ -1,247 +1,248 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import java.beans.IndexedPropertyDescriptor; import java.beans.IndexedPropertyDescriptor;
import java.beans.IntrospectionException; import java.beans.IntrospectionException;
import java.beans.Introspector; import java.beans.Introspector;
import java.beans.PropertyDescriptor; import java.beans.PropertyDescriptor;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;
/** /**
* A {@code Map} adapter for a Java Bean. * A {@code Map} adapter for a Java Bean.
* <p/> * <p>
* Ruthlessly 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>
*/ * </p>
public final class BeanMap extends AbstractMap<String, Object> implements Serializable, Cloneable { */
private final Object bean; public final class BeanMap extends AbstractMap<String, Object> implements Serializable, Cloneable {
private transient Set<PropertyDescriptor> descriptors; private final Object bean;
private transient Set<PropertyDescriptor> descriptors;
public BeanMap(Object pBean) throws IntrospectionException {
if (pBean == null) { public BeanMap(Object pBean) throws IntrospectionException {
throw new IllegalArgumentException("bean == null"); if (pBean == null) {
} throw new IllegalArgumentException("bean == null");
}
bean = pBean;
descriptors = initDescriptors(pBean); bean = pBean;
} descriptors = initDescriptors(pBean);
}
private static Set<PropertyDescriptor> initDescriptors(Object pBean) throws IntrospectionException {
final Set<PropertyDescriptor> descriptors = new HashSet<PropertyDescriptor>(); private static Set<PropertyDescriptor> initDescriptors(Object pBean) throws IntrospectionException {
final Set<PropertyDescriptor> descriptors = new HashSet<PropertyDescriptor>();
PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(pBean.getClass()).getPropertyDescriptors();
for (PropertyDescriptor descriptor : propertyDescriptors) { PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(pBean.getClass()).getPropertyDescriptors();
// Skip Object.getClass(), as you probably never want it for (PropertyDescriptor descriptor : propertyDescriptors) {
if ("class".equals(descriptor.getName()) && descriptor.getPropertyType() == Class.class) { // Skip Object.getClass(), as you probably never want it
continue; if ("class".equals(descriptor.getName()) && descriptor.getPropertyType() == Class.class) {
} continue;
}
// Only support simple setter/getters.
if (!(descriptor instanceof IndexedPropertyDescriptor)) { // Only support simple setter/getters.
descriptors.add(descriptor); if (!(descriptor instanceof IndexedPropertyDescriptor)) {
} descriptors.add(descriptor);
} }
}
return Collections.unmodifiableSet(descriptors);
} return Collections.unmodifiableSet(descriptors);
}
public Set<Entry<String, Object>> entrySet() {
return new BeanSet(); public Set<Entry<String, Object>> entrySet() {
} return new BeanSet();
}
public Object get(final Object pKey) {
return super.get(pKey); public Object get(final Object pKey) {
} return super.get(pKey);
}
public Object put(final String pKey, final Object pValue) {
checkKey(pKey); public Object put(final String pKey, final Object pValue) {
checkKey(pKey);
for (Entry<String, Object> entry : entrySet()) {
if (entry.getKey().equals(pKey)) { for (Entry<String, Object> entry : entrySet()) {
return entry.setValue(pValue); if (entry.getKey().equals(pKey)) {
} return entry.setValue(pValue);
} }
}
return null;
} return null;
}
public Object remove(final Object pKey) {
return super.remove(checkKey(pKey)); public Object remove(final Object pKey) {
} return super.remove(checkKey(pKey));
}
public int size() {
return descriptors.size(); public int size() {
} return descriptors.size();
}
private String checkKey(final Object pKey) {
if (pKey == null) { private String checkKey(final Object pKey) {
throw new IllegalArgumentException("key == null"); if (pKey == null) {
} throw new IllegalArgumentException("key == null");
// NB - the cast forces CCE if key is the wrong type. }
final String name = (String) pKey; // NB - the cast forces CCE if key is the wrong type.
final String name = (String) pKey;
if (!containsKey(name)) {
throw new IllegalArgumentException("Bad key: " + pKey); if (!containsKey(name)) {
} throw new IllegalArgumentException("Bad key: " + pKey);
}
return name;
} return name;
}
private Object readResolve() throws IntrospectionException {
// Initialize the property descriptors private Object readResolve() throws IntrospectionException {
descriptors = initDescriptors(bean); // Initialize the property descriptors
return this; descriptors = initDescriptors(bean);
} return this;
}
private class BeanSet extends AbstractSet<Entry<String, Object>> {
public Iterator<Entry<String, Object>> iterator() { private class BeanSet extends AbstractSet<Entry<String, Object>> {
return new BeanIterator(descriptors.iterator()); public Iterator<Entry<String, Object>> iterator() {
} return new BeanIterator(descriptors.iterator());
}
public int size() {
return descriptors.size(); public int size() {
} return descriptors.size();
} }
}
private class BeanIterator implements Iterator<Entry<String, Object>> {
private final Iterator<PropertyDescriptor> mIterator; private class BeanIterator implements Iterator<Entry<String, Object>> {
private final Iterator<PropertyDescriptor> mIterator;
public BeanIterator(final Iterator<PropertyDescriptor> pIterator) {
mIterator = pIterator; public BeanIterator(final Iterator<PropertyDescriptor> pIterator) {
} mIterator = pIterator;
}
public boolean hasNext() {
return mIterator.hasNext(); public boolean hasNext() {
} return mIterator.hasNext();
}
public BeanEntry next() {
return new BeanEntry(mIterator.next()); public BeanEntry next() {
} return new BeanEntry(mIterator.next());
}
public void remove() {
mIterator.remove(); public void remove() {
} mIterator.remove();
} }
}
private class BeanEntry implements Entry<String, Object> {
private final PropertyDescriptor mDescriptor; private class BeanEntry implements Entry<String, Object> {
private final PropertyDescriptor mDescriptor;
public BeanEntry(final PropertyDescriptor pDescriptor) {
this.mDescriptor = pDescriptor; public BeanEntry(final PropertyDescriptor pDescriptor) {
} this.mDescriptor = pDescriptor;
}
public String getKey() {
return mDescriptor.getName(); public String getKey() {
} return mDescriptor.getName();
}
public Object getValue() {
return unwrap(new Wrapped() { public Object getValue() {
public Object run() throws IllegalAccessException, InvocationTargetException { return unwrap(new Wrapped() {
final Method method = mDescriptor.getReadMethod(); public Object run() throws IllegalAccessException, InvocationTargetException {
// A write-only bean. final Method method = mDescriptor.getReadMethod();
if (method == null) { // A write-only bean.
throw new UnsupportedOperationException("No getter: " + mDescriptor.getName()); if (method == null) {
} throw new UnsupportedOperationException("No getter: " + mDescriptor.getName());
}
return method.invoke(bean);
} return method.invoke(bean);
}); }
} });
}
public Object setValue(final Object pValue) {
return unwrap(new Wrapped() { public Object setValue(final Object pValue) {
public Object run() throws IllegalAccessException, InvocationTargetException { return unwrap(new Wrapped() {
final Method method = mDescriptor.getWriteMethod(); public Object run() throws IllegalAccessException, InvocationTargetException {
// A read-only bean. final Method method = mDescriptor.getWriteMethod();
if (method == null) { // A read-only bean.
throw new UnsupportedOperationException("No write method for property: " + mDescriptor.getName()); if (method == null) {
} throw new UnsupportedOperationException("No write method for property: " + mDescriptor.getName());
}
final Object old = getValue();
method.invoke(bean, pValue); final Object old = getValue();
return old; method.invoke(bean, pValue);
} return old;
}); }
} });
}
public boolean equals(Object pOther) {
if (!(pOther instanceof Map.Entry)) { public boolean equals(Object pOther) {
return false; if (!(pOther instanceof Map.Entry)) {
} return false;
}
Map.Entry entry = (Map.Entry) pOther;
Map.Entry entry = (Map.Entry) pOther;
Object k1 = getKey();
Object k2 = entry.getKey(); Object k1 = getKey();
Object k2 = entry.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue(); if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v2 = entry.getValue(); Object v1 = getValue();
Object v2 = entry.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2))) {
return true; if (v1 == v2 || (v1 != null && v1.equals(v2))) {
} return true;
} }
}
return false;
} return false;
}
public int hashCode() {
return (getKey() == null ? 0 : getKey().hashCode()) ^ public int hashCode() {
(getValue() == null ? 0 : getValue().hashCode()); return (getKey() == null ? 0 : getKey().hashCode()) ^
} (getValue() == null ? 0 : getValue().hashCode());
}
public String toString() {
return getKey() + "=" + getValue(); public String toString() {
} return getKey() + "=" + getValue();
} }
}
private static interface Wrapped {
Object run() throws IllegalAccessException, InvocationTargetException; private static interface Wrapped {
} Object run() throws IllegalAccessException, InvocationTargetException;
}
private static Object unwrap(final Wrapped wrapped) {
try { private static Object unwrap(final Wrapped wrapped) {
return wrapped.run(); try {
} return wrapped.run();
catch (IllegalAccessException e) { }
throw new RuntimeException(e); catch (IllegalAccessException e) {
} throw new RuntimeException(e);
catch (final InvocationTargetException e) { }
// Javadocs for setValue indicate cast is ok. catch (final InvocationTargetException e) {
throw (RuntimeException) e.getCause(); // Javadocs for setValue indicate cast is ok.
} throw (RuntimeException) e.getCause();
} }
}
} }

View File

@@ -1,153 +1,152 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
/** /**
* DuplicateHandler * DuplicateHandler
* <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/util/DuplicateHandler.java#2 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/DuplicateHandler.java#2 $ */
*/ public interface DuplicateHandler<T> {
public interface DuplicateHandler<T> {
/**
/** * Resolves duplicates according to a certain strategy.
* Resolves duplicates according to a certain strategy. *
* * @param pOld the old value
* @param pOld the old value * @param pNew the new value
* @param pNew the new value *
* * @return the resolved value.
* @return the resolved value. *
* * @throws IllegalArgumentException is the arguments cannot be resolved for
* @throws IllegalArgumentException is the arguments cannot be resolved for * some reason.
* some reason. */
*/ public T resolve(T pOld, T pNew);
public T resolve(T pOld, T pNew);
/**
/** * Will use the first (old) value. Any new values will be discarded.
* Will use the first (old) value. Any new values will be discarded. *
* * @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler) */
*/ public final static DuplicateHandler<?> USE_FIRST_VALUE = new DuplicateHandler() {
public final static DuplicateHandler<?> USE_FIRST_VALUE = new DuplicateHandler() { /**
/** * Returns {@code pOld}.
* Returns {@code pOld}. *
* * @param pOld the old value
* @param pOld the old value * @param pNew the new value
* @param pNew the new value *
* * @return {@code pOld}
* @return {@code pOld} */
*/ public Object resolve(Object pOld, Object pNew) {
public Object resolve(Object pOld, Object pNew) { return pOld;
return pOld; }
} };
};
/**
/** * Will use the last (new) value. Any old values will be discarded
* Will use the last (new) value. Any old values will be discarded * (overwritten).
* (overwritten). *
* * @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler) */
*/ public final static DuplicateHandler<?> USE_LAST_VALUE = new DuplicateHandler() {
public final static DuplicateHandler<?> USE_LAST_VALUE = new DuplicateHandler() { /**
/** * Returns {@code pNew}.
* Returns {@code pNew}. *
* * @param pOld the old value
* @param pOld the old value * @param pNew the new value
* @param pNew the new value *
* * @return {@code pNew}
* @return {@code pNew} */
*/ public Object resolve(Object pOld, Object pNew) {
public Object resolve(Object pOld, Object pNew) { return pNew;
return pNew; }
} };
};
/**
/** * Converts duplicats to an {@code Object} array.
* Converts duplicats to an {@code Object} array. *
* * @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler) */
*/ public final static DuplicateHandler<?> DUPLICATES_AS_ARRAY = new DuplicateHandler() {
public final static DuplicateHandler<?> DUPLICATES_AS_ARRAY = new DuplicateHandler() { /**
/** * Returns an {@code Object} array, containing {@code pNew} as its
* Returns an {@code Object} array, containing {@code pNew} as its * last element.
* last element. *
* * @param pOld the old value
* @param pOld the old value * @param pNew the new value
* @param pNew the new value *
* * @return an {@code Object} array, containing {@code pNew} as its
* @return an {@code Object} array, containing {@code pNew} as its * last element.
* last element. */
*/ public Object resolve(Object pOld, Object pNew) {
public Object resolve(Object pOld, Object pNew) { Object[] result;
Object[] result;
if (pOld instanceof Object[]) {
if (pOld instanceof Object[]) { Object[] old = ((Object[]) pOld);
Object[] old = ((Object[]) pOld); result = new Object[old.length + 1];
result = new Object[old.length + 1]; System.arraycopy(old, 0, result, 0, old.length);
System.arraycopy(old, 0, result, 0, old.length); result[old.length] = pNew;
result[old.length] = pNew; }
} else {
else { result = new Object[] {pOld, pNew};
result = new Object[] {pOld, pNew}; }
}
return result;
return result; }
} };
};
/**
/** * Converts duplicates to a comma-separated {@code String}.
* Converts duplicates to a comma-separated {@code String}. * Note that all values should allready be {@code String}s if using this
* Note that all values should allready be {@code String}s if using this * handler.
* handler. *
* * @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler)
* @see CollectionUtil#invert(java.util.Map, java.util.Map, DuplicateHandler) */
*/ public final static DuplicateHandler<String> DUPLICATES_AS_CSV = new DuplicateHandler<String>() {
public final static DuplicateHandler<String> DUPLICATES_AS_CSV = new DuplicateHandler<String>() { /**
/** * Returns a comma-separated {@code String}, with the string
* Returns a comma-separated {@code String}, with the string * representation of {@code pNew} as the last element.
* representation of {@code pNew} as the last element. *
* * @param pOld the old value
* @param pOld the old value * @param pNew the new value
* @param pNew the new value *
* * @return a comma-separated {@code String}, with the string
* @return a comma-separated {@code String}, with the string * representation of {@code pNew} as the last element.
* representation of {@code pNew} as the last element. */
*/ public String resolve(String pOld, String pNew) {
public String resolve(String pOld, String pNew) { StringBuilder result = new StringBuilder(String.valueOf(pOld));
StringBuilder result = new StringBuilder(String.valueOf(pOld)); result.append(',');
result.append(','); result.append(pNew);
result.append(pNew);
return result.toString();
return result.toString(); }
} };
}; }
}

View File

@@ -1,153 +1,154 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import java.util.Iterator; import java.util.Iterator;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
/** /**
* Wraps (decorates) an {@code Iterator} with extra functionality, to allow * Wraps (decorates) an {@code Iterator} with extra functionality, to allow
* element filtering. Each * element filtering. Each
* element is filtered against the given {@code Filter}, and only elements * element is filtered against the given {@code Filter}, and only elements
* that are {@code accept}ed are returned by the {@code next} method. * that are {@code accept}ed are returned by the {@code next} method.
* <p/> * <p>
* The optional {@code remove} operation is implemented, but may throw * The optional {@code remove} operation is implemented, but may throw
* {@code UnsupportedOperationException} if the underlying iterator does not * {@code UnsupportedOperationException} if the underlying iterator does not
* support the remove operation. * support the remove operation.
* * </p>
* @see FilterIterator.Filter *
* * @see FilterIterator.Filter
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> *
* @author last modified by $Author: haku $ * @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/util/FilterIterator.java#1 $ * @author last modified by $Author: haku $
*/ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/FilterIterator.java#1 $
public class FilterIterator<E> implements Iterator<E> { */
public class FilterIterator<E> implements Iterator<E> {
protected final Filter<E> filter;
protected final Iterator<E> iterator; protected final Filter<E> filter;
protected final Iterator<E> iterator;
private E next = null;
private E current = null; private E next = null;
private E current = null;
/**
* Creates a {@code FilterIterator} that wraps the {@code Iterator}. Each /**
* element is filtered against the given {@code Filter}, and only elements * Creates a {@code FilterIterator} that wraps the {@code Iterator}. Each
* that are {@code accept}ed are returned by the {@code next} method. * element is filtered against the given {@code Filter}, and only elements
* * that are {@code accept}ed are returned by the {@code next} method.
* @param pIterator the iterator to filter *
* @param pFilter the filter * @param pIterator the iterator to filter
* @see FilterIterator.Filter * @param pFilter the filter
*/ * @see FilterIterator.Filter
public FilterIterator(final Iterator<E> pIterator, final Filter<E> pFilter) { */
if (pIterator == null) { public FilterIterator(final Iterator<E> pIterator, final Filter<E> pFilter) {
throw new IllegalArgumentException("iterator == null"); if (pIterator == null) {
} throw new IllegalArgumentException("iterator == null");
if (pFilter == null) { }
throw new IllegalArgumentException("filter == null"); if (pFilter == null) {
} throw new IllegalArgumentException("filter == null");
}
iterator = pIterator;
filter = pFilter; iterator = pIterator;
} filter = pFilter;
}
/**
* Returns {@code true} if the iteration has more elements. (In other /**
* words, returns {@code true} if {@code next} would return an element * Returns {@code true} if the iteration has more elements. (In other
* rather than throwing an exception.) * words, returns {@code true} if {@code next} would return an element
* * rather than throwing an exception.)
* @return {@code true} if the iterator has more elements. *
* @see FilterIterator.Filter#accept * @return {@code true} if the iterator has more elements.
*/ * @see FilterIterator.Filter#accept
public boolean hasNext() { */
while (next == null && iterator.hasNext()) { public boolean hasNext() {
E element = iterator.next(); while (next == null && iterator.hasNext()) {
E element = iterator.next();
if (filter.accept(element)) {
next = element; if (filter.accept(element)) {
break; next = element;
} break;
} }
}
return next != null;
} return next != null;
}
/**
* Returns the next element in the iteration. /**
* * Returns the next element in the iteration.
* @return the next element in the iteration. *
* @see FilterIterator.Filter#accept * @return the next element in the iteration.
*/ * @see FilterIterator.Filter#accept
public E next() { */
if (hasNext()) { public E next() {
current = next; if (hasNext()) {
current = next;
// Make sure we advance next time
next = null; // Make sure we advance next time
return current; next = null;
} return current;
else { }
throw new NoSuchElementException("Iteration has no more elements."); else {
} throw new NoSuchElementException("Iteration has no more elements.");
} }
}
/**
* Removes from the underlying collection the last element returned by the /**
* iterator (optional operation). This method can be called only once per * Removes from the underlying collection the last element returned by the
* call to {@code next}. The behavior of an iterator is unspecified if * iterator (optional operation). This method can be called only once per
* the underlying collection is modified while the iteration is in * call to {@code next}. The behavior of an iterator is unspecified if
* progress in any way other than by calling this method. * the underlying collection is modified while the iteration is in
*/ * progress in any way other than by calling this method.
public void remove() { */
if (current != null) { public void remove() {
iterator.remove(); if (current != null) {
} iterator.remove();
else { }
throw new IllegalStateException("Iteration has no current element."); else {
} throw new IllegalStateException("Iteration has no current element.");
} }
}
/**
* Used to tests whether or not an element fulfills certain criteria, and /**
* hence should be accepted by the FilterIterator instance. * Used to tests whether or not an element fulfills certain criteria, and
*/ * hence should be accepted by the FilterIterator instance.
public static interface Filter<E> { */
public static interface Filter<E> {
/**
* Tests whether or not the element fulfills certain criteria, and hence /**
* should be accepted. * Tests whether or not the element fulfills certain criteria, and hence
* * should be accepted.
* @param pElement the element to test *
* @return {@code true} if the object is accepted, otherwise * @param pElement the element to test
* {@code false} * @return {@code true} if the object is accepted, otherwise
*/ * {@code false}
public boolean accept(E pElement); */
} public boolean accept(E pElement);
}
} }

View File

@@ -1,178 +1,180 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import java.io.Serializable; import java.io.Serializable;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
/** /**
* A {@code Map} decorator that makes the mappings in the backing map * A {@code Map} decorator that makes the mappings in the backing map
* case insensitive * case insensitive
* (this is implemented by converting all keys to uppercase), * (this is implemented by converting all keys to uppercase),
* if the keys used are {@code Strings}. If the keys * if the keys used are {@code Strings}. If the keys
* used are not {@code String}s, it wil work as a normal * used are not {@code String}s, it wil work as a normal
* {@code java.util.Map}. * {@code java.util.Map}.
* <p/> *
* * @see java.util.Map
* @see java.util.Map *
* * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> */
*/ public class IgnoreCaseMap<V> extends AbstractDecoratedMap<String, V> implements Serializable, Cloneable {
public class IgnoreCaseMap<V> extends AbstractDecoratedMap<String, V> implements Serializable, Cloneable {
/**
/** * Constructs a new empty {@code Map}.
* Constructs a new empty {@code Map}. * The backing map will be a {@link java.util.HashMap}
* The backing map will be a {@link java.util.HashMap} */
*/ public IgnoreCaseMap() {
public IgnoreCaseMap() { super();
super(); }
}
/**
/** * Constructs a new {@code Map} with the same key-value mappings as the
* Constructs a new {@code Map} with the same key-value mappings as the * given {@code Map}.
* given {@code Map}. * The backing map will be a {@link java.util.HashMap}
* The backing map will be a {@link java.util.HashMap} * <p>
* <p/> * NOTE: As the keys in the given map parameter will be converted to
* NOTE: As the keys in the given map parameter will be converted to * uppercase (if they are strings), any duplicate key/value pair where
* uppercase (if they are strings), any duplicate key/value pair where * {@code key instanceof String && key.equalsIgnoreCase(otherKey)}
* {@code key instanceof String && key.equalsIgnoreCase(otherKey)} * is true, will be lost.
* is true, will be lost. * </p>
* *
* @param pMap the map whose mappings are to be placed in this map. * @param pMap the map whose mappings are to be placed in this map.
*/ */
public IgnoreCaseMap(Map<String, ? extends V> pMap) { public IgnoreCaseMap(Map<String, ? extends V> pMap) {
super(pMap); super(pMap);
} }
/** /**
* Constructs a new {@code Map} with the same key-value mappings as the * Constructs a new {@code Map} with the same key-value mappings as the
* given {@code Map}. * given {@code Map}.
* <p/> * <p>
* NOTE: The backing map is structuraly cahnged, and it should NOT be * NOTE: The backing map is structuraly cahnged, and it should NOT be
* accessed directly, after the wrapped map is created. * accessed directly, after the wrapped map is created.
* <p/> * </p>
* NOTE: As the keys in the given map parameter will be converted to * <p>
* uppercase (if they are strings), any duplicate key/value pair where * NOTE: As the keys in the given map parameter will be converted to
* {@code key instanceof String && key.equalsIgnoreCase(otherKey)} * uppercase (if they are strings), any duplicate key/value pair where
* is true, will be lost. * {@code key instanceof String && key.equalsIgnoreCase(otherKey)}
* * is true, will be lost.
* @param pBacking the backing map of this map. Must be either empty, or * </p>
* the same map as {@code pContents}. *
* @param pContents the map whose mappings are to be placed in this map. * @param pBacking the backing map of this map. Must be either empty, or
* May be {@code null} * the same map as {@code pContents}.
* * @param pContents the map whose mappings are to be placed in this map.
* @throws IllegalArgumentException if {@code pBacking} is {@code null} * May be {@code null}
* @throws IllegalArgumentException if {@code pBacking} differs from *
* {@code pContent} and is not empty. * @throws IllegalArgumentException if {@code pBacking} is {@code null}
*/ * @throws IllegalArgumentException if {@code pBacking} differs from
public IgnoreCaseMap(Map pBacking, Map<String, ? extends V> pContents) { * {@code pContent} and is not empty.
super(pBacking, pContents); */
} public IgnoreCaseMap(Map pBacking, Map<String, ? extends V> pContents) {
super(pBacking, pContents);
/** }
* Maps the specified key to the specified value in this map.
* Note: If the key used is a string, the key will not be case-sensitive. /**
* * Maps the specified key to the specified value in this map.
* @param pKey the map key. * Note: If the key used is a string, the key will not be case-sensitive.
* @param pValue the value. *
* @return the previous value of the specified key in this map, * @param pKey the map key.
* or null if it did not have one. * @param pValue the value.
*/ * @return the previous value of the specified key in this map,
public V put(String pKey, V pValue) { * or null if it did not have one.
String key = (String) toUpper(pKey); */
return unwrap(entries.put(key, new BasicEntry<String, V>(key, pValue))); public V put(String pKey, V pValue) {
} String key = (String) toUpper(pKey);
return unwrap(entries.put(key, new BasicEntry<String, V>(key, pValue)));
private V unwrap(Entry<String, V> pEntry) { }
return pEntry != null ? pEntry.getValue() : null;
} private V unwrap(Entry<String, V> pEntry) {
return pEntry != null ? pEntry.getValue() : null;
/** }
* Returns the value to which the specified key is mapped in this
* map. /**
* Note: If the key used is a string, the key will not be case-sensitive. * Returns the value to which the specified key is mapped in this
* * map.
* @param pKey a key in the map * Note: If the key used is a string, the key will not be case-sensitive.
* @return the value to which the key is mapped in this map; null if *
* the key is not mapped to any value in this map. * @param pKey a key in the map
*/ * @return the value to which the key is mapped in this map; null if
public V get(Object pKey) { * the key is not mapped to any value in this map.
return unwrap(entries.get(toUpper(pKey))); */
} public V get(Object pKey) {
return unwrap(entries.get(toUpper(pKey)));
/** }
* Removes the key (and its corresponding value) from this map. This
* method does nothing if the key is not in the map. /**
* Note: If the key used is a string, the key will not be case-sensitive. * Removes the key (and its corresponding value) from this map. This
* * method does nothing if the key is not in the map.
* @param pKey the key that needs to be removed. * Note: If the key used is a string, the key will not be case-sensitive.
* @return the value to which the key had been mapped in this map, *
* or null if the key did not have a mapping. * @param pKey the key that needs to be removed.
*/ * @return the value to which the key had been mapped in this map,
public V remove(Object pKey) { * or null if the key did not have a mapping.
return unwrap(entries.remove(toUpper(pKey))); */
} public V remove(Object pKey) {
return unwrap(entries.remove(toUpper(pKey)));
/** }
* Tests if the specified object is a key in this map.
* Note: If the key used is a string, the key will not be case-sensitive. /**
* * Tests if the specified object is a key in this map.
* @param pKey possible key. * Note: If the key used is a string, the key will not be case-sensitive.
* @return true if and only if the specified object is a key in this *
* map, as determined by the equals method; false otherwise. * @param pKey possible key.
*/ * @return true if and only if the specified object is a key in this
public boolean containsKey(Object pKey) { * map, as determined by the equals method; false otherwise.
return entries.containsKey(toUpper(pKey)); */
} public boolean containsKey(Object pKey) {
return entries.containsKey(toUpper(pKey));
/** }
* Converts the parameter to uppercase, if it's a String.
*/ /**
protected static Object toUpper(final Object pObject) { * Converts the parameter to uppercase, if it's a String.
if (pObject instanceof String) { */
return ((String) pObject).toUpperCase(); protected static Object toUpper(final Object pObject) {
} if (pObject instanceof String) {
return pObject; return ((String) pObject).toUpperCase();
} }
return pObject;
protected Iterator<Entry<String, V>> newEntryIterator() { }
return (Iterator) entries.entrySet().iterator();
} protected Iterator<Entry<String, V>> newEntryIterator() {
return (Iterator) entries.entrySet().iterator();
protected Iterator<String> newKeyIterator() { }
return entries.keySet().iterator();
} protected Iterator<String> newKeyIterator() {
return entries.keySet().iterator();
protected Iterator<V> newValueIterator() { }
return (Iterator<V>) entries.values().iterator();
} protected Iterator<V> newValueIterator() {
} return (Iterator<V>) entries.values().iterator();
}
}

View File

@@ -1,468 +1,466 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.util.*;
/** /**
* Generic map and linked list implementation of the {@code Map} interface, * Generic map and linked list implementation of the {@code Map} interface,
* with predictable iteration order. * with predictable iteration order.
* <p> * <p>
* Resembles {@code LinkedHashMap} from JDK 1.4+, but is backed by a generic * Resembles {@code LinkedHashMap} from JDK 1.4+, but is backed by a generic
* {@code Map}, rather than implementing a particular algoritm. * {@code Map}, rather than implementing a particular algoritm.
* <p> * <p>
* This linked list defines the iteration ordering, which is normally the order * This linked list defines the iteration ordering, which is normally the order
* in which keys were inserted into the map (<em>insertion-order</em>). * in which keys were inserted into the map (<em>insertion-order</em>).
* Note that insertion order is not affected if a key is <em>re-inserted</em> * Note that insertion order is not affected if a key is <em>re-inserted</em>
* into the map (a key {@code k} is reinserted into a map {@code m} if * into the map (a key {@code k} is reinserted into a map {@code m} if
* {@code m.put(k, v)} is invoked when {@code m.containsKey(k)} would return * {@code m.put(k, v)} is invoked when {@code m.containsKey(k)} would return
* {@code true} immediately prior to the invocation). * {@code true} immediately prior to the invocation).
* <p> * <p>
* A special {@link #LinkedMap(boolean) constructor} is provided to create a * A special {@link #LinkedMap(boolean) constructor} is provided to create a
* linked hash map whose order of iteration is the order in which its entries * linked hash map whose order of iteration is the order in which its entries
* were last accessed, from least-recently accessed to most-recently * were last accessed, from least-recently accessed to most-recently
* (<em>access-order</em>). * (<em>access-order</em>).
* This kind of map is well-suited to building LRU caches. * This kind of map is well-suited to building LRU caches.
* Invoking the {@code put} or {@code get} method results in an access to the * Invoking the {@code put} or {@code get} method results in an access to the
* corresponding entry (assuming it exists after the invocation completes). * corresponding entry (assuming it exists after the invocation completes).
* The {@code putAll} method generates one entry access for each mapping in * The {@code putAll} method generates one entry access for each mapping in
* the specified map, in the order that key-value mappings are provided by the * the specified map, in the order that key-value mappings are provided by the
* specified map's entry set iterator. * specified map's entry set iterator.
* <em>No other methods generate entry accesses.</em> * <em>No other methods generate entry accesses.</em>
* In particular, operations on collection-views do not affect the order of * In particular, operations on collection-views do not affect the order of
* iteration of the backing map. * iteration of the backing map.
* <p> * <p>
* The {@link #removeEldestEntry(Map.Entry)} method may be overridden to impose * The {@link #removeEldestEntry(Map.Entry)} method may be overridden to impose
* a policy for removing stale mappings automatically when new mappings are * a policy for removing stale mappings automatically when new mappings are
* added to the map. * added to the map.
* *
* @author inspired by LinkedHashMap from JDK 1.4+, by Josh Bloch * @author inspired by LinkedHashMap from JDK 1.4+, by Josh Bloch
* @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/util/LinkedMap.java#1 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/LinkedMap.java#1 $
* *
* @see LinkedHashMap * @see LinkedHashMap
* @see LRUMap * @see LRUMap
*/ */
public class LinkedMap<K, V> extends AbstractDecoratedMap<K, V> implements Serializable { public class LinkedMap<K, V> extends AbstractDecoratedMap<K, V> implements Serializable {
transient LinkedEntry<K, V> head; transient LinkedEntry<K, V> head;
protected final boolean accessOrder; protected final boolean accessOrder;
/** /**
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with default * Creates a {@code LinkedMap} backed by a {@code HashMap}, with default
* (insertion) order. * (insertion) order.
*/ */
public LinkedMap() { public LinkedMap() {
this(null, false); this(null, false);
} }
/** /**
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with the * Creates a {@code LinkedMap} backed by a {@code HashMap}, with the
* given order. * given order.
* *
* @param pAccessOrder if {@code true}, ordering will be "least recently * @param pAccessOrder if {@code true}, ordering will be "least recently
* accessed item" to "latest accessed item", otherwise "first inserted item" * accessed item" to "latest accessed item", otherwise "first inserted item"
* to "latest inserted item". * to "latest inserted item".
*/ */
public LinkedMap(boolean pAccessOrder) { public LinkedMap(boolean pAccessOrder) {
this(null, pAccessOrder); this(null, pAccessOrder);
} }
/** /**
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with key/value * Creates a {@code LinkedMap} backed by a {@code HashMap}, with key/value
* pairs copied from {@code pContents} and default (insertion) order. * pairs copied from {@code pContents} and default (insertion) order.
* *
* @param pContents the map whose mappings are to be placed in this map. * @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}. * May be {@code null}.
*/ */
public LinkedMap(Map<? extends K, ? extends V> pContents) { public LinkedMap(Map<? extends K, ? extends V> pContents) {
this(pContents, false); this(pContents, false);
} }
/** /**
* Creates a {@code LinkedMap} backed by a {@code HashMap}, with key/value * Creates a {@code LinkedMap} backed by a {@code HashMap}, with key/value
* pairs copied from {@code pContents} and the given order. * pairs copied from {@code pContents} and the given order.
* *
* @param pContents the map whose mappings are to be placed in this map. * @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}. * May be {@code null}.
* @param pAccessOrder if {@code true}, ordering will be "least recently * @param pAccessOrder if {@code true}, ordering will be "least recently
* accessed item" to "latest accessed item", otherwise "first inserted item" * accessed item" to "latest accessed item", otherwise "first inserted item"
* to "latest inserted item". * to "latest inserted item".
*/ */
public LinkedMap(Map<? extends K, ? extends V> pContents, boolean pAccessOrder) { public LinkedMap(Map<? extends K, ? extends V> pContents, boolean pAccessOrder) {
super(pContents); super(pContents);
accessOrder = pAccessOrder; accessOrder = pAccessOrder;
} }
/** /**
* Creates a {@code LinkedMap} backed by the given map, with key/value * Creates a {@code LinkedMap} backed by the given map, with key/value
* pairs copied from {@code pContents} and default (insertion) order. * pairs copied from {@code pContents} and default (insertion) order.
* *
* @param pBacking the backing map of this map. Must be either empty, or * @param pBacking the backing map of this map. Must be either empty, or
* the same map as {@code pContents}. * the same map as {@code pContents}.
* @param pContents the map whose mappings are to be placed in this map. * @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}. * May be {@code null}.
*/ */
public LinkedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents) { public LinkedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents) {
this(pBacking, pContents, false); this(pBacking, pContents, false);
} }
/** /**
* Creates a {@code LinkedMap} backed by the given map, with key/value * Creates a {@code LinkedMap} backed by the given map, with key/value
* pairs copied from {@code pContents} and the given order. * pairs copied from {@code pContents} and the given order.
* *
* @param pBacking the backing map of this map. Must be either empty, or * @param pBacking the backing map of this map. Must be either empty, or
* the same map as {@code pContents}. * the same map as {@code pContents}.
* @param pContents the map whose mappings are to be placed in this map. * @param pContents the map whose mappings are to be placed in this map.
* May be {@code null}. * May be {@code null}.
* @param pAccessOrder if {@code true}, ordering will be "least recently * @param pAccessOrder if {@code true}, ordering will be "least recently
* accessed item" to "latest accessed item", otherwise "first inserted item" * accessed item" to "latest accessed item", otherwise "first inserted item"
* to "latest inserted item". * to "latest inserted item".
*/ */
public LinkedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents, boolean pAccessOrder) { public LinkedMap(Map<K, Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents, boolean pAccessOrder) {
super(pBacking, pContents); super(pBacking, pContents);
accessOrder = pAccessOrder; accessOrder = pAccessOrder;
} }
protected void init() { protected void init() {
head = new LinkedEntry<K, V>(null, null, null) { head = new LinkedEntry<K, V>(null, null, null) {
void addBefore(LinkedEntry pExisting) { void addBefore(LinkedEntry pExisting) {
throw new Error(); throw new Error();
} }
void remove() { void remove() {
throw new Error(); throw new Error();
} }
public void recordAccess(Map pMap) { public void recordAccess(Map pMap) {
throw new Error(); throw new Error();
} }
public void recordRemoval(Map pMap) { public void recordRemoval(Map pMap) {
throw new Error(); throw new Error();
} }
public void recordRemoval() { public void recordRemoval() {
throw new Error(); throw new Error();
} }
public V getValue() { public V getValue() {
throw new Error(); throw new Error();
} }
public V setValue(V pValue) { public V setValue(V pValue) {
throw new Error(); throw new Error();
} }
public K getKey() { public K getKey() {
throw new Error(); throw new Error();
} }
public String toString() { public String toString() {
return "head"; return "head";
} }
}; };
head.previous = head.next = head; head.previous = head.next = head;
} }
public boolean containsValue(Object pValue) { public boolean containsValue(Object pValue) {
// Overridden to take advantage of faster iterator // Overridden to take advantage of faster iterator
if (pValue == null) { if (pValue == null) {
for (LinkedEntry e = head.next; e != head; e = e.next) { for (LinkedEntry e = head.next; e != head; e = e.next) {
if (e.mValue == null) { if (e.mValue == null) {
return true; return true;
} }
} }
} else { } else {
for (LinkedEntry e = head.next; e != head; e = e.next) { for (LinkedEntry e = head.next; e != head; e = e.next) {
if (pValue.equals(e.mValue)) { if (pValue.equals(e.mValue)) {
return true; return true;
} }
} }
} }
return false; return false;
} }
protected Iterator<K> newKeyIterator() { protected Iterator<K> newKeyIterator() {
return new KeyIterator(); return new KeyIterator();
} }
protected Iterator<V> newValueIterator() { protected Iterator<V> newValueIterator() {
return new ValueIterator(); return new ValueIterator();
} }
protected Iterator<Entry<K, V>> newEntryIterator() { protected Iterator<Entry<K, V>> newEntryIterator() {
return new EntryIterator(); return new EntryIterator();
} }
private abstract class LinkedMapIterator<E> implements Iterator<E> { private abstract class LinkedMapIterator<E> implements Iterator<E> {
LinkedEntry<K, V> mNextEntry = head.next; LinkedEntry<K, V> mNextEntry = head.next;
LinkedEntry<K, V> mLastReturned = null; LinkedEntry<K, V> mLastReturned = null;
/** /**
* The modCount value that the iterator believes that the backing * The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator * List should have. If this expectation is violated, the iterator
* has detected concurrent modification. * has detected concurrent modification.
*/ */
int mExpectedModCount = modCount; int mExpectedModCount = modCount;
public boolean hasNext() { public boolean hasNext() {
return mNextEntry != head; return mNextEntry != head;
} }
public void remove() { public void remove() {
if (mLastReturned == null) { if (mLastReturned == null) {
throw new IllegalStateException(); throw new IllegalStateException();
} }
if (modCount != mExpectedModCount) { if (modCount != mExpectedModCount) {
throw new ConcurrentModificationException(); throw new ConcurrentModificationException();
} }
LinkedMap.this.remove(mLastReturned.mKey); LinkedMap.this.remove(mLastReturned.mKey);
mLastReturned = null; mLastReturned = null;
mExpectedModCount = modCount; mExpectedModCount = modCount;
} }
LinkedEntry<K, V> nextEntry() { LinkedEntry<K, V> nextEntry() {
if (modCount != mExpectedModCount) { if (modCount != mExpectedModCount) {
throw new ConcurrentModificationException(); throw new ConcurrentModificationException();
} }
if (mNextEntry == head) { if (mNextEntry == head) {
throw new NoSuchElementException(); throw new NoSuchElementException();
} }
LinkedEntry<K, V> e = mLastReturned = mNextEntry; LinkedEntry<K, V> e = mLastReturned = mNextEntry;
mNextEntry = e.next; mNextEntry = e.next;
return e; return e;
} }
} }
private class KeyIterator extends LinkedMap<K, V>.LinkedMapIterator<K> { private class KeyIterator extends LinkedMap<K, V>.LinkedMapIterator<K> {
public K next() { public K next() {
return nextEntry().mKey; return nextEntry().mKey;
} }
} }
private class ValueIterator extends LinkedMap<K, V>.LinkedMapIterator<V> { private class ValueIterator extends LinkedMap<K, V>.LinkedMapIterator<V> {
public V next() { public V next() {
return nextEntry().mValue; return nextEntry().mValue;
} }
} }
private class EntryIterator extends LinkedMap<K, V>.LinkedMapIterator<Entry<K, V>> { private class EntryIterator extends LinkedMap<K, V>.LinkedMapIterator<Entry<K, V>> {
public Entry<K, V> next() { public Entry<K, V> next() {
return nextEntry(); return nextEntry();
} }
} }
public V get(Object pKey) { public V get(Object pKey) {
LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.get(pKey); LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.get(pKey);
if (entry != null) { if (entry != null) {
entry.recordAccess(this); entry.recordAccess(this);
return entry.mValue; return entry.mValue;
} }
return null; return null;
} }
public V remove(Object pKey) { public V remove(Object pKey) {
LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.remove(pKey); LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.remove(pKey);
if (entry != null) { if (entry != null) {
entry.remove(); entry.remove();
modCount++; modCount++;
return entry.mValue; return entry.mValue;
} }
return null; return null;
} }
public V put(K pKey, V pValue) { public V put(K pKey, V pValue) {
LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.get(pKey); LinkedEntry<K, V> entry = (LinkedEntry<K, V>) entries.get(pKey);
V oldValue; V oldValue;
if (entry == null) { if (entry == null) {
oldValue = null; oldValue = null;
// Remove eldest entry if instructed, else grow capacity if appropriate // Remove eldest entry if instructed, else grow capacity if appropriate
LinkedEntry<K, V> eldest = head.next; LinkedEntry<K, V> eldest = head.next;
if (removeEldestEntry(eldest)) { if (removeEldestEntry(eldest)) {
removeEntry(eldest); removeEntry(eldest);
} }
entry = createEntry(pKey, pValue); entry = createEntry(pKey, pValue);
entry.addBefore(head); entry.addBefore(head);
entries.put(pKey, entry); entries.put(pKey, entry);
} }
else { else {
oldValue = entry.mValue; oldValue = entry.mValue;
entry.mValue = pValue; entry.mValue = pValue;
entry.recordAccess(this); entry.recordAccess(this);
} }
modCount++; modCount++;
return oldValue; return oldValue;
} }
/** /**
* Creates a new {@code LinkedEntry}. * Creates a new {@code LinkedEntry}.
* *
* @param pKey the key * @param pKey the key
* @param pValue the value * @param pValue the value
* @return a new LinkedEntry * @return a new LinkedEntry
*/ */
/*protected*/ LinkedEntry<K, V> createEntry(K pKey, V pValue) { /*protected*/ LinkedEntry<K, V> createEntry(K pKey, V pValue) {
return new LinkedEntry<K, V>(pKey, pValue, null); return new LinkedEntry<K, V>(pKey, pValue, null);
} }
/** /**
* @todo * @return a copy of this map, with the same order and same key/value pairs.
* */
* @return a copy of this map, with the same order and same key/value pairs. public Object clone() throws CloneNotSupportedException {
*/ LinkedMap map;
public Object clone() throws CloneNotSupportedException {
LinkedMap map; map = (LinkedMap) super.clone();
map = (LinkedMap) super.clone(); // TODO: The rest of the work is PROBABLY handled by
// AbstractDecoratedMap, but need to verify that.
// TODO: The rest of the work is PROBABLY handled by
// AbstractDecoratedMap, but need to verify that. return map;
}
return map;
} /**
* Returns {@code true} if this map should remove its eldest entry.
/** * This method is invoked by {@code put} and {@code putAll} after
* Returns {@code true} if this map should remove its eldest entry. * inserting a new entry into the map. It provides the implementer
* This method is invoked by {@code put} and {@code putAll} after * with the opportunity to remove the eldest entry each time a new one
* inserting a new entry into the map. It provides the implementer * is added. This is useful if the map represents a cache: it allows
* with the opportunity to remove the eldest entry each time a new one * the map to reduce memory consumption by deleting stale entries.
* is added. This is useful if the map represents a cache: it allows *
* the map to reduce memory consumption by deleting stale entries. * <p>Sample use: this override will allow the map to grow up to 100
* * entries and then delete the eldest entry each time a new entry is
* <p>Sample use: this override will allow the map to grow up to 100 * added, maintaining a steady state of 100 entries.
* entries and then delete the eldest entry each time a new entry is * <pre>
* added, maintaining a steady state of 100 entries. * private static final int MAX_ENTRIES = 100;
* <pre> *
* private static final int MAX_ENTRIES = 100; * protected boolean removeEldestEntry(Map.Entry eldest) {
* * return size() &gt; MAX_ENTRIES;
* protected boolean removeEldestEntry(Map.Entry eldest) { * }
* return size() > MAX_ENTRIES; * </pre>
* } *
* </pre> * <p>This method typically does not modify the map in any way,
* * instead allowing the map to modify itself as directed by its
* <p>This method typically does not modify the map in any way, * return value. It <i>is</i> permitted for this method to modify
* instead allowing the map to modify itself as directed by its * the map directly, but if it does so, it <i>must</i> return
* return value. It <i>is</i> permitted for this method to modify * {@code false} (indicating that the map should not attempt any
* the map directly, but if it does so, it <i>must</i> return * further modification). The effects of returning {@code true}
* {@code false} (indicating that the map should not attempt any * after modifying the map from within this method are unspecified.
* further modification). The effects of returning {@code true} *
* after modifying the map from within this method are unspecified. * <p>This implementation merely returns {@code false} (so that this
* * map acts like a normal map - the eldest element is never removed).
* <p>This implementation merely returns {@code false} (so that this *
* map acts like a normal map - the eldest element is never removed). * @param pEldest The least recently inserted entry in the map, or if
* * this is an access-ordered map, the least recently accessed
* @param pEldest The least recently inserted entry in the map, or if * entry. This is the entry that will be removed it this
* this is an access-ordered map, the least recently accessed * method returns {@code true}. If the map was empty prior
* entry. This is the entry that will be removed it this * to the {@code put} or {@code putAll} invocation resulting
* method returns {@code true}. If the map was empty prior * in this invocation, this will be the entry that was just
* to the {@code put} or {@code putAll} invocation resulting * inserted; in other words, if the map contains a single
* in this invocation, this will be the entry that was just * entry, the eldest entry is also the newest.
* inserted; in other words, if the map contains a single * @return {@code true} if the eldest entry should be removed
* entry, the eldest entry is also the newest. * from the map; {@code false} if it should be retained.
* @return {@code true} if the eldest entry should be removed */
* from the map; {@code false} if it should be retained. protected boolean removeEldestEntry(Entry<K, V> pEldest) {
*/ return false;
protected boolean removeEldestEntry(Entry<K, V> pEldest) { }
return false;
} /**
* Linked list implementation of {@code Map.Entry}.
/** */
* Linked list implementation of {@code Map.Entry}. protected static class LinkedEntry<K, V> extends BasicEntry<K, V> implements Serializable {
*/ LinkedEntry<K, V> previous;
protected static class LinkedEntry<K, V> extends BasicEntry<K, V> implements Serializable { LinkedEntry<K, V> next;
LinkedEntry<K, V> previous;
LinkedEntry<K, V> next; LinkedEntry(K pKey, V pValue, LinkedEntry<K, V> pNext) {
super(pKey, pValue);
LinkedEntry(K pKey, V pValue, LinkedEntry<K, V> pNext) {
super(pKey, pValue); next = pNext;
}
next = pNext;
} /**
* Adds this entry before the given entry (which must be an existing
/** * entry) in the list.
* Adds this entry before the given entry (which must be an existing *
* entry) in the list. * @param pExisting the entry to add before
* */
* @param pExisting the entry to add before void addBefore(LinkedEntry<K, V> pExisting) {
*/ next = pExisting;
void addBefore(LinkedEntry<K, V> pExisting) { previous = pExisting.previous;
next = pExisting;
previous = pExisting.previous; previous.next = this;
next.previous = this;
previous.next = this; }
next.previous = this;
} /**
* Removes this entry from the linked list.
/** */
* Removes this entry from the linked list. void remove() {
*/ previous.next = next;
void remove() { next.previous = previous;
previous.next = next; }
next.previous = previous;
} /**
* If the entry is part of an access ordered list, moves the entry to
/** * the end of the list.
* If the entry is part of an access ordered list, moves the entry to *
* the end of the list. * @param pMap the map to record access for
* */
* @param pMap the map to record access for protected void recordAccess(Map<K, V> pMap) {
*/ LinkedMap<K, V> linkedMap = (LinkedMap<K, V>) pMap;
protected void recordAccess(Map<K, V> pMap) { if (linkedMap.accessOrder) {
LinkedMap<K, V> linkedMap = (LinkedMap<K, V>) pMap; linkedMap.modCount++;
if (linkedMap.accessOrder) { remove();
linkedMap.modCount++; addBefore(linkedMap.head);
remove(); }
addBefore(linkedMap.head); }
}
} /**
* Removes this entry from the linked list.
/** *
* Removes this entry from the linked list. * @param pMap the map to record removal from
* */
* @param pMap the map to record removal from protected void recordRemoval(Map<K, V> pMap) {
*/ // TODO: Is this REALLY correct?
protected void recordRemoval(Map<K, V> pMap) { remove();
// TODO: Is this REALLY correct? }
remove(); }
}
}
} }

View File

@@ -1,84 +1,85 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.util.*;
/** /**
* Generic map and linked list implementation of the {@code Set} interface, * Generic map and linked list implementation of the {@code Set} interface,
* with predictable iteration order. * with predictable iteration order.
* <p> * <p>
* Resembles {@code LinkedHashSet} from JDK 1.4+, but is backed by a generic * Resembles {@code LinkedHashSet} from JDK 1.4+, but is backed by a generic
* {@code LinkedMap}, rather than implementing a particular algoritm. * {@code LinkedMap}, rather than implementing a particular algoritm.
* <p/> * </p>
* @see LinkedMap *
* * @see LinkedMap
* @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/util/LinkedSet.java#1 $ * @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/util/LinkedSet.java#1 $
public class LinkedSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable { */
public class LinkedSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, Serializable {
private final static Object DUMMY = new Object();
private final static Object DUMMY = new Object();
private final Map<E, Object> map;
private final Map<E, Object> map;
public LinkedSet() {
map = new LinkedMap<E, Object>(); public LinkedSet() {
} map = new LinkedMap<E, Object>();
}
public LinkedSet(Collection<E> pCollection) {
this(); public LinkedSet(Collection<E> pCollection) {
addAll(pCollection); this();
} addAll(pCollection);
}
public boolean addAll(Collection<? extends E> pCollection) {
boolean changed = false; public boolean addAll(Collection<? extends E> pCollection) {
for (E value : pCollection) { boolean changed = false;
if (add(value) && !changed) { for (E value : pCollection) {
changed = true; if (add(value) && !changed) {
} changed = true;
} }
return changed; }
} return changed;
}
public boolean add(E pValue) {
return map.put(pValue, DUMMY) == null; public boolean add(E pValue) {
} return map.put(pValue, DUMMY) == null;
}
public int size() {
return map.size(); public int size() {
} return map.size();
}
public Iterator<E> iterator() {
return map.keySet().iterator(); public Iterator<E> iterator() {
} return map.keySet().iterator();
} }
}

View File

@@ -1,122 +1,123 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
/** /**
* An (immutable) empty {@link Map}, that supports all {@code Map} operations * An (immutable) empty {@link Map}, that supports all {@code Map} operations
* without throwing exceptions (in contrast to {@link Collections#EMPTY_MAP} * without throwing exceptions (in contrast to {@link Collections#EMPTY_MAP}
* that will throw exceptions on {@code put}/{@code remove}). * that will throw exceptions on {@code put}/{@code remove}).
* <p/> * <p>
* NOTE: This is not a general purpose {@code Map} implementation, * NOTE: This is not a general purpose {@code Map} implementation,
* as the {@code put} and {@code putAll} methods will not modify the map. * as the {@code put} and {@code putAll} methods will not modify the map.
* Instances of this class will always be an empty map. * Instances of this class will always be an empty map.
* * </p>
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> *
* @version $Id: com/twelvemonkeys/util/NullMap.java#2 $ * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
*/ * @version $Id: com/twelvemonkeys/util/NullMap.java#2 $
public final class NullMap<K, V> implements Map<K, V>, Serializable { */
public final int size() { public final class NullMap<K, V> implements Map<K, V>, Serializable {
return 0; public final int size() {
} return 0;
}
public final void clear() {
} public final void clear() {
}
public final boolean isEmpty() {
return true; public final boolean isEmpty() {
} return true;
}
public final boolean containsKey(Object pKey) {
return false; public final boolean containsKey(Object pKey) {
} return false;
}
public final boolean containsValue(Object pValue) {
return false; public final boolean containsValue(Object pValue) {
} return false;
}
public final Collection<V> values() {
return Collections.emptyList(); public final Collection<V> values() {
} return Collections.emptyList();
}
public final void putAll(Map pMap) {
} public final void putAll(Map pMap) {
}
public final Set<Entry<K, V>> entrySet() {
return Collections.emptySet(); public final Set<Entry<K, V>> entrySet() {
} return Collections.emptySet();
}
public final Set<K> keySet() {
return Collections.emptySet(); public final Set<K> keySet() {
} return Collections.emptySet();
}
public final V get(Object pKey) {
return null; public final V get(Object pKey) {
} return null;
}
public final V remove(Object pKey) {
return null; public final V remove(Object pKey) {
} return null;
}
public final V put(Object pKey, Object pValue) {
return null; public final V put(Object pKey, Object pValue) {
} return null;
}
/**
* Tests the given object for equality (wether it is also an empty /**
* {@code Map}). * Tests the given object for equality (wether it is also an empty
* This is consistent with the standard {@code Map} implementations of the * {@code Map}).
* Java Collections Framework. * This is consistent with the standard {@code Map} implementations of the
* * Java Collections Framework.
* @param pOther the object to compare with *
* @return {@code true} if {@code pOther} is an empty {@code Map}, * @param pOther the object to compare with
* otherwise {@code false} * @return {@code true} if {@code pOther} is an empty {@code Map},
*/ * otherwise {@code false}
public boolean equals(Object pOther) { */
return (pOther instanceof Map) && ((Map) pOther).isEmpty(); public boolean equals(Object pOther) {
} return (pOther instanceof Map) && ((Map) pOther).isEmpty();
}
/**
* Returns the {@code hashCode} of the empty map, {@code 0}. /**
* This is consistent with the standard {@code Map} implementations of the * Returns the {@code hashCode} of the empty map, {@code 0}.
* Java Collections Framework. * This is consistent with the standard {@code Map} implementations of the
* * Java Collections Framework.
* @return {@code 0}, always *
*/ * @return {@code 0}, always
public int hashCode() { */
return 0; public int hashCode() {
} return 0;
}
} }

View File

@@ -1,183 +1,183 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
/** /**
* Utility class for storing times in a simple way. The internal time is stored * Utility class for storing times in a simple way. The internal time is stored
* as an int, counting seconds. * as an int, counting seconds.
* *
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* @todo Milliseconds! */
*/ // TODO: Milliseconds!
public class Time { public class Time {
private int time = -1; private int time = -1;
public final static int SECONDS_IN_MINUTE = 60; public final static int SECONDS_IN_MINUTE = 60;
/** /**
* Creates a new time with 0 seconds, 0 minutes. * Creates a new time with 0 seconds, 0 minutes.
*/ */
public Time() { public Time() {
this(0); this(0);
} }
/** /**
* Creates a new time with the given time (in seconds). * Creates a new time with the given time (in seconds).
*/ */
public Time(int pTime) { public Time(int pTime) {
setTime(pTime); setTime(pTime);
} }
/** /**
* Sets the full time in seconds * Sets the full time in seconds
*/ */
public void setTime(int pTime) { public void setTime(int pTime) {
if (pTime < 0) { if (pTime < 0) {
throw new IllegalArgumentException("Time argument must be 0 or positive!"); throw new IllegalArgumentException("Time argument must be 0 or positive!");
} }
time = pTime; time = pTime;
} }
/** /**
* Gets the full time in seconds. * Gets the full time in seconds.
*/ */
public int getTime() { public int getTime() {
return time; return time;
} }
/** /**
* Gets the full time in milliseconds, for use in creating dates or * Gets the full time in milliseconds, for use in creating dates or
* similar. * similar.
* *
* @see java.util.Date#setTime(long) * @see java.util.Date#setTime(long)
*/ */
public long getTimeInMillis() { public long getTimeInMillis() {
return (long) time * 1000L; return (long) time * 1000L;
} }
/** /**
* Sets the seconds part of the time. Note, if the seconds argument is 60 * Sets the seconds part of the time. Note, if the seconds argument is 60
* or greater, the value will "wrap", and increase the minutes also. * or greater, the value will "wrap", and increase the minutes also.
* *
* @param pSeconds an integer that should be between 0 and 59. * @param pSeconds an integer that should be between 0 and 59.
*/ */
public void setSeconds(int pSeconds) { public void setSeconds(int pSeconds) {
time = getMinutes() * SECONDS_IN_MINUTE + pSeconds; time = getMinutes() * SECONDS_IN_MINUTE + pSeconds;
} }
/** /**
* Gets the seconds part of the time. * Gets the seconds part of the time.
* *
* @return an integer between 0 and 59 * @return an integer between 0 and 59
*/ */
public int getSeconds() { public int getSeconds() {
return time % SECONDS_IN_MINUTE; return time % SECONDS_IN_MINUTE;
} }
/** /**
* Sets the minutes part of the time. * Sets the minutes part of the time.
* *
* @param pMinutes an integer * @param pMinutes an integer
*/ */
public void setMinutes(int pMinutes) { public void setMinutes(int pMinutes) {
time = pMinutes * SECONDS_IN_MINUTE + getSeconds(); time = pMinutes * SECONDS_IN_MINUTE + getSeconds();
} }
/** /**
* Gets the minutes part of the time. * Gets the minutes part of the time.
* *
* @return an integer * @return an integer
*/ */
public int getMinutes() { public int getMinutes() {
return time / SECONDS_IN_MINUTE; return time / SECONDS_IN_MINUTE;
} }
/** /**
* Creates a string representation of the time object. * Creates a string representation of the time object.
* The string is returned on the form m:ss, * The string is returned on the form m:ss,
* where m is variable digits minutes and ss is two digits seconds. * where m is variable digits minutes and ss is two digits seconds.
* *
* @return a string representation of the time object * @return a string representation of the time object
* @see #toString(String) * @see #toString(String)
*/ */
public String toString() { public String toString() {
return "" + getMinutes() + ":" return "" + getMinutes() + ":"
+ (getSeconds() < 10 ? "0" : "") + getSeconds(); + (getSeconds() < 10 ? "0" : "") + getSeconds();
} }
/** /**
* Creates a string representation of the time object. * Creates a string representation of the time object.
* The string returned is on the format of the formatstring. * The string returned is on the format of the formatstring.
* <DL> * <DL>
* <DD>m (or any multiple of m's) * <DD>m (or any multiple of m's)
* <DT>the minutes part (padded with 0's, if number has less digits than * <DT>the minutes part (padded with 0's, if number has less digits than
* the number of m's) * the number of m's)
* m -> 0,1,...,59,60,61,... * m -&gt; 0,1,...,59,60,61,...
* mm -> 00,01,...,59,60,61,... * mm -&gt; 00,01,...,59,60,61,...
* <DD>s or ss * <DD>s or ss
* <DT>the seconds part (padded with 0's, if number has less digits than * <DT>the seconds part (padded with 0's, if number has less digits than
* the number of s's) * the number of s's)
* s -> 0,1,...,59 * s -&gt; 0,1,...,59
* ss -> 00,01,...,59 * ss -&gt; 00,01,...,59
* <DD>S * <DD>S
* <DT>all seconds (including the ones above 59) * <DT>all seconds (including the ones above 59)
* </DL> * </DL>
* *
* @param pFormatStr the format where * @param pFormatStr the format where
* @return a string representation of the time object * @return a string representation of the time object
* @throws NumberFormatException * @throws NumberFormatException
* @see TimeFormat#format(Time) * @see TimeFormat#format(Time)
* @see #parseTime(String) * @see #parseTime(String)
* @deprecated * @deprecated
*/ */
public String toString(String pFormatStr) { public String toString(String pFormatStr) {
TimeFormat tf = new TimeFormat(pFormatStr); TimeFormat tf = new TimeFormat(pFormatStr);
return tf.format(this); return tf.format(this);
} }
/** /**
* Creates a string representation of the time object. * Creates a string representation of the time object.
* The string is returned on the form m:ss, * The string is returned on the form m:ss,
* where m is variable digits minutes and ss is two digits seconds. * where m is variable digits minutes and ss is two digits seconds.
* *
* @return a string representation of the time object * @return a string representation of the time object
* @throws NumberFormatException * @throws NumberFormatException
* @see TimeFormat#parse(String) * @see TimeFormat#parse(String)
* @see #toString(String) * @see #toString(String)
* @deprecated * @deprecated
*/ */
public static Time parseTime(String pStr) { public static Time parseTime(String pStr) {
TimeFormat tf = TimeFormat.getInstance(); TimeFormat tf = TimeFormat.getInstance();
return tf.parse(pStr); return tf.parse(pStr);
} }
} }

View File

@@ -1,452 +1,452 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import java.text.FieldPosition; import java.text.FieldPosition;
import java.text.Format; import java.text.Format;
import java.text.ParsePosition; import java.text.ParsePosition;
import java.util.StringTokenizer; import java.util.StringTokenizer;
import java.util.Vector; import java.util.Vector;
/** /**
* Format for converting and parsing time. * Format for converting and parsing time.
* <P> * <P>
* The format is expressed in a string as follows: * The format is expressed in a string as follows:
* <DL> * <DL>
* <DD>m (or any multiple of m's) * <DD>m (or any multiple of m's)
* <DT>the minutes part (padded with 0's, if number has less digits than * <DT>the minutes part (padded with 0's, if number has less digits than
* the number of m's) * the number of m's)
* m -> 0,1,...,59,60,61,... * m -&gt; 0,1,...,59,60,61,...
* mm -> 00,01,...,59,60,61,... * mm -&gt; 00,01,...,59,60,61,...
* <DD>s or ss * <DD>s or ss
* <DT>the seconds part (padded with 0's, if number has less digits than * <DT>the seconds part (padded with 0's, if number has less digits than
* the number of s's) * the number of s's)
* s -> 0,1,...,59 * s -&gt; 0,1,...,59
* ss -> 00,01,...,59 * ss -&gt; 00,01,...,59
* <DD>S * <DD>S
* <DT>all seconds (including the ones above 59) * <DT>all seconds (including the ones above 59)
* </DL> * </DL>
* <P> * <P>
* May not handle all cases, and formats... ;-) * May not handle all cases, and formats... ;-)
* Safest is: Always delimiters between the minutes (m) and seconds (s) part. * Safest is: Always delimiters between the minutes (m) and seconds (s) part.
* <P> * <P>
* TODO: * Known bugs:
* Move to com.twelvemonkeys.text? * <P>
* Milliseconds! * The last character in the formatString is not escaped, while it should be.
* Fix bugs. * The first character after an escaped character is escaped while is shouldn't
* Known bugs: * be.
* <P> * <P>
* The last character in the formatString is not escaped, while it should be. * This is not a 100% compatible implementation of a java.text.Format.
* The first character after an escaped character is escaped while is shouldn't *
* be. * @see com.twelvemonkeys.util.Time
* <P> *
* This is not a 100% compatible implementation of a java.text.Format. * @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a>
* */
* @see com.twelvemonkeys.util.Time // TODO:
* // Move to com.twelvemonkeys.text?
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> // Milliseconds!
*/ // Fix bugs.
public class TimeFormat extends Format { public class TimeFormat extends Format {
final static String MINUTE = "m"; final static String MINUTE = "m";
final static String SECOND = "s"; final static String SECOND = "s";
final static String TIME = "S"; final static String TIME = "S";
final static String ESCAPE = "\\"; final static String ESCAPE = "\\";
/** /**
* The default time format * The default time format
*/ */
private final static TimeFormat DEFAULT_FORMAT = new TimeFormat("m:ss"); private final static TimeFormat DEFAULT_FORMAT = new TimeFormat("m:ss");
protected String formatString = null; protected String formatString = null;
/** /**
* Main method for testing ONLY * Main method for testing ONLY
*/ */
static void main(String[] argv) { static void main(String[] argv) {
Time time = null; Time time = null;
TimeFormat in = null; TimeFormat in = null;
TimeFormat out = null; TimeFormat out = null;
if (argv.length >= 3) { if (argv.length >= 3) {
System.out.println("Creating out TimeFormat: \"" + argv[2] + "\""); System.out.println("Creating out TimeFormat: \"" + argv[2] + "\"");
out = new TimeFormat(argv[2]); out = new TimeFormat(argv[2]);
} }
if (argv.length >= 2) { if (argv.length >= 2) {
System.out.println("Creating in TimeFormat: \"" + argv[1] + "\""); System.out.println("Creating in TimeFormat: \"" + argv[1] + "\"");
in = new TimeFormat(argv[1]); in = new TimeFormat(argv[1]);
} }
else { else {
System.out.println("Using default format for in"); System.out.println("Using default format for in");
in = DEFAULT_FORMAT; in = DEFAULT_FORMAT;
} }
if (out == null) if (out == null)
out = in; out = in;
if (argv.length >= 1) { if (argv.length >= 1) {
System.out.println("Parsing: \"" + argv[0] + "\" with format \"" System.out.println("Parsing: \"" + argv[0] + "\" with format \""
+ in.formatString + "\""); + in.formatString + "\"");
time = in.parse(argv[0]); time = in.parse(argv[0]);
} }
else else
time = new Time(); time = new Time();
System.out.println("Time is \"" + out.format(time) + System.out.println("Time is \"" + out.format(time) +
"\" according to format \"" + out.formatString + "\""); "\" according to format \"" + out.formatString + "\"");
} }
/** /**
* The formatter array. * The formatter array.
*/ */
protected TimeFormatter[] formatter; protected TimeFormatter[] formatter;
/** /**
* Creates a new TimeFormat with the given formatString, * Creates a new TimeFormat with the given formatString,
*/ */
public TimeFormat(String pStr) { public TimeFormat(String pStr) {
formatString = pStr; formatString = pStr;
Vector formatter = new Vector(); Vector formatter = new Vector();
StringTokenizer tok = new StringTokenizer(pStr, "\\msS", true); StringTokenizer tok = new StringTokenizer(pStr, "\\msS", true);
String previous = null; String previous = null;
String current = null; String current = null;
int previousCount = 0; int previousCount = 0;
while (tok.hasMoreElements()) { while (tok.hasMoreElements()) {
current = tok.nextToken(); current = tok.nextToken();
if (previous != null && previous.equals(ESCAPE)) { if (previous != null && previous.equals(ESCAPE)) {
// Handle escaping of s, S or m // Handle escaping of s, S or m
current = ((current != null) ? current : "") current = ((current != null) ? current : "")
+ (tok.hasMoreElements() ? tok.nextToken() : ""); + (tok.hasMoreElements() ? tok.nextToken() : "");
previous = null; previous = null;
previousCount = 0; previousCount = 0;
} }
// Skip over first, // Skip over first,
// or if current is the same, increase count, and try again // or if current is the same, increase count, and try again
if (previous == null || previous.equals(current)) { if (previous == null || previous.equals(current)) {
previousCount++; previousCount++;
previous = current; previous = current;
} }
else { else {
// Create new formatter for each part // Create new formatter for each part
if (previous.equals(MINUTE)) if (previous.equals(MINUTE))
formatter.add(new MinutesFormatter(previousCount)); formatter.add(new MinutesFormatter(previousCount));
else if (previous.equals(SECOND)) else if (previous.equals(SECOND))
formatter.add(new SecondsFormatter(previousCount)); formatter.add(new SecondsFormatter(previousCount));
else if (previous.equals(TIME)) else if (previous.equals(TIME))
formatter.add(new SecondsFormatter(-1)); formatter.add(new SecondsFormatter(-1));
else else
formatter.add(new TextFormatter(previous)); formatter.add(new TextFormatter(previous));
previousCount = 1; previousCount = 1;
previous = current; previous = current;
} }
} }
// Add new formatter for last part // Add new formatter for last part
if (previous != null) { if (previous != null) {
if (previous.equals(MINUTE)) if (previous.equals(MINUTE))
formatter.add(new MinutesFormatter(previousCount)); formatter.add(new MinutesFormatter(previousCount));
else if (previous.equals(SECOND)) else if (previous.equals(SECOND))
formatter.add(new SecondsFormatter(previousCount)); formatter.add(new SecondsFormatter(previousCount));
else if (previous.equals(TIME)) else if (previous.equals(TIME))
formatter.add(new SecondsFormatter(-1)); formatter.add(new SecondsFormatter(-1));
else else
formatter.add(new TextFormatter(previous)); formatter.add(new TextFormatter(previous));
} }
// Debug // Debug
/* /*
for (int i = 0; i < formatter.size(); i++) { for (int i = 0; i < formatter.size(); i++) {
System.out.println("Formatter " + formatter.get(i).getClass() System.out.println("Formatter " + formatter.get(i).getClass()
+ ": length=" + ((TimeFormatter) formatter.get(i)).digits); + ": length=" + ((TimeFormatter) formatter.get(i)).digits);
} }
*/ */
this.formatter = (TimeFormatter[]) this.formatter = (TimeFormatter[])
formatter.toArray(new TimeFormatter[formatter.size()]); formatter.toArray(new TimeFormatter[formatter.size()]);
} }
/** /**
* DUMMY IMPLEMENTATION!! * DUMMY IMPLEMENTATION!!
* Not locale specific. * Not locale specific.
*/ */
public static TimeFormat getInstance() { public static TimeFormat getInstance() {
return DEFAULT_FORMAT; return DEFAULT_FORMAT;
} }
/** DUMMY IMPLEMENTATION!! */ /** DUMMY IMPLEMENTATION!! */
/* Not locale specific /* Not locale specific
public static TimeFormat getInstance(Locale pLocale) { public static TimeFormat getInstance(Locale pLocale) {
return DEFAULT_FORMAT; return DEFAULT_FORMAT;
} }
*/ */
/** DUMMY IMPLEMENTATION!! */ /** DUMMY IMPLEMENTATION!! */
/* Not locale specific /* Not locale specific
public static Locale[] getAvailableLocales() { public static Locale[] getAvailableLocales() {
return new Locale[] {Locale.getDefault()}; return new Locale[] {Locale.getDefault()};
} }
*/ */
/** Gets the format string. */ /** Gets the format string. */
public String getFormatString() { public String getFormatString() {
return formatString; return formatString;
} }
/** DUMMY IMPLEMENTATION!! */ /** DUMMY IMPLEMENTATION!! */
public StringBuffer format(Object pObj, StringBuffer pToAppendTo, public StringBuffer format(Object pObj, StringBuffer pToAppendTo,
FieldPosition pPos) { FieldPosition pPos) {
if (!(pObj instanceof Time)) { if (!(pObj instanceof Time)) {
throw new IllegalArgumentException("Must be instance of " + Time.class); throw new IllegalArgumentException("Must be instance of " + Time.class);
} }
return pToAppendTo.append(format(pObj)); return pToAppendTo.append(format(pObj));
} }
/** /**
* Formats the the given time, using this format. * Formats the the given time, using this format.
*/ */
public String format(Time pTime) { public String format(Time pTime) {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
for (int i = 0; i < formatter.length; i++) { for (int i = 0; i < formatter.length; i++) {
buf.append(formatter[i].format(pTime)); buf.append(formatter[i].format(pTime));
} }
return buf.toString(); return buf.toString();
} }
/** DUMMY IMPLEMENTATION!! */ /** DUMMY IMPLEMENTATION!! */
public Object parseObject(String pStr, ParsePosition pStatus) { public Object parseObject(String pStr, ParsePosition pStatus) {
Time t = parse(pStr); Time t = parse(pStr);
pStatus.setIndex(pStr.length()); // Not 100% pStatus.setIndex(pStr.length()); // Not 100%
return t; return t;
} }
/** /**
* Parses a Time, according to this format. * Parses a Time, according to this format.
* <p> * <p>
* Will bug on some formats. It's safest to always use delimiters between * Will bug on some formats. It's safest to always use delimiters between
* the minutes (m) and seconds (s) part. * the minutes (m) and seconds (s) part.
* *
*/ */
public Time parse(String pStr) { public Time parse(String pStr) {
Time time = new Time(); Time time = new Time();
int sec = 0; int sec = 0;
int min = 0; int min = 0;
int pos = 0; int pos = 0;
int skip = 0; int skip = 0;
boolean onlyUseSeconds = false; boolean onlyUseSeconds = false;
for (int i = 0; (i < formatter.length) for (int i = 0; (i < formatter.length)
&& (pos + skip < pStr.length()) ; i++) { && (pos + skip < pStr.length()) ; i++) {
// Go to next offset // Go to next offset
pos += skip; pos += skip;
if (formatter[i] instanceof MinutesFormatter) { if (formatter[i] instanceof MinutesFormatter) {
// Parse MINUTES // Parse MINUTES
if ((i + 1) < formatter.length if ((i + 1) < formatter.length
&& formatter[i + 1] instanceof TextFormatter) { && formatter[i + 1] instanceof TextFormatter) {
// Skip until next format element // Skip until next format element
skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos); skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos);
// Error in format, try parsing to end // Error in format, try parsing to end
if (skip < 0) if (skip < 0)
skip = pStr.length(); skip = pStr.length();
} }
else if ((i + 1) >= formatter.length) { else if ((i + 1) >= formatter.length) {
// Skip until end of string // Skip until end of string
skip = pStr.length(); skip = pStr.length();
} }
else { else {
// Hope this is correct... // Hope this is correct...
skip = formatter[i].digits; skip = formatter[i].digits;
} }
// May be first char // May be first char
if (skip > pos) if (skip > pos)
min = Integer.parseInt(pStr.substring(pos, skip)); min = Integer.parseInt(pStr.substring(pos, skip));
} }
else if (formatter[i] instanceof SecondsFormatter) { else if (formatter[i] instanceof SecondsFormatter) {
// Parse SECONDS // Parse SECONDS
if (formatter[i].digits == -1) { if (formatter[i].digits == -1) {
// Only seconds (or full TIME) // Only seconds (or full TIME)
if ((i + 1) < formatter.length if ((i + 1) < formatter.length
&& formatter[i + 1] instanceof TextFormatter) { && formatter[i + 1] instanceof TextFormatter) {
// Skip until next format element // Skip until next format element
skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos); skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos);
} }
else if ((i + 1) >= formatter.length) { else if ((i + 1) >= formatter.length) {
// Skip until end of string // Skip until end of string
skip = pStr.length(); skip = pStr.length();
} }
else { else {
// Cannot possibly know how long? // Cannot possibly know how long?
skip = 0; skip = 0;
continue; continue;
} }
// Get seconds // Get seconds
sec = Integer.parseInt(pStr.substring(pos, skip)); sec = Integer.parseInt(pStr.substring(pos, skip));
// System.out.println("Only seconds: " + sec); // System.out.println("Only seconds: " + sec);
onlyUseSeconds = true; onlyUseSeconds = true;
break; break;
} }
else { else {
// Normal SECONDS // Normal SECONDS
if ((i + 1) < formatter.length if ((i + 1) < formatter.length
&& formatter[i + 1] instanceof TextFormatter) { && formatter[i + 1] instanceof TextFormatter) {
// Skip until next format element // Skip until next format element
skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos); skip = pStr.indexOf(((TextFormatter) formatter[i + 1]).text, pos);
} }
else if ((i + 1) >= formatter.length) { else if ((i + 1) >= formatter.length) {
// Skip until end of string // Skip until end of string
skip = pStr.length(); skip = pStr.length();
} }
else { else {
skip = formatter[i].digits; skip = formatter[i].digits;
} }
// Get seconds // Get seconds
sec = Integer.parseInt(pStr.substring(pos, skip)); sec = Integer.parseInt(pStr.substring(pos, skip));
} }
} }
else if (formatter[i] instanceof TextFormatter) { else if (formatter[i] instanceof TextFormatter) {
skip = formatter[i].digits; skip = formatter[i].digits;
} }
} }
// Set the minutes part if we should // Set the minutes part if we should
if (!onlyUseSeconds) if (!onlyUseSeconds)
time.setMinutes(min); time.setMinutes(min);
// Set the seconds part // Set the seconds part
time.setSeconds(sec); time.setSeconds(sec);
return time; return time;
} }
} }
/** /**
* The base class of TimeFormatters * The base class of TimeFormatters
*/ */
abstract class TimeFormatter { abstract class TimeFormatter {
int digits = 0; int digits = 0;
abstract String format(Time t); abstract String format(Time t);
} }
/** /**
* Formats the seconds part of the Time * Formats the seconds part of the Time
*/ */
class SecondsFormatter extends TimeFormatter { class SecondsFormatter extends TimeFormatter {
SecondsFormatter(int pDigits) { SecondsFormatter(int pDigits) {
digits = pDigits; digits = pDigits;
} }
String format(Time t) { String format(Time t) {
// Negative number of digits, means all seconds, no padding // Negative number of digits, means all seconds, no padding
if (digits < 0) { if (digits < 0) {
return Integer.toString(t.getTime()); return Integer.toString(t.getTime());
} }
// If seconds is more than digits long, simply return it // If seconds is more than digits long, simply return it
if (t.getSeconds() >= Math.pow(10, digits)) { if (t.getSeconds() >= Math.pow(10, digits)) {
return Integer.toString(t.getSeconds()); return Integer.toString(t.getSeconds());
} }
// Else return it with leading 0's // Else return it with leading 0's
//return StringUtil.formatNumber(t.getSeconds(), digits); //return StringUtil.formatNumber(t.getSeconds(), digits);
return StringUtil.pad("" + t.getSeconds(), digits, "0", true); return StringUtil.pad("" + t.getSeconds(), digits, "0", true);
} }
} }
/** /**
* Formats the minutes part of the Time * Formats the minutes part of the Time
*/ */
class MinutesFormatter extends TimeFormatter { class MinutesFormatter extends TimeFormatter {
MinutesFormatter(int pDigits) { MinutesFormatter(int pDigits) {
digits = pDigits; digits = pDigits;
} }
String format(Time t) { String format(Time t) {
// If minutes is more than digits long, simply return it // If minutes is more than digits long, simply return it
if (t.getMinutes() >= Math.pow(10, digits)) { if (t.getMinutes() >= Math.pow(10, digits)) {
return Integer.toString(t.getMinutes()); return Integer.toString(t.getMinutes());
} }
// Else return it with leading 0's // Else return it with leading 0's
//return StringUtil.formatNumber(t.getMinutes(), digits); //return StringUtil.formatNumber(t.getMinutes(), digits);
return StringUtil.pad("" + t.getMinutes(), digits, "0", true); return StringUtil.pad("" + t.getMinutes(), digits, "0", true);
} }
} }
/** /**
* Formats text constant part of the Time * Formats text constant part of the Time
*/ */
class TextFormatter extends TimeFormatter { class TextFormatter extends TimeFormatter {
String text = null; String text = null;
TextFormatter(String pText) { TextFormatter(String pText) {
text = pText; text = pText;
// Just to be able to skip over // Just to be able to skip over
if (pText != null) { if (pText != null) {
digits = pText.length(); digits = pText.length();
} }
} }
String format(Time t) { String format(Time t) {
// Simply return the text // Simply return the text
return text; return text;
} }
} }

View File

@@ -1,451 +1,453 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.util.*;
/** /**
* A {@code Map} implementation that removes (exipres) its elements after * A {@code Map} implementation that removes (exipres) its elements after
* a given period. The map is by default backed by a {@link java.util.HashMap}, * a given period. The map is by default backed by a {@link java.util.HashMap},
* or can be instantiated with any given {@code Map} as backing. * or can be instantiated with any given {@code Map} as backing.
* <P/> * <p>
* Notes to consider when using this map: * Notes to consider when using this map:
* <ul> * </p>
* <li>Elements may not expire on the exact millisecond as expected.</li> * <ul>
* <li>The value returned by the {@code size()} method of the map, or any of * <li>Elements may not expire on the exact millisecond as expected.</li>
* its collection views, may not represent * <li>The value returned by the {@code size()} method of the map, or any of
* the exact number of entries in the map at any given time.</li> * its collection views, may not represent
* <li>Elements in this map may expire at any time * the exact number of entries in the map at any given time.</li>
* (but never between invocations of {@code Iterator.hasNext()} * <li>Elements in this map may expire at any time
* and {@code Iterator.next()} or {@code Iterator.remove()}, * (but never between invocations of {@code Iterator.hasNext()}
* when iterating the collection views).</li> * and {@code Iterator.next()} or {@code Iterator.remove()},
* </ul> * when iterating the collection views).</li>
* * </ul>
* @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/util/TimeoutMap.java#2 $ * @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/util/TimeoutMap.java#2 $
* @todo Consider have this Map extend LinkedMap.. That way the removeExpired */
* method only have to run from the first element, until it finds an element // TODO: Consider have this Map extend LinkedMap.. That way the removeExpired
* that should not expire, as elements are in insertion order. // method only have to run from the first element, until it finds an element
* and next expiry time would be the time of the first element. // that should not expire, as elements are in insertion order.
* @todo Consider running the removeExpiredEntries method in a separate (deamon) thread // and next expiry time would be the time of the first element.
* @todo - or document why it is not such a good idea. // TODO: Consider running the removeExpiredEntries method in a separate (deamon) thread
*/ // TODO: - or document why it is not such a good idea.
public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements ExpiringMap<K, V>, Serializable, Cloneable { public class TimeoutMap<K, V> extends AbstractDecoratedMap<K, V> implements ExpiringMap<K, V>, Serializable, Cloneable {
/** /**
* Expiry time * Expiry time
*/ */
protected long expiryTime = 60000L; // 1 minute protected long expiryTime = 60000L; // 1 minute
////////////////////// //////////////////////
private volatile long nextExpiryTime; private volatile long nextExpiryTime;
////////////////////// //////////////////////
/** /**
* Creates a {@code TimeoutMap} with the default expiry time of 1 minute. * Creates a {@code TimeoutMap} with the default expiry time of 1 minute.
* This {@code TimeoutMap} will be backed by a new {@code HashMap} instance. * This {@code TimeoutMap} will be backed by a new {@code HashMap} instance.
* <p/> * <p>
* <small>This is constructor is here to comply with the reccomendations for * <small>This is constructor is here to comply with the recommendations for
* "standard" constructors in the {@code Map} interface.</small> * "standard" constructors in the {@code Map} interface.</small>
* * </p>
* @see #TimeoutMap(long) *
*/ * @see #TimeoutMap(long)
public TimeoutMap() { */
super(); public TimeoutMap() {
} super();
}
/**
* Creates a {@code TimeoutMap} containing the same elements as the given map /**
* with the default expiry time of 1 minute. * Creates a {@code TimeoutMap} containing the same elements as the given map
* This {@code TimeoutMap} will be backed by a new {@code HashMap} instance, * with the default expiry time of 1 minute.
* and <em>not</em> the map passed in as a paramter. * This {@code TimeoutMap} will be backed by a new {@code HashMap} instance,
* <p/> * and <em>not</em> the map passed in as a paramter.
* <small>This is constructor is here to comply with the reccomendations for * <p>
* "standard" constructors in the {@code Map} interface.</small> * <small>This is constructor is here to comply with the recommendations for
* * "standard" constructors in the {@code Map} interface.</small>
* @param pContents the map whose mappings are to be placed in this map. * </p>
* May be {@code null}. *
* @see #TimeoutMap(java.util.Map, Map, long) * @param pContents the map whose mappings are to be placed in this map.
* @see java.util.Map * May be {@code null}.
*/ * @see #TimeoutMap(java.util.Map, Map, long)
public TimeoutMap(Map<? extends K, ? extends V> pContents) { * @see java.util.Map
super(pContents); */
} public TimeoutMap(Map<? extends K, ? extends V> pContents) {
super(pContents);
/** }
* Creates a {@code TimeoutMap} with the given expiry time (milliseconds).
* This {@code TimeoutMap} will be backed by a new {@code HashMap} instance. /**
* * Creates a {@code TimeoutMap} with the given expiry time (milliseconds).
* @param pExpiryTime the expiry time (time to live) for elements in this map * This {@code TimeoutMap} will be backed by a new {@code HashMap} instance.
*/ *
public TimeoutMap(long pExpiryTime) { * @param pExpiryTime the expiry time (time to live) for elements in this map
this(); */
expiryTime = pExpiryTime; public TimeoutMap(long pExpiryTime) {
} this();
expiryTime = pExpiryTime;
/** }
* Creates a {@code TimeoutMap} with the given expiry time (milliseconds).
* This {@code TimeoutMap} will be backed by the given {@code Map}. /**
* <P/> * Creates a {@code TimeoutMap} with the given expiry time (milliseconds).
* <EM>Note that structurally modifying the backing map directly (not * This {@code TimeoutMap} will be backed by the given {@code Map}.
* through this map or its collection views), is not allowed, and will * <p>
* produce undeterministic exceptions.</EM> * <em>Note that structurally modifying the backing map directly (not
* * through this map or its collection views), is not allowed, and will
* @param pBacking the map that will be used as backing. * produce undeterministic exceptions.</em>
* @param pContents the map whose mappings are to be placed in this map. * </p>
* May be {@code null}. *
* @param pExpiryTime the expiry time (time to live) for elements in this map * @param pBacking the map that will be used as backing.
*/ * @param pContents the map whose mappings are to be placed in this map.
public TimeoutMap(Map<K, Map.Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents, long pExpiryTime) { * May be {@code null}.
super(pBacking, pContents); * @param pExpiryTime the expiry time (time to live) for elements in this map
expiryTime = pExpiryTime; */
} public TimeoutMap(Map<K, Map.Entry<K, V>> pBacking, Map<? extends K, ? extends V> pContents, long pExpiryTime) {
super(pBacking, pContents);
/** expiryTime = pExpiryTime;
* Gets the maximum time any value will be kept in the map, before it expires. }
*
* @return the expiry time /**
*/ * Gets the maximum time any value will be kept in the map, before it expires.
public long getExpiryTime() { *
return expiryTime; * @return the expiry time
} */
public long getExpiryTime() {
/** return expiryTime;
* Sets the maximum time any value will be kept in the map, before it expires. }
* Removes any items that are older than the specified time.
* /**
* @param pExpiryTime the expiry time (time to live) for elements in this map * Sets the maximum time any value will be kept in the map, before it expires.
*/ * Removes any items that are older than the specified time.
public void setExpiryTime(long pExpiryTime) { *
long oldEexpiryTime = expiryTime; * @param pExpiryTime the expiry time (time to live) for elements in this map
*/
expiryTime = pExpiryTime; public void setExpiryTime(long pExpiryTime) {
long oldEexpiryTime = expiryTime;
if (expiryTime < oldEexpiryTime) {
// Expire now expiryTime = pExpiryTime;
nextExpiryTime = 0;
removeExpiredEntries(); if (expiryTime < oldEexpiryTime) {
} // Expire now
} nextExpiryTime = 0;
removeExpiredEntries();
/** }
* Returns the number of key-value mappings in this map. If the }
* map contains more than {@code Integer.MAX_VALUE} elements, returns
* {@code Integer.MAX_VALUE}. /**
* * Returns the number of key-value mappings in this map. If the
* @return the number of key-value mappings in this map. * map contains more than {@code Integer.MAX_VALUE} elements, returns
*/ * {@code Integer.MAX_VALUE}.
public int size() { *
removeExpiredEntries(); * @return the number of key-value mappings in this map.
return entries.size(); */
} public int size() {
removeExpiredEntries();
/** return entries.size();
* Returns {@code true} if this map contains no key-value mappings. }
*
* @return {@code true} if this map contains no key-value mappings. /**
*/ * Returns {@code true} if this map contains no key-value mappings.
public boolean isEmpty() { *
return (size() <= 0); * @return {@code true} if this map contains no key-value mappings.
} */
public boolean isEmpty() {
/** return (size() <= 0);
* Returns {@code true} if this map contains a mapping for the specified }
* pKey.
* /**
* @param pKey pKey whose presence in this map is to be tested. * Returns {@code true} if this map contains a mapping for the specified
* @return {@code true} if this map contains a mapping for the specified * pKey.
* pKey. *
*/ * @param pKey pKey whose presence in this map is to be tested.
public boolean containsKey(Object pKey) { * @return {@code true} if this map contains a mapping for the specified
removeExpiredEntries(); * pKey.
return entries.containsKey(pKey); */
} public boolean containsKey(Object pKey) {
removeExpiredEntries();
/** return entries.containsKey(pKey);
* Returns the value to which this map maps the specified pKey. Returns }
* {@code null} if the map contains no mapping for this pKey. A return
* value of {@code null} does not <i>necessarily</i> indicate that the /**
* map contains no mapping for the pKey; it's also possible that the map * Returns the value to which this map maps the specified pKey. Returns
* explicitly maps the pKey to {@code null}. The {@code containsKey} * {@code null} if the map contains no mapping for this pKey. A return
* operation may be used to distinguish these two cases. * value of {@code null} does not <i>necessarily</i> indicate that the
* * map contains no mapping for the pKey; it's also possible that the map
* @param pKey pKey whose associated value is to be returned. * explicitly maps the pKey to {@code null}. The {@code containsKey}
* @return the value to which this map maps the specified pKey, or * operation may be used to distinguish these two cases.
* {@code null} if the map contains no mapping for this pKey. *
* @see #containsKey(java.lang.Object) * @param pKey pKey whose associated value is to be returned.
*/ * @return the value to which this map maps the specified pKey, or
public V get(Object pKey) { * {@code null} if the map contains no mapping for this pKey.
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey); * @see #containsKey(java.lang.Object)
*/
if (entry == null) { public V get(Object pKey) {
return null; TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
}
else if (entry.isExpired()) { if (entry == null) {
//noinspection SuspiciousMethodCalls return null;
entries.remove(pKey); }
processRemoved(entry); else if (entry.isExpired()) {
return null; //noinspection SuspiciousMethodCalls
} entries.remove(pKey);
return entry.getValue(); processRemoved(entry);
} return null;
}
/** return entry.getValue();
* Associates the specified pValue with the specified pKey in this map }
* (optional operation). If the map previously contained a mapping for
* this pKey, the old pValue is replaced. /**
* * Associates the specified pValue with the specified pKey in this map
* @param pKey pKey with which the specified pValue is to be associated. * (optional operation). If the map previously contained a mapping for
* @param pValue pValue to be associated with the specified pKey. * this pKey, the old pValue is replaced.
* @return previous pValue associated with specified pKey, or {@code null} *
* if there was no mapping for pKey. A {@code null} return can * @param pKey pKey with which the specified pValue is to be associated.
* also indicate that the map previously associated {@code null} * @param pValue pValue to be associated with the specified pKey.
* with the specified pKey, if the implementation supports * @return previous pValue associated with specified pKey, or {@code null}
* {@code null} values. * if there was no mapping for pKey. A {@code null} return can
*/ * also indicate that the map previously associated {@code null}
public V put(K pKey, V pValue) { * with the specified pKey, if the implementation supports
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey); * {@code null} values.
V oldValue; */
public V put(K pKey, V pValue) {
if (entry == null) { TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.get(pKey);
oldValue = null; V oldValue;
entry = createEntry(pKey, pValue); if (entry == null) {
oldValue = null;
entries.put(pKey, entry);
} entry = createEntry(pKey, pValue);
else {
oldValue = entry.mValue; entries.put(pKey, entry);
entry.setValue(pValue); }
entry.recordAccess(this); else {
} oldValue = entry.mValue;
entry.setValue(pValue);
// Need to remove expired objects every now and then entry.recordAccess(this);
// We do it in the put method, to avoid resource leaks over time. }
removeExpiredEntries();
modCount++; // Need to remove expired objects every now and then
// We do it in the put method, to avoid resource leaks over time.
return oldValue; removeExpiredEntries();
} modCount++;
/** return oldValue;
* Removes the mapping for this pKey from this map if present (optional }
* operation).
* /**
* @param pKey pKey whose mapping is to be removed from the map. * Removes the mapping for this pKey from this map if present (optional
* @return previous value associated with specified pKey, or {@code null} * operation).
* if there was no mapping for pKey. A {@code null} return can *
* also indicate that the map previously associated {@code null} * @param pKey pKey whose mapping is to be removed from the map.
* with the specified pKey, if the implementation supports * @return previous value associated with specified pKey, or {@code null}
* {@code null} values. * if there was no mapping for pKey. A {@code null} return can
*/ * also indicate that the map previously associated {@code null}
public V remove(Object pKey) { * with the specified pKey, if the implementation supports
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.remove(pKey); * {@code null} values.
return (entry != null) ? entry.getValue() : null; */
} public V remove(Object pKey) {
TimedEntry<K, V> entry = (TimedEntry<K, V>) entries.remove(pKey);
/** return (entry != null) ? entry.getValue() : null;
* Removes all mappings from this map. }
*/
public void clear() { /**
entries.clear(); // Finally something straightforward.. :-) * Removes all mappings from this map.
init(); */
} public void clear() {
entries.clear(); // Finally something straightforward.. :-)
/*protected*/ TimedEntry<K, V> createEntry(K pKey, V pValue) { init();
return new TimedEntry<K, V>(pKey, pValue); }
}
/*protected*/ TimedEntry<K, V> createEntry(K pKey, V pValue) {
/** return new TimedEntry<K, V>(pKey, pValue);
* Removes any expired mappings. }
*
*/ /**
protected void removeExpiredEntries() { * Removes any expired mappings.
// Remove any expired elements *
long now = System.currentTimeMillis(); */
if (now > nextExpiryTime) { protected void removeExpiredEntries() {
removeExpiredEntriesSynced(now); // Remove any expired elements
} long now = System.currentTimeMillis();
} if (now > nextExpiryTime) {
removeExpiredEntriesSynced(now);
/** }
* Okay, I guess this do resemble DCL... }
*
* @todo Write some exhausting multi-threaded unit-tests. /**
* * Okay, I guess this do resemble DCL...
* @param pTime now *
*/ * @param pTime now
private synchronized void removeExpiredEntriesSynced(long pTime) { */
if (pTime > nextExpiryTime) { // TODO: Write some exhausting multi-threaded unit-tests.
//// private synchronized void removeExpiredEntriesSynced(long pTime) {
long next = Long.MAX_VALUE; if (pTime > nextExpiryTime) {
nextExpiryTime = next; // Avoid multiple runs... ////
for (Iterator<Entry<K, V>> iterator = new EntryIterator(); iterator.hasNext();) { long next = Long.MAX_VALUE;
TimedEntry<K, V> entry = (TimedEntry<K, V>) iterator.next(); nextExpiryTime = next; // Avoid multiple runs...
//// for (Iterator<Entry<K, V>> iterator = new EntryIterator(); iterator.hasNext();) {
long expires = entry.expires(); TimedEntry<K, V> entry = (TimedEntry<K, V>) iterator.next();
if (expires < next) { ////
next = expires; long expires = entry.expires();
} if (expires < next) {
//// next = expires;
} }
//// ////
nextExpiryTime = next; }
} ////
} nextExpiryTime = next;
}
public Collection<V> values() { }
removeExpiredEntries();
return super.values(); public Collection<V> values() {
} removeExpiredEntries();
return super.values();
public Set<Entry<K, V>> entrySet() { }
removeExpiredEntries();
return super.entrySet(); public Set<Entry<K, V>> entrySet() {
} removeExpiredEntries();
return super.entrySet();
public Set<K> keySet() { }
removeExpiredEntries();
return super.keySet(); public Set<K> keySet() {
} removeExpiredEntries();
return super.keySet();
// Subclass overrides these to alter behavior of views' iterator() method }
protected Iterator<K> newKeyIterator() {
return new KeyIterator(); // Subclass overrides these to alter behavior of views' iterator() method
} protected Iterator<K> newKeyIterator() {
return new KeyIterator();
protected Iterator<V> newValueIterator() { }
return new ValueIterator();
} protected Iterator<V> newValueIterator() {
return new ValueIterator();
protected Iterator<Entry<K, V>> newEntryIterator() { }
return new EntryIterator();
} protected Iterator<Entry<K, V>> newEntryIterator() {
return new EntryIterator();
public void processRemoved(Entry pRemoved) { }
}
public void processRemoved(Entry pRemoved) {
/** }
* Note: Iterating through this iterator will remove any expired values.
*/ /**
private abstract class TimeoutMapIterator<E> implements Iterator<E> { * Note: Iterating through this iterator will remove any expired values.
Iterator<Entry<K, Entry<K, V>>> mIterator = entries.entrySet().iterator(); */
BasicEntry<K, V> mNext; private abstract class TimeoutMapIterator<E> implements Iterator<E> {
long mNow = System.currentTimeMillis(); Iterator<Entry<K, Entry<K, V>>> mIterator = entries.entrySet().iterator();
BasicEntry<K, V> mNext;
public void remove() { long mNow = System.currentTimeMillis();
mNext = null; // advance
mIterator.remove(); public void remove() {
} mNext = null; // advance
mIterator.remove();
public boolean hasNext() { }
if (mNext != null) {
return true; // Never expires between hasNext and next/remove! public boolean hasNext() {
} if (mNext != null) {
return true; // Never expires between hasNext and next/remove!
while (mNext == null && mIterator.hasNext()) { }
Entry<K, Entry<K, V>> entry = mIterator.next();
TimedEntry<K, V> timed = (TimedEntry<K, V>) entry.getValue(); while (mNext == null && mIterator.hasNext()) {
Entry<K, Entry<K, V>> entry = mIterator.next();
if (timed.isExpiredBy(mNow)) { TimedEntry<K, V> timed = (TimedEntry<K, V>) entry.getValue();
// Remove from map, and continue
mIterator.remove(); if (timed.isExpiredBy(mNow)) {
processRemoved(timed); // Remove from map, and continue
} mIterator.remove();
else { processRemoved(timed);
// Go with this entry }
mNext = timed; else {
return true; // Go with this entry
} mNext = timed;
} return true;
}
return false; }
}
return false;
BasicEntry<K, V> nextEntry() { }
if (!hasNext()) {
throw new NoSuchElementException(); BasicEntry<K, V> nextEntry() {
} if (!hasNext()) {
throw new NoSuchElementException();
BasicEntry<K, V> entry = mNext; }
mNext = null; // advance
return entry; BasicEntry<K, V> entry = mNext;
} mNext = null; // advance
} return entry;
}
private class KeyIterator extends TimeoutMapIterator<K> { }
public K next() {
return nextEntry().mKey; private class KeyIterator extends TimeoutMapIterator<K> {
} public K next() {
} return nextEntry().mKey;
}
private class ValueIterator extends TimeoutMapIterator<V> { }
public V next() {
return nextEntry().mValue; private class ValueIterator extends TimeoutMapIterator<V> {
} public V next() {
} return nextEntry().mValue;
}
private class EntryIterator extends TimeoutMapIterator<Entry<K, V>> { }
public Entry<K, V> next() {
return nextEntry(); private class EntryIterator extends TimeoutMapIterator<Entry<K, V>> {
} public Entry<K, V> next() {
} return nextEntry();
}
/** }
* Keeps track of timed objects
*/ /**
private class TimedEntry<K, V> extends BasicEntry<K, V> { * Keeps track of timed objects
private long mTimestamp; */
private class TimedEntry<K, V> extends BasicEntry<K, V> {
TimedEntry(K pKey, V pValue) { private long mTimestamp;
super(pKey, pValue);
mTimestamp = System.currentTimeMillis(); TimedEntry(K pKey, V pValue) {
} super(pKey, pValue);
mTimestamp = System.currentTimeMillis();
public V setValue(V pValue) { }
mTimestamp = System.currentTimeMillis();
return super.setValue(pValue); public V setValue(V pValue) {
} mTimestamp = System.currentTimeMillis();
return super.setValue(pValue);
final boolean isExpired() { }
return isExpiredBy(System.currentTimeMillis());
} final boolean isExpired() {
return isExpiredBy(System.currentTimeMillis());
final boolean isExpiredBy(final long pTime) { }
return pTime > expires();
} final boolean isExpiredBy(final long pTime) {
return pTime > expires();
final long expires() { }
return mTimestamp + expiryTime;
} final long expires() {
} return mTimestamp + expiryTime;
} }
}
}

View File

@@ -1,59 +1,58 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util; package com.twelvemonkeys.util;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.Iterator; import java.util.Iterator;
/** /**
* TokenIterator, Iterator-based replacement for StringTokenizer. * TokenIterator, Iterator-based replacement for StringTokenizer.
* <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/util/TokenIterator.java#1 $
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/TokenIterator.java#1 $ */
*/ public interface TokenIterator extends Iterator<String>, Enumeration<String> {
public interface TokenIterator extends Iterator<String>, Enumeration<String> { boolean hasMoreTokens();
boolean hasMoreTokens();
/**
/** * Returns the next element in the iteration as a {@code String}.
* Returns the next element in the iteration as a {@code String}. *
* * @return the next element in the iteration.
* @return the next element in the iteration. * @exception java.util.NoSuchElementException iteration has no more elements.
* @exception java.util.NoSuchElementException iteration has no more elements. */
*/ String nextToken();
String nextToken();
/**
/** * Resets this iterator.
* Resets this iterator. *
* */
*/ void reset();
void reset(); }
}

View File

@@ -1,196 +1,198 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util.convert; package com.twelvemonkeys.util.convert;
import com.twelvemonkeys.util.Time; import com.twelvemonkeys.util.Time;
import java.util.Date; import java.util.Date;
import java.util.Hashtable; import java.util.Hashtable;
import java.util.Map; import java.util.Map;
/** /**
* The converter (singleton). Converts strings to objects and back. * The converter (singleton). Converts strings to objects and back.
* This is the entry point to the converter framework. * This is the entry point to the converter framework.
* <p/> * <p>
* By default, converters for {@link com.twelvemonkeys.util.Time}, {@link Date} * By default, converters for {@link com.twelvemonkeys.util.Time}, {@link Date}
* and {@link Object} * and {@link Object}
* (the {@link DefaultConverter}) are registered by this class' static * (the {@link DefaultConverter}) are registered by this class' static
* initializer. You might remove them using the * initializer. You might remove them using the
* {@code unregisterConverter} method. * {@code unregisterConverter} method.
* * </p>
* @see #registerConverter(Class, PropertyConverter) *
* @see #unregisterConverter(Class) * @see #registerConverter(Class, PropertyConverter)
* * @see #unregisterConverter(Class)
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> *
* @author last modified by $Author: haku $ * @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/util/convert/Converter.java#1 $ * @author last modified by $Author: haku $
*/ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/Converter.java#1 $
// TODO: Get rid of singleton stuff */
// Can probably be a pure static class, but is that a good idea? // TODO: Get rid of singleton stuff
// Maybe have BeanUtil act as a "proxy", and hide this class all together? // Can probably be a pure static class, but is that a good idea?
// TODO: ServiceRegistry for registering 3rd party converters // Maybe have BeanUtil act as a "proxy", and hide this class all together?
// TODO: URI scheme, for implicit typing? Is that a good idea? // TODO: ServiceRegistry for registering 3rd party converters
// TODO: Array converters? // TODO: URI scheme, for implicit typing? Is that a good idea?
public abstract class Converter implements PropertyConverter { // TODO: Array converters?
public abstract class Converter implements PropertyConverter {
/** Our singleton instance */
protected static final Converter sInstance = new ConverterImpl(); // Thread safe & EASY /** Our singleton instance */
protected static final Converter sInstance = new ConverterImpl(); // Thread safe & EASY
/** The converters Map */
protected final Map<Class, PropertyConverter> converters = new Hashtable<Class, PropertyConverter>(); /** The converters Map */
protected final Map<Class, PropertyConverter> converters = new Hashtable<Class, PropertyConverter>();
// Register our predefined converters
static { // Register our predefined converters
PropertyConverter defaultConverter = new DefaultConverter(); static {
registerConverter(Object.class, defaultConverter); PropertyConverter defaultConverter = new DefaultConverter();
registerConverter(Boolean.TYPE, defaultConverter); registerConverter(Object.class, defaultConverter);
registerConverter(Boolean.TYPE, defaultConverter);
PropertyConverter numberConverter = new NumberConverter();
registerConverter(Number.class, numberConverter); PropertyConverter numberConverter = new NumberConverter();
registerConverter(Byte.TYPE, numberConverter); registerConverter(Number.class, numberConverter);
registerConverter(Double.TYPE, numberConverter); registerConverter(Byte.TYPE, numberConverter);
registerConverter(Float.TYPE, numberConverter); registerConverter(Double.TYPE, numberConverter);
registerConverter(Integer.TYPE, numberConverter); registerConverter(Float.TYPE, numberConverter);
registerConverter(Long.TYPE, numberConverter); registerConverter(Integer.TYPE, numberConverter);
registerConverter(Short.TYPE, numberConverter); registerConverter(Long.TYPE, numberConverter);
registerConverter(Short.TYPE, numberConverter);
registerConverter(Date.class, new DateConverter());
registerConverter(Time.class, new TimeConverter()); registerConverter(Date.class, new DateConverter());
} registerConverter(Time.class, new TimeConverter());
}
/**
* Creates a Converter. /**
*/ * Creates a Converter.
protected Converter() { */
} protected Converter() {
}
/**
* Gets the Converter instance. /**
* * Gets the Converter instance.
* @return the converter instance *
*/ * @return the converter instance
public static Converter getInstance() { */
return sInstance; public static Converter getInstance() {
} return sInstance;
}
/**
* Registers a converter for a given type. /**
* This converter will also be used for all subclasses, unless a more * Registers a converter for a given type.
* specific version is registered. * This converter will also be used for all subclasses, unless a more
* </p> * specific version is registered.
* By default, converters for {@link com.twelvemonkeys.util.Time}, {@link Date} * <p>
* and {@link Object} * By default, converters for {@link com.twelvemonkeys.util.Time}, {@link Date}
* (the {@link DefaultConverter}) are registered by this class' static * and {@link Object}
* initializer. You might remove them using the * (the {@link DefaultConverter}) are registered by this class' static
* {@code unregisterConverter} method. * initializer. You might remove them using the
* * {@code unregisterConverter} method.
* @param pType the (super) type to register a converter for * </p>
* @param pConverter the converter *
* * @param pType the (super) type to register a converter for
* @see #unregisterConverter(Class) * @param pConverter the converter
*/ *
public static void registerConverter(final Class<?> pType, final PropertyConverter pConverter) { * @see #unregisterConverter(Class)
getInstance().converters.put(pType, pConverter); */
} public static void registerConverter(final Class<?> pType, final PropertyConverter pConverter) {
getInstance().converters.put(pType, pConverter);
/** }
* Un-registers a converter for a given type. That is, making it unavailable
* for the converter framework, and making it (potentially) available for /**
* garbage collection. * Un-registers a converter for a given type. That is, making it unavailable
* * for the converter framework, and making it (potentially) available for
* @param pType the (super) type to remove converter for * garbage collection.
* *
* @see #registerConverter(Class,PropertyConverter) * @param pType the (super) type to remove converter for
*/ *
@SuppressWarnings("UnusedDeclaration") * @see #registerConverter(Class,PropertyConverter)
public static void unregisterConverter(final Class<?> pType) { */
getInstance().converters.remove(pType); @SuppressWarnings("UnusedDeclaration")
} public static void unregisterConverter(final Class<?> pType) {
getInstance().converters.remove(pType);
/** }
* Converts the string to an object of the given type.
* /**
* @param pString the string to convert * Converts the string to an object of the given type.
* @param pType the type to convert to *
* * @param pString the string to convert
* @return the object created from the given string. * @param pType the type to convert to
* *
* @throws ConversionException if the string cannot be converted for any * @return the object created from the given string.
* reason. *
*/ * @throws ConversionException if the string cannot be converted for any
public Object toObject(final String pString, final Class pType) throws ConversionException { * reason.
return toObject(pString, pType, null); */
} public Object toObject(final String pString, final Class pType) throws ConversionException {
return toObject(pString, pType, null);
/** }
* Converts the string to an object of the given type, parsing after the
* given format. /**
* * Converts the string to an object of the given type, parsing after the
* @param pString the string to convert * given format.
* @param pType the type to convert to *
* @param pFormat the (optional) conversion format * @param pString the string to convert
* * @param pType the type to convert to
* @return the object created from the given string. * @param pFormat the (optional) conversion format
* *
* @throws ConversionException if the string cannot be converted for any * @return the object created from the given string.
* reason. *
*/ * @throws ConversionException if the string cannot be converted for any
public abstract Object toObject(String pString, Class pType, String pFormat) * reason.
throws ConversionException; */
public abstract Object toObject(String pString, Class pType, String pFormat)
/** throws ConversionException;
* Converts the object to a string, using {@code object.toString()}
* /**
* @param pObject the object to convert. * Converts the object to a string, using {@code object.toString()}
* *
* @return the string representation of the object, on the correct format. * @param pObject the object to convert.
* *
* @throws ConversionException if the object cannot be converted to a * @return the string representation of the object, on the correct format.
* string for any reason. *
*/ * @throws ConversionException if the object cannot be converted to a
public String toString(final Object pObject) throws ConversionException { * string for any reason.
return toString(pObject, null); */
} public String toString(final Object pObject) throws ConversionException {
return toString(pObject, null);
/** }
* Converts the object to a string, using {@code object.toString()}
* /**
* @param pObject the object to convert. * Converts the object to a string, using {@code object.toString()}
* @param pFormat the (optional) conversion format *
* * @param pObject the object to convert.
* @return the string representation of the object, on the correct format. * @param pFormat the (optional) conversion format
* *
* @throws ConversionException if the object cannot be converted to a * @return the string representation of the object, on the correct format.
* string for any reason. *
*/ * @throws ConversionException if the object cannot be converted to a
public abstract String toString(Object pObject, String pFormat) * string for any reason.
throws ConversionException; */
} public abstract String toString(Object pObject, String pFormat)
throws ConversionException;
}

View File

@@ -1,157 +1,158 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util.convert; package com.twelvemonkeys.util.convert;
import com.twelvemonkeys.lang.BeanUtil; import com.twelvemonkeys.lang.BeanUtil;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
/** /**
* Converts strings to dates and back. * Converts strings to dates and back.
* <p/> * <p>
* <small>This class has a static cache of {@code DateFormats}, to avoid * <small>This class has a static cache of {@code DateFormats}, to avoid
* creation and parsing of date formats every time one is used.</small> * creation and parsing of date formats every time one is used.</small>
* * </p>
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> *
* @author last modified by $Author: haku $ * @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/util/convert/DateConverter.java#2 $ * @author last modified by $Author: haku $
*/ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/DateConverter.java#2 $
public class DateConverter extends NumberConverter { */
public class DateConverter extends NumberConverter {
/** Creates a {@code DateConverter} */
public DateConverter() { /** Creates a {@code DateConverter} */
} public DateConverter() {
}
/**
* Converts the string to a date, using the given format for parsing. /**
* * Converts the string to a date, using the given format for parsing.
* @param pString the string to convert. *
* @param pType the type to convert to. {@code java.util.Date} and * @param pString the string to convert.
* subclasses allowed. * @param pType the type to convert to. {@code java.util.Date} and
* @param pFormat the format used for parsing. Must be a legal * subclasses allowed.
* {@code SimpleDateFormat} format, or {@code null} which will use the * @param pFormat the format used for parsing. Must be a legal
* default format. * {@code SimpleDateFormat} format, or {@code null} which will use the
* * default format.
* @return the object created from the given string. May safely be typecast *
* to {@code java.util.Date} * @return the object created from the given string. May safely be typecast
* * to {@code java.util.Date}
* @see Date *
* @see java.text.DateFormat * @see Date
* * @see java.text.DateFormat
* @throws ConversionException *
*/ * @throws ConversionException
public Object toObject(String pString, Class pType, String pFormat) throws ConversionException { */
if (StringUtil.isEmpty(pString)) public Object toObject(String pString, Class pType, String pFormat) throws ConversionException {
return null; if (StringUtil.isEmpty(pString))
return null;
try {
DateFormat format; try {
DateFormat format;
if (pFormat == null) {
// Use system default format, using default locale if (pFormat == null) {
format = DateFormat.getDateTimeInstance(); // Use system default format, using default locale
} format = DateFormat.getDateTimeInstance();
else { }
// Get format from cache else {
format = getDateFormat(pFormat); // Get format from cache
} format = getDateFormat(pFormat);
}
Date date = StringUtil.toDate(pString, format);
Date date = StringUtil.toDate(pString, format);
// Allow for conversion to Date subclasses (ie. java.sql.*)
if (pType != Date.class) { // Allow for conversion to Date subclasses (ie. java.sql.*)
try { if (pType != Date.class) {
date = (Date) BeanUtil.createInstance(pType, new Long(date.getTime())); try {
} date = (Date) BeanUtil.createInstance(pType, new Long(date.getTime()));
catch (ClassCastException e) { }
throw new TypeMismathException(pType); catch (ClassCastException e) {
} throw new TypeMismathException(pType);
catch (InvocationTargetException e) { }
throw new ConversionException(e); catch (InvocationTargetException e) {
} throw new ConversionException(e);
} }
}
return date;
} return date;
catch (RuntimeException rte) { }
throw new ConversionException(rte); catch (RuntimeException rte) {
} throw new ConversionException(rte);
} }
}
/**
* Converts the object to a string, using the given format /**
* * Converts the object to a string, using the given format
* @param pObject the object to convert. *
* @param pFormat the format used for conversion. Must be a legal * @param pObject the object to convert.
* {@code SimpleDateFormat} format, or {@code null} which will use the * @param pFormat the format used for conversion. Must be a legal
* default format. * {@code SimpleDateFormat} format, or {@code null} which will use the
* * default format.
* @return the string representation of the object, on the correct format. *
* * @return the string representation of the object, on the correct format.
* @throws ConversionException if the object is not a subclass of *
* {@code java.util.Date} * @throws ConversionException if the object is not a subclass of
* * {@code java.util.Date}
* @see Date *
* @see java.text.DateFormat * @see Date
*/ * @see java.text.DateFormat
public String toString(Object pObject, String pFormat) throws ConversionException { */
if (pObject == null) public String toString(Object pObject, String pFormat) throws ConversionException {
return null; if (pObject == null)
return null;
if (!(pObject instanceof Date)) {
throw new TypeMismathException(pObject.getClass()); if (!(pObject instanceof Date)) {
} throw new TypeMismathException(pObject.getClass());
}
try {
// Convert to string, default way try {
if (StringUtil.isEmpty(pFormat)) { // Convert to string, default way
return DateFormat.getDateTimeInstance().format(pObject); if (StringUtil.isEmpty(pFormat)) {
} return DateFormat.getDateTimeInstance().format(pObject);
}
// Convert to string, using format
DateFormat format = getDateFormat(pFormat); // Convert to string, using format
DateFormat format = getDateFormat(pFormat);
return format.format(pObject);
} return format.format(pObject);
catch (RuntimeException rte) { }
throw new ConversionException(rte); catch (RuntimeException rte) {
} throw new ConversionException(rte);
} }
}
private DateFormat getDateFormat(String pFormat) {
return (DateFormat) getFormat(SimpleDateFormat.class, pFormat, Locale.US); private DateFormat getDateFormat(String pFormat) {
} return (DateFormat) getFormat(SimpleDateFormat.class, pFormat, Locale.US);
} }
}

View File

@@ -1,268 +1,269 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util.convert; package com.twelvemonkeys.util.convert;
import com.twelvemonkeys.lang.BeanUtil; import com.twelvemonkeys.lang.BeanUtil;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import java.lang.reflect.Array; import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
/** /**
* Converts strings to objects and back. * Converts strings to objects and back.
* <p/> * <p>
* This converter first tries to create an object, using the class' single * This converter first tries to create an object, using the class' single
* string argument constructor ({@code &lt;type&gt;(String)}) if found, * string argument constructor ({@code &lt;type&gt;(String)}) if found,
* otherwise, an attempt to call * otherwise, an attempt to call
* the class' static {@code valueOf(String)} method. If both fails, a * the class' static {@code valueOf(String)} method. If both fails, a
* {@link ConversionException} is thrown. * {@link ConversionException} is thrown.
* * </p>
* @author <A href="haraldk@iconmedialab.no">Harald Kuhr</A> *
* @author last modified by $Author: haku $ * @author <A href="haraldk@iconmedialab.no">Harald Kuhr</A>
* @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/DefaultConverter.java#2 $ * @author last modified by $Author: haku $
* * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/DefaultConverter.java#2 $
*/ *
public final class DefaultConverter implements PropertyConverter { */
public final class DefaultConverter implements PropertyConverter {
/**
* Creates a {@code DefaultConverter}. /**
*/ * Creates a {@code DefaultConverter}.
public DefaultConverter() { */
} public DefaultConverter() {
}
/**
* Converts the string to an object of the given type. /**
* * Converts the string to an object of the given type.
* @param pString the string to convert *
* @param pType the type to convert to * @param pString the string to convert
* @param pFormat ignored. * @param pType the type to convert to
* * @param pFormat ignored.
* @return the object created from the given string. *
* * @return the object created from the given string.
* @throws ConversionException if the type is null, or if the string cannot *
* be converted into the given type, using a string constructor or static * @throws ConversionException if the type is null, or if the string cannot
* {@code valueOf} method. * be converted into the given type, using a string constructor or static
*/ * {@code valueOf} method.
public Object toObject(final String pString, final Class pType, final String pFormat) throws ConversionException { */
if (pString == null) { public Object toObject(final String pString, final Class pType, final String pFormat) throws ConversionException {
return null; if (pString == null) {
} return null;
}
if (pType == null) {
throw new MissingTypeException(); if (pType == null) {
} throw new MissingTypeException();
}
if (pType.isArray()) {
return toArray(pString, pType, pFormat); if (pType.isArray()) {
} return toArray(pString, pType, pFormat);
}
// TODO: Separate CollectionConverter?
// should however, be simple to wrap array using Arrays.asList // TODO: Separate CollectionConverter?
// But what about generic type?! It's erased... // should however, be simple to wrap array using Arrays.asList
// But what about generic type?! It's erased...
// Primitive -> wrapper
Class type = unBoxType(pType); // Primitive -> wrapper
Class type = unBoxType(pType);
try {
// Try to create instance from <Constructor>(String) try {
Object value = BeanUtil.createInstance(type, pString); // Try to create instance from <Constructor>(String)
Object value = BeanUtil.createInstance(type, pString);
if (value == null) {
// createInstance failed for some reason if (value == null) {
// Try to invoke the static method valueOf(String) // createInstance failed for some reason
value = BeanUtil.invokeStaticMethod(type, "valueOf", pString); // Try to invoke the static method valueOf(String)
value = BeanUtil.invokeStaticMethod(type, "valueOf", pString);
if (value == null) {
// If the value is still null, well, then I cannot help... if (value == null) {
throw new ConversionException(String.format( // If the value is still null, well, then I cannot help...
"Could not convert String to %1$s: No constructor %1$s(String) or static %1$s.valueOf(String) method found!", throw new ConversionException(String.format(
type.getName() "Could not convert String to %1$s: No constructor %1$s(String) or static %1$s.valueOf(String) method found!",
)); type.getName()
} ));
} }
}
return value;
} return value;
catch (InvocationTargetException ite) { }
throw new ConversionException(ite.getTargetException()); catch (InvocationTargetException ite) {
} throw new ConversionException(ite.getTargetException());
catch (ConversionException ce) { }
throw ce; catch (ConversionException ce) {
} throw ce;
catch (RuntimeException rte) { }
throw new ConversionException(rte); catch (RuntimeException rte) {
} throw new ConversionException(rte);
} }
}
private Object toArray(final String pString, final Class pType, final String pFormat) {
String[] strings = StringUtil.toStringArray(pString, pFormat != null ? pFormat : StringUtil.DELIMITER_STRING); private Object toArray(final String pString, final Class pType, final String pFormat) {
Class type = pType.getComponentType(); String[] strings = StringUtil.toStringArray(pString, pFormat != null ? pFormat : StringUtil.DELIMITER_STRING);
if (type == String.class) { Class type = pType.getComponentType();
return strings; if (type == String.class) {
} return strings;
}
Object array = Array.newInstance(type, strings.length);
try { Object array = Array.newInstance(type, strings.length);
for (int i = 0; i < strings.length; i++) { try {
Array.set(array, i, Converter.getInstance().toObject(strings[i], type)); for (int i = 0; i < strings.length; i++) {
} Array.set(array, i, Converter.getInstance().toObject(strings[i], type));
} }
catch (ConversionException e) { }
if (pFormat != null) { catch (ConversionException e) {
throw new ConversionException(String.format("%s for string \"%s\" with format \"%s\"", e.getMessage(), pString, pFormat), e); if (pFormat != null) {
} throw new ConversionException(String.format("%s for string \"%s\" with format \"%s\"", e.getMessage(), pString, pFormat), e);
else { }
throw new ConversionException(String.format("%s for string \"%s\"", e.getMessage(), pString), e); else {
} throw new ConversionException(String.format("%s for string \"%s\"", e.getMessage(), pString), e);
} }
}
return array;
} return array;
}
/**
* Converts the object to a string, using {@code pObject.toString()}. /**
* * Converts the object to a string, using {@code pObject.toString()}.
* @param pObject the object to convert. *
* @param pFormat ignored. * @param pObject the object to convert.
* * @param pFormat ignored.
* @return the string representation of the object, or {@code null} if {@code pObject == null} *
*/ * @return the string representation of the object, or {@code null} if {@code pObject == null}
public String toString(final Object pObject, final String pFormat) */
throws ConversionException { public String toString(final Object pObject, final String pFormat)
throws ConversionException {
try {
return pObject == null ? null : pObject.getClass().isArray() ? arrayToString(toObjectArray(pObject), pFormat) : pObject.toString(); try {
} return pObject == null ? null : pObject.getClass().isArray() ? arrayToString(toObjectArray(pObject), pFormat) : pObject.toString();
catch (RuntimeException rte) { }
throw new ConversionException(rte); catch (RuntimeException rte) {
} throw new ConversionException(rte);
} }
}
private String arrayToString(final Object[] pArray, final String pFormat) {
return pFormat == null ? StringUtil.toCSVString(pArray) : StringUtil.toCSVString(pArray, pFormat); private String arrayToString(final Object[] pArray, final String pFormat) {
} return pFormat == null ? StringUtil.toCSVString(pArray) : StringUtil.toCSVString(pArray, pFormat);
}
private Object[] toObjectArray(final Object pObject) {
// TODO: Extract util method for wrapping/unwrapping native arrays? private Object[] toObjectArray(final Object pObject) {
Object[] array; // TODO: Extract util method for wrapping/unwrapping native arrays?
Class<?> componentType = pObject.getClass().getComponentType(); Object[] array;
if (componentType.isPrimitive()) { Class<?> componentType = pObject.getClass().getComponentType();
if (int.class == componentType) { if (componentType.isPrimitive()) {
array = new Integer[Array.getLength(pObject)]; if (int.class == componentType) {
for (int i = 0; i < array.length; i++) { array = new Integer[Array.getLength(pObject)];
Array.set(array, i, Array.get(pObject, i)); for (int i = 0; i < array.length; i++) {
} Array.set(array, i, Array.get(pObject, i));
} }
else if (short.class == componentType) { }
array = new Short[Array.getLength(pObject)]; else if (short.class == componentType) {
for (int i = 0; i < array.length; i++) { array = new Short[Array.getLength(pObject)];
Array.set(array, i, Array.get(pObject, i)); for (int i = 0; i < array.length; i++) {
} Array.set(array, i, Array.get(pObject, i));
} }
else if (long.class == componentType) { }
array = new Long[Array.getLength(pObject)]; else if (long.class == componentType) {
for (int i = 0; i < array.length; i++) { array = new Long[Array.getLength(pObject)];
Array.set(array, i, Array.get(pObject, i)); for (int i = 0; i < array.length; i++) {
} Array.set(array, i, Array.get(pObject, i));
} }
else if (float.class == componentType) { }
array = new Float[Array.getLength(pObject)]; else if (float.class == componentType) {
for (int i = 0; i < array.length; i++) { array = new Float[Array.getLength(pObject)];
Array.set(array, i, Array.get(pObject, i)); for (int i = 0; i < array.length; i++) {
} Array.set(array, i, Array.get(pObject, i));
} }
else if (double.class == componentType) { }
array = new Double[Array.getLength(pObject)]; else if (double.class == componentType) {
for (int i = 0; i < array.length; i++) { array = new Double[Array.getLength(pObject)];
Array.set(array, i, Array.get(pObject, i)); for (int i = 0; i < array.length; i++) {
} Array.set(array, i, Array.get(pObject, i));
} }
else if (boolean.class == componentType) { }
array = new Boolean[Array.getLength(pObject)]; else if (boolean.class == componentType) {
for (int i = 0; i < array.length; i++) { array = new Boolean[Array.getLength(pObject)];
Array.set(array, i, Array.get(pObject, i)); for (int i = 0; i < array.length; i++) {
} Array.set(array, i, Array.get(pObject, i));
} }
else if (byte.class == componentType) { }
array = new Byte[Array.getLength(pObject)]; else if (byte.class == componentType) {
for (int i = 0; i < array.length; i++) { array = new Byte[Array.getLength(pObject)];
Array.set(array, i, Array.get(pObject, i)); for (int i = 0; i < array.length; i++) {
} Array.set(array, i, Array.get(pObject, i));
} }
else if (char.class == componentType) { }
array = new Character[Array.getLength(pObject)]; else if (char.class == componentType) {
for (int i = 0; i < array.length; i++) { array = new Character[Array.getLength(pObject)];
Array.set(array, i, Array.get(pObject, i)); for (int i = 0; i < array.length; i++) {
} Array.set(array, i, Array.get(pObject, i));
} }
else { }
throw new IllegalArgumentException("Unknown type " + componentType); else {
} throw new IllegalArgumentException("Unknown type " + componentType);
} }
else { }
array = (Object[]) pObject; else {
} array = (Object[]) pObject;
return array; }
} return array;
}
private Class<?> unBoxType(final Class<?> pType) {
if (pType.isPrimitive()) { private Class<?> unBoxType(final Class<?> pType) {
if (pType == boolean.class) { if (pType.isPrimitive()) {
return Boolean.class; if (pType == boolean.class) {
} return Boolean.class;
if (pType == byte.class) { }
return Byte.class; if (pType == byte.class) {
} return Byte.class;
if (pType == char.class) { }
return Character.class; if (pType == char.class) {
} return Character.class;
if (pType == short.class) { }
return Short.class; if (pType == short.class) {
} return Short.class;
if (pType == int.class) { }
return Integer.class; if (pType == int.class) {
} return Integer.class;
if (pType == float.class) { }
return Float.class; if (pType == float.class) {
} return Float.class;
if (pType == long.class) { }
return Long.class; if (pType == long.class) {
} return Long.class;
if (pType == double.class) { }
return Double.class; if (pType == double.class) {
} return Double.class;
}
throw new IllegalArgumentException("Unknown type: " + pType);
} throw new IllegalArgumentException("Unknown type: " + pType);
}
return pType;
} return pType;
} }
}

View File

@@ -1,210 +1,211 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util.convert; package com.twelvemonkeys.util.convert;
import com.twelvemonkeys.lang.BeanUtil; import com.twelvemonkeys.lang.BeanUtil;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.util.LRUHashMap; import com.twelvemonkeys.util.LRUHashMap;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.BigInteger; import java.math.BigInteger;
import java.text.*; import java.text.*;
import java.util.Arrays; import java.util.Arrays;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
/** /**
* Converts strings to numbers and back. * Converts strings to numbers and back.
* <p/> * <p>
* <small>This class has a static cache of {@code NumberFormats}, to avoid * <small>This class has a static cache of {@code NumberFormats}, to avoid
* creation and parsing of number formats every time one is used.</small> * creation and parsing of number formats every time one is used.</small>
* * </p>
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> *
* @author last modified by $Author: haku $ * @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/util/convert/NumberConverter.java#2 $ * @author last modified by $Author: haku $
*/ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/NumberConverter.java#2 $
public class NumberConverter implements PropertyConverter { */
// TODO: Need to either make this non-locale aware, or document that it is... public class NumberConverter implements PropertyConverter {
// TODO: Need to either make this non-locale aware, or document that it is...
private static final DecimalFormatSymbols SYMBOLS = new DecimalFormatSymbols(Locale.US);
private static final NumberFormat sDefaultFormat = new DecimalFormat("#0.#", SYMBOLS); private static final DecimalFormatSymbols SYMBOLS = new DecimalFormatSymbols(Locale.US);
private static final Map<String, Format> sFormats = new LRUHashMap<String, Format>(50); private static final NumberFormat sDefaultFormat = new DecimalFormat("#0.#", SYMBOLS);
private static final Map<String, Format> sFormats = new LRUHashMap<String, Format>(50);
public NumberConverter() {
} public NumberConverter() {
}
/**
* Converts the string to a number, using the given format for parsing. /**
* * Converts the string to a number, using the given format for parsing.
* @param pString the string to convert. *
* @param pType the type to convert to. PropertyConverter * @param pString the string to convert.
* implementations may choose to ignore this parameter. * @param pType the type to convert to. PropertyConverter
* @param pFormat the format used for parsing. PropertyConverter * implementations may choose to ignore this parameter.
* implementations may choose to ignore this parameter. Also, * @param pFormat the format used for parsing. PropertyConverter
* implementations that require a parser format, should provide a default * implementations may choose to ignore this parameter. Also,
* format, and allow {@code null} as the format argument. * implementations that require a parser format, should provide a default
* * format, and allow {@code null} as the format argument.
* @return the object created from the given string. May safely be typecast *
* to {@code java.lang.Number} or the class of the {@code type} parameter. * @return the object created from the given string. May safely be typecast
* * to {@code java.lang.Number} or the class of the {@code type} parameter.
* @see Number *
* @see java.text.NumberFormat * @see Number
* * @see java.text.NumberFormat
* @throws ConversionException *
*/ * @throws ConversionException
public Object toObject(final String pString, final Class pType, final String pFormat) throws ConversionException { */
if (StringUtil.isEmpty(pString)) { public Object toObject(final String pString, final Class pType, final String pFormat) throws ConversionException {
return null; if (StringUtil.isEmpty(pString)) {
} return null;
}
try {
if (pType.equals(BigInteger.class)) { try {
return new BigInteger(pString); // No format? if (pType.equals(BigInteger.class)) {
} return new BigInteger(pString); // No format?
if (pType.equals(BigDecimal.class)) { }
return new BigDecimal(pString); // No format? if (pType.equals(BigDecimal.class)) {
} return new BigDecimal(pString); // No format?
}
NumberFormat format;
NumberFormat format;
if (pFormat == null) {
// Use system default format, using default locale if (pFormat == null) {
format = sDefaultFormat; // Use system default format, using default locale
} format = sDefaultFormat;
else { }
// Get format from cache else {
format = getNumberFormat(pFormat); // Get format from cache
} format = getNumberFormat(pFormat);
}
Number num;
synchronized (format) { Number num;
num = format.parse(pString); synchronized (format) {
} num = format.parse(pString);
}
if (pType == Integer.TYPE || pType == Integer.class) {
return num.intValue(); if (pType == Integer.TYPE || pType == Integer.class) {
} return num.intValue();
else if (pType == Long.TYPE || pType == Long.class) { }
return num.longValue(); else if (pType == Long.TYPE || pType == Long.class) {
} return num.longValue();
else if (pType == Double.TYPE || pType == Double.class) { }
return num.doubleValue(); else if (pType == Double.TYPE || pType == Double.class) {
} return num.doubleValue();
else if (pType == Float.TYPE || pType == Float.class) { }
return num.floatValue(); else if (pType == Float.TYPE || pType == Float.class) {
} return num.floatValue();
else if (pType == Byte.TYPE || pType == Byte.class) { }
return num.byteValue(); else if (pType == Byte.TYPE || pType == Byte.class) {
} return num.byteValue();
else if (pType == Short.TYPE || pType == Short.class) { }
return num.shortValue(); else if (pType == Short.TYPE || pType == Short.class) {
} return num.shortValue();
}
return num;
} return num;
catch (ParseException pe) { }
throw new ConversionException(pe); catch (ParseException pe) {
} throw new ConversionException(pe);
catch (RuntimeException rte) { }
throw new ConversionException(rte); catch (RuntimeException rte) {
} throw new ConversionException(rte);
} }
}
/**
* Converts the object to a string, using the given format /**
* * Converts the object to a string, using the given format
* @param pObject the object to convert. *
* @param pFormat the format used for parsing. PropertyConverter * @param pObject the object to convert.
* implementations may choose to ignore this parameter. Also, * @param pFormat the format used for parsing. PropertyConverter
* implementations that require a parser format, should provide a default * implementations may choose to ignore this parameter. Also,
* format, and allow {@code null} as the format argument. * implementations that require a parser format, should provide a default
* * format, and allow {@code null} as the format argument.
* @return the string representation of the object, on the correct format. *
* * @return the string representation of the object, on the correct format.
* @throws ConversionException if the object is not a subclass of {@link java.lang.Number} *
*/ * @throws ConversionException if the object is not a subclass of {@link java.lang.Number}
public String toString(final Object pObject, final String pFormat) */
throws ConversionException { public String toString(final Object pObject, final String pFormat)
throws ConversionException {
if (pObject == null) {
return null; if (pObject == null) {
} return null;
}
if (!(pObject instanceof Number)) {
throw new TypeMismathException(pObject.getClass()); if (!(pObject instanceof Number)) {
} throw new TypeMismathException(pObject.getClass());
}
try {
// Convert to string, default way try {
if (StringUtil.isEmpty(pFormat)) { // Convert to string, default way
return sDefaultFormat.format(pObject); if (StringUtil.isEmpty(pFormat)) {
} return sDefaultFormat.format(pObject);
}
// Convert to string, using format
NumberFormat format = getNumberFormat(pFormat); // Convert to string, using format
NumberFormat format = getNumberFormat(pFormat);
synchronized (format) {
return format.format(pObject); synchronized (format) {
} return format.format(pObject);
} }
catch (RuntimeException rte) { }
throw new ConversionException(rte); catch (RuntimeException rte) {
} throw new ConversionException(rte);
} }
}
private NumberFormat getNumberFormat(String pFormat) {
return (NumberFormat) getFormat(DecimalFormat.class, pFormat, SYMBOLS); private NumberFormat getNumberFormat(String pFormat) {
} return (NumberFormat) getFormat(DecimalFormat.class, pFormat, SYMBOLS);
}
protected final Format getFormat(Class pFormatterClass, Object... pFormat) {
// Try to get format from cache protected final Format getFormat(Class pFormatterClass, Object... pFormat) {
synchronized (sFormats) { // Try to get format from cache
String key = pFormatterClass.getName() + ":" + Arrays.toString(pFormat); synchronized (sFormats) {
Format format = sFormats.get(key); String key = pFormatterClass.getName() + ":" + Arrays.toString(pFormat);
Format format = sFormats.get(key);
if (format == null) {
// If not found, create... if (format == null) {
try { // If not found, create...
format = (Format) BeanUtil.createInstance(pFormatterClass, pFormat); try {
} format = (Format) BeanUtil.createInstance(pFormatterClass, pFormat);
catch (Exception e) { }
e.printStackTrace(); catch (Exception e) {
return null; e.printStackTrace();
} return null;
}
// ...and store in cache
sFormats.put(key, format); // ...and store in cache
} sFormats.put(key, format);
}
return format;
} return format;
} }
} }
}

View File

@@ -1,138 +1,139 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util.convert; package com.twelvemonkeys.util.convert;
import com.twelvemonkeys.lang.StringUtil; import com.twelvemonkeys.lang.StringUtil;
import com.twelvemonkeys.util.Time; import com.twelvemonkeys.util.Time;
import com.twelvemonkeys.util.TimeFormat; import com.twelvemonkeys.util.TimeFormat;
/** /**
* Converts strings to times and back. * Converts strings to times and back.
* <p/> * <p>
* <small>This class has a static cache of {@code TimeFormats}, to avoid creation and * <small>This class has a static cache of {@code TimeFormats}, to avoid creation and
* parsing of timeformats every time one is used.</small> * parsing of timeformats every time one is used.</small>
* * </p>
* @author <a href="mailto:harald.kuhr@gmail.com">Harald Kuhr</a> *
* @author last modified by $Author: haku $ * @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/util/convert/TimeConverter.java#1 $ * @author last modified by $Author: haku $
*/ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/convert/TimeConverter.java#1 $
public class TimeConverter extends NumberConverter { */
public class TimeConverter extends NumberConverter {
public TimeConverter() {
} public TimeConverter() {
}
/**
* Converts the string to a time, using the given format for parsing. /**
* * Converts the string to a time, using the given format for parsing.
* @param pString the string to convert. *
* @param pType the type to convert to. PropertyConverter * @param pString the string to convert.
* implementations may choose to ignore this parameter. * @param pType the type to convert to. PropertyConverter
* @param pFormat the format used for parsing. PropertyConverter * implementations may choose to ignore this parameter.
* implementations may choose to ignore this parameter. Also, * @param pFormat the format used for parsing. PropertyConverter
* implementations that require a parser format, should provide a default * implementations may choose to ignore this parameter. Also,
* format, and allow {@code null} as the format argument. * implementations that require a parser format, should provide a default
* * format, and allow {@code null} as the format argument.
* @return the object created from the given string. May safely be typecast *
* to {@code com.twelvemonkeys.util.Time} * @return the object created from the given string. May safely be typecast
* * to {@code com.twelvemonkeys.util.Time}
* @see com.twelvemonkeys.util.Time *
* @see com.twelvemonkeys.util.TimeFormat * @see com.twelvemonkeys.util.Time
* * @see com.twelvemonkeys.util.TimeFormat
* @throws ConversionException *
*/ * @throws ConversionException
public Object toObject(String pString, Class pType, String pFormat) */
throws ConversionException { public Object toObject(String pString, Class pType, String pFormat)
if (StringUtil.isEmpty(pString)) throws ConversionException {
return null; if (StringUtil.isEmpty(pString))
return null;
TimeFormat format;
TimeFormat format;
try {
if (pFormat == null) { try {
// Use system default format if (pFormat == null) {
format = TimeFormat.getInstance(); // Use system default format
} format = TimeFormat.getInstance();
else { }
// Get format from cache else {
format = getTimeFormat(pFormat); // Get format from cache
} format = getTimeFormat(pFormat);
}
return format.parse(pString);
} return format.parse(pString);
catch (RuntimeException rte) { }
throw new ConversionException(rte); catch (RuntimeException rte) {
} throw new ConversionException(rte);
}
}
}
/**
* Converts the object to a string, using the given format /**
* * Converts the object to a string, using the given format
* @param pObject the object to convert. *
* @param pFormat the format used for parsing. PropertyConverter * @param pObject the object to convert.
* implementations may choose to ignore this parameter. Also, * @param pFormat the format used for parsing. PropertyConverter
* implementations that require a parser format, should provide a default * implementations may choose to ignore this parameter. Also,
* format, and allow {@code null} as the format argument. * implementations that require a parser format, should provide a default
* * format, and allow {@code null} as the format argument.
* @return the string representation of the object, on the correct format. *
* * @return the string representation of the object, on the correct format.
* @throws ConversionException if the object is not a subclass of *
* {@code com.twelvemonkeys.util.Time} * @throws ConversionException if the object is not a subclass of
* * {@code com.twelvemonkeys.util.Time}
* @see com.twelvemonkeys.util.Time *
* @see com.twelvemonkeys.util.TimeFormat * @see com.twelvemonkeys.util.Time
*/ * @see com.twelvemonkeys.util.TimeFormat
public String toString(Object pObject, String pFormat) */
throws ConversionException { public String toString(Object pObject, String pFormat)
if (pObject == null) throws ConversionException {
return null; if (pObject == null)
return null;
if (!(pObject instanceof com.twelvemonkeys.util.Time))
throw new TypeMismathException(pObject.getClass()); if (!(pObject instanceof com.twelvemonkeys.util.Time))
throw new TypeMismathException(pObject.getClass());
try {
// Convert to string, default way try {
if (StringUtil.isEmpty(pFormat)) // Convert to string, default way
return pObject.toString(); if (StringUtil.isEmpty(pFormat))
return pObject.toString();
// Convert to string, using format
TimeFormat format = getTimeFormat(pFormat); // Convert to string, using format
return format.format((Time) pObject); TimeFormat format = getTimeFormat(pFormat);
} return format.format((Time) pObject);
catch (RuntimeException rte) { }
throw new ConversionException(rte); catch (RuntimeException rte) {
} throw new ConversionException(rte);
} }
}
private TimeFormat getTimeFormat(String pFormat) {
return (TimeFormat) getFormat(TimeFormat.class, pFormat); private TimeFormat getTimeFormat(String pFormat) {
} return (TimeFormat) getFormat(TimeFormat.class, pFormat);
} }
}

View File

@@ -1,107 +1,107 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util.regex; package com.twelvemonkeys.util.regex;
import com.twelvemonkeys.util.AbstractTokenIterator; import com.twelvemonkeys.util.AbstractTokenIterator;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException; import java.util.regex.PatternSyntaxException;
/** /**
* {@code StringTokenizer} replacement, that uses regular expressions to split * {@code StringTokenizer} replacement, that uses regular expressions to split
* strings into tokens. * strings into tokens.
* <p/> *
* @see java.util.regex.Pattern for pattern syntax. *@see java.util.regex.Pattern for pattern syntax.
* *
* @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/util/regex/RegExTokenIterator.java#1 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/regex/RegExTokenIterator.java#1 $
*/ */
public class RegExTokenIterator extends AbstractTokenIterator { public class RegExTokenIterator extends AbstractTokenIterator {
private final Matcher matcher; private final Matcher matcher;
private boolean next = false; private boolean next = false;
/** /**
* Creates a {@code RegExTokenIterator}. * Creates a {@code RegExTokenIterator}.
* Default pettern is {@code "\S+"}. * Default pettern is {@code "\S+"}.
* *
* @param pString the string to be parsed. * @param pString the string to be parsed.
* *
* @throws IllegalArgumentException if {@code pString} is {@code null} * @throws IllegalArgumentException if {@code pString} is {@code null}
*/ */
public RegExTokenIterator(String pString) { public RegExTokenIterator(String pString) {
this(pString, "\\S+"); this(pString, "\\S+");
} }
/** /**
* Creates a {@code RegExTokenIterator}. * Creates a {@code RegExTokenIterator}.
* *
* @see Pattern for pattern syntax. * @see Pattern for pattern syntax.
* *
* @param pString the string to be parsed. * @param pString the string to be parsed.
* @param pPattern the pattern * @param pPattern the pattern
* *
* @throws PatternSyntaxException if {@code pPattern} is not a valid pattern * @throws PatternSyntaxException if {@code pPattern} is not a valid pattern
* @throws IllegalArgumentException if any of the arguments are {@code null} * @throws IllegalArgumentException if any of the arguments are {@code null}
*/ */
public RegExTokenIterator(String pString, String pPattern) { public RegExTokenIterator(String pString, String pPattern) {
if (pString == null) { if (pString == null) {
throw new IllegalArgumentException("string == null"); throw new IllegalArgumentException("string == null");
} }
if (pPattern == null) { if (pPattern == null) {
throw new IllegalArgumentException("pattern == null"); throw new IllegalArgumentException("pattern == null");
} }
matcher = Pattern.compile(pPattern).matcher(pString); matcher = Pattern.compile(pPattern).matcher(pString);
} }
/** /**
* Resets this iterator. * Resets this iterator.
* *
*/ */
public void reset() { public void reset() {
matcher.reset(); matcher.reset();
} }
public boolean hasNext() { public boolean hasNext() {
return next || (next = matcher.find()); return next || (next = matcher.find());
} }
public String next() { public String next() {
if (!hasNext()) { if (!hasNext()) {
throw new NoSuchElementException(); throw new NoSuchElementException();
} }
next = false; next = false;
return matcher.group(); return matcher.group();
} }
} }

View File

@@ -1,63 +1,64 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util.service; package com.twelvemonkeys.util.service;
/** /**
* An optional interface that may be implemented by service provider objects. * An optional interface that may be implemented by service provider objects.
* <p/> * <p>
* If this interface is implemented, the service provider objects will receive * If this interface is implemented, the service provider objects will receive
* notification of registration and deregistration from the * notification of registration and deregistration from the
* {@code ServiceRegistry}. * {@code ServiceRegistry}.
* * </p>
* @see ServiceRegistry *
* * @see ServiceRegistry
* @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/util/service/RegisterableService.java#1 $ * @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/util/service/RegisterableService.java#1 $
public interface RegisterableService { */
/** public interface RegisterableService {
* Called right after this service provider object is added to /**
* the given category of the given {@code ServiceRegistry}. * Called right after this service provider object is added to
* * the given category of the given {@code ServiceRegistry}.
* @param pRegistry the {@code ServiceRegistry} {@code this} was added to *
* @param pCategory the category {@code this} was added to * @param pRegistry the {@code ServiceRegistry} {@code this} was added to
*/ * @param pCategory the category {@code this} was added to
void onRegistration(ServiceRegistry pRegistry, Class pCategory); */
void onRegistration(ServiceRegistry pRegistry, Class pCategory);
/**
* Called right after this service provider object is removed /**
* from the given category of the given {@code ServiceRegistry}. * Called right after this service provider object is removed
* * from the given category of the given {@code ServiceRegistry}.
* @param pRegistry the {@code ServiceRegistry} {@code this} was added to *
* @param pCategory the category {@code this} was added to * @param pRegistry the {@code ServiceRegistry} {@code this} was added to
*/ * @param pCategory the category {@code this} was added to
void onDeregistration(ServiceRegistry pRegistry, Class pCategory); */
} void onDeregistration(ServiceRegistry pRegistry, Class pCategory);
}

View File

@@ -1,54 +1,54 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.util.service; package com.twelvemonkeys.util.service;
/** /**
* Error thrown by the {@code ServiceRegistry} in case of a configuration * Error thrown by the {@code ServiceRegistry} in case of a configuration
* error. * error.
* <p/> *
* @see ServiceRegistry * @see ServiceRegistry
* *
* @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/util/service/ServiceConfigurationError.java#1 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/main/java/com/twelvemonkeys/util/service/ServiceConfigurationError.java#1 $
*/ */
public class ServiceConfigurationError extends Error { public class ServiceConfigurationError extends Error {
ServiceConfigurationError(Throwable pCause) { ServiceConfigurationError(Throwable pCause) {
super(pCause); super(pCause);
} }
ServiceConfigurationError(String pMessage) { ServiceConfigurationError(String pMessage) {
super(pMessage); super(pMessage);
} }
ServiceConfigurationError(String pMessage, Throwable pCause) { ServiceConfigurationError(String pMessage, Throwable pCause) {
super(pMessage, pCause); super(pMessage, pCause);
} }
} }

View File

@@ -1,39 +1,40 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
/** /**
* Provides a service provider registry. * Provides a service provider registry.
* <p/> * <p>
* This package contains a service provider registry, as specified in the * This package contains a service provider registry, as specified in the
* <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">JAR File Specification</a>. * <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">JAR File Specification</a>.
* * </p>
* @see <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">JAR File Specification</a> *
*/ * @see <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#Service%20Provider">JAR File Specification</a>
package com.twelvemonkeys.util.service; */
package com.twelvemonkeys.util.service;

View File

@@ -36,13 +36,12 @@ import java.util.Map;
/** /**
* BeanMapTestCase * BeanMapTestCase
* <p/>
* @todo Extend with BeanMap specific tests
* *
* @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/test/java/com/twelvemonkeys/util/BeanMapTestCase.java#2 $ * @version $Id: //depot/branches/personal/haraldk/twelvemonkeys/release-2/twelvemonkeys-core/src/test/java/com/twelvemonkeys/util/BeanMapTestCase.java#2 $
*/ */
// TODO: Extend with BeanMap specific tests
public class BeanMapTest extends MapAbstractTest { public class BeanMapTest extends MapAbstractTest {
public boolean isPutAddSupported() { public boolean isPutAddSupported() {

View File

@@ -1,188 +1,187 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.imageio.plugins.svg; package com.twelvemonkeys.imageio.plugins.svg;
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase; import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
import com.twelvemonkeys.lang.SystemUtil; import com.twelvemonkeys.lang.SystemUtil;
import javax.imageio.ImageReader; import javax.imageio.ImageReader;
import javax.imageio.spi.ServiceRegistry; import javax.imageio.spi.ServiceRegistry;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import java.io.EOFException; import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.util.Locale; import java.util.Locale;
import static com.twelvemonkeys.imageio.util.IIOUtil.deregisterProvider; import static com.twelvemonkeys.imageio.util.IIOUtil.deregisterProvider;
/** /**
* SVGImageReaderSpi * SVGImageReaderSpi
* <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: SVGImageReaderSpi.java,v 1.1 2003/12/02 16:45:00 haku Exp $
* @version $Id: SVGImageReaderSpi.java,v 1.1 2003/12/02 16:45:00 haku Exp $ */
*/ public final class SVGImageReaderSpi extends ImageReaderSpiBase {
public final class SVGImageReaderSpi extends ImageReaderSpiBase {
final static boolean SVG_READER_AVAILABLE = SystemUtil.isClassAvailable("com.twelvemonkeys.imageio.plugins.svg.SVGImageReader", SVGImageReaderSpi.class);
final static boolean SVG_READER_AVAILABLE = SystemUtil.isClassAvailable("com.twelvemonkeys.imageio.plugins.svg.SVGImageReader", SVGImageReaderSpi.class);
/**
/** * Creates an {@code SVGImageReaderSpi}.
* Creates an {@code SVGImageReaderSpi}. */
*/ @SuppressWarnings("WeakerAccess")
@SuppressWarnings("WeakerAccess") public SVGImageReaderSpi() {
public SVGImageReaderSpi() { super(new SVGProviderInfo());
super(new SVGProviderInfo()); }
}
public boolean canDecodeInput(final Object pSource) throws IOException {
public boolean canDecodeInput(final Object pSource) throws IOException { return pSource instanceof ImageInputStream && canDecode((ImageInputStream) pSource);
return pSource instanceof ImageInputStream && canDecode((ImageInputStream) pSource); }
}
@SuppressWarnings("StatementWithEmptyBody")
@SuppressWarnings("StatementWithEmptyBody") private static boolean canDecode(final ImageInputStream pInput) throws IOException {
private static boolean canDecode(final ImageInputStream pInput) throws IOException { // NOTE: This test is quite quick as it does not involve any parsing,
// NOTE: This test is quite quick as it does not involve any parsing, // however it may not recognize all kinds of SVG documents.
// however it may not recognize all kinds of SVG documents. try {
try { pInput.mark();
pInput.mark();
// TODO: This is not ok for UTF-16 and other wide encodings
// TODO: This is not ok for UTF-16 and other wide encodings // TODO: Use an XML (encoding) aware Reader instance instead
// TODO: Use an XML (encoding) aware Reader instance instead // Need to figure out pretty fast if this is XML or not
// Need to figure out pretty fast if this is XML or not int b;
int b; while (Character.isWhitespace((char) (b = pInput.read()))) {
while (Character.isWhitespace((char) (b = pInput.read()))) { // Skip over leading WS
// Skip over leading WS }
}
// If it's not a tag, this can't be valid XML
// If it's not a tag, this can't be valid XML if (b != '<') {
if (b != '<') { return false;
return false; }
}
// Algorithm for detecting SVG:
// Algorithm for detecting SVG: // - Skip until begin tag '<' and read 4 bytes
// - Skip until begin tag '<' and read 4 bytes // - if next is "?" skip until "?>" and start over
// - if next is "?" skip until "?>" and start over // - else if next is "!--" skip until "-->" and start over
// - else if next is "!--" skip until "-->" and start over // - else if next is "!DOCTYPE " skip any whitespace
// - else if next is "!DOCTYPE " skip any whitespace // - compare next 3 bytes against "svg", return result
// - compare next 3 bytes against "svg", return result // - else
// - else // - compare next 3 bytes against "svg", return result
// - compare next 3 bytes against "svg", return result
byte[] buffer = new byte[4];
byte[] buffer = new byte[4]; while (true) {
while (true) { pInput.readFully(buffer);
pInput.readFully(buffer);
if (buffer[0] == '?') {
if (buffer[0] == '?') { // This is the XML declaration or a processing instruction
// This is the XML declaration or a processing instruction while (!((pInput.readByte() & 0xFF) == '?' && pInput.read() == '>')) {
while (!((pInput.readByte() & 0xFF) == '?' && pInput.read() == '>')) { // Skip until end of XML declaration or processing instruction or EOF
// Skip until end of XML declaration or processing instruction or EOF }
} }
} else if (buffer[0] == '!') {
else if (buffer[0] == '!') { if (buffer[1] == '-' && buffer[2] == '-') {
if (buffer[1] == '-' && buffer[2] == '-') { // This is a comment
// This is a comment while (!((pInput.readByte() & 0xFF) == '-' && pInput.read() == '-' && pInput.read() == '>')) {
while (!((pInput.readByte() & 0xFF) == '-' && pInput.read() == '-' && pInput.read() == '>')) { // Skip until end of comment or EOF
// Skip until end of comment or EOF }
} }
} else if (buffer[1] == 'D' && buffer[2] == 'O' && buffer[3] == 'C'
else if (buffer[1] == 'D' && buffer[2] == 'O' && buffer[3] == 'C' && pInput.read() == 'T' && pInput.read() == 'Y'
&& pInput.read() == 'T' && pInput.read() == 'Y' && pInput.read() == 'P' && pInput.read() == 'E') {
&& pInput.read() == 'P' && pInput.read() == 'E') { // This is the DOCTYPE declaration
// This is the DOCTYPE declaration while (Character.isWhitespace((char) (b = pInput.read()))) {
while (Character.isWhitespace((char) (b = pInput.read()))) { // Skip over WS
// Skip over WS }
}
if (b == 's' && pInput.read() == 'v' && pInput.read() == 'g') {
if (b == 's' && pInput.read() == 'v' && pInput.read() == 'g') { // It's SVG, identified by DOCTYPE
// It's SVG, identified by DOCTYPE return true;
return true; }
}
// DOCTYPE found, but not SVG
// DOCTYPE found, but not SVG return false;
return false; }
}
// Something else, we'll skip
// Something else, we'll skip }
} else {
else { // This is a normal tag
// This is a normal tag if (buffer[0] == 's' && buffer[1] == 'v' && buffer[2] == 'g'
if (buffer[0] == 's' && buffer[1] == 'v' && buffer[2] == 'g' && (Character.isWhitespace((char) buffer[3]) || buffer[3] == ':')) {
&& (Character.isWhitespace((char) buffer[3]) || buffer[3] == ':')) { // It's SVG, identified by root tag
// It's SVG, identified by root tag // TODO: Support svg with prefix + recognize namespace (http://www.w3.org/2000/svg)!
// TODO: Support svg with prefix + recognize namespace (http://www.w3.org/2000/svg)! return true;
return true; }
}
// If the tag is not "svg", this isn't SVG
// If the tag is not "svg", this isn't SVG return false;
return false; }
}
while ((pInput.readByte() & 0xFF) != '<') {
while ((pInput.readByte() & 0xFF) != '<') { // Skip over, until next begin tag or EOF
// Skip over, until next begin tag or EOF }
} }
} }
} catch (EOFException ignore) {
catch (EOFException ignore) { // Possible for small files...
// Possible for small files... return false;
return false; }
} finally {
finally { //noinspection ThrowFromFinallyBlock
//noinspection ThrowFromFinallyBlock pInput.reset();
pInput.reset(); }
} }
}
public ImageReader createReaderInstance(final Object extension) throws IOException {
public ImageReader createReaderInstance(final Object extension) throws IOException { return new SVGImageReader(this);
return new SVGImageReader(this); }
}
public String getDescription(final Locale locale) {
public String getDescription(final Locale locale) { return "Scalable Vector Graphics (SVG) format image reader";
return "Scalable Vector Graphics (SVG) format image reader"; }
}
@SuppressWarnings({"deprecation"})
@SuppressWarnings({"deprecation"}) @Override
@Override public void onRegistration(final ServiceRegistry registry, final Class<?> category) {
public void onRegistration(final ServiceRegistry registry, final Class<?> category) { // TODO: Perhaps just try to create an instance, and de-register if we fail?
// TODO: Perhaps just try to create an instance, and de-register if we fail? if (!SVG_READER_AVAILABLE) {
if (!SVG_READER_AVAILABLE) { System.err.println("Could not instantiate SVGImageReader (missing support classes).");
System.err.println("Could not instantiate SVGImageReader (missing support classes).");
try {
try { // NOTE: This will break, but it gives us some useful debug info
// NOTE: This will break, but it gives us some useful debug info new SVGImageReader(this);
new SVGImageReader(this); }
} catch (Throwable t) {
catch (Throwable t) { t.printStackTrace();
t.printStackTrace(); }
}
deregisterProvider(registry, this, category);
deregisterProvider(registry, this, category); }
} }
} }
}

View File

@@ -1,102 +1,101 @@
/* /*
* Copyright (c) 2008, Harald Kuhr * Copyright (c) 2008, Harald Kuhr
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: * modification, are permitted provided that the following conditions are met:
* *
* * Redistributions of source code must retain the above copyright notice, this * * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* *
* * Redistributions in binary form must reproduce the above copyright notice, * * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* *
* * Neither the name of the copyright holder nor the names of its * * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from * contributors may be used to endorse or promote products derived from
* this software without specific prior written permission. * this software without specific prior written permission.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * 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 * 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. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
package com.twelvemonkeys.imageio.plugins.wmf; package com.twelvemonkeys.imageio.plugins.wmf;
import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase; import com.twelvemonkeys.imageio.spi.ImageReaderSpiBase;
import com.twelvemonkeys.imageio.util.IIOUtil; import com.twelvemonkeys.imageio.util.IIOUtil;
import javax.imageio.ImageReader; import javax.imageio.ImageReader;
import javax.imageio.spi.ServiceRegistry; import javax.imageio.spi.ServiceRegistry;
import javax.imageio.stream.ImageInputStream; import javax.imageio.stream.ImageInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Locale; import java.util.Locale;
import static com.twelvemonkeys.imageio.plugins.wmf.WMFProviderInfo.WMF_READER_AVAILABLE; import static com.twelvemonkeys.imageio.plugins.wmf.WMFProviderInfo.WMF_READER_AVAILABLE;
/** /**
* WMFImageReaderSpi * WMFImageReaderSpi
* <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: WMFImageReaderSpi.java,v 1.1 2003/12/02 16:45:00 wmhakur Exp $
* @version $Id: WMFImageReaderSpi.java,v 1.1 2003/12/02 16:45:00 wmhakur Exp $ */
*/ public final class WMFImageReaderSpi extends ImageReaderSpiBase {
public final class WMFImageReaderSpi extends ImageReaderSpiBase {
/**
/** * Creates a {@code WMFImageReaderSpi}.
* Creates a {@code WMFImageReaderSpi}. */
*/ public WMFImageReaderSpi() {
public WMFImageReaderSpi() { super(new WMFProviderInfo());
super(new WMFProviderInfo()); }
}
public boolean canDecodeInput(final Object source) throws IOException {
public boolean canDecodeInput(final Object source) throws IOException { return source instanceof ImageInputStream && WMF_READER_AVAILABLE && canDecode((ImageInputStream) source);
return source instanceof ImageInputStream && WMF_READER_AVAILABLE && canDecode((ImageInputStream) source); }
}
public static boolean canDecode(final ImageInputStream pInput) throws IOException {
public static boolean canDecode(final ImageInputStream pInput) throws IOException { if (pInput == null) {
if (pInput == null) { throw new IllegalArgumentException("input == null");
throw new IllegalArgumentException("input == null"); }
}
try {
try { pInput.mark();
pInput.mark();
for (byte header : WMF.HEADER) {
for (byte header : WMF.HEADER) { int read = (byte) pInput.read();
int read = (byte) pInput.read(); if (header != read) {
if (header != read) { return false;
return false; }
} }
} return true;
return true;
}
} finally {
finally { pInput.reset();
pInput.reset(); }
} }
}
public ImageReader createReaderInstance(final Object extension) throws IOException {
public ImageReader createReaderInstance(final Object extension) throws IOException { return new WMFImageReader(this);
return new WMFImageReader(this); }
}
public String getDescription(final Locale locale) {
public String getDescription(final Locale locale) { return "Windows Meta File (WMF) image reader";
return "Windows Meta File (WMF) image reader"; }
}
@SuppressWarnings({"deprecation"})
@SuppressWarnings({"deprecation"}) @Override
@Override public void onRegistration(final ServiceRegistry registry, final Class<?> category) {
public void onRegistration(final ServiceRegistry registry, final Class<?> category) { if (!WMF_READER_AVAILABLE) {
if (!WMF_READER_AVAILABLE) { IIOUtil.deregisterProvider(registry, this, category);
IIOUtil.deregisterProvider(registry, this, category); }
} }
} }
}

View File

@@ -40,7 +40,6 @@ import java.util.Arrays;
/** /**
* Abstract base class for RLE decoding as specified by in the Windows BMP (aka DIB) file format. * Abstract base class for RLE decoding as specified by in the Windows BMP (aka DIB) file format.
* <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: AbstractRLEDecoder.java#1 $ * @version $Id: AbstractRLEDecoder.java#1 $

View File

@@ -231,10 +231,11 @@ abstract class DIBHeader {
/** /**
* OS/2 BitmapCoreHeader Version 2. * OS/2 BitmapCoreHeader Version 2.
* <p/> * <p>
* NOTE: According to the docs this header is <em>variable size</em>. * NOTE: According to the docs this header is <em>variable size</em>.
* However, it seems that the size is either 16, 40 or 64, which is covered * However, it seems that the size is either 16, 40 or 64, which is covered
* (40 is the size of the normal {@link BitmapInfoHeader}, and has the same layout). * (40 is the size of the normal {@link BitmapInfoHeader}, and has the same layout).
* </p>
* *
* @see <a href="http://www.fileformat.info/format/os2bmp/egff.htm">OS/2 Bitmap File Format Summary</a> * @see <a href="http://www.fileformat.info/format/os2bmp/egff.htm">OS/2 Bitmap File Format Summary</a>
*/ */
@@ -290,9 +291,10 @@ abstract class DIBHeader {
* Represents the DIB (Device Independent Bitmap) Windows 3.0 Bitmap Information header structure. * Represents the DIB (Device Independent Bitmap) Windows 3.0 Bitmap Information header structure.
* This is the common format for persistent DIB structures, even if Windows * This is the common format for persistent DIB structures, even if Windows
* may use the later versions at run-time. * may use the later versions at run-time.
* <p/> * <p>
* Note: Some variations that includes the mask fields into the header size exists, * Note: Some variations that includes the mask fields into the header size exists,
* but is no longer part of the documented spec. * but is no longer part of the documented spec.
* </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: DIBHeader.java,v 1.0 25.feb.2006 00:29:44 haku Exp$ * @version $Id: DIBHeader.java,v 1.0 25.feb.2006 00:29:44 haku Exp$

View File

@@ -48,14 +48,13 @@ import java.awt.image.*;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.*;
import java.util.List; import java.util.List;
import java.util.*;
/** /**
* ImageReader for Microsoft Windows ICO (icon) format. * ImageReader for Microsoft Windows ICO (icon) format.
* 1, 4, 8 bit palette support with bitmask transparency, and 16, 24 and 32 bit * 1, 4, 8 bit palette support with bitmask transparency, and 16, 24 and 32 bit
* true color support with alpha. Also supports Windows Vista PNG encoded icons. * true color support with alpha. Also supports Windows Vista PNG encoded icons.
* <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: ICOImageReader.java,v 1.0 25.feb.2006 00:29:44 haku Exp$ * @version $Id: ICOImageReader.java,v 1.0 25.feb.2006 00:29:44 haku Exp$

View File

@@ -37,7 +37,6 @@ import java.util.List;
/** /**
* Directory * Directory
* <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: Directory.java,v 1.0 25.feb.2006 00:29:44 haku Exp$ * @version $Id: Directory.java,v 1.0 25.feb.2006 00:29:44 haku Exp$

View File

@@ -36,7 +36,6 @@ import java.util.Arrays;
/** /**
* Implements 4 bit RLE decoding as specified by in the Windows BMP (aka DIB) file format. * Implements 4 bit RLE decoding as specified by in the Windows BMP (aka DIB) file format.
* <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: RLE4Decoder.java#1 $ * @version $Id: RLE4Decoder.java#1 $

View File

@@ -36,7 +36,6 @@ import java.util.Arrays;
/** /**
* Implements 8 bit RLE decoding as specified by in the Windows BMP (aka DIB) file format. * Implements 8 bit RLE decoding as specified by in the Windows BMP (aka DIB) file format.
* <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: RLE8Decoder.java#1 $ * @version $Id: RLE8Decoder.java#1 $

View File

@@ -72,12 +72,13 @@ public abstract class ImageReaderBase extends ImageReader {
/** /**
* Constructs an {@code ImageReader} and sets its * Constructs an {@code ImageReader} and sets its
* {@code originatingProvider} field to the supplied value. * {@code originatingProvider} field to the supplied value.
* <p/> * <p>
* <p> Subclasses that make use of extensions should provide a * Subclasses that make use of extensions should provide a
* constructor with signature {@code (ImageReaderSpi, * constructor with signature {@code (ImageReaderSpi,
* Object)} in order to retrieve the extension object. If * Object)} in order to retrieve the extension object. If
* the extension object is unsuitable, an * the extension object is unsuitable, an
* {@code IllegalArgumentException} should be thrown. * {@code IllegalArgumentException} should be thrown.
* </p>
* *
* @param provider the {@code ImageReaderSpi} that is invoking this constructor, or {@code null}. * @param provider the {@code ImageReaderSpi} that is invoking this constructor, or {@code null}.
*/ */
@@ -205,9 +206,10 @@ public abstract class ImageReaderBase extends ImageReader {
/** /**
* Returns the {@code BufferedImage} to which decoded pixel data should be written. * Returns the {@code BufferedImage} to which decoded pixel data should be written.
* <p/> * <p>
* As {@link javax.imageio.ImageReader#getDestination} but tests if the explicit destination * As {@link javax.imageio.ImageReader#getDestination} but tests if the explicit destination
* image (if set) is valid according to the {@code ImageTypeSpecifier}s given in {@code types}. * image (if set) is valid according to the {@code ImageTypeSpecifier}s given in {@code types}.
* </p>
* *
* @param param an {@code ImageReadParam} to be used to get * @param param an {@code ImageReadParam} to be used to get
* the destination image or image type, or {@code null}. * the destination image or image type, or {@code null}.
@@ -328,9 +330,10 @@ public abstract class ImageReaderBase extends ImageReader {
* Utility method for getting the area of interest (AOI) of an image. * Utility method for getting the area of interest (AOI) of an image.
* The AOI is defined by the {@link javax.imageio.IIOParam#setSourceRegion(java.awt.Rectangle)} * The AOI is defined by the {@link javax.imageio.IIOParam#setSourceRegion(java.awt.Rectangle)}
* method. * method.
* <p/> * <p>
* Note: If it is possible for the reader to read the AOI directly, such a * Note: If it is possible for the reader to read the AOI directly, such a
* method should be used instead, for efficiency. * method should be used instead, for efficiency.
* </p>
* *
* @param pImage the image to get AOI from * @param pImage the image to get AOI from
* @param pParam the param optionally specifying the AOI * @param pParam the param optionally specifying the AOI
@@ -348,12 +351,14 @@ public abstract class ImageReaderBase extends ImageReader {
* The subsampling is defined by the * The subsampling is defined by the
* {@link javax.imageio.IIOParam#setSourceSubsampling(int, int, int, int)} * {@link javax.imageio.IIOParam#setSourceSubsampling(int, int, int, int)}
* method. * method.
* <p/> * <p>
* NOTE: This method does not take the subsampling offsets into * NOTE: This method does not take the subsampling offsets into
* consideration. * consideration.
* <p/> * </p>
* <p>
* Note: If it is possible for the reader to subsample directly, such a * Note: If it is possible for the reader to subsample directly, such a
* method should be used instead, for efficiency. * method should be used instead, for efficiency.
* </p>
* *
* @param pImage the image to subsample * @param pImage the image to subsample
* @param pParam the param optionally specifying subsampling * @param pParam the param optionally specifying subsampling

View File

@@ -60,12 +60,13 @@ public abstract class ImageWriterBase extends ImageWriter {
* Constructs an {@code ImageWriter} and sets its * Constructs an {@code ImageWriter} and sets its
* {@code originatingProvider} instance variable to the * {@code originatingProvider} instance variable to the
* supplied value. * supplied value.
* <p/> * <p>
* <p> Subclasses that make use of extensions should provide a * Subclasses that make use of extensions should provide a
* constructor with signature {@code (ImageWriterSpi, * constructor with signature {@code (ImageWriterSpi,
* Object)} in order to retrieve the extension object. If * Object)} in order to retrieve the extension object. If
* the extension object is unsuitable, an * the extension object is unsuitable, an
* {@code IllegalArgumentException} should be thrown. * {@code IllegalArgumentException} should be thrown.
* </p>
* *
* @param provider the {@code ImageWriterSpi} that is constructing this object, or {@code null}. * @param provider the {@code ImageWriterSpi} that is constructing this object, or {@code null}.
*/ */
@@ -145,9 +146,10 @@ public abstract class ImageWriterBase extends ImageWriter {
* Utility method for getting the area of interest (AOI) of an image. * Utility method for getting the area of interest (AOI) of an image.
* The AOI is defined by the {@link javax.imageio.IIOParam#setSourceRegion(java.awt.Rectangle)} * The AOI is defined by the {@link javax.imageio.IIOParam#setSourceRegion(java.awt.Rectangle)}
* method. * method.
* <p/> * <p>
* Note: If it is possible for the writer to write the AOI directly, such a * Note: If it is possible for the writer to write the AOI directly, such a
* method should be used instead, for efficiency. * method should be used instead, for efficiency.
* </p>
* *
* @param pImage the image to get AOI from * @param pImage the image to get AOI from
* @param pParam the param optionally specifying the AOI * @param pParam the param optionally specifying the AOI
@@ -165,12 +167,14 @@ public abstract class ImageWriterBase extends ImageWriter {
* The subsampling is defined by the * The subsampling is defined by the
* {@link javax.imageio.IIOParam#setSourceSubsampling(int, int, int, int)} * {@link javax.imageio.IIOParam#setSourceSubsampling(int, int, int, int)}
* method. * method.
* <p/> * <p>
* NOTE: This method does not take the subsampling offsets into * NOTE: This method does not take the subsampling offsets into
* consideration. * consideration.
* <p/> * </p>
* <p>
* Note: If it is possible for the writer to subsample directly, such a * Note: If it is possible for the writer to subsample directly, such a
* method should be used instead, for efficiency. * method should be used instead, for efficiency.
* </p>
* *
* @param pImage the image to subsample * @param pImage the image to subsample
* @param pParam the param optionally specifying subsampling * @param pParam the param optionally specifying subsampling

View File

@@ -50,10 +50,11 @@ import java.util.Properties;
/** /**
* A helper class for working with ICC color profiles and color spaces. * A helper class for working with ICC color profiles and color spaces.
* <p /> * <p>
* Standard ICC color profiles are read from system-specific locations * Standard ICC color profiles are read from system-specific locations
* for known operating systems. * for known operating systems.
* <p /> * </p>
* <p>
* Color profiles may be configured by placing a property-file * Color profiles may be configured by placing a property-file
* {@code com/twelvemonkeys/imageio/color/icc_profiles.properties} * {@code com/twelvemonkeys/imageio/color/icc_profiles.properties}
* on the classpath, specifying the full path to the profiles. * on the classpath, specifying the full path to the profiles.
@@ -61,8 +62,10 @@ import java.util.Properties;
* can be downloaded from * can be downloaded from
* <a href="http://www.color.org/profiles2.xalter">ICC</a>, * <a href="http://www.color.org/profiles2.xalter">ICC</a>,
* <a href="http://www.adobe.com/downloads/">Adobe</a> or other places. * <a href="http://www.adobe.com/downloads/">Adobe</a> or other places.
* <p /> * * </p>
* <p>
* Example property file: * Example property file:
* </p>
* <pre> * <pre>
* # icc_profiles.properties * # icc_profiles.properties
* ADOBE_RGB_1998=/path/to/Adobe RGB 1998.icc * ADOBE_RGB_1998=/path/to/Adobe RGB 1998.icc
@@ -117,9 +120,10 @@ public final class ColorSpaces {
/** /**
* Creates an ICC color space from the given ICC color profile. * Creates an ICC color space from the given ICC color profile.
* <p /> * <p>
* For standard Java color spaces, the built-in instance is returned. * For standard Java color spaces, the built-in instance is returned.
* Otherwise, color spaces are looked up from cache and created on demand. * Otherwise, color spaces are looked up from cache and created on demand.
* </p>
* *
* @param profile the ICC color profile. May not be {@code null}. * @param profile the ICC color profile. May not be {@code null}.
* @return an ICC color space * @return an ICC color space
@@ -245,11 +249,12 @@ public final class ColorSpaces {
/** /**
* Tests whether an ICC color profile is known to cause problems for {@link java.awt.image.ColorConvertOp}. * Tests whether an ICC color profile is known to cause problems for {@link java.awt.image.ColorConvertOp}.
* <p /> * <p>
* <em> * <em>
* Note that this method only tests if a color conversion using this profile is known to fail. * Note that this method only tests if a color conversion using this profile is known to fail.
* There's no guarantee that the color conversion will succeed even if this method returns {@code false}. * There's no guarantee that the color conversion will succeed even if this method returns {@code false}.
* </em> * </em>
* </p>
* *
* @param profile the ICC color profile. May not be {@code null}. * @param profile the ICC color profile. May not be {@code null}.
* @return {@code true} if known to be offending, {@code false} otherwise * @return {@code true} if known to be offending, {@code false} otherwise
@@ -277,11 +282,12 @@ public final class ColorSpaces {
/** /**
* Tests whether an ICC color profile is valid. * Tests whether an ICC color profile is valid.
* Invalid profiles are known to cause problems for {@link java.awt.image.ColorConvertOp}. * Invalid profiles are known to cause problems for {@link java.awt.image.ColorConvertOp}.
* <p /> * <p>
* <em> * <em>
* Note that this method only tests if a color conversion using this profile is known to fail. * Note that this method only tests if a color conversion using this profile is known to fail.
* There's no guarantee that the color conversion will succeed even if this method returns {@code false}. * There's no guarantee that the color conversion will succeed even if this method returns {@code false}.
* </em> * </em>
* </p>
* *
* @param profile the ICC color profile. May not be {@code null}. * @param profile the ICC color profile. May not be {@code null}.
* @return {@code profile} if valid. * @return {@code profile} if valid.
@@ -298,9 +304,10 @@ public final class ColorSpaces {
/** /**
* Returns the color space specified by the given color space constant. * Returns the color space specified by the given color space constant.
* <p /> * <p>
* For standard Java color spaces, the built-in instance is returned. * For standard Java color spaces, the built-in instance is returned.
* Otherwise, color spaces are looked up from cache and created on demand. * Otherwise, color spaces are looked up from cache and created on demand.
* </p>
* *
* @param colorSpace the color space constant. * @param colorSpace the color space constant.
* @return the {@link ColorSpace} specified by the color space constant. * @return the {@link ColorSpace} specified by the color space constant.

View File

@@ -38,9 +38,10 @@ import java.io.InputStream;
/** /**
* IIOInputStreamAdapter * IIOInputStreamAdapter
* <p/> * <p>
* Note: You should always wrap this stream in a {@code BufferedInputStream}. * Note: You should always wrap this stream in a {@code BufferedInputStream}.
* If not, performance may degrade significantly. * If not, performance may degrade significantly.
* </p>
* *
* @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: haraldk$ * @author last modified by $Author: haraldk$

Some files were not shown because too many files have changed in this diff Show More